I created an extension that renders and imports quite complex 2D geometry (edges and faces) into Sketchup, in a multilevel group structure. The different groups in this structure may have been transformed in multiple ways (scaling, moving, rotating, skewing, mirroring) and ‘painted’, to finally arrive at the desired design. Ultimately, all grouped geometry is collected in a single top level group. This top level group (as well as the childs) have their y-axis oriented downwards (as a consequence of how the source geometry data is supplied). I would like to have the group axis pointing upwards, and positioned at the overall origin. The picture below shows a simple example of the current situation (no faces involved here, but that’s not relevant for the issue at hand). The top group axis system is near the upper left corner of the group (in the center of the red circle, and the Y axis points DOWNWARDS. The geometry in the group is shown in the selected group (blue rectangle), and is oriented upwards, and this should stay. The geometry is also positioned as wanted (i.e. the bottom left corner is at the global origin).
What I would like to achieve, via Ruby code, is that the group axis system moves to the global origin (at the center of the green circle at the left bottom), and that the y-axis points UPWARDS!, while the geometry stays where it is (bottom left of the bounding box at the origin, and oriented upside up, as is the case already). I’m aware that I can set the axis manually as desired via the UI, but I would like to add this as a final step of the extension, because y-axis pointing downwards doesn’t feel ‘natural’ in Sketchup and may result e.g. in upside down glueing when converting the top group to a component, and then trying to glue it (unless the component axes are manually defined). I’ve researched this forum, and although there are a number of posts dealing with axis management via the Ruby API, I could not find a solution that solves my issue (i.e. reverting the direction of a GROUP axis (not the global axis), while not changing the location (at world origin) and the upside orientation of the geometry).
# Flip the grp instance as a whole vertically:
grp.transform!(flip)
… and then applying a translation to move the insertion point to the model ORIGIN.
Ex:
# Move the grp to the model ORIGIN:
pt = grp.transformation.origin
dist = pt.vector_to(ORIGIN)
grp.transform!( Geom::Transformation::translation(dist) )
Putting it all together in an undoable method ... (click to expand) ...
def correct_y
model = Sketchup.active_model
if model.selection.empty?
UI.messagebox("Select a group or component!")
return
end
grp = model.selection[0]
model.start_operation("Correct Y", true)
#
ents = grp.definition.entities
# Vertically flip the entities in grp's definition:
flip = Geom::Transformation.axes(ORIGIN, X_AXIS, Y_AXIS.reverse, Z_AXIS)
ents = grp.definition.entities
ents.transform_entities(flip, *ents)
# Move the entities by the height:
box = Geom::BoundingBox.new
box.add( ents.map(&:bounds) )
height = box.height
t = Geom::Transformation::translation([0, height, 0])
ents.transform_entities(t, *ents)
# Flip the grp instance as a whole vertically:
grp.transform!(flip)
# Move the grp to the model ORIGIN:
pt = grp.transformation.origin
dist = pt.vector_to(ORIGIN)
grp.transform!( Geom::Transformation::translation(dist) )
#
model.commit_operation
end
The group (blue rectangle) should have its bounding box minimum at the global origin (i.e. shift by the red arrow). Applying an additional move transformation to do so, seems to move the group origin as well (–> BAD). As far as I have been able to verify, your solution seems to work when the initial group axis is located on the upper left of the group bounding box (Y axis pointing downwards). In these cases, the result of your code snippet moves the group axis to the lower left corner of the bounding box (GOOD), and turns the Y axis upwards (GOOD), while the group’s bounding box min vertex stays at the global origin (GOOD). But, in my extension, this prerequisite for the initial group axis isn’t guaranteed… Any further suggestions to fix this are highly appreciated. I’ll keep trying from my end as well, but this becomes more of a ‘trial and error’ attempt..
A small update on my last post, which was not really correct. Actually, the fix presented in the code snippet did the following:
It reverts the direction of the group’s Y axis (GOOD)
It keeps the bounding box of the group at the global world origin (GOOD)
But, it does NOT place the origin of the group axis at the global origin (BAD).
The confusion in my previous post comes from a wrong interpretation of where the global origin actually was…
Thus: the correction that is still needed is to move the group origin to the lower left corner of the blue bounding box (which also corresponds to the global origin). Thus: a move in the opposite direction of the red arrow.
Wow, dezmo’s solution seems indeed to do what I was expecting. Still need to integrate and test in my code.
For what it’s worth, as said, I also continued the ‘trial and error’ approach on my side, and I was able to find something that worked for my case, based on Dan’s input. I basically had to change the origin of the flip transformation.
My current code, that ‘seems’ to work correctly (don’t ask me why, the Ruby API documentation is basically ‘worthless’ in this area), goes as follows:
ents = su_group.definition.entities
flip = Geom::Transformation.axes(Geom::Point3d.new(-bb.min.x, bb.min.y, 0.0), X_AXIS, Y_AXIS.reverse, Z_AXIS)
ents.transform_entities(flip, *ents) # Vertically flip the entities in grp's definition
move = Geom::Transformation::translation([0, bb.height, 0])
ents.transform_entities(move, *ents) # Move the entities by the bounding box height
su_group.transform!(flip) # Vertically flip the grp instance as a whole
pt = su_group.transformation.origin
dist = pt.vector_to(ORIGIN)
su_group.transform!( Geom::Transformation::translation(dist) ) # Move the grp to the model ORIGIN
The core of my 'trick' was in setting the origin of the flip transformation.
Please notice that in my code, the top level group is called su_group and it's bounding box is defined as bb.
But, I think that dezmo's solution is better, as it seems to have only 3 transformations, and also, it seems to be more generic, which might make it feasible to apply this solution recursively to all multilevel child groups of the toplevel group. So, I'll certainly need to integrate and test that version.
Thanks a lot!
Sorry dezmo, I integrated your version, it worked in a number of cases, but not always. Anyway, I had to remove the Z-axis inversion in your code, because in my code I ensure upfront that my faces are created in a plane above the XY plane (and thus don’t face automatically downwards). But, with that correction, your solution did not work all the time. I can’t see it very good in your film with a perspective camera, but I think your case works (like in Dan’s case) when both axis systems (global and group) are in the same x plane (and y plane - difficult to see in perspective view). The issue is that in my code pipeline that is often not the case (see e.g. the example in my earlier posts). Therefore, I believe that I really need the translation transformation, and flipping alone doesn’t solve all cases. I’m also not sure that my code version solves all cases (for my pipeline) but so far, I haven’t found any counter indications (trial and error).
What if you create a new group with the existing group (su_group ) as a parameter, then explode the original top level group. That way the new group will be the top level group and - since it is placed to the model origin by default - you do not need to brother with the transformations…
model = Sketchup.active_model
su_group_new = model.entities.add_group(su_group)
su_group.explode
Hi dezmo, you must have been reading my mind… While my ‘solution’ seemed to cover all cases, I suddenly discovered an even more bizar phenomenom with one specific test case. Group axis was placed and oriented correctly, and the group stayed at the world origin. BUT: all of a sudden, the bounding box of the group was no longer aligned with the boundaries of the group’s geometry??? (i.e. there was a shift up and to the right). I’ve never encountered such a group and bounding box in many years of using Sketchup. I assume it must be a bug in Sketchup…
So, I was searching for alternatives and recalled that somewhere else in my code, I had ‘moved’ child components from their parent to a newly created parent, after exploding the old parent. That code was a bit ‘special’ because it was actually a ‘move’ operation, and worked with persistentID’s, because it was not guaranteed that the move would happen during the same session. But from that piece of code, I remembered that the explode action seemed to ‘erase’ the transformation info at the new parent. So, I wanted to start testing that alternative, and then your reply arrived… Your code is actually even simpler, as it does’t require persistent_ids. The only extra thing I need to foresee (for my needs) is a transfer of group name and attributes, but that’s much simpler than the Sketchup transformation labyrinth. I ran a first couple of tests with your last code snippet, and it seems to work, including avoiding the extremely bizar ‘out-of-sync’ between the group geometry and the group bounding box.
I could not test your model because you did not post it in the original post. It it good to give a test model when asking for help.
…
Bounding boxes are determined by the extents of an object’s entities, NOT by it’s axes origin! (I.e., the origin canbe and oftenisoutside the bounds of it’s entities collection.)
Well, sorry, but again I was going off of the image you originally posted, lacking an actual model to test.
ADD: I had to quickly draw my own model for testing, which of course (as you surmised) had the group origin at the top left of the entities bounding box.
That’s okay because I am was confused about what you wanted anyway.
Take what I gave as a general pointer to using transformations and play with it. Consider it a learning experience.