Changing component boundingbox rotation axes

Is there a simple way to change component axis rotation in ruby? It’s very simple in sketchup but I need to do that in order to retrieve the right dimensions of the wall with ruby.
I have read and trying a lot but nothing works

1 Like

One “very simple” method to do it involving 4 times interaction of the USER:

  • Right click on a desired component to select Change Axes
  • Pick point for origin of axes
  • Pick direction for red axis
  • Pick direction for green axis

Actually you need to write a methods to determine the object, a point and at least two axes in a question.
Then you have to create a transformation and apply to the object then apply the inverse of this transformation to the object entities.

Perhaps you can start to check this:


Also, you can check if this one already fulfills your needs:
(Be careful because the developer is lying about the price of this extension on EWH)

1 Like

thanks @dezmo

¿I did the first part but how can I aply the inverse to the object entiies?

a = Sketchup.active_model.selection[0]
targetPoint =
vector =,0,1)
degreesToRotate = 45.degrees

this doesn’t works

for ent in a.definition.entities

Thanks a lot @dezmo I’m sure it will helps me.

@rtches Here is an example modified from @thomthom’s Skippy library
(which is used by the his Axes Tools.)

I modified it to try to determine what the new origin and 3 axis should be.
Test fully and modify further as needed.

Here is a test model:
instance_axes_reset.skp (21.7 KB)

module SomeAuthor::SomePlugin

  extend self

  def reset_axes(
      instance = Sketchup.active_model.selection.first
    return nil unless instance && instance.respond_to?(:definition)
    model = instance.model
    definition = instance.definition
    # Get a new axes for the instance's definition:
    new_axes = get_new_axes(definition)
    # Adjust all the definition's entities to the new internal axes:
    model.start_operation("Adjust Axes", true)
      # Transform all of the definition's entities to the new axes:
      # Adjust the instance transformations:
      definition.instances.each do |instance|
        instance.transformation *= new_axes
    return instance

  def get_new_axes(definition)
    entities = definition.entities
    edges = entities.grep(Sketchup::Edge)
    pts = edges.flat_map(&:vertices)
    # Determine a new origin point for the new axes:
    origin = get_new_origin(pts)
    # Determine a new axes for the entities based upon edges
    # that use the vertex at the new origin point.
    edges.keep_if do |e|
      e.vertices.any? {|v| v.position == origin }
    # Map the edges to the points of their other vertex:
    pts = do |e|
        e.vertices.find {|v| v.position == origin }
    # Now map those pts to normalized vectors along the edges:
    vecs = {|pt| origin.vector_to(pt).normalize }
    # Get an array of offset points from the normalized vectors:
    pts = {|vec| origin.offset(vec) }
    # Sort by the z value:
    pts.sort! {|a,b| a.z <=> b.z }
    # Keep the 2 vectors with the smallest z values:
    vecs.keep_if {|vec| pts[0..1].include?(origin.offset(vec)) }
    # Sort these 2 vectors by their angle to the X_AXIS:
    vecs.sort! {|a,b| a.angle_between(X_AXIS) <=> b.angle_between(X_AXIS) }
    # The first vector in the sorted array will use as xaxis:
    xaxis = vecs.first
    # The zaxis will be a vector perpendicular to both other vectors.
    # We use the cross product to find it:
    zaxis = xaxis.cross(vecs[1])
    # We do not know if vecs[1] is perpendicular in the XY plane to
    # the xaxis. (Ie, the edges found may not be at right angles.)
    # So we again produce another perpendicular vector by cross product:
    yaxis = zaxis.cross(xaxis)
    return Geom::Transformation::axes(origin, xaxis, yaxis, zaxis)

  def get_new_origin(pts)
    # Sort by the z value:
    pts.sort! {|a,b| a.z <=> b.z }
    # Keep only those whose z value is minimal:
    z_min = pts.first.z
    pts.keep_if {|pt| pt.z == z_min }
    # Look for the point closest to X_AXIS (with smallest y value):
    # Sort by the y value:
    pts.sort! {|a,b| a.y <=> b.y }
    y_min = pts[0].y
    # Keep only points whose y is minimum:
    pts.keep_if {|pt| pt.y == y_min }
    if pts.size == 1
      # Use it:
      # There are duplicate points equidistant from X_AXIS, so
      # use the one furthest to the "right" with highest x value:
      pts.sort! {|a,b| a.x <=> b.x }

end # extension namespace module

@DanRathbun Brilliant!

My main goal is to make an extension that attach to the IFC classification of each element a PSet containing the quantity take off information as specified by Building SMART. When I collaborate with other teams their IFCs have such a PSet because Revit or Archicad add them but mine doesn’t.

Example with IfcWall

At the moment I have achieved this:
What IFC Manager adds to the component:

What my pluging is adding now hence my aim to get the correct axes.

When I export the model to IFC that information goes with the model and IFC viewers (Bim Vision, Bim Collabs Zoom, Trimble Connect, etc) show them as properties.

Thanks again @DanRathbun also @dezmo and @thomthom for your contributions.

Uh… huh. Thanks for the context (but it is a bit more information than is really needed for this specific topic.)

So just for a bit more advice, be aware of issues with getting the volume of scaled instances.

To discuss volume further, respond in topic:
How do you reliably get a group/component instances volume?

Thanks @DanRathbun I suffered it but in order to export to IFC I try to simplify geometry before.