Split sphere problem - is there better way?

Hello. I am dealing with unpleasant problem and I would like to ask people for help.

I work on script that makes joints without boolean operations (because of computational time). The outcome consists of cylinders and sphere. The joint looks like this:

I calculate arcs, where cylinder cuts sphere. And the goal is to cut spere to get only residual surface, which is not affected by any cylinder. I was not able to find computationaly fast solution to achieve that.
My approach so far:

  • split sphere with curves
  • measure distance of each cylinder centroid and all fragments
  • get distances equal or larger than cylinder radius
  • get rid of all fragments that are located closer than radius

My problem is rounding. Borders of fragments are somehow approximated and I am not able to set the threshold properly, so I sometimes filter out wrong frafments (on both sides). I try to use "similarity"component to get any closer to good result, but sometimes there are little fragments that ruin this approach completely (in uploaded code, itā€™s branch {32}.

I found out that the biggest issue is ā€œstitchā€ of sphere surface.

So I politely ask for help or some hints that would help me solve this problem. I bet there is a way how to do this better. Would you be so glad to look at my script, please?

splitting spheres problem.gh (74.2 KB)

Hi @PetrVacek,

Not an answer, but I have some observations:
a) I donā€™t know if I understand your intent here:
image
I thought you wanted to remove duplicates, but this doesnā€™t do that. It just takes the first line emanating from each of your vertices resulting in an incomplete set of lines to create the intersections with the spheres.
b) You state that your goal is to produce a result that is computationally quicker than booleaning. Given that you are doing pretty much everything that a boolean has to do, but with the overhead of doing it in Grasshopper, I am not convinced that you can achieve that aim.
c) If you have the skills, you might consider writing a gh component in C#, allowing you to trim each sphere with one cylinder at a time instead of simultaneously - that would prevent the fragmentation of the unwanted surfaces which is causing you problems.

Regards
Jeremy

Just in case this is going to be 3D printed, you donā€™t necessarily have to bother with the intersections. In order to avoid voids inside the node, Iā€™d create cylinders with holes not going all the way through. You could export the Breps of each node as STL without joining or meshing. Most slicers donā€™t require joined meshes and simply combine whatā€™s there.

Hereā€™s a video of a similar node in Preform, Formlabs print software:

4 Likes

Hi @jeremy5

Thanks a lot for your comment. Regarding to your observations.

a) My intention is to orient sphere axis to one (doesnā€™t matter which) edge, so the cut always removes a big portion of surface edge. I found out that splitting WorldXY oriented sphere can produce more little fragments that are difficult to remove.

b) It actually is way quicker, because I donā€™t work with solids but only ruled surfaces and find the intersection mathematically by evaluating domains. Hence the biggest issue is to figure out sphere intersections. I will share time coparsion once the result will be comparable. I even tried mesh booleaning, which was faster than solid booleaning, but it just didnā€™t work every time and was strongly dependent on meshing settingsā€¦actually it was not realiable at all.

c) I was considering that, but my C# is power is not ready yet :slight_smile: What I wanted to explore is to cut sphere by planes and always get rid of the fragment that is oriented above the plane. I will definitely look into Rhion dev database.

Hello @martinsiegrist

Thanks for your comment and example. You are right, this will be 3D printed and we already have some promising samples. I know that slicers can handle quite a big mess. What I can do is to move each ā€œholeā€ not to cut through joint sphere, itā€™s easy. But what is more challenging are cylinders. Hereā€™s why:

1 Like

Ok, got you!

I have been playing around with boolean union a while ago. I attached a definition with a loop, using Anemone components. It creates a union one after another.

20_05_15_boolean_union_loop.gh (25.1 KB)

Sorry, canā€™t help you on the depth of the holes right now. Not enough time.

2 Likes

Hi @PetrVacek,

This looks like a simpler way to get the spherical segments, without the unwanted bits.


splitting spheres problem_j1.gh (92.5 KB)

Speed seems OK on my laptop (<11 secs), but I donā€™t know what your target is. I added in a deduping stage for the links but it made no significant difference to the overall solution time.

Regards
Jeremy

1 Like

Try Sasquatch Trim with many planes

4 Likes

Hey @anon39580149,

Nice solution - 10 times quicker. Thanks for the intro to Sasquatch, itā€™ll be part of my toolset from now on.

Regards
Jeremy

why not just boolean union everything?

Thank you @jeremy5 and @martinsiegrist for your scripts and advices. I looked into it today. I was hoping for about 500ms to complete this operation, but it seems impossible.
What @anon39580149 recommended is exactly what I was looking for (and itā€™s a great collection of useful tools, so thanks for sharing!). But I would like to run my script on @shapediver and they just donā€™t support Sasquatch plugin.
So I tried hard with my limited knowledge of C# and came up with a messy buggy code that does the same, just somehow looses a few fragments somewhere on the way :smiley: And still it takes double time to compute than Sasquatchā€¦

The code:

// set data tree for resulting fragments
DataTree<Brep> fragmentsTree = new DataTree<Brep>();

int branchIndex = 0;
double tolerance = 0.01;

// loop for each sphere (one sphere per each node)
foreach (List<Surface> spheres in S.Branches)
{
  Surface sphere = spheres[0];

  // set list for fragments of each of spheres
  List<Brep> fragments = new List<Brep>();
  // convert surface to brep
  Brep sphereBrep = Brep.CreateFromSurface(sphere);
  // add brep to list for first iteration
  fragments.Add(sphereBrep);

  // now take every plane in each branch and perform splitting process for fragments in the list
  foreach (Plane plane in P.Branch(branchIndex))
  {

    foreach(Brep fragment in fragments)
    {
      Curve[] intCurve = {};
      Point3d[] intPt = {};

      // intersect plane and brep to get splitting curve
      bool result = Rhino.Geometry.Intersect.Intersection.BrepPlane(fragment, plane, tolerance, out intCurve, out intPt);
      if (result) // some planes might not cut any fragment
      {

        // split fragment with curve
        Brep[] split = fragment.Split(intCurve, tolerance);
        // set temporary storage for fragments to pass to next iteration
        List<Brep> tempFragments = new List<Brep>();

        foreach (Brep testedFragment in split)
        {
          // get bounding box of tested fragment
          BoundingBox bBox = testedFragment.GetBoundingBox(plane);
          // get center point of that bbox
          Point3d center = bBox.Center;
          // evaluate if this center exists above or below the plane
          if (center.Z < 0)
          {
            // is the fragment is below the plane, add it to temporary storage
            tempFragments.Add(testedFragment);
          }

          // pass all "valid" fragments to the next nested iteration for further splitting
          fragments = tempFragments;
        }
      }
    }
  }

  // add residual portions of sphere to corresponding branch and iterate on next sphere
  GH_Path path = new GH_Path(branchIndex);
  fragmentsTree.AddRange(fragments, path);

  branchIndex += 1;
}

F = fragmentsTree;

The script: splitting spheres progress.gh (35.9 KB)

With Python you can trim brep with plane , this is a simple code work with single plane cutter.
Maybe someone have a solution to cut tree of breps with tree of planes

import rhinoscriptsyntax as rs
import Rhino.Geometry as rg


tol = 0.001

result = rs.TrimBrep(brep,cutter,tol)

result = rg.Brep.Trim(brep,cutter,tol)
1 Like

Thanks @anon39580149 for tips, it is really useful.
I tried to substitute my C# script with Python and itā€™s 0.3 sec faster and has no issues. But still way slower than Sasquatch. I feel that I am pretty close to satisfying result, but what makes the difference is parallel computing. When I turn parallel computing in Sasquatch component off, it has the same computational time that I get with python. Does anybody know how to perform parallel computing on my code, please? How should the function look like? I read several threads, but itā€™s still unclear to me, so simple hint will be appreciatedā€¦

import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
import ghpythonlib.treehelpers as th

