/// <summary>
        /// Extracts all minimal cycles (neither cycle contains another nor intersects another).
        /// </summary>
        /// <param name="cycles">The cycles to extract from.</param>
        /// <returns>All extracted minimal cycles in the given enumerable.</returns>
        private IEnumerable <IReadOnlyCollection <Vector3> > ExtractMinimalCycles(
            IEnumerable <IReadOnlyCollection <Vector3> > cycles)
        {
            var minimalCycles = new LinkedList <IReadOnlyCollection <Vector3> >();

            // Filter away any cycles that overlap minimal ones; they're not minimal
            foreach (var cycle in cycles)
            {
                // Cancel if requested
                if (CancelToken.IsCancellationRequested)
                {
                    return(null);
                }

                // Find out if the cycle is overlapping any minimal one
                var cycleXz       = cycle.Select(Vec3ToVec2);
                var isOverlapping = minimalCycles.Any(minimalCycle =>
                                                      Maths2D.AnyPolygonCenterOverlaps(cycleXz, minimalCycle.Select(Vec3ToVec2)));

                // If this cycle doesn't overlap any minimal cycle, it is minimal as well
                if (!isOverlapping && !minimalCycles.Any(cycle.ContainsAll))
                {
                    minimalCycles.AddLast(cycle);
                }
            }

            return(minimalCycles);
        }
 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);
        }
 // 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);
 }
 // 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);
 }
