Geometry Merge (Explode/Re-parent/Combine)

Hi there,

I’m facing some unexpected behavior when handling geometry with the ruby API (for info I’m working on SU2017, the version deployed in my company). Here is a use case explaining what I’m facing that I’m not able to understand and handle correctly… I hope someone may help on this (I’ve made some reseach on google and on the community without success, maybe the keywords used wheren’t the good ones).

Here is an exemple (TEST_cnc.skp (145.9 KB) ), where I’m trying to combine the CNC_hole component geometry into the ITM group.

My problem is to correctly send the component geometry into the group so (to shorter the code below I’m using the object name instead of variables) :

  • If I’m doing something like ITM.entities.add_group(CNC.explode), I get the error below which leads me to understand that not all exploded geometry should be reparented but maybe a part of it (Instead of the error sometime it works but I also get a BugSplat in the meantime).
    Error: #<ArgumentError: All Entities must have a common parent>
  • If I’m exploding the CNC component (CNC.explode.collect{|e| e.class}.uniq) to look what I’m getting , I get this list of classes [Sketchup::Edge, Sketchup::Face, Sketchup::Vertex, Sketchup::EdgeUse, Sketchup::Loop].
  • If I’m only reparenting the [Sketchup::Edge, Sketchup::Face] by using this command ITM.entities.add_group(CNC.explode.select{|e| [Sketchup::Edge, Sketchup::Face].include? e.class}) (trying to include any of these classes [Sketchup::Vertex, Sketchup::EdgeUse, Sketchup::Loop] leads to the starting error). Then I’m getting what I want but the group is generated into the model but not in the ITM group. WTF !?!
  • An another strange thing is that trying to send the CNC component into the group leads to the same behavior as the previous bullet (ITM.entities.add_group(CNC)).

Am I trying the right way to do it ? Did I missed something ? After that if my geometry is at the right place I’ve been able to manage/lead correctly the geometry merge process : I have the start and the end but something is going wrong in between.

Thanks all for reading so far and for your appreciated help !

The way you are going about this is attempting to nest a group inside the ITM group. Is that what you intended? If so, a simpler way would be:

# assume itm is the group named ITM and hole_def is the ComponentDefinition for CNC_hole
# and that transformation places the new instance within itm's local coordinates
itm.entities.add_instance(hole_def, transformation)

Following that, if you want the hole to cut the faces of itm, you will have to do an intersect_faces operation and delete the required faces.

1 Like

Since this is a company, you must have Pro, which has boolean operations available.

So after doing as Steve says …

cnc = itm.entities.add_instance(hole_def, transformation)
itm_grp = itm.subtract(cnc)
if itm_grp
  # itm_grp will be nil if unsuccessful, so test it booleanwise
end

Your example code shows you using constants as local references. This is not proper Ruby coding syntax.

@slbaumgartner Thanks for your proposal, I guess that should do the trick by removing the instance and store a new one directly into the group, I wasn’t thinking that way assuming that existing components were already present in the scenes I have to clean. For the post process that clean the geometry (cut, intersect, delete) that’s already ok and working

@DanRathbun Thanks for your suggestion but it won’t be applicable for all the usecases I have to cover. For the code suggestion, as I said, to shorter the code above I’m using the object name in upcase instead of variables (I’m ot using constants that way in my code…)

Your both solutions are nice other ways of thinkinking which is fine to me, anyway does it seems normal to you the observed beaviors ? Aren’t they bugs ? Should we think to upgrade to newer Sketchup version to get rid of it ?

No

Error: #<ArgumentError: All Entities must have a common parent>

… means YOU have used entities.add_group method incorrectly.

The reason for your code error is because cnc is a member of model.entities not itm.entities.

IF you add the cnc instance into itm.entities (as Steve showed) you will not get the error using itm.entities.add_group, however then you do not even need to add a group as cnc will be a component instance. (A group is a special kind of component instance that is hidden from the “In Model” collection shown in the Components Inspector.)

Once inside itm.entities you may decide what pattern to choose. Explode it, or do a boolean subtract, whatever. With explode you will need to find and delete the upper and lower faces, whereas the boolean operation will remove them for you.


The other option is to draw one face inside itm.entities and then do a pushpull to the thickness of itm.

You’re totaly right about the error, sorry for the missunderstanding, I wasn’t enought clear, I wasn’t arguing about the error (as I said myself I understood that I did something wrong an analyzed what was returned by the explod method to filter what was really usefull : Faces and Edges).

I was much more talking about the fact that requesting a group creation inside an another one with entities as arguments leads to a group creation under the model.entities instead of the itm.entities:

itm_group.entities.add_group(cnc_component.explode.select{|e| [Sketchup::Edge, Sketchup::Face].include? e.class})

Did I filter correctly the exploded geometry ? Did I missed something ? Why it doesn’t work in this case ? I’m just trying to understand how it works backstage.

