Move! relative vs absolute positioning

What I have read indicates that a move! or transform! should move the object relative to it’s original position, but for me move! is using absolute coordinates.Here is a snippet:
jamb_right.move! [section_width - 1.5,0,flr_chan_ded]

How do I get move! to work “relative”

it’s not relative to original position,… it is relative to it’s parent context .transformation.origin

In other words, it is relative it the local origin, if the object being moved is nested within a component or group context.

If the object being moved’s parent is the model entities context, then it is relative to the global model origin (regardless of what the user may have done with the temporary visible axis using the AxisTool. Ie, the AxisTool does not change the global ORIGIN point [and neither should your code.])

Simple. Use the object’s current transformation.origin point in creating a vector argument for the new Geom::Transfomation.

start = jamb_right.transformation.origin
stop = start + [section_width - 1.5,0,flr_chan_ded]
relvec = start.vector_to(stop)
relative = Geom::Transformation::translation(relvec)
jamb_right.transform!(relative)

And you should only use move!() for animation.

Perhaps you are using move for animation. In that case, the move! method’s argument is a transformation whose internal axis represent the local coordinate system of the grouped object, similar to the “transformation=” method (i.e. absolute positioning). I don’t believe its description in the API documentation is correct. I documented this in the “Ruby API Documentation - Errors & Suggestion” topic. It was the post:

To determine whether a transformation argument is absolute or relative, supply it with the IDENTITY transformation constant. Given an entity variable that references a grouped object (Sketchup group or component instance), ensure that is moved off of the Sketchup world origin. Then perform the following:

entity.move! IDENTITY

Do this with an grouped object that isn’t nested inside of another one. If the move method is a relative update operation, then nothing should happened (nothing will happen with “transform!” method either) as applying the identity transformation is like multiplying by 1. The behavior for the “move!” method is absolute, with the following changes occuring:

  • The object’s transformation is set to 0, 0, 0;
  • The object is de-rotated (i.e. its orientation is the same as when it was first grouped); and
  • It is descaled.

Move needs to be used like “transformation=”. In order to apply an incremental operation for those type of absolute or “poses”, then use the following:

jamp_right.move! relative * jamp_right.transformation

The order of the multiplication sequence is important.

Thanks for the helpful reply.
I had read that the only (or main) diff between move! and transform! was that move! did not support Undo (which is fine for my purposes)
Nevertheless I will use your advice :slight_smile:
I thought it would be faster/cleaner to copy similar objects and move them to the req’d coordinates, but 5 lines of code are required to move my object (plus the copy line)… I might as well just draw the copies “from scratch”, which would be faster/preferable?

No not really. I (and other sages) often break down operations into smaller statements so that newb Rubyists can see the simple steps more clearly.

I can of course write it all in one statement, that does not init named references:


jamb_right.transform!(
  Geom::Transformation::translation(
    jamb_right.transformation.origin.vector_to(
      jamb_right.transformation.origin + [section_width - 1.5,0,flr_chan_ded]
    )
  )
)

It is the opposite. move!() purposely does not insert entries into the undo log, and was made specifically for animation situations.

In normal operations it is always best to allow the user to undo logical editing operations. See the methods for model#start_operation() and model#commit_operation().

Oh you need COPIES. You did not say this in your original post. That is a bit easier, but still requires a transformation object.

As Bruce showed above, you can use the matrix multiplication method to apply your movement vector. (Ruby is multi- paradigm.):

movement = Geom::Vector3d::new( 
  [ section_width - 1.5, 0, flr_chan_ded ]
)

copy = model.active_entities.add_instance(
  orig.definition,
  orig.transformation * Geom::Transformation::new(movement)
)

* The above statements are shown multi-line only for clarity. (You can remove all the linefeeds if you prefer really long single line statements.)

OK I am learnin’ some stuff here…
What about speed? I can also draw jamb_right with 2 lines of code.
jamb_left = ent.add_face [0,0,flr_chan_ded],[1.5,0,flr_chan_ded],[1.5,0,section_height],[0,0,section_height],[0,0,flr_chan_ded]
jamb_left.pushpull -wall_thickness
jamb_right = ent.add_face [section_width - 1.5,0,flr_chan_ded],[1.5,0,flr_chan_ded],[1.5,0,section_height],[0,0,section_height],[section_width - 1.5,0,flr_chan_ded]
jamb_right.pushpull -wall_thickness

