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 ā¦

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

#initialize(x, y, z) ā Geom::Vector3d vs #initialize(x, y, z) ā Geom::Vector3d Parameters: x (Numeric) y (Numeric) z (Numeric)
OR: #initialize(array3d) ā Geom::Point3d vs #initialize(array3d) ā Geom::Vector3d Parameters: 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ā¦

Um, yeah. It was a long night last night fighting with the APIā¦ 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 ā¦ Sketchup::active_model()

ā¦ and to use # in place of a dot or scope operator only for instance methods. Ie ā¦ Geom::Vector3d#transform()

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.)

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 newGeom::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
nil

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ā¦ )

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.

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 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:

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:

And manipulator. You can change individual matrix elements and see the results.
See itās Extension Warehouse page for more information and more screenshots.

Vectors are a direction, and when a translation type of transformation matrix is applied to it (i.e. linear movement), the translation is not suppose to not affect the vector. Internally, Sketchup performs the multiplication differently depending on whether it is a Point3d or Vector3d to achieve the expect convention in geometry. Perhaps the code would be a bit less confusing with:

P_pt.transform( T )
V_vec.transform( T )

Someone looking at the code can expect different outcomes if the transformed object is of a different type.

Yes, I understand that they are performed differently, but, why? What purpose would that serve except to confuse the transcription of matrix multiplication? When overloading operators, one shouldnāt stray from expected results (i.e. make it more confusing).

Further, I still donāt see what is the purpose for multiplying a vector on the right of a transformation matrix. Where would I use such an operation? Even so, if I wanted to right multiply, it would make more sense to do:

P_pt * T
T * V_vec

Which would explicitly show the multiplication order. Then, if I wanted to have a vector be operated on like a point, I could simply do:

V_vec * T

and it is immediately obvious as to what is expected (vector or point on left of transformation is treated as a row matrix, whereas on the right it is treated as a column matrix, and what is returned is whatever type was put in). Instead we have, T * P_pt really meaning P_pt * T and P_pt * T being undefined (as is also V_vec * T).

Why not? I myself have wanted to do such an operation on a vector and I have to do ugly conversions of a vector to a point and back again.

I just find the inconstancy between how matrix multiplication is portrayed and actual matrix multiplication annoying and confusing, with no real justification for it.

Then I suggest you open a documentation issue in the API Issue Tracker.

Please understand that the Geom module classes were implemented like 20 years ago and the authors are likely no longer SketchUp employees. So the reasons why things were done may be lost to history if there are no comments in the code.

I donāt know what you were asking, and you didnāt provide any example code of what you are trying to achieve, so I havenāt answered until recently. Mathematicians of some sort long ago decided that we needed separate constructs for the geometric concepts of position and direction. When operations are applied upon them, it is desirable that they behave differently, even though they both can be represented with a triplet of 3 numeric values. Vectors are not line segments on a piece of paper, not geometric lines at a specific position, and not a point. You can think of them like the wind blowing over a field:

That model is just a imperfect metaphor. A translation transformation changes the position of objects, but not direction. How can you change the position of the windās direction blowing over the field? It doesnāt have a position to begin with. Its a direction, not a position.

For the position or point, a similar situation occurs with the rotation transformation. The rotation constructor needs a position, vector and angle. The position and vector define a geometric line that is the rotation axis. If you try to rotate a point that lies on that line, its position wonāt change. The same triplet that is a vector will be rotated by the rotation (unless the vector is parallel to the axis of rotation). Matrix operations affect position and direction differently intentionally.

You said:

You can limit some, but not all ugly conversions with duck typing, including the ā.to_aā method. But, if you start using an array as a Point3d or Vector3d, you need to test to see how the array is being treated. It would be nice to have the Vector3d class have a āto_ptā cast and the Point3d have a āto_vecā cast.

# SomeAuthor_RefinedGeom.rb
module SomeAuthor
module RefinedGeom
refine ::Geom::Vector3d do
def to_pt
ORIGIN.transform(self)
end
end
refine ::Geom::Point3d do
def to_vec
ORIGIN.vector_to(self)
end
end
end
end

require File.join(__dir__,"SomeAuthor_RefinedGeom")
# Must be called at toplevel for older Ruby versions
# and refinement only applies in the current file:
using SomeAuthor::RefinedGeom
module SomeAuthor
module SomeExtension
# module code using the refinement
end
end

I know some other APIs like that of Unity use a single vector class to represent both directions and positions. In those cases you make the choice of whether the translation part of the transformation should be taken into account when you multiply.

In the SketchUp Ruby API different classes represents directions and positions. The choice of how multiplications are done is made already when you create the object.

I think both designs are valid but both can be confusing if you are used to the other. SketchUp is originally created as a 3D modeler āfor the rest of usā. The program was designed to be more intuitive and require less technical knowledge to be used than existing programs, and I think this can be seen also in the API design. If you took linear algebra in college, thinking in matrices and N-dimensional vectors as abstract mathematical concepts makes sense, but if you have little or no professional math knowledge, thinking in visual concepts like positions, directions and ādifferences in coordinate systemsā makes more sense.

My recommendation is to be explicit when you create an object about what it represent. Use Vector3d for all directions and Point3ds for all positions and transformations should just āmagicallyā work.

Yes, I agree. Having the transformationās multiply operator * accept point/vector/plane is strange.

Iāve personally never used those overloads. I use the * operator only to multiply against other matrices. And I use the explicit transform method on point and vector objects.

Iām not sure what the reasons behind these overloads are. They are Old Code, from long before anyone still on the SU dev team, so that is lost to history. We are alas stuck with it now.

Another thing I have a gripe with is that we overload the meaning of Array a lot. It can represent point, vector, line, plane, transformation etc. It can be convenient some times, but can also lead to unfortunate ambiguity. And for the concepts of Line and Plane you have to use arrays. I would have preferred dedicated types. But at this point there is so much existing code and logic that Iām doubtful how valuable introducing new types would be.

It might be that the original implementers added the * transformation overloads as a way to allow planes and lines to be transformed. Since they donāt have a type of their own the only way is this backward syntax (transform * plane).

It might also be a case of misplaced code re-use. Internally the point and vector class have no transform method. And the API layer reaches out for transform.*(point) and transform.*(vector). Maybe the original implementer of the API wasnāt aware this wasnāt aware that the order of operation mattered. Unknown at this point.

By this point weāre unable to do anything about it. It would break almost every extension out there. But we could improve documentation.

Btw, I would also recommend being explicit in using Geom::Vector3d, Geom::Point3d, Geom::Transformation instead of using the Array shorthand notation. Eliminates ambiguity.