# How to use a method to dynamically add to group

I am trying to add to a group using a definition and I am not sure what I am doing wrong. Maybe I need to create two separate groups and merge them. maybe the group should be a global variable? The first photo shows the first via with the second via commented out and the second shows when I try to run the same definition twice. It says that the main_face has been deleted. Shouldn’t it be recreated as it runs through the method a second time though?

``````SKETCHUP_CONSOLE.clear
def via(group,pts,thickness,lateral_extension=0,inversion=false) #,:mode=>nil)
min_x = pts.map{|p| p[0]}.min
max_x = pts.map{|p| p[0]}.max
min_y = pts.map{|p| p[1]}.min
max_y = pts.map{|p| p[1]}.max
if main_face.normal.z = -1
main_face.reverse!
end
for v in main_face.vertices do
min_v = v.position
break if min_v.x == min_x && min_v.y == min_y
end
main_face.pushpull thickness
cut_vector1 = [-lateral_extension,0,thickness]
cut_vector2 = [0,0,thickness]
cut_edges = group.entities.grep(Sketchup::Edge)
cut_edges.each(&:find_faces)
cut.faces[0].followme main_face.edges
if inversion == true
center = group.bounds.center
vector = Geom::Vector3d.new(1,0,0)
angle = 180.degrees
transformation = Geom::Transformation.rotation(center, vector, angle)
group.transformation= transformation
end
return group
end

ents = Sketchup.active_model.entities
z = 0
pts = [0,0,z], [5,0,z], [5,8,z], [0,8,z]
etch1 = via(etch,pts,10,6)
etch2 = via(etch,pts,10,6,true)
``````

NEVER USE GLOBALS ! All of your code should run within your own namespace modules.

I see nothing in your code that directly references any group’s definition.
However, `group.entities` is really a shortcut for `group.definition.entities`.

Please be aware that SketchUp itself will “clean up” it’s `DefintionList` … removing any group definitions that have 0 instances. So at this point, you need to ask yourself if you should really be using component instances instead of groups.

The code indicates that the group is created outside the `via()` method. But the snippet never shows how this method is called (where the group is actually created.)

To answer the topic’s main question … if you have a reference to a valid group definition …
… and want to add another group instance …

``````# group1 is an existing instance of a group definition ...
active_ents = model.active_entities
gdef = group1.definition
``````

Note that an array of Numerics can be accepted by many of the API methods in place of a transformation object. (The array is converted internally by the API.)

Sorry, I meant methods not definitions. If I want to add a group, then add faces and extrude using a method the second run around the method says that the group has been deleted.

Also, I accidentally changed the method name at the bottom without changing it at the top. It was supposed to be etch at the bottom.

``````# group1 is an existing instance of a group definition ...
active_ents = model.active_entities
gdef = group1.definition
``````

Do you have any examples of how this is used? I am not really sure how this works.

What exactly is a definition?
Would adding group2 in the way you showed make it part of group1? Or would it still be it’s own seperate group?

I cannot see where the group is being deleted. But as I said, SketchUp will delete groups because their definitions have no instances, and if it’s entities collection has no entities. So what we usually do is add a cpoint at the origin of a group entities to keep SketchUp from deleteing it if we have other stuff to do.

In your code the next few lines are creating numeric values and arrays of Numerics, instead of dealing immediately with the group object. You are giving SketchUp time to delete the group.

Secondly, you have local variables that are not within a method or module. If you are loading code from a file, Ruby will delete local variables at the top level ObjectSpace to prevent them from propagating into everyone else’s Module and Class objects.

Lastly get in the habit of using the `active_entities` instead of the top level model’s entities …

``````ents = Sketchup.active_model.active_entities
``````

… unless you know that geometry should be at the top level.

A definition is the object that owns (ie has) the `Entities` collection and other `Behavior` properties that instances will use.

There are 3 kinds of instances.

• `Group`
Groups are hidden from the Component Inspector (Browser) “In Model” collection listing (ie the model’s `DefinitionList`, even though their defintions are a member of the list.) There are most often used when only one (or a few) instances is needed and it is not something that needs to be named or reused like a part or a window.
Manually editing one group instance (of a definition that has multiple instances) in the GUI will cause SketchUp to uniqueify that instance by creating a new unique definition for it to use.

• `ComponentInstance`
Components are usually used when a modeler will insert many instances of a component into a model. Or when behavior needs to be controlled. Thier definitions are always listed in the Component Inspector (Browser) “In Model” collection listing.
Editing ANY instance of a component (manually or via code) will affect and change ALL instances of that component, as it is changing the definition.

• `Image`
Images are basically an texture (from a file) applied to a face with a hidden material and bounded by edges. Extensions should not manipulate these objects without exploding them.

There are likely quite a few topics on the difference between a group and a component instance.

NO.

It would be a separate instance of the same group definition sharing the same entities.
Any change to the definition’s entities using code will affect both instances.

However, manually entering the editing context of a group instance when there are more than 1 instance will cause SketchUp to make that instance unique by cloning the definition and renaming it. Thereafter each group can have separate changes as they no longer share the same definition (and so have differing entities collections.)

Using Groups and Components via the Ruby API requires understanding some technical aspects of how they work that go beyond what a user needs to know when using the GUI. It is vital to learn the relationships between ComponentDefinition, ComponentInstance, and Group.

in a nutshell

