Help with short script please

Hello everyone,
I have been using the following script written by @sdmitch. It will take the selected components in the model and place them on the ground plane with the blue axis facing up in a neat little row. Very useful for CNC machining etc. However, it also resets the scale of any component it finds.

From my reading of the Ruby docs, it appears that the ‘transformation.inverse’ method is responsible for resetting the scale, among other things.

I am wondering if there is a modification to this script that would allow a component to retain its scale but still perform the same rowing function. I’ve been trying to play around with the script with no immediate success, so I need some help from one of you Ruby geniuses!

Thanks in advance.

mod = Sketchup.active_model
ent = mod.active_entities
sel = mod.selection
org =
spc =
cdn = ent.grep(Sketchup::ComponentInstance).map { |ci| }.uniq.sort
cdn.each { |n|
  cis = ent.grep(Sketchup::ComponentInstance).each { |ci|
    next unless == n
    ci.transform! ci.transformation.inverse
    ci.transform! Geom::Transformation.translation(org - ci.transformation.origin)
    if ci.bounds.width > ci.bounds.height
      ci.transform! Geom::Transformation.translation(org - ci.bounds.corner(2))
      ci.transform! Geom::Transformation.rotation(org, Z_AXIS, 90.degrees)
    if ci.bounds.depth > ci.bounds.width && ci.bounds.depth > ci.bounds.height
      ci.transform! Geom::Transformation.rotation(org, X_AXIS, 90.degrees)
    if ci.bounds.min.y < org.y
      ci.transform! Geom::Transformation.rotation(org, X_AXIS, 180.degrees)
      ci.transform! Geom::Transformation.translation(org - ci.bounds.corner(0))
    org.offset!([ci.bounds.width + spc, 0, 0])

This might point you in the right direction (pun intended).

Below is some highly untested code.

# Get the first instance in the model
mod = Sketchup.active_model
ent = mod.active_entities[0]

# Print the transformation
tr = ent.transformation
tr.to_a.each_slice(4){ |slice| p slice }

# determine the Translation and Rotation matrix of the instance
tr_T_R = Geom::Transformation.axes(tr.origin, tr.xaxis, tr.yaxis, tr.zaxis)

# transform by the inverse matrix
#  -> this should result in a scaled instance of the object at the origin of the model.

# print the resulting transformation
tr = ent.transformation
tr.to_a.each_slice(4){ |slice| p slice }

I appreciate your contribution. However, it is the ‘.inverse’ operation that resets the scale of the selected component.

For example, I have a component instance whose container has been manually scaled after it was originally defined as a component, using the scale tool. Let’s say this component now has a scale of -1 (essentially a reflected component). Applying the inverse transformation method will result in a scale component = 1. Therefore, the transformed component will have its original scale.

I want to keep the scale factor of -1 (for example) when changing the rotation and position of said component, to orient it with its blue axis aligned with the global blue axis… And in case of many components, distribute them in one row.

The original script I posted does a pretty good job of this, except that it resets the scale of any component in the process.

I hope I have clarified.

The published script acts on ALL the instances in the active entities of the model.
it does No it looks like it groups the instances of the select set.

Do you have a lightweight example model to try? Please add to the initial post.

1 like

Attached is an example file containing a group that has been scaled in the X, Y, and Z directions by factors of -1, -2, and -3 respectively. And below is the code to extract those constants from the group transformation matrix.

Matrix example TRS.skp (104.2 KB)

# Get the first instance in the model
mod = Sketchup.active_model
ent = mod.active_entities[0]

# Print the transformation
puts "Initial Transformation:"
tr = ent.transformation
tr.to_a.each_slice(4){ |slice| p{ |value| value.round(4)}.join(" ") }

puts "\nGroup's Axes:"
p tr.xaxis
p tr.yaxis
p tr.zaxis

axis_x = tr.xaxis[0] >= 0.0 ? tr.xaxis : tr.xaxis.reverse!
axis_y = tr.yaxis[1] >= 0.0 ? tr.yaxis : tr.yaxis.reverse!
axis_z = tr.zaxis[2] >= 0.0 ? tr.zaxis : tr.zaxis.reverse!

# determine the Rotation * Translation matrix of the instance
tr_T_R = Geom::Transformation.axes(tr.origin, axis_x, axis_y, axis_z)

# print the transformation
puts "\nResulting Transformation:" 
tr = ent.transformation
tr.to_a.each_slice(4){ |slice| p{ |value| value.round(4)}.join(" ") }

puts "X scale = #{tr.to_a[0].round(4)}"
puts "Y scale = #{tr.to_a[5].round(4)}"
puts "Z scale = #{tr.to_a[10].round(4)}"

I wrote a sample plugin to do this, so you won’t need to cut and paste large blocks of code into the console.

The above code is still there wrapped in the ” methodold_way()” for nostalgia, but the extension doesn’t use it.

I wrote a completely new method to move selected instances that don’t No wear transformation.inverse.


  • It remembers the last space used, allowing it to be set via an input box.
  • The changes are wrapped in an undo operation.
  • The command appears on the Extensions menu and on the
    right-click the context menu if instances are selected.

See: [Example] Place a set of selected instances in a row along the X axis

Hi DanRathbun, you’ve really gone the extra mile by including the script in an extension, so thank you very much for your effort already. The only problem is that your extension also resets the scale of a component! The best way to illustrate this is with a video…

1 like

Well, it’s version 1.0.0. (I’ve noted this known issue in the example post.)

I ordered a test model. I created one but I did No testing a negative scale only positive scales on one or more axes. So the next version would be to handle the scaling of the negative axis.

Yes, I’m sorry I’ve been away from my desk all day. I was going to post a test model, but they beat me to it. Waiting for V2 with great anticipation. :slightly_smiling_face:

I’m going to bed. We will have to wait until tomorrow.

1 like

Here is a longer video showing the problem in more detail. In the video, the two parts that I move to the foreground are opposite. That is, the right side is simply a mirrored instance of the left. When I run the original script, or yours, I am left with two ‘unscaled’ parts. That is, the right side part has lost its -1 scale.

Hopefully this will illustrate why a job script is a huge time saver for CNC machining. Once the parts are laid flat, the final step is to export a 3D DXF and then clean up the layers in the CAM software.

Attached is a model containing two pairs of mirrored parts.

Mirrored_Parts.skp (1.8MB)

Yes, I forgot to take this into account.

I also see that the parts appear to be arranged with their short side along the X axis. I didn’t do this either. I saw a comparison between the width and height of the bounds in the original script, but I didn’t understand what it was for.

Is this “short side” reorientation necessary?

Hi again Dan, The x/y orientation doesn’t matter because the CAM software can rotate the parts when I nest the parts on the sheet.

In short, all the parts arranged from the long side to the green axis would be nice but not absolutely necessary.

Thanks again

Source link

James D. Brown
James D. Brown
Articles: 8681