/// <summary> /// Case 2: edge origin lies outside the domain. /// </summary> private void HandleCase2(HalfEdge edge, TVertex v1, TVertex v2) { // The vertices of the infinite edge. var p1 = (Point)edge.origin; var p2 = (Point)edge.twin.origin; // The two edges leaving p1, pointing into the mesh. var e1 = edge.twin.next; var e2 = e1.twin.next; // Find the two intersections with boundary edge. IntersectionHelper.IntersectSegments(v1, v2, e1.origin, e1.twin.origin, ref p2); IntersectionHelper.IntersectSegments(v1, v2, e2.origin, e2.twin.origin, ref p1); // The infinite edge will now lie on the boundary. Update pointers: e1.twin.next = edge.twin; edge.twin.next = e2; edge.twin.face = e2.face; e1.origin = edge.twin.origin; edge.twin.twin = null; edge.twin = null; // Close the cell. var gen = factory.CreateVertex(v1.x, v1.y); var he = factory.CreateHalfEdge(gen, edge.face); edge.next = he; he.next = edge.face.edge; // Let the face edge point to the edge leaving at generator. edge.face.edge = he; edges.Add(he); he.id = edges.Count; gen.id = offset++; vertices.Add(gen); }
/// <summary> /// Case 1: edge origin lies inside the domain. /// </summary> private void HandleCase1(HalfEdge edge, TVertex v1, TVertex v2) { //int mark = GetBoundaryMark(v1); // The infinite vertex. var v = (Point)edge.twin.origin; // The half-edge is the bisector of v1 and v2, so the projection onto the // boundary segment is actually its midpoint. v.x = (v1.x + v2.x) / 2.0f; v.y = (v1.y + v2.y) / 2.0f; // Close the cell connected to edge. var gen = factory.CreateVertex(v1.x, v1.y); var h1 = factory.CreateHalfEdge(edge.twin.origin, edge.face); var h2 = factory.CreateHalfEdge(gen, edge.face); edge.next = h1; h1.next = h2; h2.next = edge.face.edge; gen.leaving = h2; // Let the face edge point to the edge leaving at generator. edge.face.edge = h2; edges.Add(h1); edges.Add(h2); int count = edges.Count; h1.id = count; h2.id = count + 1; gen.id = offset++; vertices.Add(gen); }
/**Sets the vertex position and UV as the interpolation of two other vertices **/ public void Lerp(Vertex a, Vertex b, float t) { position = Vector3.Lerp(a.position, b.position, t); UV = Vector2.Lerp(a.UV, b.UV, t); }
// Creates a linked list of all vertices in the polygon, with the hole vertices joined to the hull at optimal points. LinkedList <Vertex> GenerateVertexList(Polygon polygon) { LinkedList <Vertex> vertexList = new LinkedList <Vertex>(); LinkedListNode <Vertex> currentNode = null; // Add all hull points to the linked list for (int i = 0; i < polygon.numHullPoints; i++) { int prevPointIndex = (i - 1 + polygon.numHullPoints) % polygon.numHullPoints; int nextPointIndex = (i + 1) % polygon.numHullPoints; bool vertexIsConvex = IsConvex(polygon.points[prevPointIndex], polygon.points[i], polygon.points[nextPointIndex]); Vertex currentHullVertex = new Vertex(polygon.points[i], i, vertexIsConvex); if (currentNode == null) { currentNode = vertexList.AddFirst(currentHullVertex); } else { currentNode = vertexList.AddAfter(currentNode, currentHullVertex); } } // Process holes: List <HoleData> sortedHoleData = new List <HoleData>(); for (int holeIndex = 0; holeIndex < polygon.numHoles; holeIndex++) { // Find index of rightmost point in hole. This 'bridge' point is where the hole will be connected to the hull. Vector2 holeBridgePoint = new Vector2(float.MinValue, 0); int holeBridgeIndex = 0; for (int i = 0; i < polygon.numPointsPerHole[holeIndex]; i++) { if (polygon.GetHolePoint(i, holeIndex).x > holeBridgePoint.x) { holeBridgePoint = polygon.GetHolePoint(i, holeIndex); holeBridgeIndex = i; } } sortedHoleData.Add(new HoleData(holeIndex, holeBridgeIndex, holeBridgePoint)); } // Sort hole data so that holes furthest to the right are first sortedHoleData.Sort((x, y) => (x.bridgePoint.x > y.bridgePoint.x) ? -1 : 1); foreach (HoleData holeData in sortedHoleData) { // Find first edge which intersects with rightwards ray originating at the hole bridge point. Vector2 rayIntersectPoint = new Vector2(float.MaxValue, holeData.bridgePoint.y); List <LinkedListNode <Vertex> > hullNodesPotentiallyInBridgeTriangle = new List <LinkedListNode <Vertex> >(); LinkedListNode <Vertex> initialBridgeNodeOnHull = null; currentNode = vertexList.First; while (currentNode != null) { LinkedListNode <Vertex> nextNode = (currentNode.Next == null) ? vertexList.First : currentNode.Next; Vector2 p0 = currentNode.Value.position; Vector2 p1 = nextNode.Value.position; // at least one point must be to right of holeData.bridgePoint for intersection with ray to be possible if (p0.x > holeData.bridgePoint.x || p1.x > holeData.bridgePoint.x) { // one point is above, one point is below if (p0.y > holeData.bridgePoint.y != p1.y > holeData.bridgePoint.y) { float rayIntersectX = p1.x; // only true if line p0,p1 is vertical if (!Mathf.Approximately(p0.x, p1.x)) { float intersectY = holeData.bridgePoint.y; float gradient = (p0.y - p1.y) / (p0.x - p1.x); float c = p1.y - gradient * p1.x; rayIntersectX = (intersectY - c) / gradient; } // intersection must be to right of bridge point if (rayIntersectX > holeData.bridgePoint.x) { LinkedListNode <Vertex> potentialNewBridgeNode = (p0.x > p1.x) ? currentNode : nextNode; // if two intersections occur at same x position this means is duplicate edge // duplicate edges occur where a hole has been joined to the outer polygon bool isDuplicateEdge = Mathf.Approximately(rayIntersectX, rayIntersectPoint.x); // connect to duplicate edge (the one that leads away from the other, already connected hole, and back to the original hull) if the // current hole's bridge point is higher up than the bridge point of the other hole (so that the new bridge connection doesn't intersect). bool connectToThisDuplicateEdge = holeData.bridgePoint.y > potentialNewBridgeNode.Previous.Value.position.y; if (!isDuplicateEdge || connectToThisDuplicateEdge) { // if this is the closest ray intersection thus far, set bridge hull node to point in line having greater x pos (since def to right of hole). if (rayIntersectX < rayIntersectPoint.x || isDuplicateEdge) { rayIntersectPoint.x = rayIntersectX; initialBridgeNodeOnHull = potentialNewBridgeNode; } } } } } // Determine if current node might lie inside the triangle formed by holeBridgePoint, rayIntersection, and bridgeNodeOnHull // We only need consider those which are reflex, since only these will be candidates for visibility from holeBridgePoint. // A list of these nodes is kept so that in next step it is not necessary to iterate over all nodes again. if (currentNode != initialBridgeNodeOnHull) { if (!currentNode.Value.isConvex && p0.x > holeData.bridgePoint.x) { hullNodesPotentiallyInBridgeTriangle.Add(currentNode); } } currentNode = currentNode.Next; } // Check triangle formed by hullBridgePoint, rayIntersection, and bridgeNodeOnHull. // If this triangle contains any points, those points compete to become new bridgeNodeOnHull LinkedListNode <Vertex> validBridgeNodeOnHull = initialBridgeNodeOnHull; foreach (LinkedListNode <Vertex> nodePotentiallyInTriangle in hullNodesPotentiallyInBridgeTriangle) { if (nodePotentiallyInTriangle.Value.index == initialBridgeNodeOnHull.Value.index) { continue; } // if there is a point inside triangle, this invalidates the current bridge node on hull. if (Maths2D.PointInTriangle(holeData.bridgePoint, rayIntersectPoint, initialBridgeNodeOnHull.Value.position, nodePotentiallyInTriangle.Value.position)) { // Duplicate points occur at hole and hull bridge points. bool isDuplicatePoint = validBridgeNodeOnHull.Value.position == nodePotentiallyInTriangle.Value.position; // if multiple nodes inside triangle, we want to choose the one with smallest angle from holeBridgeNode. // if is a duplicate point, then use the one occurring later in the list float currentDstFromHoleBridgeY = Mathf.Abs(holeData.bridgePoint.y - validBridgeNodeOnHull.Value.position.y); float pointInTriDstFromHoleBridgeY = Mathf.Abs(holeData.bridgePoint.y - nodePotentiallyInTriangle.Value.position.y); if (pointInTriDstFromHoleBridgeY < currentDstFromHoleBridgeY || isDuplicatePoint) { validBridgeNodeOnHull = nodePotentiallyInTriangle; } } } // Insert hole points (starting at holeBridgeNode) into vertex list at validBridgeNodeOnHull currentNode = validBridgeNodeOnHull; for (int i = holeData.bridgeIndex; i <= polygon.numPointsPerHole[holeData.holeIndex] + holeData.bridgeIndex; i++) { int previousIndex = currentNode.Value.index; int currentIndex = polygon.IndexOfPointInHole(i % polygon.numPointsPerHole[holeData.holeIndex], holeData.holeIndex); int nextIndex = polygon.IndexOfPointInHole((i + 1) % polygon.numPointsPerHole[holeData.holeIndex], holeData.holeIndex); if (i == polygon.numPointsPerHole[holeData.holeIndex] + holeData.bridgeIndex) // have come back to starting point { nextIndex = validBridgeNodeOnHull.Value.index; // next point is back to the point on the hull } bool vertexIsConvex = IsConvex(polygon.points[previousIndex], polygon.points[currentIndex], polygon.points[nextIndex]); Vertex holeVertex = new Vertex(polygon.points[currentIndex], currentIndex, vertexIsConvex); currentNode = vertexList.AddAfter(currentNode, holeVertex); } // Add duplicate hull bridge vert now that we've come all the way around. Also set its concavity Vector2 nextVertexPos = (currentNode.Next == null) ? vertexList.First.Value.position : currentNode.Next.Value.position; bool isConvex = IsConvex(holeData.bridgePoint, validBridgeNodeOnHull.Value.position, nextVertexPos); Vertex repeatStartHullVert = new Vertex(validBridgeNodeOnHull.Value.position, validBridgeNodeOnHull.Value.index, isConvex); vertexList.AddAfter(currentNode, repeatStartHullVert); //Set concavity of initial hull bridge vert, since it may have changed now that it leads to hole vert LinkedListNode <Vertex> nodeBeforeStartBridgeNodeOnHull = (validBridgeNodeOnHull.Previous == null) ? vertexList.Last : validBridgeNodeOnHull.Previous; LinkedListNode <Vertex> nodeAfterStartBridgeNodeOnHull = (validBridgeNodeOnHull.Next == null) ? vertexList.First : validBridgeNodeOnHull.Next; validBridgeNodeOnHull.Value.isConvex = IsConvex(nodeBeforeStartBridgeNodeOnHull.Value.position, validBridgeNodeOnHull.Value.position, nodeAfterStartBridgeNodeOnHull.Value.position); } return(vertexList); }