Brep.CreateBooleanDifferecnce Not working consistently, and I need at least a workaround

rhino
rhinocommon
boolean
bug

#1

The problem I am having, is that I am successfully creating two Brep objects.
If you reference the second animation, they are successfully being generated and added to the active document.
I have even taken all of these cases and successfully performed a booleandifference on them via Rhino manually with a successful result in all cases.
I’ve even spent some time to ensure that each of these Breps are valid via the IsSolid && SolidOrientation properties to ensure that both of these are respectively set to “true” & “outward”.

I’ve spent also a significant amount of time trying other approaches to get this particular function to produce a consistent valid output. One of which you may find helpful to some degree if there truly is a bug somewhere.

Here is my alternative result that showed a little more promise
b2 is a scaled, rotated, and translated copy of b1.
I got to suspecting that when each of these particular brep methods where invoked, that it was to translate by definition, but not reset the resulting operation to treat the new “b2” brep as an object that is at a 1 scale, 0 rotation angle, 0 distance from its original origin, instead it seemed like it was hanging on to the transformation values. So I began looking for a way to apply these values and landed on b2 = b2.DuplicateBrep(); after I performed any of the three before mentioned translation methods. This is when I was able to get at least one successful profile to generate the way I expected it to, but the other three are still failing.

However when I run the following (C#) code:

RhinoDoc acDoc = RhinoDoc.ActiveDoc;
Brep b1, b2;
Brep[] b3;

//Code not shown for simplicity
// b1 = valid brep Number 1
// b2 = valid brep Number 2
b3 = Brep.CreateBooleanDifferecnce(b1, b2, 0.001);
if (b3 != null && b3.Length > 0) {
for (int i;i<b3.Length;i++) {
acDoc.Objects.AddBrep(b3[i]);
}
}

The only valid result if from my 1st profile.

I’m hoping that someone can either help me get this function working (ideal),
OR
I’m hoping for a valid reliable approach that will get me the end result I’m looking for.

Thanks for considering my struggles your own :smile:


(Dale Fugier) #2

Hi Rick,

Can you provide me a way of repeating the problem you are having?

– Dale


#3

I would recommend placing the following code in two buttons and on line 22 change the argument from p1 to p4 on the 2nd button:

        RhinoDoc acDoc = RhinoDoc.ActiveDoc;
	//p1 verts
	List<Point3d> p1 = new List<Point3d>();
	p1.Add(new Point3d(-1,0,0));
	p1.Add(new Point3d(-1,-1,0));
	p1.Add(new Point3d(-2,-1,0));
	p1.Add(new Point3d(-2,-2,0));
	p1.Add(new Point3d(2,-2,0));
	p1.Add(new Point3d(1,-1,0));
	p1.Add(new Point3d(1,0,0));
	p1.Add(new Point3d(-1,0,0));
	
	List<Point3d> p4 = new List<Point3d>();
	p4.Add(new Point3d(-1,0,0));
	p4.Add(new Point3d(-1,-1,0));
	p4.Add(new Point3d(-2,-1,0));
	p4.Add(new Point3d(-2,-2,0));
	p4.Add(new Point3d(-1,0,0));
	
	//Change Value between p1 & p4 Here
	profile1stAdd = acDoc.Objects.AddPolyline(p1);
	
	//The Raw Rhino Object
	my1stProfileObject = acDoc.Objects.Find(profile1stAdd);
	//Create Curves For the Top And Bottom of the Extrusion - To be converted to surfaces later
	myFirstCurve = my1stProfileObject.Geometry as PolylineCurve;
   
	extrusionHeight = 20.0;
	
	//Append the Side wall Extrusion Surfaces to the new variable
	myNewExtrusionSurf = BrepFace.CreateExtrusion(myFirstCurve, new Vector3d(new Point3d(0, 0, extrusionHeight)));
	
	cap1 = myNewExtrusionSurf.ToBrep();
	cap1 = cap1.CapPlanarHoles(0.001);

	for (int k = 0; k < cap1.Faces.Count; k++) {
		cap1.Faces[k].RebuildEdges(0.001, true, true);
	}

	cap1 = cap1.DuplicateBrep();

	if (cap1.SolidOrientation == BrepSolidOrientation.Inward) {
		cap1.Flip();
	}

	cap1 = cap1.DuplicateBrep();
	cap1.Compact();

	cap2 = cap1.DuplicateBrep();

	cap2.Rotate((Math.PI*0.50), new Vector3d(10.00, 0.00, 0.00), new Point3d(0.00, 0.00, (0.50 * extrusionHeight)));
	cap2 = cap2.DuplicateBrep();

	cap2.Translate(new Vector3d(0.00, -1.001, 5.001));
	cap2 = cap2.DuplicateBrep();

	cap2.Scale((double) 0.75);
	cap2 = cap2.DuplicateBrep();

	for (int k = 0; k < cap2.Faces.Count; k++) {
		cap1.Faces[k].RebuildEdges(0.001, true, true);
	}

	cap2 = cap2.DuplicateBrep();

	if (cap2.SolidOrientation == BrepSolidOrientation.Inward) {
		cap2.Flip();
	}

	cap2 = cap2.DuplicateBrep();
	cap2.Compact();

	//Un-Comment The next two lines to see the valid breps before boolean
	//acDoc.Objects.AddBrep(cap1);
	//acDoc.Objects.AddBrep(cap2);

	cap3 = Brep.CreateBooleanDifference(cap1, cap2, (double) 0.000000001);


	int inc = 0;

	//while (inc < 1000) {
		if (cap3 != null && cap3.Length > 0) {
			for (int i = 0; i < cap3.Length; i++) {
				if (cap3[i].SolidOrientation == BrepSolidOrientation.Inward) {
					cap3[i].Flip();

				}
				
				for (int j = 0; j < cap3.Length; j++) {
					for (int k = 0; k < cap3[j].Faces.Count; k++) {
						cap3[j].Faces[k].RebuildEdges(0.001, true, true);
					}
				}

				cap3[i] = cap3[i].DuplicateBrep();
				acDoc.Objects.AddBrep(cap3[i]);

				acDoc.Views.Redraw();
				acDoc.Views.Redraw();
				inc = 1000;
			}
		} else {
			//MessageBox.Show("No Items in cap3 - Boolean Failed");
			inc++;
		}
	}
	
	acDoc.Views.Redraw();`

(Dale Fugier) #4

Hi Rick,

Your second set of points creates a “bow-tie” polyline, which is going to cause all sorts of grief when extruding, etc.

List<Point3d> p4 = new List<Point3d>();
p4.Add(new Point3d(-1,0,0));
p4.Add(new Point3d(-1,-1,0));
p4.Add(new Point3d(-2,-1,0));
p4.Add(new Point3d(-2,-2,0));
p4.Add(new Point3d(-1,0,0));

Is this intentional?


#5

Sorry No, I transposed those in a hurry trying to get into a meeting:

It should be:

List<Point3d> p4 = new List<Point3d>();
p4.Add(new Point3d(-1,0,0));
p4.Add(new Point3d(-1,-1,0));
p4.Add(new Point3d(1,-1,0));
p4.Add(new Point3d(1,0,0));
p4.Add(new Point3d(-1,0,0));


#6

I guess, I can boil this down to a simpler question that I feel is more fitting for this purpose, so I am going to ask it here.

I want to know (if someone here can answer it), why I can generate these two breps, such as in my second animation provided, and use the “Rhino User Command: BooleanDifference” with reliable success (on the code generated breps), however when I do the (Brep.CreateBooleanDifference(b1, b2, 0.001)) through code, I don’t have nearly the same success rate.

So the real question is based on these proven facts is:
Why is the .NET method different from what is happening on the user-side?

I hope that didn’t come off rude, but this question somewhat baffles me logically speaking.

I really do not mean for my bluntness to come off as a destructive attitude.
I really appreciate that there is even a group of people in a forum such as this to help solve problems.
So with my lack of word tact, please know that I have nothing but appreciation for any help provided.


(Dale Fugier) #7

Hi Rick,

I’ve cleaned up your code a bit. Let me know if this works any better for you.

https://github.com/mcneel/rhino-developer-samples/blob/6/rhinocommon/cs/SampleCsCommands/SampleCsBooleanDifference3.cs

– Dale


#8

That worked - amazing.

This one function fixed it all for me:
‘brep.Faces.SplitKinkyFaces’

Your explanation that this is what rhino does on draw, even answered my last question asked.

Thank you so much for this solution, I’ve been spending hours on it, even loosing sleep over it.

Can you point me to a more technical reference of understanding the importance of “Kinked/Degree” type of curves?
You’ve already helped me so much already, I almost hate to ask any more of you.

Thank You, Thank You, Thank You.

Kind Regards,
R. Riggs


(Dale Fugier) #9

A kink is a location where a curve or surface dramatically changes direction. For example, the corners of a rectangle are kinks. Kinks can also happen at locations where a curve or surface dramatically changes the amount it curves. For example a rounded rectangle has kinks where the line segments turn into arcs.

Like I mentioned in the sample, kinky surfaces can cause problems later on in the modeling process. So by default, Rhino splits up kinky surfaces when they are added to the document.

Regarding degree, there is some good information on this in the Rhino help file - just search for the “ChangeDegree” command.

Degree=1 curves are not the only curves that can have kinks. A full multiplicity knot in the middle of the knot list means there is a place on the curve or surface that can be bent into a sharp kink. To see an example of this, draw a Circle, turn on its control points, and drag one of them. When extruded, this curve will create a Brep with three faces, not one.

Does this help?


ObjectTable.AddSurface() can add multiple surfaces
#10

Yes, thank you for everything :grin:


#11

Thanks for the explanation. Did not know why some breps didn’t work, but they did when I first added them to the file.

P.S. I see it solved it sometimes. Still got 2 breps that dont union good, but do work when I add them to the file first before BooleanUnion. Are there more automated functions that will run when a brep is added to the file?


(Dale Fugier) #12

No, the sample I reference pretty much covers it.

If you have geometry that does not Boolean, please post it here in Discourse. If the geometry does not work they Rhino’s Boolean commands, then post it in either the “Rhino for Windows” or “Rhino for Mac”. If you are ambitious, you might also make a YouTrack item.

If you have geometry that does not Boolean with your source code, but it does with Rhino commands, then please post in this category along with any source code you have that demonstrates the problem.


#13

Can’t get it to work. Ill create a new topic.


(Giulio Piacentino) #14

This case is similar to RH-23914


#15

I found a part of the problem for me… The brep was created by loft the direction of 1 curve was in the other direction so I got a self intersecting brep. Rhino did not know what was the in- or outside. So I first try too boolean it now… If failed. I flip the brep because it seems to be inside instead of outside. Then it works perfectly…

Somehow Rhino can’t determine what is inside or outside (with self intersecting breps) so it sometimes picks the wrong direction so boolean union fails.

Hope this helps.


#16

Hey @dale, the provided link doesn’t work anymore. Could you please refresh it?

Cheers, Heinz


(Dale Fugier) #17

Hi @lungenstrudel,

All of our developer samples can be found here:

I’ve fixed the link (above).

– Dale


#18

thanks a lot, @dale :grinning: