My C# application using RhinoCommon running inside Rhino.Compute needs to perform boolean intersection & difference operations on many pairs of coplanar single-face breps. Essentially for each pair I need to get the intersection regions, the regions only in the first input, and the regions only in the second input. A boolean full-outer-join, if you will.
I find that about 2-5% of the time, the planar boolean operations Brep.CreatePlanarIntersection & Brep.CreatePlanarDifference fail in one of the following ways:
- The operation returns null instead of an empty array (documentation says this is the fail state)
- The operations do return arrays, but the sum of the area of the intersections + differences for one or both surfaces does not equal the area of the original surface
- The operations return arrays which conserve area, but Intersect(a,b) != Intersect(b,a), i.e. the intersection returns a different result based on the order I pass in the breps.
Here is a link to another post I made with an example of a relatively simple scenario leading to a noticeably ungood result:
A very helpful support team member from McNeel (thanks Brian J!) found that a combination of ShrinkTrimmedSrf and Rebuild performed in Rhino caused the operations to succeed. I believe I found the equivalent operations in the RhinoCommon API, BrepFace.ShrinkFace and BrepFace.Rebuild. These things go deeper into the NURBS-guts than I have gone thus far, but I found that when I encounter one of the above error conditions, passing my input breps through the function below and trying again (in a loop with maxRetries=4) does in fact seem to resolve the cases I’ve seen thus far:
private Brep RebuildBrepFace(Brep brep, double tolerance)
{
if (brep.Faces.Count > 1)
throw new InvalidOperationException("Cannot rebuild brep with multiple faces");
var face = brep.Faces[0];
face.ShrinkFace(BrepFace.ShrinkDisableSide.ShrinkAllSides);
var u = face.Degree(0);
var v = face.Degree(1);
var newSurface = face.Rebuild(u*2,v*2,u*4,v*4);
var newBrep = Brep.CopyTrimCurves(face, newSurface, tolerance);
return newBrep;
}
So my questions are:
- Am I doing the above correctly? Does anyone who knows what they are doing see anything silly / dumb / unneccesary going on in that function?
- Is there a more effecient way of performing that refine/repair method?
- I’d love any deeper understanding anyone could share around what exactly the above code is doing, and why it would change the behavior of boolean operations.