Beispiel #6
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);
    }
        public override IEnumerable <Plot> Generate()
        {
            var plots       = new HashSet <Plot>();
            var roadNetwork = Injector.Get();
            var rand        = new System.Random();

            var p1 = new Plot(RandomRotatedRect(rand, new Vector3(0f, 0f, 0f), new Vector3(5f, 0f, 5f), 5f, 5f));

            plots.Add(p1);

            bool roadCollision = false;

            foreach (var(start, end) in roadNetwork.GetRoadParts())
            {
                if (Maths2D.LinePolyCollision(start, end, p1.Vertices))
                {
                    roadCollision = true;
                }
            }
            Debug.Log("Plots are colliding with road (t/f): " + roadCollision);

            return(plots);
        }
        /// <summary>
        /// Gets all minimal cycles in the XZ-plane where the road network's XZ-projection intersections are found.
        /// </summary>
        /// <returns>All minimal cycles in the XZ-plane</returns>
        private IEnumerable <IReadOnlyCollection <Vector3> > GetMinimalCyclesInXZ()
        {
            // XZ-projection of the undirected road network
            var roadNetwork = Injector.Get().GetXZProjection().GetAsUndirected();

            // If there aren't at least three vertices in the road network, there can't possibly be a cycle in it
            if (roadNetwork.VertexCount < 3)
            {
                return(new List <IReadOnlyCollection <Vector3> >());
            }

            // Get all cycles in the road network and sort them from the smallest area
            // to the largest in order to later only save the minimal cycles
            var cycles = GetAllCycles(roadNetwork).ToList();

            cycles.Sort((cycle1, cycle2) =>
            {
                var area1 = Maths2D.CalculatePolygonArea(cycle1.Select(Vec3ToVec2));
                var area2 = Maths2D.CalculatePolygonArea(cycle2.Select(Vec3ToVec2));
                return(area1 <area2 ? -1 : area1> area2 ? 1 : 0);
            });

            return(ExtractMinimalCycles(cycles));
        }
    void SetInnerRing(Vector3[] verts, List <Vector3> validQuads, List <Vector3> validTris)
    {
        bool isClosed = verts[0] == verts[verts.Length - 1];

        Vector2[] points = new List <Vector2>(verts.Select(v => new Vector2(v.x, v.z))).GetRange(0, verts.Count() - (isClosed ? 1 : 0)).ToArray();

        debugList = new List <Vector3>();
        List <Vector3> pathVerts = new List <Vector3>();

        if (points.Length >= 3)
        {
            Polygon polygon   = new Polygon(points);
            int[]   triangles = new Triangulator(polygon).Triangulate();

            Vector3 min = GetScaledMin();
            for (float i = min.x; i < bounds.max.x; i += gridScale)
            {
                for (float j = min.z; j < bounds.max.z; j += gridScale)
                {
                    bool intersects1  = false;
                    bool intersects2  = false;
                    bool intersects34 = false;
                    for (int k = 0; k < outerRing.Count - 1; k++)
                    {
                        if (Maths2D.LineSegmentsIntersect(new Vector2(i, j), new Vector2(i + gridScale, j), new Vector2(outerRing[k].x, outerRing[k].z), new Vector2(outerRing[k + 1].x, outerRing[k + 1].z)))
                        {
                            intersects1 = true;
                            break;
                        }
                        if (Maths2D.LineSegmentsIntersect(new Vector2(i, j), new Vector2(i, j + gridScale), new Vector2(outerRing[k].x, outerRing[k].z), new Vector2(outerRing[k + 1].x, outerRing[k + 1].z)))
                        {
                            intersects2 = true;
                            break;
                        }
                        if (Maths2D.LineSegmentsIntersect(new Vector2(i + gridScale, j), new Vector2(i + gridScale, j + gridScale), new Vector2(outerRing[k].x, outerRing[k].z), new Vector2(outerRing[k + 1].x, outerRing[k + 1].z)) ||
                            Maths2D.LineSegmentsIntersect(new Vector2(i, j + gridScale), new Vector2(i + gridScale, j + gridScale), new Vector2(outerRing[k].x, outerRing[k].z), new Vector2(outerRing[k + 1].x, outerRing[k + 1].z)))
                        {
                            intersects34 = true;
                        }
                    }
                    if (intersects1)
                    {
                        if (PointInTriangles(polygon.points, triangles, new Vector2(i, j)))
                        {
                            pathVerts.Add(new Vector3(i, pathHeight, j));
                        }
                        if (PointInTriangles(polygon.points, triangles, new Vector2(i + gridScale, j)))
                        {
                            pathVerts.Add(new Vector3(i + gridScale, pathHeight, j));
                        }
                    }
                    else if (intersects2)
                    {
                        if (PointInTriangles(polygon.points, triangles, new Vector2(i, j)))
                        {
                            pathVerts.Add(new Vector3(i, pathHeight, j));
                        }
                        if (PointInTriangles(polygon.points, triangles, new Vector2(i, j + gridScale)))
                        {
                            pathVerts.Add(new Vector3(i, pathHeight, j + gridScale));
                        }
                    }
                    else if (!intersects34 && PointInTriangles(polygon.points, triangles, new Vector2(i, j)))
                    {
                        validQuads.Add(new Vector3(i, pathHeight, j));
                    }
                    if (intersects1 || intersects2 || intersects34)
                    {
                        List <Vector3> possibleTris = new List <Vector3>();

                        if (PointInTriangles(polygon.points, triangles, new Vector2(i, j)))
                        {
                            possibleTris.Add(new Vector3(i, pathHeight, j));
                        }
                        if (PointInTriangles(polygon.points, triangles, new Vector2(i + gridScale, j)))
                        {
                            possibleTris.Add(new Vector3(i + gridScale, pathHeight, j));
                        }
                        if (PointInTriangles(polygon.points, triangles, new Vector2(i, j + gridScale)))
                        {
                            possibleTris.Add(new Vector3(i, pathHeight, j + gridScale));
                        }
                        if (PointInTriangles(polygon.points, triangles, new Vector2(i + gridScale, j + gridScale)))
                        {
                            possibleTris.Add(new Vector3(i + gridScale, pathHeight, j + gridScale));
                        }

                        if (possibleTris.Count == 3)
                        {
                            validTris.AddRange(possibleTris);
                        }
                        if (possibleTris.Count == 2)
                        {
                            if (!possibleTris.Contains(new Vector3(i, pathHeight, j)) && !possibleTris.Contains(new Vector3(i + gridScale, pathHeight, j)) && PointInTriangles(polygon.points, triangles, new Vector2(i + gridScale + gridScale, j)))
                            {
                                pathVerts.Add(possibleTris[1]);
                                //validTris.AddRange(new Vector3[] { possibleTris[0], possibleTris[1], new Vector3(i + gridScale + gridScale, 0, j) });
                            }

                            /*
                             * if (!possibleTris.Contains(new Vector3(i + gridScale, 0, j)) && !possibleTris.Contains(new Vector3(i + gridScale, 0, j + gridScale)) &&
                             *  PointInTriangles(polygon.points, triangles, new Vector2(i + gridScale, j - gridScale)) && !PointInTriangles(polygon.points, triangles, new Vector2(i, j + gridScale + gridScale))) {
                             *  //validTris.AddRange(new Vector3[] { possibleTris[0], possibleTris[1], new Vector3(i + gridScale, 0, j - gridScale) });
                             * }*/
                        }
                    }
                }
            }

            pathVerts = Utility.TrimDuplicates(pathVerts);

            innerRing = new List <Vector3> {
                pathVerts[0]
            };

            Vector3 previous     = pathVerts[0];
            int     reverseCount = pathVerts.Count;
            //if (debug) debugList.Add(pathVerts[0]);
            pathVerts.RemoveAt(0);

            while (pathVerts.Count > 0 && reverseCount > 0)
            {
                Vector3 closest = previous;
                foreach (Vector3 vert in pathVerts)
                {
                    if (Vector3.Distance(previous, vert) < gridScale * 1.1f)
                    {
                        closest = vert;
                        break;
                    }
                }
                if (closest == previous)
                {
                    foreach (Vector3 vert in pathVerts)
                    {
                        if (Vector3.Distance(previous, vert) < gridScale * 1.5f)
                        {
                            closest = vert;
                            break;
                        }
                    }
                }
                if (closest != previous)
                {
                    innerRing.Add(closest);
                    pathVerts.Remove(closest);
                    if (debug)
                    {
                        debugList.Add(closest);
                    }
                    previous = closest;
                }
                else
                {
                    --reverseCount;
                    innerRing.Reverse();
                    previous = innerRing[innerRing.Count - 1];
                }
            }

            if (pathVerts.Count > 0)
            {
                for (int i = 0; i < pathVerts.Count; i++)
                {
                    debugSegList.Add(new LineSegment(pathVerts[i], pathVerts[i] + Vector3.up * 10));
                }
                innerRing.AddRange(pathVerts);
                print("Verts Left: " + pathVerts.Count);
            }
            if (Vector3.Distance(innerRing[0], Utility.NoY(outerRing[0])) > gridScale * 1.5f && !isClosed)
            {
                innerRing.Reverse();
            }
        }
    }
Beispiel #10
0
        internal Lot(IList <Vector3> vertices)
        {
            Vertices = vertices;

            this.area = Maths2D.CalculatePolygonArea((IEnumerable <Vector2>)ToXZ(vertices));
        }
Beispiel #11
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);
    }
Beispiel #12
0
 // 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);
 }