If I understand the issue correctly, at that exact point the non-manifold condition results in some ambiguity that is causes the Boolean Subtract to fail… is there any way to make it work to get the desired result other than to make it slightly the wrong size?
If Non Manifold edges result in an invalid Brep, and effectively nothing is output, then isn’t that the same as when Cap Holes fails to produce any output? So, shouldn’t it turn red with an error the same way Cap Holes does?
@magicteddy put together a C# script that detects when a Boolean operation fails, but it’s a workaround, I really think that if Solid Difference produces an invalid Brep for whatever reason, it should be producing the same error that Cap Holes produces. Just sitting there being all nice and grey like nothing is wrong makes it really difficult to figure out, especially if you have a complicated model and with a lot of operations, and the whole model just disappears, and you have no way to see why it’s gone, you have to manually go look one item at a time until you figure it out, instead of just looking at the red thing.
I have no idea why this convoluted workaround works (at least, on this example), but… it does !
ClosedBrep.gh (9.9 KB)
@magicteddy Thanks for figuring that out.
I can see why it works, It’s removing the Non-Manifold condition by breaking the part and creating a second intersection between the cylinder and the part.
It would only work if it was on one of the edges aligned with the Y axis, but I think I can make it work no matter what edge it is on. I thought I could just check to see if it was on the edge of X or Y and use whatever is needed, but then it would not work if the edge of the part was on a diagonal.
So I think I can maybe check to see if the cylinder is tangent to the closest edge of the part and if it is, put the plane in and rotate it 90 degrees to the tangent point and cut the part there, then it should work no matter what angle it happens to be at.
Edit:
I got to thinking about this and I think that if I cut the part in both the X and Y direction and then do the Solid Difference, then put them all back together, it would always work no matter the angle, because there would always be 2 points of contact with the cylinder no matter how it was rotated. It seems to work:
ClosedBrep XY.gh (11.8 KB)
Edit… Again:
using this method with both X and Y has a flaw… this can happen:
325 Degrees:
An alternate method that works in this case is to rebuild and replace the invalid brep face.
Bool_Sub_Fail_Repair_230108.gh (13.8 KB)
-Kevin
Doing the same operations in Rhino results in a Brep with a non-manifold edge.
Another topic with a similar problem:
@Gijs why isn’t an invalid brep output displayed orange or red?
@Kevin
I like this idea better, because when you split the Brep and put it back together, you end up with these extra seams in the cylinder.
but this method doesn’t do that. It also seems to work no matter the angle. I put the rotation in to test it, then I noticed that this only needs to be fixed when it lands on an edge that is aligned to the X or Y Axis. So I made it only run the repair if it is needed, that should speed things up.
180 Degrees:
239 Degrees:
Bool_Sub_Fail_Repair_Rotated.gh (12.6 KB)
It would be nice if the Boolean Subtract in both Rhino and Grasshopper checked for this issue and rebuilt the surface internally the way @kev.r fixed it. It seems it would be possible to have the boolean function just do a check at the end to see if the Brep is invalid, and if it is, apply the steps to repair it, otherwise just output it.
Hi,
I have merged several ideas posted here, and made the pipe so that the seam of the cylinder is always on the inside. This seems to be consistent, whatever rotation, position of the point, radius or depth is chosen.
ClosedBrep.gh (16.4 KB)
Rotating the cube and cylinder around a common center point should produce the same result. As long as they have faces that are touching, they should produce a non-manifold edge.
You need to be careful about passing data through text panels like you are doing here:
This is rounding these values to what you see in the text panel (looks like 6 decimal places here but depends on grasshopper settings). It’s strange but even with the text panel bypassed your file only produces an invalid brep at certain rotation angles.
Check this file:
Bool_Sub_Fail_Repair_Rotated_re.gh (14.8 KB)
The Solid Difference component here produces an invalid brep regardless of the rotation angle.
Same result here (as expected) invalid brep at all rotation angles:
Bool_Sub_Fail_Repair_Rotated_re2.gh (15.3 KB)
The difference between your file with the text panel bypassed and these two methods is minute (centroids of the cylinders differ by 3.17E-15 or less) but it’s apparently enough to change the results.
-Kevin
@kev.r I didn’t realize the output of the panel was any different than the input, unless I typed something in there. Thank you for letting me know that, I will avoid using the panels in this way.
James
I just noticed a new issue, if I pass supply multiple inputs, it works up to the point where it needs to be repaired, but then it doesn’t continue with the rest of the inputs.
Here I gave it 5 input points, and I get 5 holes as expected:
But when I rotate it to a position where I have the invalid Brep, everything after the invalid Brep disappears.
Bool_Sub_Fail_Repair_Rotated_Multiple.gh (17.4 KB)
Also, I am not passing through the panels anymore but I still get this only when it’s at angles aligned to the XY axis. maybe it has to do with my Rhino tolerances? I think I am just in inches - small parts
It works if the “problematic” hole is the last one on the list… I guess the Solid difference stops working when it hits an invalid Brep ?
That’s what seems to be happening. Is there some way to process each hole one at a time and keep passing the output of each stage to the next so that one in the middle could be fixed if needed and continue?
Bruteforce, maybe ?
private void RunScript(Brep B, List<Brep> cutters, ref object holedBrep)
{
Brep brep = B;
for (int i = 0; i < cutters.Count; i++)
{
Brep[] sd = Brep.CreateBooleanDifference(brep, cutters[i], Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance);
brep = sd[0];
if (!brep.IsValid)
{
List<Brep> bps = new List<Brep>();
foreach (BrepFace bf in brep.Faces)
{
Brep bfp = bf.DuplicateFace(false);
if(!bfp.IsValid)
{
Curve[] nedges = bfp.DuplicateNakedEdgeCurves(true, false);
Curve[] contour = Curve.JoinCurves(nedges);
Brep[] newfaces = Brep.CreatePlanarBreps(contour, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance);
bps.Add(newfaces[0]);
}
else
{
bps.Add(bfp);
}
}
brep = Brep.JoinBreps(bps, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)[0];
}
}
holedBrep = brep;
}
Bool_Sub_Fail_Repair_Rotated_Multiple.gh (16.5 KB)
That’s a great solution!
I thought it would be nice to know if the repair was needed, so I added a warning… but then I thought it would be even better if there was a second test after the repair, and if it was invalid again to issue an error… in case there is some situation where it’s not able to be repaired in this way, but I’m not sure what the syntax would be to make the second test after the repair.
So if it detected the invalid Brep and was able to repair it, issue a warning of “Invalid Brep Detected and Repaired”
and if it detects an invalid Brep a second time after the repair, issue an error of “Invalid Brep Detected. Unable to repair.”
I don’t know, but it would make sense to have it turn red though.
RH-72320 solid difference component doesn’t turn red on invalid result
Something like this, though there are still plenty of places where the code could fail - pretty much everytime I use [0] on an array that is potentially null.
private void RunScript(Brep B, List<Brep> cutters, ref object holedBrep)
{
Brep brep = B;
for (int i = 0; i < cutters.Count; i++)
{
Brep[] sd = Brep.CreateBooleanDifference(brep, cutters[i], Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance);
brep = sd[0];
if (!brep.IsValid)
{
List<Brep> bps = new List<Brep>();
foreach (BrepFace bf in brep.Faces)
{
Brep bfp = bf.DuplicateFace(false);
if(!bfp.IsValid)
{
Curve[] nedges = bfp.DuplicateNakedEdgeCurves(true, false);
Curve[] contour = Curve.JoinCurves(nedges);
Brep[] newfaces = Brep.CreatePlanarBreps(contour, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance);
if (newfaces != null && newfaces.Length > 0)
{
bps.Add(newfaces[0]);
}
}
else
{
bps.Add(bfp);
}
}
Brep b2 = Brep.JoinBreps(bps, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)[0];
if (b2 != null && b2.IsValid)
{
brep = b2;
this.Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Repair successful at cutter n°" + i.ToString());
}
else
{
this.Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Repair failed at cutter n°" + i.ToString());
}
}
}
holedBrep = brep;
}
@magicteddy I gave this new script a try, and it was going pretty well, but then I came across some kind of issue that caused it to fail and not produce any error… but it has no output at all.
I was wondering if it’s possible to pass along the same kind of information from the grasshopper solid difference so when this kind of thing happens maybe it will help track it down? maybe not though because the grasshopper report doesn’t look any different than it did before and the message on line 3 is just the same point on the edge… and your script fixed that as well as 4
Bool_Sub_Fail_Repair_Rotated_Multiple v2.gh (25.8 KB)
Perhaps there is a way to make a warning for every input with the status of the resulting Brep at each stage? kind of a special version for diagnostic purposes? Because it just fails and I don’t know which point caused it or what the problem is.
It would also be helpful if one Solid difference failed in the stream, it could detect that if failed, just go get the pervious version, and continue with the rest of them anyway… that would make it easy to see which operation failed… just look for the one missing thing, then see what’s different about that.
I really appreciate your help! I try to muddle around in the script to try to make it do things, but I’m not all that experienced in C# at all, and have even less experience with Grasshopper or Rhino scripting.
That levitating cutter !!!
I added another line of code to check for failed booleans.
private void RunScript(Brep B, List<Brep> cutters, ref object holedBrep)
{
Brep brep = B;
for (int i = 0; i < cutters.Count; i++)
{
Brep[] sd = Brep.CreateBooleanDifference(brep, cutters[i], Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance);
if (sd.Length > 0)
{
brep = sd[0];
if (!brep.IsValid)
{
List<Brep> bps = new List<Brep>();
foreach (BrepFace bf in brep.Faces)
{
Brep bfp = bf.DuplicateFace(false);
if(!bfp.IsValid)
{
Curve[] nedges = bfp.DuplicateNakedEdgeCurves(true, false);
Curve[] contour = Curve.JoinCurves(nedges);
Brep[] newfaces = Brep.CreatePlanarBreps(contour, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance);
if (newfaces != null && newfaces.Length > 0)
{
bps.Add(newfaces[0]);
}
}
else
{
bps.Add(bfp);
}
}
Brep b2 = Brep.JoinBreps(bps, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)[0];
if (b2 != null && b2.IsValid)
{
brep = b2;
this.Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Repair successful at cutter n°" + i.ToString());
}
else
{
this.Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Repair failed at cutter n°" + i.ToString());
}
}
}
else
{
this.Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Boolean failed at cutter n°" + i.ToString());
}
}
holedBrep = brep;
}