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);
        }
Exemplo n.º 3
0
    // 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);
    }
Exemplo n.º 4
0
    // 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);
    }