[Partially Solved] Rotation transformation not working for me - why not?

In an extension I’m working on to array numbers and optionally rotate them individually or as a collection in a group, my first rotation works, but the second almost identical one doesn’t, and I can’t see why not.

        # Rotate the text just drawn to the selected angle
          # Calculate text centre point
          cp = Geom::Point3d.new(textx_position, 0, 0)
  #          ents.add_cpoint cp # Use for testing to see component origin point position
          text_rotate = Geom::Transformation.rotation cp, Z_AXIS, rotate_angle.degrees
          my_flat_text_group.transform! text_rotate

That does what I intend: for example, with the rotate_angle set to 45°:
image

A few lines later, when I have assembled a text_group containing several individual items of text (as shown in the image above), I can’t get this group to rotate as a whole.

 # Rotate the array to the selected slope_angle
    array_rotate =  Geom::Transformation.rotation ORIGIN, Z_AXIS, slope_angle.degrees
    text_group.transform!  array_rotate

    name = text_component.definition.name = text_name
    text_component = text_group.to_component
    model.place_component text_component.definition

In the Ruby console I checked that all the values in the second Geom::Transformation exist

slope_angle = 30.0
true
> Z_AXIS
(0, 0, 1)
> ORIGIN
(0", 0", 0")
> 

The format of the two transformations is identical. I know that text_group exists because in the next few lines of code I convert it to a component and place it in the model. And THAT part works. It just won’t rotate first!

It’s probably something dumb that I’m not seeing, but I’m pretty sure it isn’t a typo, and the two lines with .transform! in them appear identical in format.

But one works, and the other doesn’t, and at the moment I don’t understand why not.

In the meantime I’ll get on with working on other parts of the code, but I’d really like to understand what’s wrong with this bit. No errors are shown in the Ruby console.

Suggestions welcome!

The transformation is a property of an instance (a group is a special sort of instance), not of the definition. When you make the group into a component, the transformation still applies to that instance, but not to the definition, and then when you place a new instance of the definition it does not “inherit” the transformation from the previous instance.

Makes sense, now. How do I get it to apply, then, to at least this instance of the component?

Or better, to the definition?

I tried

text_component.definition.transform! array_rotate

But apparently I can’t do that.

Well, partially solved. I can place a line, at an angle, as a component using this code:

model = Sketchup::active_model
entities = model.active_entities
text_group = entities.add_group
ents = text_group.entities
line = ents.add_edges [0,0,0], [10,0,0]
array_rotate =  Geom::Transformation.rotation ORIGIN, Z_AXIS, 30.degrees
ents.transform_entities( array_rotate, text_group )
text_component = text_group.to_component
model.place_component text_component.definition

And I can get my text_group of numbers created at the selected slope angle as a component at the origin, using this:

    array_rotate =  Geom::Transformation.rotation ORIGIN, Z_AXIS, slope_angle.degrees
    ents.transform_entities( array_rotate, text_group )
    text_component = text_group.to_component
    text_component.definition.name = text_name

What I can’t yet figure out how to do is to make the rotated entity the component definition, not the instance, nor how to enable place_component to place a rotated instance of it.

What am I missing?


And I’ve noticed a possibly unrelated oddity in SU 2020.1 on Mac Mojave.

When I drag the component out of the Component Browser, it comes into the model a long way from the cursor

Anyone know why this is happening?

Well, I’m one step further now. After a second search of the forum, I found this topic and this post in it:
Transform geometry within comonent or sub-components.

That gave me enough clues to simplify it for one component definition only to one line:

text_component.definition.entities.transform_entities(array_rotate, 
   text_component.definition.entities.to_a)

This is the placed component, with the internal entries rotated to 45° as asked for with the slope_angle set to 45°.

The elements of the component definition have been rotated inside the component definition, but the bounding box no longer fits the entities closely.

Next step - how to redefine the bounding box in code so it looks like this (image created after using the user interface and Change Axes for the component).

Will try a little longer tonight, but if it’s difficult, may have to wait until tomorrow.

[LATER]
If I have both a slope_angle and an opposite text rotation_angle set, I get an odd bounding box, which doesn’t even contain the text elements:

And I’ve searched the forum for how to change a component’s axes using Ruby, and can’t find anything helpful yet.

Any suggestions, please?

Do you want to apply the rotation in relation to the local axes rather than the parent axes?

Cross multiplication isn’t commutative, meaning the order of the operands matter. You can think of the order of the transformations as the order the action os carried out, move or rotation first.

transformation = Geom::Transformation.new(ORIGIN, Z_AXIS, 20.degrees)

# Rotate around local origin.
Sketchup.active_model.selection.each do |entity|
  entity.transformation = entity.transformation * transformation
end

# Rotate around parent origin.
Sketchup.active_model.selection.each do |entity|
  entity.transformation = transformation * entity.transformation
end

Also, a transformation doesn’t necessarily represent a change in position but can also be a static position. The transformation of a group or component corresponds to how it would have to be moved from being lined up with the parent axes into having its current placement.

Thanks for your response, Julia Christina.

I’ve worked out how to get the number elements in the array in the right place in the model. But the text_component axes are still aligned with the model axes, so the bounding box is very much bigger than it needs to be.

I’d like to change thetext_component axes so its x-axis points along the length of the array, at slope_angle relative to the model x-axis.

That’s the bit I can’t work out how to do.

I’ve searched both this forum, and in the SU Ruby API docs, for various combinations of component, component definition, axis, axes, change axes and a few others, but can’t find an example.

I’m not sure how I would use your examples to do this, though they will be helpful to my understanding of SU Ruby, which is slowly growing.

This method moves the axes of a definition.

def self.place_axes(definition, new_axes, adjust_instances = true)
    definition.entities.transform_entities(
      new_axes.inverse,
      definition.entities.to_a
    )

    if adjust_instances
      definition.instances.each do |instance|
        instance.transformation *= new_axes
      end
    end

    nil
  end

You can use e.g. the bounds min point as origin to copy SketchUp’s behavior for groupign/component creation with the origin in the bottom front left corner.

transformation = Geom::Transformation.new(definition.bounds.min)

1 Like

That looks to be just what I need. I’d never have worked that out on my own!

I’m fine with the component origin where it is - at the model origin before .place_component s used, but good to know how to change it.

Many thanks.

Hmmm. I think I’m not understanding how to set the new_axes in @eneroth3’s method.

At first, I though just an array would do the trick:

new_axes = [Math.cos(slope_angle), Math.sin(slope_angle), 0]

The Ruby console gives a ‘No method’ error for new_axes.inverse.

But an array doesn’t have an #inverse method, only a Geom::Transformation does, I find from consulting the SU Ruby API docs.

Then I thought, well I already have the transformation I want to invert - array_rotate - which rotated the elements of text_component_definition to the slope_angle where I wanted them to go.

So I plugged in array_rotate to the place_axes method call.

place_axes(text_component.definition, array_rotate, adjust_instances = true)

No error, but that doesn’t work to change the axes, and seems to cancel out the rotation of the definition’s elements, producing this:
image

… instead of this without the place_axes line and slope_angle = 30°

And again, the bounding box doesn’t include the text elements inside it, except for the starting zero.

So sorry, I’m not quite there yet.

Further help would be welcome.