Grasshopper

algorithmic modeling for Rhino

Help creating a smart loop for repetitive solid unions

I have an array of spheres along a spline, in an attempt to create a twisted solid shape that does not intersect itself (like it would if it were piped). 

I have created a loop to do this (using anenome), but at least one solid union within the loop will invariably fail, causing everything that preceded it to be deleted. Is there any way to alter my code such that, if one union fails, it merely skips it rather than deleting the solid shape that has been created? This has me scratching my head

Views: 7957

Attachments:

Replies to This Discussion

All that interface hogging Anenome can be replaced by a one time delay of Python.

The process is slow due to Rhino Boolean unions being slow (by nature I assume).

It works to skip over failed steps, but has a seeming second perhaps unrelated glitch that loses whole tails as it goes. It's so slow to run that it's quite hard to debug except in short portions.

The pink ball was skipped when the Boolean returned nothing.

Tolerance setting may let more or less steps work, I'm not sure how.

Here is a simple, non-looping version that takes a single pair of breps and does a smart Boolean union, returning the original first item if the Boolean fails, as if it worked:


This should work with your Anenome cycles, but watch out for the second failure mode that I don't yet understand. Maybe Rhino really wacks out and spits out a wrong answer instead of just a fail? Straggler line 9 in the second script should be deleted, sorry.

If you play with Python learn how there are right click input wire options, for list/item etc. access and also a crucial submenu for type hint, so I had to use the default list access and change the type hint to Brep. You magnify way into the Grasshopper canvas to see +/- buttons to add new inputs.

Attachments:

At the risk of asking a very basic/stupid question - how can I get this to work? The issue I'm having is that the python script does not seem to recognise the term 'Breps'.

"Runtime error (UnboundNameException): name 'Breps' is not defined"

Magical thinking on my end, that I could quick tweak the script to make it more self-explanatory by renaming an input while leaving the robot inside the same.

Robot happy now.

When I changed the name (right clicking on "Breps" to change it to "Brep_Pair_List"), the output still looked like it worked, so the robot in my skull said "OK, that's good."

The way scripts work is you merely invoke the same name of what you've assigned input/output names to on the Python container node, as variables you grab or assign in the script.

Python is great since there's none of the wacky headers and bizarre variable type assignments that make you wonder if the bug is hiding in the attic. No, the bug is right in front of you!

Attachments:

Attachments:

To run this I have to double click the right Anenome component after turning off the Preview Only Selected toolbar button.

Interesting that it runs fairly fast, then gets slower and slower as the model becomes complex. After an hour or two it gives a good result though. The Rhino interface becomes very sluggish though even with the Grasshopper solver disabled in the menu, so I have to use the Rhino command GrasshopperUnloadPlugin. It seems the preview itself is causing the problem. Turning off Grasshopper preview after baking frees Rhino up too.

The result is two pieces which Rhino cannot Boolean union, so one bad step snuck through. This may be related to the error I saw above but my script failed to retain the tail instead of leaving it there.

Python will not readily afford stepwise preview updates, so combining Python with Anenome makes sense actually except that one of the reasons it may be slow is that it's generating preview meshes. Let's turn that off to see if LLXXZZ's script runs faster.... Not a big difference actually.

Replacing the Grasshopper Boolean Union with my little pairwise non-looping Python script shows that the Python is much slower (3.5X) actually, and it's not overhead, but the actual Boolean union line I can disable to get the time down to 10ms:

breps_union = Rhino.Geometry.Brep.CreateBooleanUnion(bs,0.01) # The number is tolerance.

Ah, it's just the tolerance settings, and a much lower tolerance matches the speeds, namely 0.0001 instead of 0.01:

Further reduction in tolerance only makes it a few ms faster.

For the first 100 balls from the bottom:

Full Python internal looping script with internal loop: 1 minute.

Original Anenome script with preview meshing off: 1 minutes 25 seconds.

So there is some overhead in the large Grasshopper script.

Running the entire ~400 balls via looping Python script:

ONLY 4.7 MINUTES! Oh, this is much better than Anenome. And with the new tolerance setting, it's a complete piece, with seemingly *no* rejects?! That's just bizarre, but the new tolerance settings seems to afford that:

The final Python preview doesn't slow down Rhino either.

I wonder if the Python multi-threading feature could first union pairs in parallel?

I'll see if there's a way to create at least a counter progress output from Python, if not a full preview. The way Grasshopper works makes this ambiguous since Grasshopper only thinks in single solution waves whereas Python is doing things within a single solution that Grasshopper wasn't designed to accommodate. Some lead a timer component into Python and use a global variable to connect each cycle. Ugh.

Attachments:

Djordje shows Python parallel processing for pairs of Boolean differences, here:

http://www.grasshopper3d.com/forum/topics/ghpython-booleandifferenc...

Alas, he makes it confusing by using Grasshopper trees as input with code for trees too, instead of just lists as input (via right click Python input options).

Parallel processing is simply a single function call at a time that you feed a list of values, in this case pairs of every two balls.

We could then run the output into the same script again.