Not clear what you are asking…whether that code will be quicker due inherently, or whether omitting linefeeds makes the code quicker. The latter has a negligible effect, as the Ruby interpreter only processes the linefeeds the first time it reads the code.

Also, your talking about differences in speed of hundredths of a second, which the end user will not notice when running your command.

And the biggest time waster in my first example, was creating the named references when they really might not be used later on in the code.

It is not just speed. There is the issue of “entity bloat”. If the jambs are identical, they should be component instances.

The main rule about Ruby coding style (usually listed as rule 1 or near the top of most style guides,) is “Write human readable code.” Ie, easy to understand. You’ll always need to come back after a few months and fix some bug, and then you’ll need to be able to re-grasp what the code is doing quickly, without wasting a bunch of time studying it all over again.

1 Like

I was not referring to the amount of lines but the execution of the code in general
There will be as many as 10 wall sections, each with side jambs, sills, headers, sashes, etc. (it’s a sunroom BTW)

Other than the concern of entity bloat, I don’t need any of the features that a component provides. So if I convert the groups to components I could expect smaller file size and faster response?

No. Groups are actually “special” component instances whose definition’s are hidden from the Component Browser inspector panel.

So, for example, asking a group’s definition whether it is for a group will return true:

grp.definition.group?

… but false for the definitions of a normal component, or an image (which is also a special kind of component instance hidden from the component browser. There is also an .image? query method to test for image definitions.)

Also, be aware that grp.entities is just a shortcut wrapper for grp.definition.entities (as component instances, of all 3 types, do not have their own entities collections. The GUI hides the fact that groups are really components from the average user, and the API used to also obscure this fact. But this obfuscation also caused bugs, so it was removed and the .definition method added to groups, but not images, because manipulating images at the definition level causes crashes.)

Components or groups are the only usable geometry separators. (Layers in SketchUp do not have geometry collections.)

You decide which is best. Some coders and modelers avoid groups, as they have some “quirks.” And, also, the Trimble Dynamic Components extensions works only with components as the outer most level of the nesting. (They can have child groups inside them.) [There are many topics on this already.]

Thanks Dan!

Just to add some closing confusion…
There are different types of Geom::Transformation
The simplest is perhaps
.new(some_point)
this transforms the object it is applied to, so as to be located at some_point - aka an absolute move.
Then there is
.translation(start_point.vector_to(another_point))
this is a relative move - where start point could be the
object.transformation.origin and the other_point could be constructed as a say an ‘offset’, where:
other_point=start_point.offset(some_vector, some_distance)
so if
some_vector=X_AXIS and some_distance=123
the transformation moves the object from where it is 123" to the right…

Then there’s .rotation() and .scaling()
http://www.sketchup.com/intl/en/developer/docs/ourdoc/transformation

EDIT: Fixed two typos…

So to use Dan’s example:
start = jamb_right.transformation.origin
stop = start + [section_width - 1.5,0,flr_chan_ded]
relvec = start.vector_to(stop)
relative = Geom::Transformation::translation(relvec)
jamb_right.transform!(relative)

could be written as?
jamb_right.transform!(object.transformation.origin, [section_width - 1.5,0,flr_chan_ded])

Where is the Geom::Transformation in your rehash ?
You are not passing a transformation - it is actually two arguments - a point and a three element array containing what I take to be 3 distances - which could then be interpreted as a point…
Please read up on transformations - as I linked above…

Also what is your ‘object’ reference, I only used it as my ‘placeholder’ for whatever it was you want to reference !?
Dan’s example is quite clear.
Trying to wrap everything into one line of code makes it hard to read / understand.

Understood :slight_smile:
Thanks!

Thanks all. Can someone give a brief example using ob3d =entities add_3d_text (…)_ which the clever folks who wrote SU put at 0,0,z ; they let you present a “z” but not x,y. Nor any rotation… I simply want after the created ob3d moved from [0,0,z] to [x,y,z]

Firstly - add your 3dText into a new group.entities
Then transform that ‘group’.
By default it’s made at the ORIGIN and laid on the ground and extruded up in the Z.
You can then apply any type of transformation you desire to the group…

2 Likes