Example #1
0
            // 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);
            }
Example #2
0
 // Test if the shapes overlap partially (test will fail if one shape entirely contains other shape, i.e. one is parent of the other).
 public bool OverlapsPartially(CompositeShapeData otherShape)
 {
     // Check for intersections between line segs of this shape and otherShape (any intersection will validate the overlap test)
     for (int i = 0; i < points.Length; i++)
     {
         LineSegment segA = new LineSegment(points[i], points[(i + 1) % points.Length]);
         for (int j = 0; j < otherShape.points.Length; j++)
         {
             LineSegment segB = new LineSegment(otherShape.points[j], otherShape.points[(j + 1) % otherShape.points.Length]);
             if (Maths2D.LineSegmentsIntersect(segA.a, segA.b, segB.a, segB.b))
             {
                 return(true);
             }
         }
     }
     return(false);
 }
Example #3
0
 // Checks if any of the line segments making up this shape intersect
 public bool IntersectsWithSelf()
 {
     for (int i = 0; i < points.Length; i++)
     {
         LineSegment segA = new LineSegment(points[i], points[(i + 1) % points.Length]);
         for (int j = i + 2; j < points.Length; j++)
         {
             if ((j + 1) % points.Length == i)
             {
                 continue;
             }
             LineSegment segB = new LineSegment(points[j], points[(j + 1) % points.Length]);
             if (Maths2D.LineSegmentsIntersect(segA.a, segA.b, segB.a, segB.b))
             {
                 return(true);
             }
         }
     }
     return(false);
 }
        // 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);
        }
 // v1 is considered a convex vertex if v0-v1-v2 are wound in a counter-clockwise order.
 bool IsConvex(Vector2 v0, Vector2 v1, Vector2 v2)
 {
     return(Maths2D.SideOfLine(v0, v2, v1) == -1);
 }