Sounds strange to me as well. You can log a bug report at the issue tracker.

But 2017 will never be updated.
You can check the release notes to see if any bug like this has been fixed in later versions.

So using the @slbaumgartner workaround is fine when using components (same problem occurs if the geometry to merge is held by a group instead of a component instance). Here is my code below for component instances :

#Insert machining geometry into the plate container
cnc_local_transform = current_cnc.transformation * plate_container.transformation.inverse
cnc_container = plate_container.entities.add_instance(current_cnc.definition, cnc_local_transform)
cnc_container.explode
current_cnc.erase!

#Force the geometry to merge
plate_faces_snap = plate_container.entities.to_a.grep(Sketchup::Face)
plate_container.entities.to_a.grep(Sketchup::Edge).each{|e| e.find_faces}

#Clean the extra faces created during geometry merge process
plate_extra_faces = plate_container.entities.to_a.grep(Sketchup::Face) - plate_faces_snap
plate_extra_faces.each{|f| f.erase!}

@DanRathbun I’ll give a try on SU2019 and log a bug only if I can reproduce the behavior I’m facing in SU2017. As I can imagine only SU2019 would be updated right ?

Thanks both of you for the discussion around these behaviors

1 Like

@slbaumgartner @DanRathbun

Back again,

I think that the reported behavior is somehow related to this Entities.add_group causes Bugsplat!.

When trying to use the method add_group(entities) like so some_container.entities.add_group(an_array_of_some_entities) when what is to be added into the new group is NOT already in the model.active_entities then the group is created under the Sketchup.active_model.entities

This is really tricky and I dont get how to perform the @TIG solution explained in his post :

If what is to be added into the new group is NOT already in the model.active_entities , then use
group = some_entities.add_group()
then use add entities to group.entities afterwards…

I can’t find any way to perform that, it’s like reparenting entities into an another group what is impossible in Sketchup. I’m I wrong ?

More over, problem still existing in SU2019…

You will need to work with the way that the API and SketchUp’s core are designed to work.
Just because it does not work the way you wish it to, does not mean there is a bug.

It’s quite tricky to deal with it assuming that :

  • There is no way for reparenting entities.
  • There is no way for creating a group with entities when its not in the model.active_entities (which is quite always the case when scripting because the model.active_entities == model_entities by default when opening a model)
  • There is no way for changing the model.active_entities (except ascend in path using the model.close_active which doesn’t let enought control to setup the requested model.active_path.

The only solution I think of is always working in the model.entities context and then be able to recreate asending hierrarchy by using the Sketchup.active_model.entities.add_group(an_array_of_some_entities) where :

  • an_array_of_some_entities is the enties I’d like to reparent.
  • add_group is the parent I’d like

That means you need to know exactly what hierarchy you need to build from the really begining and if you’re dealing with an existing scene (which is actually my case) that you need to clean and reorganize there is no API entries to do this. The only way it the @slbaumgartner one that means :

  1. transform each group you need to reparent into a component.
  2. delete the component instance created.
  3. instanciate the component where it needs to be from the definiton created.
  4. explode the new instance created
  5. delete the definition created (which is also an another problem 'cause only able to purge model definitions but not remove a single specific definiton)

Sorry to say this, but I’ve never worked with a 3D software API like so !!! I’m really stuned. SU seams really powerfull in some fields but really really really weak in the way it implents/handle the groups hiearchy…

It is not true that there “is no way”.

Other coders are working within the system. Yes it is a bit challenging.

The “trick” is to NOT use the #add_group method with entity arguments.

Instead create an empty group in the entities collection that has the objects you wish to “reparent”.

Add a cpoint to the group to keep SketchUp from garbage collecting the new empty group.

Then add copies of the objects that you wish to “reparent” into the new group’s entities collection.
You might need to recreate attribute dictionaries and copy other properties to the new object copies.
If the objects to “reparent” are themselves group or component instances, then this is easily done using #add_instance. If the objects to copy are geometric primitives, then you’ll need to recreate them in the new group’s entities collection.

Once the copies are made, you can delete the originals.

Then, add a new instance of the new group instance’s definition to the new parent entities …
new_parent.definition.entities.add_instance(new_group.definition, transform)

Lastly, erase the new group (the 1st instance, ie, the one used to make the copies,) leaving the 2nd instance now in the new parent’s definition’s entities collection.

When you company updates to v2018 or above you’ll have the DefinitionList#remove method.

The SketchUp core was written as a GUI application for real people to interact with. When they do the users change the active entities and the core code is written to use the active entities. This is just how things are. (ie, the API wasn’t added until around v4.)

But, you are free to file a API Feature request to “reparent” entities across contexts.

Thanks again Dan for trying to get me understandand the way of thinking in SU to how efficiently operate with group hierarchy and geometry.

This is now pretty clear to me even if this remain really tricky.

Thanks again for all.

1 Like