bool PointInTriangles(Vector2[] p, int[] tris, Vector2 v) { for (int t = 0; t < tris.Length; t += 3) { if (Maths2D.PointInTriangle(p[tris[t]], p[tris[t + 1]], p[tris[t + 2]], v)) { return(true); } } return(false); }
// A parent is a shape which fully contains another shape public bool IsParentOf(CompositeShapeData otherShape) { if (otherShape.parents.Contains(this)) { return(true); } if (parents.Contains(otherShape)) { return(false); } // check if first point in otherShape is inside this shape. If not, parent test fails. // if yes, then continue to line seg intersection test between the two shapes // (this point test is important because without it, if all line seg intersection tests fail, // we wouldn't know if otherShape is entirely inside or entirely outside of this shape) bool pointInsideShape = false; for (int i = 0; i < triangles.Length; i += 3) { if (Maths2D.PointInTriangle(polygon.points[triangles[i]], polygon.points[triangles[i + 1]], polygon.points[triangles[i + 2]], otherShape.points[0])) { pointInsideShape = true; break; } } if (!pointInsideShape) { return(false); } // Check for intersections between line segs of this shape and otherShape (any intersections will fail the parent test) for (int i = 0; i < points.Length; i++) { LineSegment parentSeg = new LineSegment(points[i], points[(i + 1) % points.Length]); for (int j = 0; j < otherShape.points.Length; j++) { LineSegment childSeg = new LineSegment(otherShape.points[j], otherShape.points[(j + 1) % otherShape.points.Length]); if (Maths2D.LineSegmentsIntersect(parentSeg.a, parentSeg.b, childSeg.a, childSeg.b)) { return(false); } } } return(true); }
// check if triangle contains any verts (note, only necessary to check reflex verts). bool TriangleContainsVertex(Vertex v0, Vertex v1, Vertex v2) { LinkedListNode <Vertex> vertexNode = vertsInClippedPolygon.First; for (int i = 0; i < vertsInClippedPolygon.Count; i++) { if (!vertexNode.Value.isConvex) // convex verts will never be inside triangle { Vertex vertexToCheck = vertexNode.Value; if (vertexToCheck.index != v0.index && vertexToCheck.index != v1.index && vertexToCheck.index != v2.index) // dont check verts that make up triangle { if (Maths2D.PointInTriangle(v0.position, v1.position, v2.position, vertexToCheck.position)) { return(true); } } } vertexNode = vertexNode.Next; } return(false); }
// 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); }