public void test1() { Solid s1 = Builtin_Solids.CreateCube("s1", 5); bsp3d bsp = new bsp3d(s1); Assert.AreEqual(PointInPoly.Inside, bsp.PointInPolyhedron(new xyz(1, 1, -1))); Assert.AreEqual(PointInPoly.Outside, bsp.PointInPolyhedron(new xyz(6, 6, -6))); }
public void test_seginside_thing() { Solid s1 = Builtin_Solids.CreateCube("s1", 10); Solid s2 = wood.CreateBoard(BoardMaterial.Find(BoardMaterial.SOLID_OAK_RED), "cut2", 6, 6, 6); s2.Translate(2, 2, -1); Solid s3 = bool3d.Subtract(s1, s2); bsp3d bsp = new bsp3d(s3); bool b = s3.SegmentInside(bsp, new xyz(5, 2, 2), new xyz(8, 2, 2)); }
public static Solid Subtract(Solid s1, Solid s2) { bsp3d bsp1 = new bsp3d(s1); bsp3d bsp2 = new bsp3d(s2); BoundingBox3d bb1 = s1.GetBoundingBox(); BoundingBox3d bb2 = s2.GetBoundingBox(); Solid s3 = new Solid(s1.name, s1.material); if (s1.board_origin != null) { s3.board_origin = s1.board_origin.copy(); s3.board_u = s1.board_u.copy(); s3.board_v = s1.board_v.copy(); s3.board_w = s1.board_w.copy(); } List <segpile> piles = new List <segpile>(); for (int ndx_f1 = 0; ndx_f1 < s1.Faces.Count; ndx_f1++) { Face f1 = s1.Faces[ndx_f1]; piles.Add(PartitionFace(s3, f1, s2, bb1, bb2, bsp1, bsp2, false)); } for (int ndx_f2 = 0; ndx_f2 < s2.Faces.Count; ndx_f2++) { Face f2 = s2.Faces[ndx_f2]; piles.Add(PartitionFace(s3, f2, s1, bb1, bb2, bsp2, bsp1, true)); } foreach (segpile sp in piles) { if (sp.segs.Count > 0) { s3.CreateFacesFromPileOfSegments(sp.name, sp.OriginalName, sp.Shade, sp.segs, sp.reverse); } } s3.FixCollinearTrios(); return(s3); }
public void test_cube() { Solid s1 = Builtin_Solids.CreateCube("s1", 5); bsp3d bsp = new bsp3d(s1); BoundingBox3d bb = BoundingBox3d.FromArrayOfPoints(s1.Vertices); foreach (xyz v in s1.Vertices) { Assert.AreEqual(PointInPoly.Coincident, bsp.PointInPolyhedron(v)); Assert.IsTrue(bb.PointInside(v)); } foreach (Face f in s1.Faces) { xyz v = f.GetCenter(); xyz n = f.UnitNormal(); Assert.AreEqual(PointInPoly.Coincident, bsp.PointInPolyhedron(v)); Assert.AreEqual(PointInPoly.Outside, bsp.PointInPolyhedron(v + n)); Assert.AreEqual(PointInPoly.Inside, bsp.PointInPolyhedron(v - n)); Assert.IsTrue(bb.PointInside(v)); Assert.IsFalse(bb.PointInside(v + n)); Assert.IsTrue(bb.PointInside(v - n)); } foreach (Face f in s1.Faces) { foreach (HalfEdge h in f.MainLoop) { xyz v = h.Center(); xyz n = h.GetInwardNormal(); Assert.IsTrue(bb.PointInside(v)); Assert.IsTrue(bb.PointInside(v + n)); Assert.IsFalse(bb.PointInside(v - n)); Assert.AreEqual(PointInPoly.Coincident, bsp.PointInPolyhedron(v)); Assert.AreEqual(PointInPoly.Coincident, bsp.PointInPolyhedron(v + n)); Assert.AreEqual(PointInPoly.Outside, bsp.PointInPolyhedron(v - n)); } } Assert.AreEqual(PointInPoly.Inside, bsp.PointInPolyhedron(s1.GetCenter())); Assert.IsTrue(bb.PointInside(s1.GetCenter())); }
public static segpile PartitionFace(Solid s3, Face f1, Solid s2, BoundingBox3d bb1, BoundingBox3d bb2, bsp3d bsp1, bsp3d bsp2, bool reverse) { #if BOOL_DEBUG Console.Out.WriteLine("PartitionFace: f1.name={0} s2={1} reverse={2}", f1.name, s2.name, reverse); #endif List <seg3d> segs = f1.CollectAllSegments(); #if BOOL_DEBUG ut.DumpSegments3d("CollectAllSegments", segs); #endif PartitionFace_CalcFaceIntersections(segs, f1, s2, reverse); #if BOOL_DEBUG ut.DumpSegments3d("CalcFaceIntersections", segs); #endif PartitionFace_HandleOverlaps(segs, f1, s2, reverse); #if BOOL_DEBUG ut.DumpSegments3d("HandleOverlaps", segs); #endif PartitionFace_SplitEdges(segs, s2, bb2, reverse); #if BOOL_DEBUG ut.DumpSegments3d("SplitEdges", segs); #endif #if true // TODO: If we remove this call, only one unit test fails. // that means we usually don't need it. I'd love to find a // way to decide when we need it so we can save the call // most of the time. The failing unit test is the two // mortises overlapping, a case which should never happen // in practice, AFAIK. if (!reverse) // TODO for now, only call this on the !reverse pass { PartitionFace_SplitEdges(segs, f1.solid, bb1, !reverse); } #if BOOL_DEBUG ut.DumpSegments3d("SplitEdges2", segs); #endif #endif PartitionFace_RemoveObvious(segs, f1, s2, bsp2, reverse); #if BOOL_DEBUG ut.DumpSegments3d("RemoveObvious", segs); #endif PartitionFace_HandleCoplanarStuff(segs, f1, s2, reverse); #if BOOL_DEBUG ut.DumpSegments3d("HandleCoplanarStuff", segs); #endif PartitionFace_RemoveByDirection(segs, f1, s2, reverse); #if BOOL_DEBUG ut.DumpSegments3d("RemoveByDirection", segs); #endif PartitionFace_Edges_StuffInOtherSolid(segs, s2, bsp2, reverse); #if BOOL_DEBUG ut.DumpSegments3d("StuffInOtherSolid", segs); #endif PartitionFace_HandleEdges(segs, f1, s2, bsp2, reverse); #if BOOL_DEBUG ut.DumpSegments3d("HandleEdges", segs); #endif Debug.Assert((segs.Count == 0) || (segs.Count >= 3)); string name; if (reverse) { name = string.Format("{0}_{1}", f1.solid.name, f1.name); } else { name = f1.name; } return(new segpile(segs, name, f1.OriginalName, f1.Shade, reverse)); }
public static void PartitionFace_Edges_StuffInOtherSolid(List <seg3d> segs, Solid s2, bsp3d bsp2, bool reverse) { // TODO this function feels like such a hack. List <seg3d> rm = new List <seg3d>(); foreach (seg3d s in segs) { bool bkeep; if (reverse) { bkeep = false; if (s2.SegmentInside(bsp2, s.a, s.b)) { bkeep = true; } else if (s2.SegmentOnSurface(bsp2, s.a, s.b)) { bkeep = true; } } else { bkeep = true; if ( s2.SegmentInside(bsp2, s.a, s.b) && !(s2.SegmentOnSurface(bsp2, s.a, s.b)) ) { bkeep = false; } } if (!bkeep) { rm.Add(s); } } foreach (seg3d s in rm) { segs.Remove(s); } }
public static void PartitionFace_HandleEdges(List <seg3d> segs, Face f1, Solid s2, bsp3d bsp2, bool reverse) { List <seg3d> rm = new List <seg3d>(); segs.ForEach(delegate(seg3d s) { HalfEdge he1a; if (f1.IsAnEdge(s, out he1a)) { HalfEdge he1b = he1a.Opposite(); for (int ndx_f2 = 0; ndx_f2 < s2.Faces.Count; ndx_f2++) { Face f2 = s2.Faces[ndx_f2]; HalfEdge he2a = null; if (f2.IsAnEdge(s, out he2a)) { HalfEdge he2b = he2a.Opposite(); bool bkeep; xyz n1 = he1a.GetInwardNormal(); xyz n2a = he2a.GetInwardNormal(); xyz n2b = he2b.GetInwardNormal(); bkeep = bool3d.SplitsVEP(n1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal()); if (reverse) { } else { bkeep = !bkeep; } if (!bkeep) { rm.Add(s); } break; } PointFaceIntersection pfi_a; PointFaceIntersection pfi_b; if (f2.CalcSegmentFaceIntersection_SamePlane(s, out pfi_a, out pfi_b)) { bool bProceed = false; if ( ( (pfi_a == PointFaceIntersection.Inside) && (pfi_b == PointFaceIntersection.Inside) ) || ( (pfi_a == PointFaceIntersection.Inside) && (pfi_b == PointFaceIntersection.OnEdge) ) || ( (pfi_a == PointFaceIntersection.OnEdge) && (pfi_b == PointFaceIntersection.Inside) ) ) { bProceed = true; } else if ( ( (pfi_a == PointFaceIntersection.OnEdge) && (pfi_b == PointFaceIntersection.OnEdge) ) ) { if (f2.solid.SegmentOnSurface(bsp2, s.a, s.b)) { bProceed = true; } } if (bProceed) { bool bkeep; double dot = xyz.dot(he1a.GetInwardNormal(), f2.UnitNormal()); if (fp.eq_dot_unit(dot, 0)) { // this occurs on the outside of f2 bkeep = true; } else if (dot < 0) { // this intersect occurs on the inside of f2 bkeep = false; } else { // this occurs on the outside of f2 bkeep = true; } if (reverse) { bkeep = !bkeep; } if (!bkeep) { rm.Add(s); if (rm.Count == segs.Count) { break; } } } } } } else { #if false // so far, it looks like we don't need this code. It was written when CreateTenonInFaceWithHoleBool3dBug turned up. I rewrote tenon to use the 4-cut method again and the bug "went away". :-) foreach (Face f2 in s2.Faces) { HalfEdge he2a = null; if (f2.IsAnEdge(s, out he2a)) { HalfEdge he2b = he2a.Opposite(); bool bkeep; xyz n1 = xyz.cross((s.b - s.a).normalize_in_place(), f1.UnitNormal()).normalize_in_place(); xyz n2a = he2a.GetInwardNormal(); xyz n2b = he2b.GetInwardNormal(); if (bool3d.SplitsVEP(n1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal())) { bkeep = true; } else if (bool3d.SplitsVEP(-n1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal())) { bkeep = true; } else { bkeep = false; } if (reverse) { } else { bkeep = !bkeep; } if (!bkeep) { rm.Add(s); } break; } } #endif } } ); foreach (seg3d s in rm) { segs.Remove(s); } }
public static void PartitionFace_RemoveObvious(List <seg3d> segs, Face f1, Solid s2, bsp3d bsp2, bool reverse) { List <seg3d> rm = new List <seg3d>(); foreach (seg3d s in segs) { PointInPoly pip_a = bsp2.PointInPolyhedron(s.a); if (pip_a == PointInPoly.Coincident) { break; } PointInPoly pip_b = bsp2.PointInPolyhedron(s.b); if (pip_b == PointInPoly.Coincident) { break; } if (pip_a != pip_b) { break; } #if false // coverage says this never gets hit bool bNone = true; foreach (Face f2 in s2.Faces) { if (f2.SegmentTouches(s)) { // coverage says this line is never hit // which may mean this entire check is // unneeded. bNone = false; break; } } if (bNone) #endif { bool bKeep = false; if (s2.PointInside(bsp2, s.b)) { bKeep = false; } else { bKeep = true; } if (reverse) { bKeep = !bKeep; } if (!bKeep) { rm.Add(s); } } } foreach (seg3d s in rm) { segs.Remove(s); } }
public static bool CheckIfTwoSolidsShareAnySpace(Solid s1, Solid s2) { BoundingBox3d bb1 = s1.GetBoundingBox(); BoundingBox3d bb2 = s2.GetBoundingBox(); if (!BoundingBox3d.intersect(bb1, bb2)) { return(false); } BoundingBox3d bb3 = BoundingBox3d.CalcIntersection(bb1, bb2); if (fp.eq_tol(bb3.volume, 0, 0.0001)) { return(false); } bsp3d bsp1 = new bsp3d(s1); bsp3d bsp2 = new bsp3d(s2); xyz c1 = s1.GetCenter(); if ( (bsp1.PointInPolyhedron(c1) == PointInPoly.Inside) && (bsp2.PointInPolyhedron(c1) == PointInPoly.Inside) ) { return(true); } xyz c2 = s2.GetCenter(); if ( (bsp2.PointInPolyhedron(c2) == PointInPoly.Inside) && (bsp1.PointInPolyhedron(c2) == PointInPoly.Inside) ) { return(true); } foreach (Face f1 in s1.Faces) { xyz p = f1.GetCenter() - f1.UnitNormal() * 0.01; if ( (bsp2.PointInPolyhedron(p) == PointInPoly.Inside) && (bsp1.PointInPolyhedron(p) == PointInPoly.Inside) ) { return(true); } } foreach (Face f2 in s2.Faces) { xyz p = f2.GetCenter() - f2.UnitNormal() * 0.01; if ( (bsp1.PointInPolyhedron(p) == PointInPoly.Inside) && (bsp2.PointInPolyhedron(p) == PointInPoly.Inside) ) { return(true); } } if (s1.AnyEdgePiercesAnyFace(s2)) { return(true); } if (s2.AnyEdgePiercesAnyFace(s1)) { return(true); } // TODO we apparently need another testhere return(false); }