Why is transforming vectors different from transforming points?

This caused me a lot of confusion and headaches when I was writing my code.

Seems that if you translate a vector using Geom::Vector3d#transform(transform) or Geom::Transformation#*(vector), you get the equivalent of the matrix operation TV, but if you translate a point using Geom::Point3d#transform(transform) or Geom::Transformation#*(point), you get the equivalent of the matrix operation PT. What is the point of this?

I guess there are none of the method exist in Ruby API that you are referring to. You maybe wanted to mention e.g. Vector3d#transform -instance_method:wink:

Most probably I’m not the best to explain, but there is what I recognised:

If you check both of the Geom::Vector3d and Geom::Point3d Constructor details…

#initialize(x, y, z) ⇒ Geom::Vector3d vs #initialize(x, y, z) ⇒ Geom::Vector3d
x (Numeric)
y (Numeric)
z (Numeric)
#initialize(array3d) ⇒ Geom::Point3d vs #initialize(array3d) ⇒ Geom::Vector3d
array3d (Array(Numeric, Numeric, Numeric))

pt1 = Geom::Point3d.new(100,200,300)
vec1 = Geom::Vector3d.new(100,200,300)
pt2 = [100,200,300] #only the context will tell what it is
vec2 = [100,200,300] #only the context will tell what it is

…you should not surprise about the similarity of matrix operations.

I hope there will come another real expert who will explain better… :innocent:

1 Like

Um, yeah. It was a long night last night fighting with the API… :slight_smile: I’ve updated the question.

The issue is that transformations using vectors are like when multiplying transformation matrix times a vector matrix (TV), but transformations using points are like when multiplying a point by a transformation matrix (PT). I would have expected they would be both like the latter, not different. That, or that the order could be controlled by a clearer idiom, such as to have a Vector3d#*(transformation)/Point3d#*(transformation) for VT/PT and Transformation#*(vector)/Transformation#*(point) for TV/TP and all vector/point transformations through a member function called transform being VT/PT.

I’m not even sure what the point of multiplying a transformation matrix to a vector/point would even get you (but then again, my matrix algebra is pretty rusty).

For future reference and use with the Ruby language:

It is customary to refer to only module or class methods using the :: scope operator as the method prefix. Ie …

… and to use # in place of a dot or scope operator only for instance methods. Ie …

This way we know immediately what kind of method is being discussed. And we can go to the proper section in the documentation for the class to find the method definition. (Class methods are usually listed above instance methods.)

You will see this followed throughout the Ruby Core documentation.

What dezmo is getting at is with the SketchUp API, they wrote many of the methods in the classes to use Arrays, Vectors, Points and Transforms interchangeably. Often a simple literal array is used to represent a vector for a translational transform, and the API methods accept this. Ex:

# Where old_pt is a Geom::Point3d object:
new_pt = old_pt.transform([0,0,23.7])

ADD: The result is a new Geom::Point3d object that is 23.7 inches above (Z-wise) the old one.

This code might clear things up a little (or not).

pt1 = Geom::Point3d.new(100,200,300)
vc1 = Geom::Vector3d.new(100,200,300)
offset = Geom::Point3d.new(10,20,30)

# create a translating Transformation
puts "\nTransformation Matrix"
tr = Geom::Transformation.new(offset)
tr.to_a.each_slice(4) {|a| p a}

# translate the Point3d object by the Transformation and by an Array
puts "\nPoint operations"
puts  pt1.transform(tr)
puts  pt1.transform(offset.to_a)
puts  pt1 + offset.to_a

# The Vector3d class is used to represent vectors in a 3 dimensional space. 
# Vectors in SketchUp have a direction and a length, but not a starting point.
# https://ruby.sketchup.com/Geom/Vector3d.html#%2B-instance_method
# The implication here is that if you translate a vector via the tranform method
# you get back the vector unchanged, but, you can add an array to a vector to
# get the expected result as shown in the last example

# translate the vector3d object by 
puts "\nVector operations"
puts vc1.transform(tr)
puts vc1.transform(offset.to_a)
puts vc1 + offset.to_a

point1 = [10, 0, 0]
point2 = [10, 0, 10]
line1 = [point1, point2]

I supposed line1 will be parallel to Z-axis but was not!!! Finally, I understood Sketchup considers point2 as a vector, not as a point.

