Entities.add_group causes Bugsplat!

The API documentation warns:
“NOTE: calling add_group with entities in its parameters has been known to crash SketchUp before version 8.0. It is preferable to create an empty group and then add things to its Entities collection.”

But there’s more to the problem. The entity array you pass can contain ONLY edges, faces, text, groups, and/or component instances (EFTGC). If you array contains other entities, add_group should gracefully ignore them. But instead you usually get a bugsplat.

The problem arises if, for example, you want to convert a component instance to a group. So you need to first explode the instance and add the resulting array to a new group. But the array returned from componentInstance.explode returns EFTGC plus Edgeuse, Loop, Vertex, Curve and ArcCurve entities. Add_group doesn’t need them in the array but derives them from the AFTCG entities to put them in the new group.

A while back, TIG posted a script to do this but it left out some of the above entity tests. Here’s a revised version:

def self.comptogroup(selcomp)
mod = Sketchup.active_model # Open model
ent = mod.active_entities # All entities in model
sel = mod.selection # Current selection
#converts component instance to a group
tr = selcomp.transformation
dad = selcomp.parent
nsm =selcomp.definition.behavior.no_scale_mask?
# attributes

ent.transform_entities(tr.inverse, selcomp)

entarr = Array.new(0)
#puts "next is explode"
entarr = selcomp.explode

#puts "explode done " + entarr.length.to_s
# now remove entities that will confuse add_group.
grouparr = Array.new(0)
for item in entarr
  if item.class.name == "Sketchup::EdgeUse"
  elsif item.class.name == "Sketchup::Loop"
  elsif item.class.name == "Sketchup::Vertex"
  elsif item.class.name == "Sketchup::Curve" 
  elsif item.class.name == "Sketchup::ArcCurve" 
  #puts item.to_s
  grouparr << item
#puts "number of entities for group = " + grouparr.length.to_s
group = ent.add_group
group = ent.add_group(grouparr)
#puts "group added"

ent.transform_entities(tr, group)
# group not allowed to have face_me, gluing, cutting behaviors 
# but you can limit scaling 
group.definition.behavior.no_scale_mask = nsm
# attributes
        group.set_attribute(na, k, v)
} if ads
return group
end #def

#=begin # to test insert # before =
mod = Sketchup.active_model # Open model
ent = mod.active_entities # All entities in model
sel = mod.selection # Current selection
selcomp = sel[0]
group = self.comptogroup(selcomp)
#=end # to test insert # before =
1 Like
grouparr = Array.new(0)
for item in entarr
  if item.class.name == "Sketchup::EdgeUse"
  elsif item.class.name == "Sketchup::Loop"
  elsif item.class.name == "Sketchup::Vertex"
  elsif item.class.name == "Sketchup::Curve" 
  elsif item.class.name == "Sketchup::ArcCurve" 
  #puts item.to_s
  grouparr << item

… is positively ugly (and slow string comparison.)

Class comparison is fast!

grouparr = entarr.reject {|item|
  item.is_a?(Sketchup::EdgeUse) ||
  item.is_a?(Sketchup::Loop)    ||
  item.is_a?(Sketchup::Vertex)  ||
  item.is_a?(Sketchup::Curve)   ||


bad = [

grouparr = entarr.reject {|item| bad.include?(item.class) }

Re. attribute dictionaries.

The method above ignores those attached to the component instance, and copies only the first level attached to the component definition, to the new group instance. (Groups are also components that just have their definition hidden in the component browser.)

There are some components (Trimble extensions) that use multi-level dictionaries.

group = ent.add_group
group = ent.add_group(grouparr)

… the first line seems frivolous ?

Okay,… now what happens if you first move the component instance into the new group, then explode it afterward ?

Exploding it in the model first may cause it’s geometry to interact with unprotected geomtery.

I like the last idea although my understanding is that geometry interaction won’t occur until at a commit (which in my case would occur in the method that calls this one).

Your other style comments apply to the original code supplied by TIG. I just added Curve and ArcCurve to the filter.
Since I’m dealing only with a single small instance each time a user clicks on the command, (maybe a dozen entities), speed is not important so I left it that way.

I agree SketchUp shouldn’t BugSplat, but all you can do is file a bug report and work around the problem.

You could first create a Group from the Instance:

grp = entities.add_group(instance)

Then set all the Group’s attributes/properties, then explode the Instance afterwards.

Alternatively if you want to keep your existing code, it is better to explicitly include what you want instead of excluding what you do not:

entarr = entarr.grep(Sketchup::Drawingelement)


To reiterate…
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…

If the entity or entities are in the active_entities context then you can use:
group = some_active_entities.add_group(some_active_entity)
group = some_active_entities.add_group(model.selection.to_a)
group = some_active_entities.add_group(an_array_of_some_active_entities)

Obviously, you can’t add an entity from one context directly into another [a BugSlat awaits!] - however, if the entity is a group or component_instance, then you can get the definition of that and use add_instance(), copying the original’s transformation, making allowances for the context’s transformation etc.
Similarly you could ‘clone’ faces and edges etc - but not easily…
If you merely want to copy it, then there will be two instances [for a group you might want to consider using make_unique]
If you want to move it from one context to another, then after the add_instance() you will need to use .erase! on the original item…



Any difference between:



item.class == Sketchup::EdgeUse

is_a? will match if its argument is item’s class or any superclass. If you are interested in whether item can be used the same way as an instance of a particular superclass, that is the test to use.

The equality test will match only if Sketchup::EdgeUse is the exact class of item.

Unless your code has created a subclass of Sketchup::EdgeUse the difference is unimportant. But creating subclasses of SketchUp Ruby API classes is risky because they tie to behind-the-scenes C structures and not all of them were implemented with a mind to allowing Ruby subclasses.

1 Like

I would not explode an existing component instance, use the returned entities array, and create a group, to convert it to a group. Doing so risks having the geometry merged with existing geometry in the same drawing contexts, applies layer and materials from the instance if not reset to nil, and loses the axes placement. Instead you can create a new empty group, add a component instance with the unity transformation to it, and explode this component instance.


sorry, I meant any difference between the two approaches in terms of speed as that was Dan’s main point.

Yes there is. Method calls eat time. The first (.is_a?) is one method call. The second …
item.class.==(Sketchup::EdgeUse) … is 2 method calls.

(The Ruby interpreter allows us mere humans to omit the dot notation for the == method and the parenthesis around it’s argument list. It is known as having “keyword status” because it is basically a global method, being defined within BasicObject, Object or Kernel.)


And this holds true as long as classes do not override the == method inherited by BasicObject.

Equality — At the Object level, == returns true only if obj and other are the same object. Typically, this method is overridden in descendant classes to provide class-specific meaning.

A check on Sketchup::EdgeUse and it’s superclass Sketchup::Entity reveals that this method has not been overridden.

This is not the case throughout the API. Many Geom module classes, and the Length class override the == method for class specific purposes.

In general the difference is so low that it makes no difference. I’ve never seen .is_a? be a bottleneck.

Time and profile your code for each scenario - general rule of thumbs gets you only so far. And you always want to work on the bottlenecks.

I’ve been working on getting the RubyProf profiler working in SketchUp. It’s still a work in progress, but if you want to experiment: GitHub - SketchUp/speedup: Profiling tool for SketchUp Ruby extensions.