/// <summary> /// Construct a solid by sweeping a face along a curve. /// </summary> /// <param name="outer">The perimeter of the face to sweep.</param> /// <param name="inner">The holes of the face to sweep.</param> /// <param name="curve">The curve along which to sweep.</param> /// <param name="material">The solid's material.</param> /// <param name="startSetback">The setback of the sweep from the start of the curve.</param> /// <param name="endSetback">The setback of the sweep from the end of the curve.</param> /// <returns>A solid.</returns> public static Solid SweepFaceAlongCurve(Polygon outer, Polygon[] inner, ICurve curve, Material material = null, double startSetback = 0, double endSetback = 0) { var solid = new Solid(material); var l = curve.Length(); var ssb = startSetback / l; var esb = endSetback / l; var transforms = curve.Frames(ssb, esb); if (curve is Polygon) { for (var i = 0; i < transforms.Length; i++) { var next = i == transforms.Length - 1 ? transforms[0] : transforms[i + 1]; solid.SweepPolygonBetweenPlanes(outer, transforms[i].XY, next.XY); } } else { // Add start cap. Face cap = null; Edge[][] openEdges; if (inner != null) { cap = solid.AddFace(transforms[0].OfPolygon(outer), transforms[0].OfPolygons(inner)); openEdges = new Edge[1 + inner.Length][]; } else { cap = solid.AddFace(transforms[0].OfPolygon(outer)); openEdges = new Edge[1][]; } // last outer edge var openEdge = cap.Outer.GetLinkedEdges(); openEdge = solid.SweepEdges(transforms, openEdge); openEdges[0] = openEdge; if (inner != null) { for (var i = 0; i < cap.Inner.Length; i++) { openEdge = cap.Inner[i].GetLinkedEdges(); // last inner edge for one hole openEdge = solid.SweepEdges(transforms, openEdge); openEdges[i + 1] = openEdge; } } solid.Cap(openEdges, true); } return(solid); }
public static Solid CreateLamina(Polygon perimeter, IList <Polygon> voids = null) { var solid = new Solid(); if (voids != null && voids.Count > 0) { solid.AddFace(perimeter, voids); solid.AddFace(perimeter, voids, true, reverse: true); } else { solid.AddFace(perimeter); solid.AddFace(perimeter, null, true, reverse: true); } return(solid); }
public static Solid CreateLamina(Polygon perimeter, IList <Polygon> voids = null) { var solid = new Solid(); if (voids != null && voids.Count > 0) { solid.AddFace(perimeter, voids); solid.AddFace(perimeter.Reversed(), voids.Select(h => h.Reversed()).ToArray(), true); } else { solid.AddFace(perimeter); solid.AddFace(perimeter.Reversed(), null, true); } return(solid); }
/// <summary> /// Construct a lamina solid. /// </summary> /// <param name="perimeter">The perimeter of the lamina's faces.</param> /// <param name="material">The solid's material.</param> public static Solid CreateLamina(Vector3[] perimeter, Material material) { var solid = new Solid(material); var loop1 = new Loop(); var loop2 = new Loop(); for (var i = 0; i < perimeter.Length; i++) { var a = solid.AddVertex(perimeter[i]); var b = solid.AddVertex(perimeter[i == perimeter.Length - 1 ? 0 : i + 1]); var e = solid.AddEdge(a, b); loop1.AddEdgeToEnd(e.Left); loop2.AddEdgeToStart(e.Right); } solid.AddFace(loop1); solid.AddFace(loop2); return(solid); }
/// <summary> /// Construct a lamina solid. /// </summary> /// <param name="perimeter">The perimeter of the lamina's faces.</param> public static Solid CreateLamina(IList <Vector3> perimeter) { var solid = new Solid(); var loop1 = new Loop(); var loop2 = new Loop(); for (var i = 0; i < perimeter.Count; i++) { var a = solid.AddVertex(perimeter[i]); var b = solid.AddVertex(perimeter[i == perimeter.Count - 1 ? 0 : i + 1]); var e = solid.AddEdge(a, b); loop1.AddEdgeToEnd(e.Left); loop2.AddEdgeToStart(e.Right); } solid.AddFace(loop1); solid.AddFace(loop2); return(solid); }
/// <summary> /// Compute the union of two solids. /// </summary> /// <param name="a">The first solid.</param> /// <param name="aTransform">A local transformation of a.</param> /// <param name="b">The second solid.</param> /// <param name="bTransform">A local transformation of b.</param> /// <returns>A solid which is the union of a and b.</returns> public static Solid Union(Solid a, Transform aTransform, Solid b, Transform bTransform) { var allFaces = Intersect(a, aTransform, b, bTransform); var s = new Solid(); foreach (var p in allFaces.Where(o => o.classification == SetClassification.AOutsideB || o.classification == SetClassification.BOutsideA).Select(o => o.polygon)) { s.AddFace(p, mergeVerticesAndEdges: true); } var result = MergeCoplanarFaces(allFaces, Union); if (result != null) { foreach (var(perimeter, holes) in result) { s.AddFace(perimeter, holes, mergeVerticesAndEdges: true); } } return(s); }
/// <summary> /// Construct a solid by sweeping a face in a direction. /// </summary> /// <param name="perimeter">The perimeter of the face to sweep.</param> /// <param name="holes">The holes of the face to sweep.</param> /// <param name="direction">The direction in which to sweep.</param> /// <param name="distance">The distance to sweep.</param> /// <param name="bothSides">Should the sweep start offset by direction distance/2? </param> /// <returns>A solid.</returns> public static Solid SweepFace(Polygon perimeter, Polygon[] holes, Vector3 direction, double distance, bool bothSides = false) { // We do a difference of the polygons // to get the clipped shape. This will fail in interesting // ways if the clip creates two islands. if (holes != null) { var newPerimeter = perimeter.Difference(holes); perimeter = newPerimeter[0]; holes = newPerimeter.Skip(1).Take(newPerimeter.Count - 1).ToArray(); } var solid = new Solid(); Face fStart = null; if (bothSides) { var t = new Transform(direction.Negated() * (distance / 2)); if (holes != null) { fStart = solid.AddFace(t.OfPolygon(perimeter.Reversed()), t.OfPolygons(holes.Reversed())); } else { fStart = solid.AddFace(t.OfPolygon(perimeter.Reversed())); } } else { if (holes != null) { fStart = solid.AddFace(perimeter.Reversed(), holes.Reversed()); } else { fStart = solid.AddFace(perimeter.Reversed()); } } var fEndOuter = solid.SweepLoop(fStart.Outer, direction, distance); if (holes != null) { var fEndInner = new Loop[holes.Length]; for (var i = 0; i < holes.Length; i++) { fEndInner[i] = solid.SweepLoop(fStart.Inner[i], direction, distance); } solid.AddFace(fEndOuter, fEndInner); } else { solid.AddFace(fEndOuter); } return(solid); }
/// <summary> /// Construct a solid by sweeping a face in a direction. /// </summary> /// <param name="outerLoop">The perimeter of the face to sweep.</param> /// <param name="innerLoops">The holes of the face to sweep.</param> /// <param name="direction">The direction in which to sweep.</param> /// <param name="distance">The distance to sweep.</param> /// <param name="bothSides">Should the sweep start offset by direction distance/2? </param> /// <param name="material">The solid's material.</param> /// <returns>A solid.</returns> public static Solid SweepFace(Polygon outerLoop, Polygon[] innerLoops, Vector3 direction, double distance, Material material = null, bool bothSides = false) { var solid = new Solid(material); Face fStart = null; if (bothSides) { var t = new Transform(direction.Negated() * (distance / 2)); if (innerLoops != null) { fStart = solid.AddFace(t.OfPolygon(outerLoop.Reversed()), t.OfPolygons(innerLoops.Reversed())); } else { fStart = solid.AddFace(t.OfPolygon(outerLoop.Reversed())); } } else { if (innerLoops != null) { fStart = solid.AddFace(outerLoop.Reversed(), innerLoops.Reversed()); } else { fStart = solid.AddFace(outerLoop.Reversed()); } } var fEndOuter = solid.SweepLoop(fStart.Outer, direction, distance); if (innerLoops != null) { var fEndInner = new Loop[innerLoops.Length]; for (var i = 0; i < innerLoops.Length; i++) { fEndInner[i] = solid.SweepLoop(fStart.Inner[i], direction, distance); } solid.AddFace(fEndOuter, fEndInner); } else { solid.AddFace(fEndOuter); } return(solid); }
/// <summary> /// Construct a solid by sweeping a face along a curve. /// </summary> /// <param name="perimeter">The perimeter of the face to sweep.</param> /// <param name="holes">The holes of the face to sweep.</param> /// <param name="curve">The curve along which to sweep.</param> /// <param name="startSetback">The setback distance of the sweep from the start of the curve.</param> /// <param name="endSetback">The setback distance of the sweep from the end of the curve.</param> /// <returns>A solid.</returns> public static Solid SweepFaceAlongCurve(Polygon perimeter, IList <Polygon> holes, ICurve curve, double startSetback = 0, double endSetback = 0) { var solid = new Solid(); var l = curve.Length(); // The start and end setbacks can't be more than // the length of the beam together. if ((startSetback + endSetback) >= l) { startSetback = 0; endSetback = 0; } // Calculate the setback parameter as a percentage // of the curve length. This will not work for curves // without non-uniform parameterization. var ssb = startSetback / l; var esb = endSetback / l; var transforms = curve.Frames(ssb, esb); if (curve is Polygon) { for (var i = 0; i < transforms.Length; i++) { var next = i == transforms.Length - 1 ? transforms[0] : transforms[i + 1]; solid.SweepPolygonBetweenPlanes(perimeter, transforms[i], next); } } else if (curve is Bezier) { var startCap = solid.AddFace(transforms[0].OfPolygon(perimeter)); for (var i = 0; i < transforms.Length - 1; i++) { var next = transforms[i + 1]; solid.SweepPolygonBetweenPlanes(perimeter, transforms[i], next); } var endCap = solid.AddFace(transforms[transforms.Length - 1].OfPolygon(perimeter).Reversed()); } else { // Add start cap. Face cap = null; Edge[][] openEdges; if (holes != null) { cap = solid.AddFace(transforms[0].OfPolygon(perimeter), transforms[0].OfPolygons(holes)); openEdges = new Edge[1 + holes.Count][]; } else { cap = solid.AddFace(transforms[0].OfPolygon(perimeter)); openEdges = new Edge[1][]; } // last outer edge var openEdge = cap.Outer.GetLinkedEdges(); openEdge = solid.SweepEdges(transforms, openEdge); openEdges[0] = openEdge; if (holes != null) { for (var i = 0; i < cap.Inner.Length; i++) { openEdge = cap.Inner[i].GetLinkedEdges(); // last inner edge for one hole openEdge = solid.SweepEdges(transforms, openEdge); openEdges[i + 1] = openEdge; } } solid.Cap(openEdges, true); } return(solid); }
/// <summary> /// Construct a solid by sweeping a face in a direction. /// </summary> /// <param name="perimeter">The perimeter of the face to sweep.</param> /// <param name="holes">The holes of the face to sweep.</param> /// <param name="direction">The direction in which to sweep.</param> /// <param name="distance">The distance to sweep.</param> /// <param name="bothSides">Should the sweep start offset by direction distance/2? </param> /// <param name="rotation">An optional rotation in degrees of the perimeter around the direction vector.</param> /// <returns>A solid.</returns> public static Solid SweepFace(Polygon perimeter, IList <Polygon> holes, Vector3 direction, double distance, bool bothSides = false, double rotation = 0.0) { // We do a difference of the polygons // to get the clipped shape. This will fail in interesting // ways if the clip creates two islands. // if(holes != null) // { // var newPerimeter = perimeter.Difference(holes); // perimeter = newPerimeter[0]; // holes = newPerimeter.Skip(1).Take(newPerimeter.Count - 1).ToArray(); // } var solid = new Solid(); Face fStart = null; if (bothSides) { var t = new Transform(direction.Negate() * (distance / 2), rotation); if (holes != null) { fStart = solid.AddFace(perimeter, holes, transform: t, reverse: true); } else { fStart = solid.AddFace(perimeter, transform: t, reverse: true); } } else { if (holes != null) { fStart = solid.AddFace(perimeter, holes, reverse: true); } else { fStart = solid.AddFace(perimeter, reverse: true); } } var fEndOuter = solid.SweepLoop(fStart.Outer, direction, distance); if (holes != null) { var fEndInner = new Loop[holes.Count]; for (var i = 0; i < holes.Count; i++) { fEndInner[i] = solid.SweepLoop(fStart.Inner[i], direction, distance); } solid.AddFace(fEndOuter, fEndInner); } else { solid.AddFace(fEndOuter); } return(solid); }
/// <summary> /// Compute the difference of two solids. /// </summary> /// <param name="a">The first solid.</param> /// <param name="aTransform">A local transformation of a.</param> /// <param name="b">The second solid.</param> /// <param name="bTransform">A local transformation of b.</param> /// <returns>A solid which is the difference of a and b.</returns> public static Solid Difference(Solid a, Transform aTransform, Solid b, Transform bTransform) { var allFaces = Intersect(a, aTransform, b, bTransform); var s = new Solid(); // Group the faces according to their classification. // AOutsideB for everything outside B which should remain. // AInsideB for everything inside A which should become a hole. var outsideFaces = allFaces.Where(o => o.classification == SetClassification.AOutsideB).ToList(); // TODO: The following is a hack because our Polygon.IntersectOneToMany // method returns overlapping polygons where there are disjoint polygons. var insideFaces = allFaces.Where(i => i.classification == SetClassification.AInsideB).ToList(); foreach (var(polygon, classification, coplanarClassification) in outsideFaces) { if (insideFaces.Count == 0) { s.AddFace(polygon, mergeVerticesAndEdges: true); } else { var plane = polygon._plane; var holes = new List <Polygon>(); foreach (var insideFace in insideFaces) { if (polygon.Contains3D(insideFace.polygon)) { // We need to do this edge overlap check to ensure // that we're not making a profile where the openings // overlaps the edges of the perimeter, creating a face // with zero thickness between the outer and inner // loops. var hasEdgeOverlap = false; foreach (var(from, to) in polygon.Edges()) { foreach (var edge in insideFace.polygon.Edges()) { if (from.DistanceTo(edge, out _).ApproximatelyEquals(0) && to.DistanceTo(edge, out _).ApproximatelyEquals(0)) { hasEdgeOverlap = true; break; } } } if (!hasEdgeOverlap) { if (plane.Normal.Dot(insideFace.polygon._plane.Normal).ApproximatelyEquals(1.0)) { holes.Add(insideFace.polygon.Reversed()); } else { holes.Add(insideFace.polygon); } } } } s.AddFace(polygon, holes, mergeVerticesAndEdges: true); } } // TODO: Can we invert the faces first? var bInsideFaces = allFaces.Where(o => o.classification == SetClassification.BInsideA).ToList(); foreach (var(polygon, classification, coplanarClassification) in bInsideFaces) { s.AddFace(polygon.Reversed(), mergeVerticesAndEdges: true); } var result = MergeCoplanarFaces(allFaces, Difference); if (result != null) { foreach (var(perimeter, holes) in result) { s.AddFace(perimeter, holes, mergeVerticesAndEdges: true); } } return(s); }