/// <summary> /// Returns ordered Lists of perimeter edges from a Mesh. /// There is no guarantee of directionality, only spatially sequential lines. /// </summary> /// <returns> /// A List of Lines. /// </returns> public static List <List <Line> > EdgesPerimeters(this Mesh mesh) { var edges = new List <Line>(); mesh.Triangles.ToList().ForEach(t => edges.AddRange(t.Edges())); var pEdges = edges.Where(e => e.Occurs(edges) == 1).ToList(); var perimeters = new List <List <Line> >(); while (pEdges.Count() > 0) { var edge = pEdges.First(); pEdges = pEdges.Skip(1).ToList(); var perimeter = new List <Line> { edge }; var connected = pEdges.Where(e => e.Start.IsAlmostEqualTo(edge.End) || e.End.IsAlmostEqualTo(edge.End)).ToList(); while (connected.Count() > 0) { pEdges.Remove(connected.First()); edge = new Line(edge.End, edge.End.FarthestFrom(connected.First().Points())); perimeter.Add(edge); connected = pEdges.Where(e => e.Start.IsAlmostEqualTo(edge.End) || e.End.IsAlmostEqualTo(edge.End)).ToList(); } perimeters.Add(perimeter); } if (perimeters.Count == 1) { return(perimeters); } return(perimeters.OrderByDescending(p => Shaper.TotalLength(p)).ToList()); }
/// <summary> /// Returns the perpendicular distance from this Line to the supplied Vector3 point. /// </summary> /// <param name="point">Vector3 representing a point.</param> /// <returns>A double.</returns> public static double PerpendicularDistanceTo(this Line line, Vector3 point) { if (line.PointOnLine(point, true)) { return(0.0); } var area = Math.Abs(Shaper.MakePolygon(new[] { line.Start, line.End, point }.ToList()).Area()); return(area / (line.Length() * 0.5)); }
/// <summary> /// Returns a new Polygon rotated around a supplied Vector3 by the specified angle in degrees. /// </summary> /// <param name="pivot">The Vector3 base point of the rotation.</param> /// <param name="angle">The desired rotation angle in degrees.</param> /// <returns> /// A new Polygon. /// </returns> public static Polygon Rotate(this Polygon polygon, Vector3 pivot, double angle) { var theta = angle * (Math.PI / 180); var vertices = new List <Vector3>(); foreach (Vector3 vertex in polygon.Vertices) { var rX = (Math.Cos(theta) * (vertex.X - pivot.X)) - (Math.Sin(theta) * (vertex.Y - pivot.Y)) + pivot.X; var rY = (Math.Sin(theta) * (vertex.X - pivot.X)) + (Math.Cos(theta) * (vertex.Y - pivot.Y)) + pivot.Y; var rZ = vertex.Z; vertices.Add(new Vector3(rX, rY, rZ)); } return(Shaper.MakePolygon(vertices)); }
/// <summary> /// Returns the List of Polygons that can merge with this Polygon. /// </summary> /// <param name="polygons">List of Polygons to test.</param> /// <returns>A List of Polygons that can be merged with this Polygon.</returns> public static List <Polygon> CanMerge(this Polygon polygon, List <Polygon> polygons) { var mrgPolygons = new List <Polygon>(); foreach (var poly in polygons) { var polys = polygon.ToList(); polys.Add(poly); if (Shaper.Merge(polys).Count == 1) { mrgPolygons.Add(poly); } } return(mrgPolygons); }
/// <summary> /// Attempts to scale up a Polygon until coming within the tolerance percentage of the target area. /// </summary> /// <param name="polygon">Polygon to scale.</param> /// <param name="area">Target area of the Polygon.</param> /// <param name="tolerance">Area total tolerance.</param> /// <param name="origin">Alignment location for final Polygon.</param> /// <param name="within">Polygon acting as a constraining outer boundary.</param> /// <param name="among">List of Polygons to avoid intersecting.</param> /// <returns> /// A new Polygon. /// </returns> public static Polygon ExpandToArea(this Polygon polygon, double area, double ratio, double tolerance = 0.1, Orient origin = Orient.C, Polygon within = null, List <Polygon> among = null) { if (polygon.IsClockWise()) { polygon = polygon.Reversed(); } if (Math.Abs(polygon.Area() - area) <= Math.Abs(tolerance * area)) { return(polygon); } var position = polygon.Compass().PointBy(origin); Polygon tryPoly = Shaper.RectangleByArea(area, ratio); tryPoly = tryPoly.MoveFromTo(tryPoly.Compass().PointBy(origin), position); double tryArea = tryPoly.Area(); do { var t = new Transform(); t.Scale(tryArea / area, tryPoly.Compass().PointBy(origin)); tryPoly = tryPoly.TransformedPolygon(t); if (within != null && tryPoly.Intersects(within)) { var tryPolys = within.Intersection(tryPoly); if (tryPolys != null && tryPolys.Count > 0) { tryPoly = tryPolys.First(); } } if (among != null && tryPoly.Intersects(among)) { var tryPolys = Shaper.Differences(tryPoly.ToList(), among); if (tryPolys != null && tryPolys.Count > 0) { tryPoly = tryPolys.First(); } } tryArea = tryPoly.Area(); }while (!Shaper.NearEqual(tryPoly.Area(), area, tolerance * area) && !Shaper.NearEqual(tryPoly.Area(), tryArea, tolerance)); return(tryPoly); }
/// <summary> /// Creates the largest Polygon fitted to supplied intersecting Polygons. /// </summary> /// <param name="among">List of Polygons against which this Polygon must conform.</param> /// <returns> /// A new Polygon. /// </returns> public static Polygon FitAmong(this Polygon polygon, List <Polygon> among) { if (among == null || among.Count == 0) { return(polygon); } var polygons = Shaper.Differences(polygon.ToList(), among); if (polygons.Count == 0) { return(null); } polygon = polygons.OrderByDescending(p => Math.Abs(p.Area())).First(); if (polygon.IsClockWise()) { return(polygon.Reversed()); } return(polygon); }
/// <summary> /// Creates a new CCW Polygon of the same vertices with the start point now at the indexed Polygon vertex. /// </summary> /// <param name="start">The index of the point from which to start the new Polygon.</param> /// <returns>A new Polygon.</returns> public static Polygon RewindFrom(this Polygon polygon, int start) { var vertices = polygon.Vertices; if (start < 0 || start > vertices.Count - 1) { return(null); } var points = new List <Vector3>() { vertices[start] }; var i = start + 1; while (i < vertices.Count) { points.Add(vertices[i % vertices.Count]); i++; } return(Shaper.MakePolygon(points)); }
/// <summary> /// Returns a List of Polygons derived from the Polygon's straight skeleton. /// </summary> /// <returns>A List of Polygons.</Line></returns> public static List <Polygon> Jigsaw(this Polygon polygon) { var vertices2d = new List <Vector2d>(); foreach (var vertex in polygon.Vertices) { vertices2d.Add(new Vector2d(vertex.X, vertex.Y)); } var skeleton = SkeletonBuilder.Build(vertices2d); var polygons = new List <Polygon>(); foreach (var edgeResult in skeleton.Edges) { var vertices = new List <Vector3>(); foreach (var vertex in edgeResult.Polygon) { vertices.Add(new Vector3(vertex.X, vertex.Y, 0.0)); } polygons.Add(Shaper.MakePolygon(vertices)); } return(polygons.OrderBy(p => p.Centroid()).ToList()); }
/// <summary> /// Creates a Polygon with a reduced quantity of points tested against a deviation tolerance. /// </summary> /// <param name="tolerance">The deviation tolerance for inclusion in the final Vector3 List.</param> /// <returns> /// A new Polygon. /// </returns> public static Polygon Straighten(this Polygon polygon, double tolerance) { return(Shaper.MakePolygon(SimplifyNet.Straighten(polygon.Vertices.ToList(), tolerance))); }
/// <summary> /// Reduces Polygon vertices. /// </summary> /// <param name="tolerance">The tolerated deviation to include a vertex.</param> /// <returns>A new Polyline.</returns> public static Polygon Simplify(this Polygon polygon, double minLength, double filletFactor = 1.0) { return(Shaper.MakePolygon(Shaper.Simplify(polygon.Vertices.ToList(), minLength, filletFactor))); }
/// <summary> /// Reduces Polyline vertices. /// </summary> /// <param name="minLength">The tolerated deviation to include a vertex.</param> /// <returns>A new Polyline.</returns> public static Polyline Simplify(this Polyline polyline, double minLength, double filletFactor = 1.0) { return(new Polyline(Shaper.Simplify(polyline.Vertices.ToList(), minLength, filletFactor))); }
/// <summary> /// Creates a rotated 2D grid of Vector3 points from the supplied Polygon, axis intervals, and angle. /// </summary> /// <param name="perimeter">Polygon boundary of the point grid.</param> /// <param name="xInterval">Spacing of the grid along the x-axis.</param> /// <param name="yInterval">Spacing of the grid along the y-axis.</param> /// <param name="angle">Rotation of the grid around the Polygon centroid.</param> /// <returns> /// A new CoordGrid. /// </returns> public CoordinateGrid(Polygon polygon, double xInterval = 1.0, double yInterval = 1.0, double angle = 0.0) { if (polygon == null) { return; } random = new Random(); Allocated = new List <Vector3>(); Available = new List <Vector3>(); Perimeter = Shaper.MakePolygon(polygon.Vertices.ToList()); var centroid = polygon.Centroid(); var box = new CompassBox(polygon); var points = new List <Vector3>(); // Northeast quadrant var x = centroid.X + (xInterval * 0.5); var y = centroid.Y + (yInterval * 0.5); while (y <= box.NW.Y) { while (x <= box.SE.X) { points.Add(new Vector3(x, y)); x += xInterval; } x = centroid.X + (xInterval * 0.5); y += yInterval; } // Northwest quadrant x = centroid.X - (xInterval * 0.5); y = centroid.Y + (yInterval * 0.5); while (y <= box.NW.Y) { while (x >= box.SW.X) { points.Add(new Vector3(x, y)); x -= xInterval; } x = centroid.X - (xInterval * 0.5); y += yInterval; } // Southeast quadrant x = centroid.X + (xInterval * 0.5); y = centroid.Y - (yInterval * 0.5); while (y >= box.SW.Y) { while (x <= box.SE.X) { points.Add(new Vector3(x, y)); x += xInterval; } x = centroid.X + (xInterval * 0.5); y -= yInterval; } // Southwest quadrant x = centroid.X - (xInterval * 0.5); y = centroid.Y - (yInterval * 0.5); while (y >= box.SW.Y) { while (x >= box.SW.X) { points.Add(new Vector3(x, y)); x -= xInterval; } x = centroid.X - (xInterval * 0.5); y -= yInterval; } foreach (var pnt in points) { var point = pnt.Rotate(centroid, angle); if (polygon.Covers(point)) { Available.Add(point); } } }
/// <summary> /// Returns a List of Lines representing the Arc divided into the specified quantity of segments. /// </summary> /// <param name="segments">Quantity of desired segments.</param> /// <returns> /// A List of Lines. /// </returns> public static List <Line> ToLines(this Arc arc, int segments) { return(Shaper.PointsToLines(arc.Divide(segments))); }