Boolean: how do I 'outershell' multiple groups/components?
-
Hello,
I have a question about the Boolean functions 'outer_shell' and 'union'.
See attached file.
If I select all (6 groups ) and use the mouse to 'outershell' I get the 3 single groups plus the new united group.How do I mimic this behavior in code??
All I could imagine is this rather brute force technique:<span class="syntaxdefault">entlength </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> entities</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length <br /> for f in 0</span><span class="syntaxkeyword">...</span><span class="syntaxdefault">entlength<br /> for g in 0</span><span class="syntaxkeyword">...</span><span class="syntaxdefault">entlength<br /> begin<br /> entities</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">f</span><span class="syntaxkeyword">].</span><span class="syntaxdefault">outer_shell</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">entities</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">g</span><span class="syntaxkeyword">])<br /></span><span class="syntaxdefault"> rescue<br /> end<br /> end <br /> end</span>
any ideas are appreciated!
Thnx , liquid
-
Your code will fail for several reasons. If you are going to use
for
..in
, you must subtract1
from the length of the array/set/collection. (It's better to use theeach
iterators, to avoid "fence post" errors.)Secondly, you must make an
Array
copy, of theentities
(or any C++ collection,) if you will be making model changes during the iteration.Third you need to test the objects for class
Sketchup::ComponentInstance
orSketchup::Group
, etc., before doing something that you can only do to those class objects, and it's also safer to test if they are manifold, viaent.manifold?
, before attempting boolean operations on them.Fourth, boolean methods are pro only, so a conditional to test
Sketchup.is_pro?
is also needed.Have you checked the [ Code Snippets ] Index in the Developers Forum ??
Or searched the Developers Forum ?? (TIG has explained both basic and advanced entities iteration many times over.) -
Hi Dan,
Thanx for your input. Used to post my questions in the 'plugins' section, my apologies for that.
First thanx for pointing me out, the code snippet section was totally new for me, so that's a good one!I realize I was to quick posting my code. Indeed, the iteration range was not ok. The tree tests (right class?,
manifold? and pro) aren't relevant in my situation.@dan rathbun said:
Secondly, you must make an Array copy, of the entities (or any C++ collection,) if you will be making model changes during the iteration.
This is the part I don't get. You say TIG wrote a lot about this subject..
I'will look for it then, and come back here if I still don't get it.Thank you!
-
@liquid98 said:
The tree tests (right class?, manifold? and pro) aren't relevant in my situation.
Yes they are.. you just do not realize it yet.
You cannot callouter_shell()
ormanifold?
on an entity, unless it is either aSketchup::Group
orSketchup::ComponentInstance
instance object.
IF you callouter_shell()
whenSketchup.is_pro?
is false (Free edition,) an Error will be raised.@liquid98 said:
@dan rathbun said:
Secondly, you must make an Array copy, of the entities (or any C++ collection,) if you will be making model changes during the iteration.
This is the part I don't get.Because when changes are made to the model on the C++ side, the objects in the C++ collections are shuffled around. Your iterator will "lose it's way," and miss processing some entities, and process others multiple times.
So use the
to_a
method, to make anArray
, of ONLY the groups in the C++ collection, like this:grps = [] model.entities.to_a.each {|ent| grps << ent if ent.is_a?(Sketchup;;Group) }
Or use the
find_all
method (which always returns an array.):grps = model.entities.find_all {|ent| ent.is_a?(Sketchup;;Group) }
Then you can safely iterate using the members of the
grps
array (and when the entities collection rehashes itself, it will not effect your iteration.)unless grps.empty? grps.each {|grp| # process each group, one at a time } end
-
Hi Dan,
@dan rathbun said:
Yes they are.. you just do not realize it yet.
I do realize that. But the script I'm writing is for internal use. So everything
this script will process meets the criteria already. But you couldn't know that..
By the way 'outer_shell
' is also available in free version, I think.The reason for adding the groups to an array is also clear to me now, otherwise the iteration process is not going well and SU could skip a few items.
Now for the real problem:
First I need to add all the candidates for the boolean unite operation to an array. In other words: only group-entities that intersect will be processed.
I tried
BoundingBox.intersect
but it gives me false positives in cases of close
proximity. So I've done some more reading and I ended up in the loops section of the SU API. Can the loop class help me here tot detect if groups really intersect??This is the only problem left, the boolean problem I solved by my self. This script unifies all intersecting groups to one solid:
<span class="syntaxdefault"> file </span><span class="syntaxkeyword">=</span><span class="syntaxstring">"D;\\Desktop\\bool.skp"<br /></span><span class="syntaxdefault"> Sketchup</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">open_file</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">file</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> <br /> model </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Sketchup</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">active_model<br /> entities </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> model</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">active_entities <br /> <br /> foo</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> nonfoo </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">[],</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">[]<br /></span><span class="syntaxdefault"> grps </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> model</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">entities</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">find_all </span><span class="syntaxkeyword">{|</span><span class="syntaxdefault">ent</span><span class="syntaxkeyword">|</span><span class="syntaxdefault"> ent</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">is_a</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Sketchup</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">Group</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> <br /> grps</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">each </span><span class="syntaxkeyword">{</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">|</span><span class="syntaxdefault">a</span><span class="syntaxkeyword">|</span><span class="syntaxdefault"> unless a </span><span class="syntaxkeyword">==</span><span class="syntaxdefault"> grps</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">last <br /> a</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">name </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">'foo'</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> foo</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">push a<br /> else<br /> a</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">name </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">'nonfoo'</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> nonfoo</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">push a<br /> end <br /> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> <br /> until foo</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0</span><span class="syntaxkeyword">]</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">==</span><span class="syntaxdefault"> nil<br /> foo</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">each </span><span class="syntaxkeyword">{</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">|</span><span class="syntaxdefault">ff</span><span class="syntaxkeyword">|</span><span class="syntaxdefault"> <br /> if </span><span class="syntaxkeyword">((</span><span class="syntaxdefault">ff</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">bounds</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">intersect</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">nonfoo</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0</span><span class="syntaxkeyword">].</span><span class="syntaxdefault">bounds</span><span class="syntaxkeyword">)).empty?</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">==</span><span class="syntaxdefault"> false<br /> </span><span class="syntaxkeyword">(</span><span class="syntaxdefault">ff</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">outer_shell</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">nonfoo</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0</span><span class="syntaxkeyword">])).</span><span class="syntaxdefault">name </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">'nonfoo'<br /></span><span class="syntaxdefault"> end<br /> <br /> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> foo </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> model</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">entities</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">find_all </span><span class="syntaxkeyword">{|</span><span class="syntaxdefault">ent</span><span class="syntaxkeyword">|</span><span class="syntaxdefault"> ent</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">name </span><span class="syntaxkeyword">==</span><span class="syntaxdefault"> </span><span class="syntaxstring">'foo'</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}</span><span class="syntaxdefault"> <br /> nonfoo </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> model</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">entities</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">find_all </span><span class="syntaxkeyword">{|</span><span class="syntaxdefault">ent</span><span class="syntaxkeyword">|</span><span class="syntaxdefault"> ent</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">name </span><span class="syntaxkeyword">==</span><span class="syntaxdefault"> </span><span class="syntaxstring">'nonfoo'</span><span class="syntaxkeyword">}</span><span class="syntaxdefault"> <br /> end</span>
-
@liquid98 said:
I tried
BoundingBox.intersect
but it gives me false positives in cases of close
proximity.BoundingBox.intersect
always returns aBoundingBox
object.From memory.. either one of these Happen:
(1) If it really is a 3D method: (I think,) you may have to test the result for dimension. In other words if there is no intersection, the result
BoundingBox
would have0
for depth, width and height.(2) (And I think this is the case,) it's not a 3D method, it is a planar method. (I remember Jim explaining this in another topic, some time ago.) It sort of like the "projections" of each BB are cast onto the 3 axis planes. If there is no "interference" between the cast rectangles, then we consider there not to be an intersect. It is weird and confusing, and two groups that do not even touch each other, can have "projections" that intersect.
So anyway.. IF your groups ARE manifold solids... why not use
Group.intersect
instead ?? -
Hi Dan,
I think the
Boundingbox.intersect
method is not really suitable for testing whether geometry is intersecting..
It is true thatBoundingbox.intersect
returns an empty boundingbox if there is no 'intersection'.
So we can test for that, but it is not reliable..Furthermore the
Group.intersect
method is just a Boolean method, it is not intended for testing intersection of solids..I feel the loop class can clarify things..
-
@dan rathbun said:
If you are going to use
for
..in
, you must subtract1
from the length of the array/set/collection.He did, because he used
...
instead of..
in the Range. -
Is it smart at all to test for intersecting geometry before attempting a Boolean operation??
-
Are you wishing to run this in batch-mode ??
As in: Pick a folder of SKP files, and open them one after the other, union the intersecting groups, save the model under a "differed" filename, ... loop until all files in directory have been "converted" ??
-
good guess! that's the plan indeed.
-
@liquid98 said:
good guess! that's the plan indeed.
I did not guess ... I can read your mind (scary music: [Oooo ooOOooo ooooo])
No actually, I read your sample code in the post of 17 MAR ... the first two lines you are opening a skp file, within the code.
In that post, you wrote:
@liquid98 said:
But the script I'm writing is for internal use.
Is this to be used within a commercial setting (for commercial work.) ??
Advertisement