/// <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 Vector2 holeVertexWithHighestX = new Vector2(float.MinValue, 0f); int holeVertexIndexWithHighestX = -1; for (int loopVertex = 0; loopVertex < actHole.m_vertices.Length; loopVertex++) { if (actHole.m_vertices[loopVertex].X > holeVertexWithHighestX.X) { holeVertexWithHighestX = actHole.m_vertices[loopVertex]; holeVertexIndexWithHighestX = loopVertex; } } if (cutPoints != null) { cutPoints.Add(holeVertexWithHighestX); } //Define a ray from the found vertex pointing in x direction Ray2D 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; int actLineIndex = 0; foreach (Line2D actLine in this.Lines) { var actIntersection = actLine.Intersect(ray2D); if (actIntersection.Item1) { Ray2D rayToIntersectionPoint = new Ray2D( ray2D.Origin, Vector2.Normalize(actIntersection.Item2 - ray2D.Origin)); float 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 smalles distance to intersection point foundLine = Tuple.Create(actLineIndex, lengthToIntersectionPoint, actIntersection.Item2); } } } actLineIndex++; } if (cutPoints != null) { cutPoints.Add(foundLine.Item3); } //Check for found intersection if (foundLine == null) { throw new SeeingSharpException("No point found on which given polygons can be combinded!"); } //Now generate result polygon List <Vector2> resultBuilder = new List <Vector2>(this.m_vertices.Length + actHole.m_vertices.Length + 2); for (int loopFillVertex = 0; loopFillVertex < this.m_vertices.Length; loopFillVertex++) { //Add current vertex from filling polygon first resultBuilder.Add(m_vertices[loopFillVertex]); //Do special logic on cut point if (loopFillVertex == foundLine.Item1) { //Cut point.. place here the hole polygon if (!m_vertices[loopFillVertex].Equals(foundLine.Item3)) { resultBuilder.Add(foundLine.Item3); } //Add all vertices from the hole polygon resultBuilder.Add(actHole.m_vertices[holeVertexIndexWithHighestX]); int loopHoleVertex = holeVertexIndexWithHighestX + 1; while (loopHoleVertex != holeVertexIndexWithHighestX) { if (loopHoleVertex >= actHole.m_vertices.Length) { loopHoleVertex = 0; } resultBuilder.Add(actHole.m_vertices[loopHoleVertex]); loopHoleVertex++; if (loopHoleVertex >= actHole.m_vertices.Length) { loopHoleVertex = 0; } } //Add cutpoints again to continue with main polygon if (mergeOptions.MakeMergepointSpaceForTriangulation) { resultBuilder.Add(actHole.m_vertices[holeVertexIndexWithHighestX] + new Vector2(0f, 0.001f)); //Add the cutpoint again resultBuilder.Add(foundLine.Item3 + new Vector2(0f, 0.001f)); } else { resultBuilder.Add(actHole.m_vertices[holeVertexIndexWithHighestX]); //Add the cutpoint again resultBuilder.Add(foundLine.Item3); } //Handle the case in which next vertex would equal current cut point if ((m_vertices.Length > loopFillVertex + 1) && (m_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(MergeWithHole(actHole, mergeOptions, null)); }