/// <summary> /// Given an edge AB, it searches for the triangle that has an edge with the same vertices in the same order. /// </summary> /// <remarks> /// Remember that the vertices of a triangle are sorted counter-clockwise. /// </remarks> /// <param name="edgeVertexA">The index of the first vertex of the edge.</param> /// <param name="edgeVertexB">The index of the second vertex of the edge.</param> /// <returns>The data of the triangle.</returns> public DelaunayTriangleEdge FindTriangleThatContainsEdge(int edgeVertexA, int edgeVertexB) { DelaunayTriangleEdge foundTriangle = new DelaunayTriangleEdge(NOT_FOUND, NOT_FOUND, edgeVertexA, edgeVertexB); for (int i = 0; i < TriangleCount; ++i) { for (int j = 0; j < 3; ++j) { if (m_triangleVertices[i * 3 + j] == edgeVertexA && m_triangleVertices[i * 3 + (j + 1) % 3] == edgeVertexB) { foundTriangle.TriangleIndex = i; foundTriangle.EdgeIndex = j; break; } } } return(foundTriangle); }
/// <summary> /// Adds an edge to the triangulation in such a way that it keeps there even if it form triangles that do not fulfill the Delaunay constraint. /// If the edge already exists, nothing will be done. /// </summary> /// <remarks> /// The order in which the vertices of the edges are provided is important, as the edge may be part of a polygon whose vertices are sorted CCW. /// </remarks> /// <param name="endpointAIndex">The index of the first vertex of the edge, in the existing triangulation.</param> /// <param name="endpointBIndex">The index of the second vertex of the edge, in the existing triangulation.</param> private void AddConstrainedEdgeToTriangulation(int endpointAIndex, int endpointBIndex) { // Detects if the edge already exists if (m_triangleSet.FindTriangleThatContainsEdge(endpointAIndex, endpointBIndex).TriangleIndex != NOT_FOUND) { return; } Vector2 edgeEndpointA = m_triangleSet.GetPointByIndex(endpointAIndex); Vector2 edgeEndpointB = m_triangleSet.GetPointByIndex(endpointBIndex); // 5.3.1: Search for the triangle that contains the beginning of the new edge int triangleContainingA = m_triangleSet.FindTriangleThatContainsLineEndpoint(endpointAIndex, endpointBIndex); // 5.3.2: Get all the triangle edges intersected by the constrained edge List <DelaunayTriangleEdge> intersectedTriangleEdges = new List <DelaunayTriangleEdge>(); m_triangleSet.GetIntersectingEdges(edgeEndpointA, edgeEndpointB, triangleContainingA, intersectedTriangleEdges); List <DelaunayTriangleEdge> newEdges = new List <DelaunayTriangleEdge>(); while (intersectedTriangleEdges.Count > 0) { DelaunayTriangleEdge currentIntersectedTriangleEdge = intersectedTriangleEdges[intersectedTriangleEdges.Count - 1]; intersectedTriangleEdges.RemoveAt(intersectedTriangleEdges.Count - 1); // 5.3.3: Form quadrilaterals and swap intersected edges // Deduces the data for both triangles currentIntersectedTriangleEdge = m_triangleSet.FindTriangleThatContainsEdge(currentIntersectedTriangleEdge.EdgeVertexA, currentIntersectedTriangleEdge.EdgeVertexB); DelaunayTriangle intersectedTriangle = m_triangleSet.GetTriangle(currentIntersectedTriangleEdge.TriangleIndex); DelaunayTriangle oppositeTriangle = m_triangleSet.GetTriangle(intersectedTriangle.adjacent[currentIntersectedTriangleEdge.EdgeIndex]); Triangle2D trianglePoints = m_triangleSet.GetTrianglePoints(currentIntersectedTriangleEdge.TriangleIndex); // Gets the opposite vertex of adjacent triangle, knowing the fisrt vertex of the shared edge int oppositeVertex = NOT_FOUND; //List<int> debugP = intersectedTriangle.DebugP; //List<int> debugA = intersectedTriangle.DebugAdjacent; //List<int> debugP2 = oppositeTriangle.DebugP; //List<int> debugA2 = oppositeTriangle.DebugAdjacent; int oppositeSharedEdgeVertex = NOT_FOUND; // The first vertex in the shared edge of the opposite triangle for (int j = 0; j < 3; ++j) { if (oppositeTriangle.p[j] == intersectedTriangle.p[(currentIntersectedTriangleEdge.EdgeIndex + 1) % 3]) // Comparing with the endpoint B of the edge, since the edge AB is BA in the adjacent triangle { oppositeVertex = oppositeTriangle.p[(j + 2) % 3]; oppositeSharedEdgeVertex = j; break; } } Vector2 oppositePoint = m_triangleSet.GetPointByIndex(oppositeVertex); if (MathUtils.IsQuadrilateralConvex(trianglePoints.p0, trianglePoints.p1, trianglePoints.p2, oppositePoint)) { // Swap int notInEdgeTriangleVertex = (currentIntersectedTriangleEdge.EdgeIndex + 2) % 3; SwapEdges(currentIntersectedTriangleEdge.TriangleIndex, intersectedTriangle, notInEdgeTriangleVertex, oppositeTriangle, oppositeSharedEdgeVertex); // Refreshes triangle data after swapping intersectedTriangle = m_triangleSet.GetTriangle(currentIntersectedTriangleEdge.TriangleIndex); //oppositeTriangle = m_triangles.GetTriangle(intersectedTriangle.adjacent[(currentIntersectedTriangleEdge.EdgeIndex + 2) % 3]); //debugP = intersectedTriangle.DebugP; //debugA = intersectedTriangle.DebugAdjacent; //debugP2 = oppositeTriangle.DebugP; //debugA2 = oppositeTriangle.DebugAdjacent; // Check new diagonal against the intersecting edge Vector2 intersectionPoint; int newTriangleSharedEdgeVertex = (currentIntersectedTriangleEdge.EdgeIndex + 2) % 3; // Read SwapEdges method to understand the +2 Vector2 newTriangleSharedEdgePointA = m_triangleSet.GetPointByIndex(intersectedTriangle.p[newTriangleSharedEdgeVertex]); Vector2 newTriangleSharedEdgePointB = m_triangleSet.GetPointByIndex(intersectedTriangle.p[(newTriangleSharedEdgeVertex + 1) % 3]); DelaunayTriangleEdge newEdge = new DelaunayTriangleEdge(NOT_FOUND, NOT_FOUND, intersectedTriangle.p[newTriangleSharedEdgeVertex], intersectedTriangle.p[(newTriangleSharedEdgeVertex + 1) % 3]); if (newTriangleSharedEdgePointA != edgeEndpointB && newTriangleSharedEdgePointB != edgeEndpointB && // Watch out! It thinks the line intersects with the edge when an endpoint coincides with a triangle vertex, this problem is avoided thanks to this conditions newTriangleSharedEdgePointA != edgeEndpointA && newTriangleSharedEdgePointB != edgeEndpointA && MathUtils.IntersectionBetweenLines(edgeEndpointA, edgeEndpointB, newTriangleSharedEdgePointA, newTriangleSharedEdgePointB, out intersectionPoint)) { // New triangles edge still intersects with the constrained edge, so it is returned to the list intersectedTriangleEdges.Insert(0, newEdge); } else { newEdges.Add(newEdge); } } else { // Back to the list intersectedTriangleEdges.Insert(0, currentIntersectedTriangleEdge); } } // 5.3.4. Check Delaunay constraint and swap edges for (int i = 0; i < newEdges.Count; ++i) { // Checks if the constrained edge coincides with the new edge Vector2 triangleEdgePointA = m_triangleSet.GetPointByIndex(newEdges[i].EdgeVertexA); Vector2 triangleEdgePointB = m_triangleSet.GetPointByIndex(newEdges[i].EdgeVertexB); if ((triangleEdgePointA == edgeEndpointA && triangleEdgePointB == edgeEndpointB) || (triangleEdgePointB == edgeEndpointA && triangleEdgePointA == edgeEndpointB)) { continue; } // Deduces the data for both triangles DelaunayTriangleEdge currentEdge = m_triangleSet.FindTriangleThatContainsEdge(newEdges[i].EdgeVertexA, newEdges[i].EdgeVertexB); DelaunayTriangle currentEdgeTriangle = m_triangleSet.GetTriangle(currentEdge.TriangleIndex); int triangleVertexNotShared = (currentEdge.EdgeIndex + 2) % 3; Vector2 trianglePointNotShared = m_triangleSet.GetPointByIndex(currentEdgeTriangle.p[triangleVertexNotShared]); DelaunayTriangle oppositeTriangle = m_triangleSet.GetTriangle(currentEdgeTriangle.adjacent[currentEdge.EdgeIndex]); Triangle2D oppositeTrianglePoints = m_triangleSet.GetTrianglePoints(currentEdgeTriangle.adjacent[currentEdge.EdgeIndex]); //List<int> debugP = currentEdgeTriangle.DebugP; //List<int> debugA = currentEdgeTriangle.DebugAdjacent; //List<int> debugP2 = oppositeTriangle.DebugP; //List<int> debugA2 = oppositeTriangle.DebugAdjacent; if (MathUtils.IsPointInsideCircumcircle(oppositeTrianglePoints.p0, oppositeTrianglePoints.p1, oppositeTrianglePoints.p2, trianglePointNotShared)) { // Finds the edge of the opposite triangle that is shared with the other triangle, this edge will be swapped int sharedEdgeVertexLocalIndex = 0; for (; sharedEdgeVertexLocalIndex < 3; ++sharedEdgeVertexLocalIndex) { if (oppositeTriangle.adjacent[sharedEdgeVertexLocalIndex] == currentEdge.TriangleIndex) { break; } } // Swap SwapEdges(currentEdge.TriangleIndex, currentEdgeTriangle, triangleVertexNotShared, oppositeTriangle, sharedEdgeVertexLocalIndex); } } //Debug.DrawLine(edgeEndpointA, edgeEndpointB, Color.magenta, 10.0f); }
/// <summary> /// Given the outline of a closed polygon, expressed as a list of vertices, it finds all the triangles that lay inside of the figure. /// </summary> /// <param name="polygonOutline">The outline, a list of vertex indices sorted counter-clockwise.</param> /// <param name="outputTrianglesInPolygon">The list where the triangles found inside the polygon will be added. No elements are removed from this list.</param> public void GetTrianglesInPolygon(List <int> polygonOutline, List <int> outputTrianglesInPolygon) { // This method assumes that the edges of the triangles to find were created using the same vertex order // It also assumes all triangles are inside a supertriangle, so no adjacent triangles are -1 Stack <int> adjacentTriangles = new Stack <int>(); // First it gets all the triangles of the outline for (int i = 0; i < polygonOutline.Count; ++i) { // For every edge, it gets the inner triangle that contains such edge DelaunayTriangleEdge triangleEdge = FindTriangleThatContainsEdge(polygonOutline[i], polygonOutline[(i + 1) % polygonOutline.Count]); // A triangle may form a corner, with 2 consecutive outline edges. This avoids adding it twice if (outputTrianglesInPolygon.Count > 0 && (outputTrianglesInPolygon[outputTrianglesInPolygon.Count - 1] == triangleEdge.TriangleIndex || // Is the last added triangle the same as current? outputTrianglesInPolygon[0] == triangleEdge.TriangleIndex)) // Is the first added triangle the same as the current, which is the last to be added (closes the polygon)? { continue; } outputTrianglesInPolygon.Add(triangleEdge.TriangleIndex); int previousOutlineEdgeVertexA = polygonOutline[(i + polygonOutline.Count - 1) % polygonOutline.Count]; int previousOutlineEdgeVertexB = polygonOutline[i]; int nextOutlineEdgeVertexA = polygonOutline[(i + 1) % polygonOutline.Count]; int nextOutlineEdgeVertexB = polygonOutline[(i + 2) % polygonOutline.Count]; for (int j = 1; j < 3; ++j) // For the 2 adjacent triangles of the other 2 edges { int adjacentTriangle = m_adjacentTriangles[triangleEdge.TriangleIndex * 3 + (triangleEdge.EdgeIndex + j) % 3]; bool isAdjacentTriangleInOutline = false; // Compares the contiguous edges of the outline, to the right and to the left of the current one, flipped and not flipped, with the adjacent triangle's edges for (int k = 0; k < 3; ++k) { int currentTriangleEdgeVertexA = m_triangleVertices[adjacentTriangle * 3 + k]; int currentTriangleEdgeVertexB = m_triangleVertices[adjacentTriangle * 3 + (k + 1) % 3]; if ((currentTriangleEdgeVertexA == previousOutlineEdgeVertexA && currentTriangleEdgeVertexB == previousOutlineEdgeVertexB) || (currentTriangleEdgeVertexA == previousOutlineEdgeVertexB && currentTriangleEdgeVertexB == previousOutlineEdgeVertexA) || (currentTriangleEdgeVertexA == nextOutlineEdgeVertexA && currentTriangleEdgeVertexB == nextOutlineEdgeVertexB) || (currentTriangleEdgeVertexA == nextOutlineEdgeVertexB && currentTriangleEdgeVertexB == nextOutlineEdgeVertexA)) { isAdjacentTriangleInOutline = true; } } if (!isAdjacentTriangleInOutline && !outputTrianglesInPolygon.Contains(adjacentTriangle)) { adjacentTriangles.Push(adjacentTriangle); } } } // Then it propagates by adjacency, stopping when an adjacent triangle has already been included in the list // Since all the outline triangles have been added previously, it will not propagate outside of the polygon while (adjacentTriangles.Count > 0) { int currentTriangle = adjacentTriangles.Pop(); // The triangle may have been added already in a previous iteration if (outputTrianglesInPolygon.Contains(currentTriangle)) { continue; } for (int i = 0; i < 3; ++i) { int adjacentTriangle = m_adjacentTriangles[currentTriangle * 3 + i]; if (adjacentTriangle != NO_ADJACENT_TRIANGLE && !outputTrianglesInPolygon.Contains(adjacentTriangle)) { adjacentTriangles.Push(adjacentTriangle); } } outputTrianglesInPolygon.Add(currentTriangle); } }