public static bool intersect(BoundingBox3d bb1, BoundingBox3d bb2) { if (fp.lt_inches(bb1.xmax, bb2.xmin)) { return(false); } if (fp.lt_inches(bb2.xmax, bb1.xmin)) { return(false); } if (fp.lt_inches(bb1.ymax, bb2.ymin)) { return(false); } if (fp.lt_inches(bb2.ymax, bb1.ymin)) { return(false); } if (fp.lt_inches(bb1.zmax, bb2.zmin)) { return(false); } if (fp.lt_inches(bb2.zmax, bb1.zmin)) { return(false); } return(true); }
public void test_bb3d() { BoundingBox3d bb = BoundingBox3d.FromArrayOfPoints(ut.MakePoly(new xyz(0, 0, 0), new xyz(5, 5, 5))); Assert.IsTrue(bb.PointInside(new xyz(3, 3, 3))); Assert.IsTrue(bb.PointInside(new xyz(0, 0, 0))); Assert.IsTrue(bb.PointInside(new xyz(5, 5, 5))); Assert.IsTrue(bb.PointInside(new xyz(5, 0, 5))); Assert.IsTrue(bb.PointInside(new xyz(0, 0, 5))); Assert.IsTrue(bb.PointInside(new xyz(0, 4, 0))); Assert.IsFalse(bb.PointInside(new xyz(0, 6, 0))); Assert.IsFalse(bb.PointInside(new xyz(0, 0, -1))); Assert.IsFalse(bb.PointInside(new xyz(7, 6, 0))); Assert.IsFalse(bb.PointInside(new xyz(7, 7, 7))); BoundingBox3d bb2 = BoundingBox3d.FromArrayOfPoints(ut.MakePoly(new xyz(8, 8, 8), new xyz(9, 9, 9))); bb = bb + bb2; Assert.IsTrue(bb.PointInside(new xyz(3, 3, 3))); Assert.IsTrue(bb.PointInside(new xyz(0, 0, 0))); Assert.IsTrue(bb.PointInside(new xyz(5, 5, 5))); Assert.IsTrue(bb.PointInside(new xyz(5, 0, 5))); Assert.IsTrue(bb.PointInside(new xyz(0, 0, 5))); Assert.IsTrue(bb.PointInside(new xyz(0, 4, 0))); Assert.IsTrue(bb.PointInside(new xyz(8, 8, 8))); Assert.IsTrue(bb.PointInside(new xyz(7, 7, 7))); }
public static CompoundSolid Subtract(CompoundSolid s1, Solid s2) { CompoundSolid result = new CompoundSolid(); BoundingBox3d bb2 = s2.GetBoundingBox(); bool bCheck = result.HasSubOverlaps; foreach (Solid s in s1.Subs) { BoundingBox3d bb1 = s.GetBoundingBox(); if (!BoundingBox3d.intersect(bb1, bb2)) { result.AddSub(s.Clone(), bCheck); } else { result.AddSub(Subtract(s, s2), bCheck); } } #if DEBUG result.AssertNoNameClashes(); #endif return(result); }
public static bool MightIntersect_BB(List <xyz> main1, List <xyz> main2) { BoundingBox3d bb1 = BoundingBox3d.FromArrayOfPoints(main1); BoundingBox3d bb2 = BoundingBox3d.FromArrayOfPoints(main2); return(BoundingBox3d.intersect(bb1, bb2)); }
public static CompoundSolid Crosscut_Or_Rip(CompoundSolid sol, HalfEdge he, Inches distAlongEdge, double miter, double tilt) { Face f = he.face; // TODO fix these names string[] fnames = new string[] { "miter_top", "miter_right", "miter_left", "miter_bottom", "miter", // the primary face name which gets left "miter_below", }; xyz nrml = f.UnitNormal(); xyz origin = he.to + he.GetInwardNormal() * distAlongEdge; // vx is in the direction of the cut xyz vx = ut.RotateUnitVector(-he.UnitVector(), -miter, nrml); // now rotate nrml for tilt nrml = ut.RotateUnitVector(nrml, tilt, vx); // origin is going to be the corner of the cutter box. // right now it is sitting on the face. we need to // move it further away. calculate intersections with // the boundingbox of the compoundsolid as a whole. BoundingBox3d bb = sol.GetBoundingBox(); double d_vx = bb.IntersectRay_Planes_Max(origin, -vx); double d_nrml = bb.IntersectRay_Planes_Max(origin, nrml); // TODO this approach creates a bb which is too big. do we care? // now move origin far enough back behind the face origin.subtract_in_place((d_vx + 2) * vx); // and up above the face origin = origin + (d_nrml + 2) * nrml; xyz vy = xyz.cross(nrml, vx).normalize_in_place(); xyz vz = -nrml; d_vx = bb.IntersectRay_Planes_Max(origin, vx); double d_vy = bb.IntersectRay_Planes_Max(origin, vy); double d_vz = bb.IntersectRay_Planes_Max(origin, vz); // now calculate proper sizes for the cutter double dx = d_vx + 2; double dy = d_vy + 2; double dz = d_vz + 2; Solid cutter = CreateCutter_Box(string.Format("{0}_{1}", f.name, he.Opposite().face.name), origin, vx, nrml, new xyz(dx, dy, dz), fnames); //sol = sol.Clone(); //sol.AddSub(cutter); //return sol; return(bool3d.Subtract(sol, cutter)); }
public void Add(BoundingBox3d b) { xmin = Math.Min(xmin, b.xmin); ymin = Math.Min(ymin, b.ymin); zmin = Math.Min(zmin, b.zmin); xmax = Math.Max(xmax, b.xmax); ymax = Math.Max(ymax, b.ymax); zmax = Math.Max(zmax, b.zmax); }
public static BoundingBox3d CalcIntersection(BoundingBox3d a, BoundingBox3d b) { BoundingBox3d r = new BoundingBox3d(); r.xmin = Math.Max(a.xmin, b.xmin); r.ymin = Math.Max(a.ymin, b.ymin); r.zmin = Math.Max(a.zmin, b.zmin); r.xmax = Math.Min(a.xmax, b.xmax); r.ymax = Math.Min(a.ymax, b.ymax); r.zmax = Math.Min(a.zmax, b.zmax); return(r); }
public static BoundingBox3d operator +(BoundingBox3d a, BoundingBox3d b) { BoundingBox3d r = new BoundingBox3d(); r.xmin = Math.Min(a.xmin, b.xmin); r.ymin = Math.Min(a.ymin, b.ymin); r.zmin = Math.Min(a.zmin, b.zmin); r.xmax = Math.Max(a.xmax, b.xmax); r.ymax = Math.Max(a.ymax, b.ymax); r.zmax = Math.Max(a.zmax, b.zmax); return(r); }
// TODO this is only used in the unit tests internal static BoundingBox3d FromPoints(params xyz[] a) { BoundingBox3d bb = new BoundingBox3d(); foreach (xyz v in a) { bb.xmin = Math.Min(bb.xmin, v.x); bb.ymin = Math.Min(bb.ymin, v.y); bb.zmin = Math.Min(bb.zmin, v.z); bb.xmax = Math.Max(bb.xmax, v.x); bb.ymax = Math.Max(bb.ymax, v.y); bb.zmax = Math.Max(bb.zmax, v.z); } return(bb); }
internal static BoundingBox3d FromArrayOfPoints(List <xyz> a) { BoundingBox3d bb = new BoundingBox3d(); a.ForEach(delegate(xyz v) { bb.xmin = Math.Min(bb.xmin, v.x); bb.ymin = Math.Min(bb.ymin, v.y); bb.zmin = Math.Min(bb.zmin, v.z); bb.xmax = Math.Max(bb.xmax, v.x); bb.ymax = Math.Max(bb.ymax, v.y); bb.zmax = Math.Max(bb.zmax, v.z); } ); return(bb); }
public static void PartitionFace_SplitEdges(List <seg3d> segs, Solid s2, BoundingBox3d bb2, bool reverse) { List <seg3d> rm = new List <seg3d>(); List <seg3d> news = new List <seg3d>(); for (int i = 0; i < segs.Count; i++) { seg3d s = segs[i]; if (bb2.SegmentCannotIntersect(s.a, s.b)) { continue; } for (int j = 0; j < s2.Faces.Count; j++) { Face f2 = s2.Faces[j]; xyz hit = f2.CalcSegmentFaceIntersection_HitOnly(s); if (hit != null) { rm.Add(s); seg3d to_the_face = new seg3d(s.a, hit, s.origin); seg3d away_from_the_face = new seg3d(hit, s.b, s.origin); news.Add(to_the_face); news.Add(away_from_the_face); break; } } } if (news.Count > 0) { PartitionFace_SplitEdges(news, s2, bb2, reverse); foreach (seg3d s in rm) { segs.Remove(s); } foreach (seg3d s in news) { segs.Add(s); } } }
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 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); }