/// <summary> /// Merges the with hole. /// </summary> public Polygon2D MergeWithHole(Polygon2D actHole, Polygon2DMergeOptions mergeOptions, List <Vector2>?cutPoints) { //This algorithm uses the method described in http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf //Find the hole vertex with the highest x value var holeVertexWithHighestX = new Vector2(float.MinValue, 0f); var holeVertexIndexWithHighestX = -1; for (var loopVertex = 0; loopVertex < actHole._vertices.Length; loopVertex++) { if (actHole._vertices[loopVertex].X > holeVertexWithHighestX.X) { holeVertexWithHighestX = actHole._vertices[loopVertex]; holeVertexIndexWithHighestX = loopVertex; } } cutPoints?.Add(holeVertexWithHighestX); //Define a ray from the found vertex pointing in x direction var ray2D = new Ray2D(holeVertexWithHighestX, new Vector2(1f, 0f)); //Find the line on current filling polygon with intersects first with the created ray Tuple <int, float, Vector2>?foundLine = null; var actLineIndex = 0; foreach (var actLine in this.Lines) { var actIntersection = actLine.Intersect(ray2D); if (actIntersection.Item1) { var rayToIntersectionPoint = new Ray2D( ray2D.Origin, Vector2.Normalize(actIntersection.Item2 - ray2D.Origin)); var lengthToIntersectionPoint = Vector2.Distance(actIntersection.Item2, ray2D.Origin); if (lengthToIntersectionPoint > 0f && rayToIntersectionPoint.EqualsWithTolerance(ray2D)) { if (foundLine == null) { //First found intersection foundLine = Tuple.Create(actLineIndex, lengthToIntersectionPoint, actIntersection.Item2); } else if (lengthToIntersectionPoint < foundLine.Item2) { //More intersections found.. take the one with smallest distance to intersection point foundLine = Tuple.Create(actLineIndex, lengthToIntersectionPoint, actIntersection.Item2); } } } actLineIndex++; } if (cutPoints != null && foundLine != null) { cutPoints.Add(foundLine.Item3); } // Check for found intersection // Return a duplicate of this polygon as the result if no intersection found if (foundLine == null) { var newPolygonVertices = new Vector2[_vertices.Length]; Array.Copy(_vertices, newPolygonVertices, _vertices.Length); return(new Polygon2D(newPolygonVertices)); } //Now generate result polygon var resultBuilder = new List <Vector2>(_vertices.Length + actHole._vertices.Length + 2); for (var loopFillVertex = 0; loopFillVertex < _vertices.Length; loopFillVertex++) { //Add current vertex from filling polygon first resultBuilder.Add(_vertices[loopFillVertex]); //Do special logic on cut point if (loopFillVertex == foundLine.Item1) { //Cut point.. place here the hole polygon if (!_vertices[loopFillVertex].Equals(foundLine.Item3)) { resultBuilder.Add(foundLine.Item3); } //Add all vertices from the hole polygon resultBuilder.Add(actHole._vertices[holeVertexIndexWithHighestX]); var loopHoleVertex = holeVertexIndexWithHighestX + 1; while (loopHoleVertex != holeVertexIndexWithHighestX) { if (loopHoleVertex >= actHole._vertices.Length) { loopHoleVertex = 0; } resultBuilder.Add(actHole._vertices[loopHoleVertex]); loopHoleVertex++; if (loopHoleVertex >= actHole._vertices.Length) { loopHoleVertex = 0; } } //Add cutpoints again to continue with main polygon if (mergeOptions.MakeMergepointSpaceForTriangulation) { resultBuilder.Add(actHole._vertices[holeVertexIndexWithHighestX] + new Vector2(0f, 0.001f)); //Add the cutpoint again resultBuilder.Add(foundLine.Item3 + new Vector2(0f, 0.001f)); } else { resultBuilder.Add(actHole._vertices[holeVertexIndexWithHighestX]); //Add the cutpoint again resultBuilder.Add(foundLine.Item3); } //Handle the case in which next vertex would equal current cut point if (_vertices.Length > loopFillVertex + 1 && _vertices[loopFillVertex + 1].Equals(foundLine.Item3)) { loopFillVertex++; } } } //Return new generate polygon return(new Polygon2D(resultBuilder.ToArray())); }
/// <summary> /// Merges this polygon with the given one defining a hole. The result is a new polygon. /// </summary> public Polygon2D MergeWithHole(Polygon2D actHole, Polygon2DMergeOptions mergeOptions) { return(this.MergeWithHole(actHole, mergeOptions, null)); }