tol = 0.001
fragmentsTree = []

for i in range(S.BranchCount):

    sphere = rg.Brep.CreateFromSurface(S.Branch(i)[0])
    fragments = []
    fragments.Add(sphere)

    for plane in P.Branch(i):
        newFragments = []
        for fragment in fragments:
            newFragment = rs.TrimBrep(fragment,plane,tol)
            
            if len(newFragment) != 0:
                for f in newFragment:
                    newFragments.Add(f)
            else:
                newFragments.Add(fragment)
                
        fragments = newFragments
    
    fragmentsTree.append(fragments)
    
fragmentsTree = th.list_to_tree(fragmentsTree,source=[])
F = fragmentsTree

1 Like

Check this:

1 Like

Good new and bad new. I finally scripted ā€œTrim Brep With Many Planesā€ into Python using parallel computing. Itā€™s working perfectly the same time as @sasquatch. Thatā€™s the good new:

comparsion

The bad new is that my script behaves somehow unstable. I once opened .gh file and component gave me message, that some index is out oof range. Also, this happened when trying to place ā€œpanelā€ onto canvas:

No idea what it means. So I restarted Rhino & Grasshopper and everything works just fine. Any ideas how to get this more reliable? Is there some problem with memory or? This is way above my knowledge, so any help is appreciated.

GH for testing, if anyone is interestedā€¦ TrimBrepWithPlanesParallel.gh (39.0 KB)

The script is:

import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
import ghpythonlib.treehelpers as th
import ghpythonlib.parallel as gp

# define function that trim sphere with many places and return list of fragments
def getFragments(index):
    
    i = index
    # convert surface to brep
    sphere = rg.Brep.CreateFromSurface(S.Branch(i)[0])
    # create empty list to store resulting fragments after each iteration
    fragments = []
    # add first fragment, which, for the first iteration, is actually full sphere 
    fragments.Add(sphere)

    for plane in P.Branch(i):
        # new list at the beginning of trimming with each new plane 
        newFragments = []
        # try to trim each fragment by actual trimming plane
        for fragment in fragments:
            newFragment = rs.TrimBrep(fragment,plane,tol)            
            # in case that there is intersection, add result of trimming
            if len(newFragment) != 0:
                for f in newFragment:
                    newFragments.Add(f)
            else:
                # in case that tere is no intersection, add actual fragment 
                newFragments.Add(fragment)
        # assing new fragments to the list to trim with next plane  
        fragments = newFragments
    
    return fragments

# check for inputs
if S.BranchCount != 0 and P.BranchCount != 0:
    
    # tolerance for trimming
    tol = 0.001
    
    # create empty list to be filled with fragments 
    fragmentsTree = []
    
    # create list of indexes that will serve as argument of .parallel method 
    index = []
    for i in range(S.BranchCount):
        index.append(i)
    
    # execute trim brep function using multithreading 
    result = gp.run(getFragments, index, False)
    fragmentsTree.append(result)
    
    # convert list to data tree
    fragmentsTree = th.list_to_tree(fragmentsTree,source=[])
    F = fragmentsTree
else:
    
    # return empty list if no inputs are provided
    F = []

yes !!
need to find the smallest angle between pipes at node then we can figure out the min distance

i tried to find the min angle but could not find it
if some one could refer that here be so much thankfulā€¦

RP

I would avoid using rhinoscriptsyntax inside of any multithreaded code. rhinoscriptsyntax directly works with the document and typically adds geometry to the document. Specifically, I can see that rs.TrimBrep is going to be problematic in the above script.

Use RhinoCommon functions instead

1 Like

Use ā€˜cross referenceā€™ to get all possible combinations of curves in node, measure angle between them, sort and list the smallest one. The rest is some math. You need to structure your data inside your data tree wisely.

Sorry but Iā€™m not by computer now, so I canā€™t post any code or snap.

I got what I asked for and even more - I revealed some of mysteries of parallel computing. McNeel community is just amazing, so BIG THANKS everyone for your help!