I want to ask for advice from experienced users of SketchUp and coders on the implementation of ideas for creating a plugin. I will be very grateful to you.
Which I already have. I have code that finds certain groups in a model using the “cutter” attribute and creates a new “drill” group using the intersection_with method in the middle of the group it intersects. Simply put, this is the designation of drilling and routing for grooves in woodworking.
Ideas for future improvement.
I want the code after a new run to ignore “cutter” groups that have already created “drill” groups and only work with newly added “cutter” groups.
If the “cutter” group, which already created the “drill” group, was moved to other coordinates, then the code should track this and remove the previously created “drill” group labels, and also mark the moved “cutter” groups as such. which can be reused to create new drill groups.
How would you do it? Adding attributes, watchers, other capabilities? We are interested in opportunities that would simplify the process as much as possible and load large models less. I will be grateful for your ideas and recommendations.
When you create the ‘cutter’ group give it an attribute with a unique ID.
e.g. cutter_group1.set_attribute('KScher_cutter', 'id', Time.now.to_i )
when your code ‘drills’ add this to the attribute. cutter_group1.set_attribute('KScher_cutter', 'drilled', true )
so next time you ‘drill’ anything that cutter is ignored, because cutter_group1.get_attribute('KScher_cutter', 'drilled', false) only returns ‘true’ is unused…
As for the ‘drilled’ part… when it’s made add an attribute to that, matching the ‘cutter’ id
e.g. drilled_group1.set_attribute('KScher_drilled', 'id', id)
You need to add some observers to the cutter after it drills. https://ruby.sketchup.com/Sketchup/EntityObserver.html https://ruby.sketchup.com/Sketchup/InstanceObserver.html
etc
So that when it changes - e.g. it’s deleted, or it’s moved etc, then the matching id sharing drilled-group is deleted, and the cutter its flag is reset cutter_group#1.set_attribute('KScher_cutter', 'drilled', false ) so it can be used to drill again.
There’s obviously lots more to check etc, but this should give you the general idea…
Thank you for your attention. I wrote it down in my notes). I am currently gathering information and creating an implementation roadmap. Of course, as you write and test the code, there will be additional questions and issues that will need to be addressed by changing certain approaches.
I don’t have much coding experience. It’s just a hobby and self-study).
I also want to add that this is not a commercial project. I will be happy if someone later uses it in their own implementations.
I also try to understand in advance what limitations await me and how best to get around them.
So, I have a more specific question.
I’m trying to better understand the intersect_with method. This works well if there is a common parent context for both entities. Can and how does this work if the entities have different parent contexts? For example, “cutter” is in group “A” and the target group is in group “B”?
This code works if there is a shared parent context.
Code
# Function to find all groups with a specific name
def find_groups_by_name(entities, name, groups=[])
entities.each do |entity|
if entity.is_a?(Sketchup::Group) && entity.name == name
groups << entity
elsif entity.respond_to?(:entities)
find_groups_by_name(entity.entities, name, groups)
end
end
groups
end
# Function to intersect two instances and create a group with intersect edges in the context of inst2
def intersect_objects(inst1, inst2)
model = inst1.model
model.start_operation('Intersect Objects', true)
begin
# Create a new group in the context of inst2
group = inst2.entities.add_group
group.name = "drill"
# Perform the intersection
edges = inst1.entities.intersect_with(
false, inst1.transformation, group.entities, inst2.transformation, false, inst2
)
rescue => e
puts "Error: #{e.message}"
model.abort_operation
return nil
ensure
model.commit_operation
end
return nil if !edges || edges.empty?
group
end
# Main function to find all "cutter" groups and intersect them with other groups
def main
model = Sketchup.active_model
all_groups = find_groups(model.entities)
cutter_groups = find_groups_by_name(model.entities, "cutter")
cutter_groups.each do |cutter_group|
all_groups.each do |group|
next if group == cutter_group || group.parent != cutter_group.parent || !group.respond_to?(:transformation)
intersect_objects(cutter_group, group)
end
end
end
# Helper function to find all groups in the model
def find_groups(entities, groups=[])
entities.each do |entity|
if entity.is_a?(Sketchup::Group)
groups << entity
find_groups(entity.entities, groups)
elsif entity.respond_to?(:entities)
find_groups(entity.entities, groups)
end
end
groups
end
# Run the main function
main
FYI: Groups are the only instance member object of a SketchUp #entities collection that itself has the #entities wrapper method. It is just a shim for group.definition.entities that traces is existence back to when the Group class did not have a #definition method. (The API guys back then thought [wrongly] it was better to keep it secret that a Group was really a special kind of ComponentInstance with special behaviors.)
Another way of writing this could be:
def find_groups_by_name(entities, name, groups=[])
entities.grep(Sketchup::Group) do |grp|
grp.name == name ? groups << grp : find_groups_by_name(grp.entities, name, groups)
end
groups
end
For a non-case sensitive substring match:
def find_groups_with_name(entities, name, groups=[])
entities.grep(Sketchup::Group) do |grp|
grp.name =~ /#{name}/i ? groups << grp : find_groups_by_name(grp.entities, name, groups)
end
groups
end
@DanRathbun thank you for your explanations.
I found a very interesting thread on the forum Intersect_with revisited | sketchucation
In it, @slbaumgartner (thank you) gave a very insightful answer explaining the intersect_with method. In particular, an explanation of how to work with entities that have different parent contexts. I will try to check it now.
It is also strange that this document cannot be found here on the forum. That’s why I’m adding it. SketchUpIntersect_with.pdf (86.3 КБ)
Continuing to uncover secrets, which Dan already revealed…
You can find all group (instances) of the model by its definition group? method to determine if the component definition is used to hold the elements of a group and if yes, then collect its instances e.g.:
def find_groups( model = Sketchup.active_model )
model.definitions.select(&:group?).flat_map(&:instances)
end
then you can use it to further filter by its name e.g.:
def find_groups_by_name( name, model = Sketchup.active_model )
find_groups().select{|gi|
gi.name == name
}
end