# Find the orientation of a component relative to the model

Hello,

With the method below, I am able to find the tilt angle in X of a component relative to the SketchUp scene.

``````mod = Sketchup.active_model
sel = mod.selection
sel.grep(Sketchup::ComponentInstance).each do |s|
xaxis = s.transformation.xaxis
angle_between = xaxis.angle_between(X_AXIS)
end
``````

The problem is that if I tilt my component at 45 ° positive or negative on Y, the feedback will always be positive at 45°.

Angle.skp (85.5 KB)

Is it possible to find the component orientation to display -45 ° and 45 °?

Ps: I have already gone through the Sketchup :: Axes, Geom :: Transformation, Geom :: BoundingBox, Geom :: Point3d and Geom :: Vector3d class without finding a solution.

Perhaps like this ?:

1 Like

P.S. - Please use correct coding language prefix so code is rendered correctly color lexed for Ruby.

It doesn’t seem to work with components.
I tried to readjust the formula for a selected component and the result is always -90 °.

``````mod = Sketchup.active_model
sel = mod.selection
sel.grep(Sketchup::ComponentInstance).each do |s|
tr = s.transformation
xaxis = tr.xaxis
yaxis = tr.yaxis
normal = Z_AXIS
angle = Math.atan2((yaxis * xaxis) % normal, xaxis % yaxis)
end
``````

I am not sure which are vector1 and vector2 in the example eneroth3?

Is the code formatted correctly now?
While editing I am unable to format my code in ruby format.
Yet I use: The Discourse Forum lexer does not know what “rubis” is.

Use ````ruby`

Julia’s example has a method description:

Counter-clockwise angle from `vector2` to `vector1`, as seen from `normal`.

So therefore, `vector1` is the vector that is being tested, and `vector2` is the reference vector.

Of course a local X_AXIS vector will always be 90 degrees from it’s complementary Y_AXIS with an orthogonal axes.

You need to use the parent entities context’s reference vector which may not be equal to the world (model) X_AXIS if the parent instance has been rotated.

2 Likes

But if the instance is at the top model entities context, then you can use the world X_AXIS as a reference.

``````angle_in_plane( sel.transformation.xaxis, X_AXIS ).radians.round
``````
1 Like

Amazing it works!

``````def angle_in_plane(vector1, vector2, normal = Z_AXIS)
Math.atan2((vector2 * vector1) % normal, vector1 % vector2)
end

mod = Sketchup.active_model
sel = mod.selection
``````

This proves once again that I still have a lot to learn.

I can also write it like this:

``````mod = Sketchup.active_model
sel = mod.selection
sel.grep(Sketchup::ComponentInstance).each do |s|
xaxis = s.transformation.xaxis
angle = Math.atan2((X_AXIS * xaxis) % Z_AXIS, xaxis % X_AXIS).radians.round
p angle
end
``````

Or like this:

``````Sketchup.active_model.selection.grep(Sketchup::ComponentInstance).each{|s| p Math.atan2((X_AXIS * s.transformation.xaxis) % Z_AXIS, s.transformation.xaxis % X_AXIS).radians.round }
``````

I still have little experience with ruby and trigonometry is a distant memory from school.
But I am going to study this wonderful method which is full of teaching.

Why would you want to?

It is more self-documenting to use the `angle_in_plane` method within your extension submodule or class.

For more information, … the `Enumerable#grep` method takes a block.
You do not really need to use `Array#each`.

You are right Dan!

I am doing this only to better understand what is going on.
In reality if the code is executed in a single block, it is easier for newbies in ruby like me to understand what is going on.

This is how I dissect a method to better understand it:

``````# Calculate the angle of rotation X of a selected component (Explanation)
mod = Sketchup.active_model
sel = mod.selection
sel.grep(Sketchup::ComponentInstance).each do |s|
p xaxis = s.transformation.xaxis #X axis of the selected component => Vector3d (-0.707107, 0.707107, 0)
p X_AXIS #X axis of SketchUp Scene # => Vector3d (1, 0, 0)
p ( X_AXIS * xaxis ) # Method * is used to calculate the cross product between two vectors => Vector3d (0, 0, 0.707107)
p Z_AXIS #Z axis of the SketchUp Scene => Vector3d (0, 0, 1)
p ( X_AXIS * xaxis ) % Z_AXIS #The % method is used to calculate the dot product between two vectors => 0.7071067811865475
p ( xaxis % X_AXIS ) #Calculate the dot product between the X axis of the component and the X axis of the scene => 0.7071067811865476
p Math #Module for trigonometric functions
p Math.atan2((X_AXIS * xaxis) % Z_AXIS, xaxis % X_AXIS) #Compute the given arc tangent y and x => 0.7853981633974483
p Math.atan2((X_AXIS * xaxis) % Z_AXIS, xaxis % X_AXIS).radians #The radians method is used to convert radians to degrees => 45.0°
end
``````

It might not make sense to you, but it helps me learn.

Please note that your comment is not exactly true in all cases.

The API constants `ORIGIN`, `X_AXIS`, `Y_AXIS`, and `Z_AXIS` are the preset world (model) axes of the `IDENTITY` transformation. These should never change (ie, they are constants.)

Each scene `Page` can have it’s own `axes` settings that can differ from the unchangeable world (model) axes.

Note that the API code example does not work correctly and omits updating the scene page object.

This is a working example to set a special axes for a scene page:

``````def new_scene_with_special_axes
model = Sketchup.active_model
xaxis = Geom::Vector3d.new(3, 5, 0)
yaxis = xaxis * Z_AXIS
model.axes.set([10,0,0], xaxis, yaxis, Z_AXIS)
page.update(PAGE_USE_ALL)
page.axes
end

a = new_scene_with_special_axes()
a.origin
``````

Notice how it borrows the `Z` world axis, but sets it’s own `x` axis and calculates it’s `y` axis using the dot product of the `x` and `Z` axis vectors.

Before we start Happy New Year everyone!

Your example Dan opened unexpected doors for me to solve a problem that seems difficult to solve otherwise.

Explanation:
By default, if we create a component with an inclined face, the bounding box will be based on the constants of the API ORIGIN, X_AXIS, Y_AXIS, and Z_AXIS:

So to get around the problem in order to limit the engobing box to the dimensions of the faces, we had to find a way to change the axes of the page according to the normals of the face.

``````def face_axes(sel,face_axes)
sel.grep(Sketchup::Face).each do |f|
face_axes << f.normal.axes
end
end

mod = Sketchup.active_model
sel = mod.selection
ents = mod.active_entities
mod.start_operation('Convert faces to components', true)
face_axes = []
face_axes(sel,face_axes)
axes1 = face_axes.flatten!
page.axes.set(ORIGIN, axes1,axes1,axes1)
mod.pages.selected_page = mod.pages #Force the update of the page scene object
sel.grep(Sketchup::Face).each do |f|
inst = grp.to_component
defname = inst.definition.name = "Component#1"
end
mod.pages.selected_page = mod.pages
mod.pages.erase(page)
mod.pages.erase(mod_page)
mod.commit_operation
``````

We now have a bounding box which follows the normals of the face:

As you said the sample API code is not working properly and omits updating the scene page object.
In my case even “page.update (PAGE_USE_ALL)” does not allow the update.

So I worked around the problem with the “selected_page” method which forces the update of the scene page object before creating the component.

I’ve been studying vectors, axes, and transformations for a few days now.
It seems like a big learning curve because geometry in 3D space is not the easiest to assimilate.