What's difference: vx.transform!(tt) vs vx.transform(tt)?



I am studying TIG’s TextureTools code trying to understand how his extension works.

class Shunt, method process contains this:

  tt=Geom::Transformation.new(pts[0], face.normal)
  tr=tx * ty

Can you explain what is difference? What the exclamation means? Is it something with multiplication or power? Aslo what is the tr.

I continues like this:

    tpts << pts[i].transform(tr)
    tpts << uvs[i]

there the transform is used again with the tr argument. It is not clear to me what the argument is, because this looks like the transform method needs the count of pixels in the target image. How does the transform exactly work in this case?

Also is the * operator overloaded function / method for Geom::Transformation.translation? What exactly does it do?


By convention, a method whose name ends in ! modifies an object in place rather than creating a new object with the change. So vx.transform!(tt) transforms vx itself whereas vx.transform(tt) would return a new transformed vector and leave vx unchanged.

SketchUp’s Transformation class overloads the * operator to mean concatenation of transformations. That is, it returns a Transformation that corresponds to applying the two arguments in succession. So, tr is a Transformation that has the same effect as first applying ty and then applying tx.


So what is result of tr=tx * ty is it array of two vectors x,y,z? I cannot imagine how you can concatenate two points or vectors in 3D space.


tx and ty are Transformations, so tr = tx * ty is a Transformation that has the same effect as applying first ty and then tx. If v is a Vector3d, then


produces the same effect on v as



Thank you for explanation.

So this is how I understand it:

tr = creates transformation which contains array (or container) which is filled by the two transformations tx and ty.

This is later used to transform points and UV vectors. The overloaded operator * basically adds transformation to array:

tpts << pts[i].transform(tr)
tpts << uvs[i]

May I ask you what the transformation object contains? It contains only the translated vectors or there are some other important informations which are hidden?

There is also the arguments pts[0] , face.normal of the new operator. I suppose the first argument is origin of the face, but the second face.normal is unknown for me.

I wonder why the reference is so confusing me. I would expect that instead

#initialize ⇒ Geom::Transformation
#initialize(point) ⇒ Geom::Transformation 
#initialize(vector) ⇒ Geom::Transformation 
#initialize(transform) ⇒ Geom::Transformation 
#initialize(array) ⇒ Geom::Transformation 
#initialize(scale) ⇒ Geom::Transformation 
#initialize(origin, zaxis) ⇒ Geom::Transformation 
#initialize(origin, xaxis, yaxis) ⇒ Geom::Transformation 
#initialize(pt, axis, angle) ⇒ Geom::Transformation 
#initialize(xaxis, yaxis, zaxis, origin) ⇒ Geom::Transformation 

should be

Geom::Transformation.new ⇒ Geom::Transformation
Geom::Transformation.new(point) ⇒ 
Geom::Transformation.new(vector) ⇒ 
Geom::Transformation.new(transform) ⇒ 
Geom::Transformation.new(array) ⇒ 
Geom::Transformation.new(scale) ⇒ 
Geom::Transformation.new(origin, zaxis) ⇒ 
Geom::Transformation.new(origin, xaxis, yaxis) ⇒ 
Geom::Transformation.new(pt, axis, angle) ⇒ 
Geom::Transformation.new(xaxis, yaxis, zaxis, origin) ⇒ 

Why the # is present everywhere? Ruby is mysterious for me.


Yes, it is. As Clarke’s Third Law points out: “Any sufficiently advanced technology is indistinguishable from magic.”


You seem not to understand the concept of Geom::Transformation. It is an object that manipulates the x,y,z values of an entity such as a Point3d or Vector3d. It “contains” the information necessary to translate (i.e. move), rotate, or scale the entity. It is not a container in the sense of a place you accumulate data; the code is not adding translated vectors to it. In this specific code snippet, tx is created as a Transformation to move an object by vector vx, ty to move by vector vy, and tr to combine these two movements in one Transformation.

I assume you mean the << operator, not *. On a Ruby Array, << means “append the argument to the end of the Array”. Transformed points are being accumulated in the tpts Array.

Regarding the API documentation, the Ruby community has a convention that when describing a Class, a # in front of a name is used to indicate a method. So

#initialize(…) is shorthand for Geom::Transformation.initialize(…)

The other tidbit of Ruby 101 is that the new operator allocates memory for a new object and then calls the #initialize method to set the initial values of the attributes of the object. The allocation part of “new” is built-in, whereas the programmer of the Class can cause #initialize to do whatever is needed. So, indeed the API could have documented the Transformation constructors the second way you wrote, but the way it is written is technically more correct.

If you are going to delve into extension code, you really should devote some time to studying Ruby. You can do both at the same time if that’s an effective learning pattern for you, but you should have a good Ruby reference at hand to look up basic syntax and concepts.


Last thing.

I checked on wiki for normal and tangent. So the second argument in new - the face.normal, is it something like mid of transformation? IDK how to describe it’s purpose if it is what I think it is… But in Photoshop or similar programs when we do manual transformation of image, we define a point around which the image rotates…

Which means that in this place the vector or point is not changed. Is this what is called normal in 3D transformation?


To be more specific, the bang operator is added if there is a non-mutating alternative. It’s not added for all methods that modifies the object. (A common misconception. All though, the SketchUp API haven’t been fully consistent i that respect. But that’s the convention of the Ruby Core.)


The # is a convention that YARD uses to denote instance methods. #foo represent an instance method while .foorepresent a class method.



Is this what is called normal in 3D transformation?

No, the normal is the direction the face is actually facing in 3D space, it usually has nothing to do with the texture coordinates. I’m not sure what TIGs script is actually doing with it.

Tangents (and BiTangents, which are sometimes incorrectly called BiNormals), are used to align a texture image during the final rendering and are not included in Sketchup, but may be calculated internally by Sketchup for final rendering. I am aware that the same vertex shared by two faces and with the same UVs, may have different Tangents and BiTangents. But in short, you don’t need to be concerned with them unless you are dabbling with OpenGL and writing your own rendering engine.

In TIGs example code, the tpts array is the final point/uv array for the texture mapping.
It may be beneficial for you to examine something simpler, or you could write your own code to simply cylinder-wrap an object with a given texture, as you already know the basics for applying a texture.


One way to think about a Transformation is that it converts a set of x,y,z coordinates in one coordinate system into their equivalent values in a different coordinate system. And a coordinate system is defined by an origin, three unit vectors defining the directions for the x, y, and z axes, and a scale factor. So, the particular Transformation initializer TIG chose to use takes arguments of the new origin and the new z axis direction. The API docs say that the x and y directions are set based on some undocumented logic.

#initialize(origin, zaxis) ⇒ Geom::Transformation
Creates a Transformation where origin is the new origin, and zaxis is the z axis. The x and y axes are determined using an arbitrary axis rule.

origin (Geom::Point3d)
zaxis (Geom::Vector3d)

TIG happened to want the new coordinates to have their z vector perpendicular to the face, so he used the face’s normal vector in the sense that @Centaur documented.


Thank you for all the useful information. It helps me to make general idea how Sketchup Ruby API works.

Actually I just need to create tool to pick texture, temporally save tpts array, then to either use material selected in materials (assumptions: 1) material is using texture 2) the material is not the same as picked) or open dialog to enter the name of material. Then after confirmation and click on different face, the next face will accept new texture settings: the new material with the old texture position and sizing.

This would be useful when you need to set a) the same image with different material settings (e.g. opacity) b) different image of the same dimentions … and in both cases the position and sizing should be same for both materials.


This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.