/// <summary> /// Constructor that uses a List of Points and an optional List of Point-Indices /// </summary> /// <param name="points">The Polygon-Defining Points</param> /// <param name="indices">Optional List of Point-Indices</param> public PolygonData(List <Point> points, List <int> indices = null) { // Initialize mPoints = new List <PolygonPoint>(points.Select(p => new PolygonPoint(p))); mHoles = new List <List <PolygonPoint> >(); mNumBoundaryPoints = mPoints.Count; // If no Indices were specified, add them manually if (indices == null) { for (int i = 0; i < mPoints.Count; i++) { mPoints[i].Index = i; } } // If there were Indices specified, use them to set the PolygonPoint's Index Property else { for (int i = 0; i < mPoints.Count; i++) { mPoints[i].Index = indices[i]; } } // Add Edges between the Points (to be able to navigate along the Polygon easily later) var cnt = mPoints.Count; for (int i = 0; i < cnt; i++) { var lastIdx = (i + cnt - 1) % cnt; var edge = new PolygonEdge(mPoints[lastIdx], mPoints[i]); mPoints[lastIdx].EdgeTwo = edge; mPoints[i].EdgeOne = edge; } }
/// <summary> /// Constructor taking an Edge and a Helper /// </summary> /// <param name="edge">The Edge of the StatusHelperElement</param> /// <param name="point">The Helper for the Edge of the StatusHelperElement</param> internal StatusHelperElement(PolygonEdge edge, PolygonPoint point) { this.Edge = edge; this.Helper = point; var vector = edge.PointTwo.Point - edge.PointOne.Point; this.mFactor = vector.X / vector.Y; this.MinX = Math.Min(edge.PointOne.X, edge.PointTwo.X); }
/// <summary> /// For a Point, last used Edge and possible Edges, retrieve the best next Edge /// </summary> /// <param name="point">The current Point</param> /// <param name="lastEdge">The last used Edge</param> /// <param name="possibleEdges">The possible next Edges</param> /// <returns>Best next Edge</returns> internal static PolygonEdge BestEdge(PolygonPoint point, PolygonEdge lastEdge, List <PolygonEdge> possibleEdges) { // If just Starting, return the first possible Edge of the Point // If only one possibility, return that if ((lastEdge.PointOne == null && lastEdge.PointTwo == null) || possibleEdges.Count == 1) { return(possibleEdges[0]); } // Variables needed to determine the next Edge var bestEdge = possibleEdges[0]; var bestAngle = (float)Math.PI * 2; // Vector from last Point to current Point var lastVector = (lastEdge.PointTwo.Point - lastEdge.PointOne.Point); lastVector.Normalize(); // Using CCW Point Order, so the left Vector always points towards the Polygon Center var insideVector = new Point(-lastVector.Y, lastVector.X); // Check all possible Edges foreach (var possibleEdge in possibleEdges) { // Next Edge Vector var edgeVector = (possibleEdge.PointTwo.Point - possibleEdge.PointOne.Point); edgeVector.Normalize(); // Dot determines if the Vector also points towards the Polygon Center or not (> 0, yes, < 0, no) var dot = insideVector.X * edgeVector.X + insideVector.Y * edgeVector.Y; // Cos represents the Angle between the last Edge and the next Edge var cos = lastVector.X * edgeVector.X + lastVector.Y * edgeVector.Y; var angle = 0f; // Depending on the Dot-Value, calculate the actual "inner" Angle if ((insideVector.X * edgeVector.X + insideVector.Y * edgeVector.Y) > 0) { angle = (float)Math.PI - (float)Math.Acos(cos); } else { angle = (float)Math.PI + (float)Math.Acos(cos); } // Replace the old Values if a better Edge was found if (angle < bestAngle) { bestAngle = angle; bestEdge = possibleEdge; } } return(bestEdge); }
/// <summary> /// 将孔的点添加到PolygonData /// Add Points of a Hole to the PolygonData /// </summary> /// <param name="points">The Points that define the Hole in the Polygon</param> internal void AddHole(List <Point> points) { // Make Hole Clockwise if (SweepLinePolygonTriangulator.IsCCW(points)) { points.Reverse(); } // The Hole Points var polyPoints = points.Select(p => new PolygonPoint(p)).ToList(); // If Endpoint equals Startpoint if (polyPoints[0].Equals(polyPoints[polyPoints.Count - 1])) { polyPoints.RemoveAt(polyPoints.Count - 1); } mHoles.Add(polyPoints); var cntBefore = mPoints.Count; var pointCount = points.Count; // Add the PolygonPoints for this Polygon Object mPoints.AddRange(polyPoints); // Add the Indices for (int i = cntBefore; i < mPoints.Count; i++) { polyPoints[i - cntBefore].Index = i; } // Add Edges between the Points (to be able to navigate along the Polygon easily later) var cnt = mPoints.Count; for (int i = 0; i < pointCount; i++) { var lastIdx = (i + pointCount - 1) % pointCount; var edge = new PolygonEdge(polyPoints[lastIdx], polyPoints[i]); polyPoints[lastIdx].EdgeTwo = edge; polyPoints[i].EdgeOne = edge; } }
/// <summary> /// Removes all StatusHelperElements with a specific Edge /// </summary> /// <param name="edge"></param> internal void Remove(PolygonEdge edge) { this.EdgesHelpers.RemoveAll(she => she.Edge == edge); }
/// <summary> /// Split Polygon into subpolagons using the calculated Diagonals /// </summary> /// <param name="poly">The Base-Polygon</param> /// <param name="diagonals">The Split-Diagonals</param> /// <returns>List of Subpolygons</returns> private static List <PolygonData> SplitIntoPolygons(PolygonData poly, List <Tuple <int, int> > diagonals) { if (diagonals.Count == 0) { return new List <PolygonData>() { poly } } ; diagonals = diagonals.OrderBy(d => d.Item1).ThenBy(d => d.Item2).ToList(); var edges = new SortedDictionary <int, List <PolygonEdge> >(); foreach (var edge in poly.Points.Select(p => p.EdgeTwo) .Union(diagonals.Select(d => new PolygonEdge(poly.Points[d.Item1], poly.Points[d.Item2]))) .Union(diagonals.Select(d => new PolygonEdge(poly.Points[d.Item2], poly.Points[d.Item1])))) { if (!edges.ContainsKey(edge.PointOne.Index)) { edges.Add(edge.PointOne.Index, new List <PolygonEdge>() { edge }); } else { edges[edge.PointOne.Index].Add(edge); } } var subPolygons = new List <PolygonData>(); var cnt = 0; foreach (var edge in edges) { cnt += edge.Value.Count; } // For each Diagonal while (edges.Count > 0) { // Start at first Diagonal Point var currentPoint = edges.First().Value.First().PointOne; var nextEdge = new PolygonEdge(null, null); var subPolygonPoints = new List <PolygonPoint>(); // March along the edges to form a monotone Polygon // Until the current Point equals the StartPoint do { // Add the current Point subPolygonPoints.Add(currentPoint); // Select the next Edge var possibleEdges = edges[currentPoint.Index].ToList(); nextEdge = BestEdge(currentPoint, nextEdge, possibleEdges); // Remove Edge from possible Edges edges[currentPoint.Index].Remove(nextEdge); if (edges[currentPoint.Index].Count == 0) { edges.Remove(currentPoint.Index); } // Move to the next Point currentPoint = nextEdge.PointTwo; }while (subPolygonPoints[0].Index != currentPoint.Index); // Add the new SubPolygon subPolygons.Add(new PolygonData(subPolygonPoints)); } return(subPolygons); }
/// <summary> /// Constructor taking an Edge and a Helper /// </summary> /// <param name="edge">The Edge of the StatusHelperElement</param> /// <param name="point">The Helper for the Edge of the StatusHelperElement</param> internal StatusHelperElement(PolygonEdge edge, PolygonPoint point) { this.Edge = edge; this.Helper = point; }