• A ComponentDefinition provides a sort of mini-model of a collection of geometry (it actually saves as a SketchUp model file with a few flags set). Its geometry is held in its own Entities collection. The coordinate values of those Entities are independent of the model’s coordinates, they are “abstract” or “local”. All of the ComponentDefinitions in a model are kept in its DefinitionList.
• A ComponentInstance is a concrete placement of a copy of the mini-model into your model. It is located, oriented, and scaled from the mini-model using a Transformation that maps the ComponentDefinition’s local coordinates into the full model’s coordinates. Its Entities are still held separate from the model’s Entities (and from any other Component’s Entities), which is what prevents them from sticking to other geometry. The SketchUp engine takes care of presenting the instance at the appropriate place, orientation, and scale. Many people find it convenient to think of the ComponentDefinition as a pattern or template and a ComponentInstance as the result you get by spraying paint through it at a particular place on a drawing.
• A Group is a special kind of ComponentInstance that has a flag telling SketchUp to handle it differently than a standard ComponentInstance. It also has some shortcut methods to access information from its ComponentDefinition without explicitly going through the definition method, though that will also work. @DanRathbun noted these differences in his post.

When a user opens a Component or Group for edit via the GUI, they are actually opening the mini-model and working within the ComponentDefinition’s local coordinates. But the GUI takes care of presenting the Entities in the correct location, orientation, and scale for that instance so that if necessary placement of new geometry can be relative to surrounding objects.

But when you access the ComponentDefinition’s Entities via the Ruby API, you are working purely within the abstract local coordinates. If you want to relate those coordinates to the model, you have to transform them the same way as the GUI does to display an instance in the model.

In the attached clip. I use the way that the 2020 GUI’s Tape measure tool displays coordinates if you float over a point. If the group is not open for edit, the coordinates are model coordinates. If it is open for edit, they are the definition’s local coordinates. You can see that the x and y values match because the group’s origin is mapped to the model’s z axis by its transformation, but the z values differ because the group’s origin is actually placed below the model origin. Your edges are offset vertically because your code isn’t accounting for this origin shift.

2 Likes

I’m still having trouble with this. When I try to input the second group it says that the entity has been deleted.

etch_v5.rb

``````SKETCHUP_CONSOLE.clear
def etch(group,pts,thickness,lateral_extension=0,inversion=false)
min_x = pts.map{|p| p[0]}.min
max_x = pts.map{|p| p[0]}.max
min_y = pts.map{|p| p[1]}.min
max_y = pts.map{|p| p[1]}.max
p "groupa = #{group}"
g =  group.entities
p "g=#{g}"
p "groupb=#{group}"
if face.normal.z = -1
face.reverse!
end
for v in face.vertices do
min_v = v.position
break if min_v.x == min_x && min_v.y == min_y
end
face.pushpull thickness,true
cut_vector1 = [-lateral_extension,0,thickness]
cut_vector2 = [0,0,thickness]
cut.followme face.edges
if inversion == true
center = group.bounds.center
vector = Geom::Vector3d.new(1,0,0)
angle = 180.degrees
transformation = Geom::Transformation.rotation(center, vector, angle)
group.transformation= transformation
end
end

ents = Sketchup.active_model.entities
active_ents = model.active_entities
gdef = via.definition
p "via = #{via}"
p "sub = #{sub}"
z = 0
pts = [0,0,z], [5,0,z], [5,8,z], [0,8,z]
etch1 = etch(via,pts,10,6)
etch2 = etch(sub,pts,10,6,true)

``````

For some reason this works

``````
etch1 = etch(via,pts,10,6)
via = via.to_component
etch2 = etch(sub,pts,10,6,true)
``````

but this doesn’t

``````sub = ents.add_group
etch1 = etch(via,pts,10,6)
via = via.to_component
etch2 = etch(sub,pts,10,6,true)
``````

In Ruby, it is allowed (but discouraged by style guides) to make assignments within the conditinal expression of an `if`. You are doing this but I don’t think you really meant to.

Perhaps you mean to do:

``````  face.reverse! if face.normal.z == -1.to_l
``````

… or …

``````  face.reverse! if face.normal == Z_AXIS.reverse
``````

Both of the above use the API’s overridden `==` method for the class (`Length` in the former, `Geom::Vector3d` in the latter example.) These API methods do comparison within SketchUp’s internal tolerance.

… is likely better written as …

``````  min_x = pts.map(&:x).min
max_x = pts.map(&:x).max
``````

Ie, the SketchUp API adds some nifty methods to Ruby’s base `Array` class.

SEE:

Regarding this …

You have an array of points (`pts`) that you’ve used to make a face (`face`), and you want the minimum point (which was used for the minimum position for the face’s vertices,) so you might as well just search the `pts` array rather than create a new array (`face.vertices`) and then create new point objects (`min_v = v.position`) for each one of the vertices for comparison.

Secondly, leverage the fact that the Ruby core `Enumerable` module is mixed into Array and API collection classes. (Ie you do not need to author custom search loops.)

``````  min_v = pts.find {|pt| pt.x == min_x && pt.y == min_y }
# safe to use min_v
end
``````

There is no need to call the `==` method here and compare against `true` as Ruby evaluates boolean expressions for the logical `if`. So just use …
` if inversion`

Keep in mind that in Ruby only `false` and `nil` evaluate as falsehoods. Everything else evals truthy including empty arrays, empty strings and the integer `0`

Other potential problems:

• Your code is not verifying that the faces were correctly created before using them for other geometry creation. Check that a face reference is truthy (ie, not `nil`) or valid (`Entity.valid?`) after creation, and before and after doing a followme.