HOWEVER...any Boolean failures now take out two or four small segments at a time, so parallel is inherently screwed for this particular operation where we are trying to ignore failed single balls.

Then again, a pair-wise strategy may avoid many original Boolean failures too.

Ah, huge problem, that parallel processing itself scrambles the order! They arrive back at whatever speed each processor is getting along at, I assume, in groups of four for my 4-core CPU.

That ruins the next step, lest I have to re-sort them along a curve, which will be slow.

Well, I can instead somehow keep track of the order in Python via key/index assignment. This works OK:

Still have to worry about retaining strays that didn't form pairs due to odd number collections.

Worried about the lack of smart feature now though that will skip bad Booleans, I wonder about forcing those Booleans to work instead of filtering them out, by randomly jostling the failed part(s) in space, to turn kissing surfaces into real overlaps. The perhaps 0.001 or 0.005 units movement required shouldn't even be perceptible to the user.

This parallel processing strategy, only available in pairs since I need to feed a list to the Grasshopper Python parallel command, is worth pursuing further.

By the way, most of the Python match and sorting lines I just do an English language search in Google for and up pops various programing site answers, rated like Amazon reviews, so Google is the ultimate manual for Python in real time! First hit for "flatten a list in Python":

Transferring the first odd-man-out item of each list for each cycle of pairwise Booleans stops losing odd numbered final items that can't for a pair. It takes six cycles for this segment of 42 balls:

But if nothing fails, I can't test my jostling code. The whole array of ~400 progresses through the stages quite fast, but gets progressively slower as the structures get more complex, taking nine steps to complete in 1 minutes 14 seconds, or indeed 4X faster than non-parallel on my 4-core CPU.

Next step...debug and tweak the jostling of items to force Booleans to work (fingers crossed).

Well, it's not failing to do a Boolean yet, so I'll have to tweak it in Rhino to get failure. This system only works because the items are ordered along a curve, and will only fail when the structure becomes complicated to afford kissing surfaces lacking well defined overlap. It already works where Rhino and Grasshopper fail though. Perhaps this divide and conquer strategy actually avoids many natural failures? Or might using Rhinocommon at a specific tolerance setting be the bonus?

I can't seem to get it to fail yet.

Here are 1200 balls from the original thread full path, using parallel computing in subsequent pairs:

This only takes one minute. OK, 2400, will that take twice as long merely? Well, the first step alone took a minute and the second step failed. Not an informative error though, since instead of a Boolean failure, it's a parallel processing generic bug in the middle of the run, and it's not easy to pare down to only a few bad pieces since it's temperamental about input:

Runtime error (AggregateException): One or more errors occurred.
Traceback:
line 27, in run, "C:\Users\Nik\AppData\Roaming\McNeel\Rhinoceros\5.0\Plug-ins\IronPython (814d908a-e25c-493d-97e9-ee3861957f49)\settings\lib\ghpythonlib\parallel.py"
line 31, in script

Since there's no rhyme or reason to it as I pare down the model pieces, I'm stuck. I'll have to figure out the smallest group I can input to reproduce it and post to the scripting forum here.

I note that the parallel command seems to run the part pairs in its input list in rather different order each time, but the error only depends on the input, not coming and going with re-running the script. It seems unrelated to geometry, more like list length with some odd and even effect too.

The identical error is cited here, with no answer:

http://www.grasshopper3d.com/forum/topics/random-errors-through-mul...

Ah, it's just that the parallel system won't return Rhinocommon or in fact any function errors well, but just freaks out and returns a generic error. I must test my function alone to find the errors when this happens.

It was just a typo, a renamed variable I didn't rename in my function so it wasn't assigned any more. There is reason to this after all. It only happened when there was in fact a Boolean fail, so with 2400 balls instead of 1200 I can finally debug the Boolean fixing (jostling) system, hopefully.

You can see how my jostling kludge system works, with an extreme value used to fix all three failed Boolean unions, by jostling the second item in those failed pairs that have evidently kissing instead of interpenetrating surfaces somewhere:

Now all links (except a possible single ball on the very end of odd numbered ball series) are four balls long, including the jostled ones. Without that step, those items simply don't appear in the output, leaving way too big of gaps to ignore, eventually leaving huge gaps at later stages of segment doubling:

So if I turn the jostling multiplication factor way down it should work imperceptibly:


Ta-dah! The jostling strategy WORKS! Granted, only in this special case where I know I'm dealing with adjacent pairs of worms along a curve, not generic objects arranged in space by some artist.

Now I just need to wrap the multiple Python script components I'm stringing together into one script.

How long does the full 2400 balls take, finally? It took 12 Python scripts that merge pairs, to achieve this breakdown: 2400 -> 1200 -> 600 -> 300 -> 150 -> 75 -> 38 -> 19 -> 9 -> 5 -> 3 -> 2 -> 1. Time was 2 minutes 50 seconds, so there is some extra struggle for 2X as many balls as 1200 that took 1 minute 20 seconds, but only ten more seconds.

Attachments:

RSS

About

Translate

Search

Photos

  • Add Photos
  • View All

© 2024   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service