Example #1
0
        /// <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);
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
        /// <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);
        }
Example #7
0
        /// <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);
        }
Example #8
0
        /// <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);
        }
Example #9
0
        /// <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);
        }
Example #10
0
        /// <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);
        }
Example #11
0
        /// <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);
        }