Basically, given a group with several subgroups within it, I’d like to loop through and union all of them…
def self.union_entities(group_in)
# convert entity list into array
temp_union_array = group_in.entities.to_a
# remove the first member from of array and define as new variable
union_result = temp_union_array.shift
# run union on each item in remaining array
temp_union_array.each_with_index{|item,index|
#print "#{index}: #{item}\n"
next unless item.valid?
union_result = union_result.union(item)
}
end # end union_entities
You need to test the items for manifoldness before subjecting them to a union (or any solid boolean operation.)
Otherwise nil is returned from the operation and it mucks up your loop.
OK, not necessarily hang but take a longer time then you might think. Here is a test model. The TEMP_VOIDS group is the one that I am looping through to union them all together (in preparation for a subsequent subtract operation).
The union process takes about 12 seconds. Compared to the rest of the script this is certainly the bottleneck!
If you want to know, I am unioning everything because if I simply explode everything in here, you sometimes end up with an invalid solid because of conditions where two “voids” overlap.
No, I take that back… HANG! Its definitely taking too long. The result is visible in the display window in about 12 seconds but the “working” cursor is present for about another 40 seconds… doesn’t seem right.
OK, not sure how I would check if a certain item id was already in union but meanwhile, I tried .each instead of .each_with_index with no luck. I’m not sure how re-ordering would work at all, Isn’t .each just a loop through every entity? It wouldn’t do the same entity twice would it?
The first rule of collection iteration is, … do not modify the collection whilst iterating it.
If you do, the loop gets confused, skips members or iterates them more than once, especially if the “mods” result in the size of the collection changing (ie, members being deleted or becoming invalid.)
This code from an earlier post was done correctly.
You made an array copy of the entities collection using #to_a, and used the array copy to iterate whilst actually modifying the entities collection.
OK so if the original code was correct, I’m still not sure what is causing this operation to take so long. Did you guys experience a similar lag on the test model I uploaded?
That triggers a memory that a few version ago there was a reported large difference in time taken for operations inside a group and outside at the top model level. ( I don’t remember which was longer, but I think I recall that the results were opposite what we’d expect.)
I don’t think it is making a clone of all the objects, just an array of references to the SketchUp groups.
I tried to work out if Entities#to_a actually gives you a clone of the entities in an array or an array of references to the entities. If you open test4.skp, select the “TEMP VOIDS” group and paste this in to the Ruby console:
g = Sketchup.active_model.selection[0]
g_copy = g.entities.to_a
g_copy[0].name ="Erase me"
You should see the name of one of the groups being changed in the outliner. Follow with this to remove that group
g_copy[0].erase!
All done from the array given by Entites#to_a. So I think you are still iterating through a collection while modifying that collection. It did work when I tried it, but maybe that was just to do with some random order that the collection was iterated through. Try using this while loop instead:
def self.union_entities(group_in)
temp_union_array = group_in.entities.to_a
union_result = temp_union_array.shift
while temp_union_array.size > 0 do
union_result = union_result.union(temp_union_array.shift)
end #while
end #union_entities
Regarding your comment about cloning, that seems to be the meat of the problem. When we run:
g_copy = g.entities.to_a
I think you are correct that we are not cloning the group because nothing changes in the Outliner. We are merely creating a ruby object that references the group and its entities. Therefore, any iterating, whether its on the group itself or the array reference to it, would seem to be breaking Dan’s first rule of iterating:
Does that make sense to you too?
If so, I’ll have to perhaps work on making a copy of each entity or the whole group and work on that I would imagine. (scratches head).
Yes it makes sense. That’s why I used the while loop. I’m expecting the array to be smaller each time.
You’re still getting much slower execution times though. Maybe there’s something else that’s different. Are you running something else at the same time? Running out of RAM, etc?