1 Like

Yes because a line can be represented as either an Array of a point and a vector, as “default”. (“default” is Not written in documentation, you have to discover it yourself… :wink: )

line1 = [Geom::Point3d.new(10, 0, 0), Geom::Vector3d.new(10, 0, 10)]

Since both variable above (point1 , point2) are currently an Array the context here will interpret the second “point2” as vector. The line will cross a point1 and the direction is a 45° vector.

But a “second form” is as an Array of two points.

line1 = [Geom::Point3d.new(10, 0, 0), Geom::Point3d.new(10, 0, 10)]

Certainly, The line will different: cross a point1 and the direction is vector from point1 to point2 (0,0,10)

If you define more precisely in the begening

point1 = Geom::Point3d.new(10, 0, 0)
point2 = Geom::Point3d.new(10, 0, 10)
line1 = [point1, point2]

Then the expected result is clearer… :wink:

1 Like

Thanks. I’m from a C++ background so this is new to me. I’ll update my posts above.

Oh, that’s an interesting undocumented operation. I thought that you needed to do it this way:

new_pt = old_pt.transform(Geom::Transformation.translation(Geom::Point3d.new(0,0,23.7)))

Can an array be used in all places a Geom::Transformation object is expected? Or is this specific to Geom::Point3d#transform? I’ve noticed that arrays can be used in certain contexts to mean a point or vector, but in others it causes an error, making the use inconsistent, at least (perhaps) at first glance? It’s definitely not fully documented.

I’ve not seen lines yet used in the API. I’ll keep this in mind, even though this isn’t exactly pertaining to my question.

Anyway. Looks like what I’m actually asking is just flying over everyone’s head. I understand these transforms via my matrix algebra that I took many moons ago. To transform a point, you would have the point/vector as a row matrix and multiply it against the transformation matrix. Thus PT, where P is the point in row form, and the T is the transformation matrix. However, matrix multiplication is not transitive, thus PT != TP.

The way the SU lib is designed, Geom::Transformation#* is specified in such a way as to make one think you are multiplying the transformation matrix against the point, which isn’t the case (you’re multiplying the point against the transformation). You are, however, actually multiplying a transformation against a vector, which is a further surprise. Coming from a CS and Math background, I’m surprised at how this API was constructed. Contextually, this API is a bit all over the place.

Now, given what I just said, please reread what I said in my other posts with this as context and maybe you’ll see what I am getting at.

The SketchUp API extends the core Ruby Array class thus …

Class: Array — SketchUp Ruby API Documentation


The SketchUp Array class adds additional methods to the standard Ruby Array class. Specifically, it contains methods allowing an array to behave just as a Geom::Vector3d or Geom::Point3d object (which can be thought of as arrays of 3 coordinate values). Therefore, you can use the Array class in place of a Geom::Point3d or Geom::Vector3d as a way to pass coordinate values.

Okay? Now many of the API method docs will list Point3ds, Vector3ds and Array as options. But it is not a hard and fast rule that the API team will do so in the documentation.(More about that later.)

When we find a method that expects a point or a vector, but will not take an array, we open an issue in the API Issue Tracker and usually it will get fixed.

I can say that it can be used other places …such as:

etc., for Point2d, Vector2d, Vector3d classes.

Similarly, …

… can take 3-element Arrays or Geom::Transformation objects, in the vectors array argument in place of Geom::Vector3d objects.

NOW, all that said, the method docs for all these methods do not specifically mention the variable argument types (ie, overloads.)

I asked that this be done in some way, recently in the official API tracker, but was met with resistance. Ie, they said it way too much work to doc all the overloads. I think I then asked that at least put a note in the overview for the Geom::Transformation class similar to the Overview blurb for Array. Ie, … speaking to the interchangeability between transforms, arrays, vectors and points.

Anyway, I can’t find the exact issue where we discussed this. All I remember was (I think it was about a month ago,) and the idea did not go over well.

They are abstract (ie, no actual class.) The description is in the Geom module Overview.
Don’t overlook that module level. There are some nifty module methods there.

I haven’t talked specific to that as I’ve not run into an issue.

Hmmmm… I think SketchUp transformation matrices may be in column major order.

Seems like a good time for you to install ThomThom’s Transformation Inspector plugin: