Пример #1
0
    public static Curve[] splitByMargin(Curve curve, float indicatorMargin_0Bound, float indicatorMargin_1Bound)
    {
        Curve[] rtn = new Curve[3];

        if (indicatorMargin_0Bound > 0)
        {
            Curve margin0Curve = curve.cut(0f, indicatorMargin_0Bound / curve.length);
            margin0Curve.z_start  = curve.At(0f).y;
            margin0Curve.z_offset = 0f;
            rtn[0] = margin0Curve;
        }

        Curve middleCurve = curve.cut(indicatorMargin_0Bound / curve.length, 1f - indicatorMargin_1Bound / curve.length);

        middleCurve.z_start  = curve.At(0f).y;
        middleCurve.z_offset = curve.At(1f).y - curve.At(0f).y;
        rtn[1] = middleCurve;

        if (indicatorMargin_1Bound > 0)
        {
            Curve margin1Curve = curve.cut(1f - indicatorMargin_1Bound / curve.length, 1f);
            margin1Curve.z_start  = curve.At(1f).y;
            margin1Curve.z_offset = 0f;
            rtn[2] = margin1Curve;
        }
        return(rtn);
    }
Пример #2
0
    private static List <Vector3> filter(List <Vector2> points, Curve c1, Curve c2)
    {
        var valids =
            from point in points
            where c2.contains_2d(point) && c1.contains(c2.At((float)c2.paramOf(point)))
            select new Vector3(point.x, c2.At((float)c2.paramOf(point)).y, point.y);

        return(valids.ToList());
    }
Пример #3
0
    Mesh CreateMesh(Curve curve, Material mainMaterial, Vector2 offset1, Vector2 offset2)
    {
        GetComponent <MeshRenderer>().sharedMaterial = mainMaterial;

        int segmentCount = Mathf.CeilToInt(curve.length * curve.maximumCurvature / maxAngleDiff);

        segmentCount = Mathf.Max(segmentCount, 2);

        Vector3[] vertices = new Vector3[segmentCount + 2];
        Vector2[] uvs      = new Vector2[segmentCount + 2];


        for (int i = -1; i <= segmentCount; ++i)
        {
            float curveParam = Mathf.Clamp01(i * 1.0f / (segmentCount - 1));
            vertices[i + 1] = (i == -1 || i % 2 == 1) ? curve.At(curveParam) + curve.RightNormal(curveParam) * offset1.x + Vector3.up * offset1.y :
                              curve.At(curveParam) + curve.RightNormal(curveParam) * offset2.x + Vector3.up * offset2.y;
            uvs[i + 1] = (i == -1 || i % 2 == 1) ? new Vector2(0f, curveParam * curve.length / (offset1.x - offset2.x)) :
                         new Vector2(1f, curveParam * curve.length / (offset1.x - offset2.x));
        }

        int[] triangles = new int[segmentCount * 3];
        for (int i = 0; i != segmentCount; ++i)
        {
            if (i % 2 == 0)
            {
                triangles[3 * i]     = i;
                triangles[3 * i + 1] = i + 1;
                triangles[3 * i + 2] = i + 2;
            }
            else
            {
                triangles[3 * i]     = i;
                triangles[3 * i + 2] = i + 1;
                triangles[3 * i + 1] = i + 2;
            }
        }

        Mesh mesh = new Mesh();

        mesh.vertices  = vertices;
        mesh.triangles = triangles;
        mesh.uv        = uvs;

        return(mesh);
    }
Пример #4
0
 public override Curve concat(Curve b)
 {
     Debug.Assert(b is Line);
     if (Algebra.isclose(at_ending_2d(true), b.at_ending_2d(true)))
     {
         return(Line.TryInit(at_ending_2d(false), b.at_ending_2d(false), at(1f).y, b.At(1f).y - At(1f).y));
     }
     if (Algebra.isclose(at_ending_2d(true), b.at_ending_2d(false)))
     {
         return(Line.TryInit(at_ending_2d(false), b.at_ending_2d(true), At(0f).y, b.At(1f).y - At(0f).y));
     }
     if (Algebra.isclose(at_ending_2d(false), b.at_ending_2d(true)))
     {
         return(Line.TryInit(at_ending_2d(true), b.at_ending_2d(false), At(1f).y, b.At(0f).y - At(1f).y));
     }
     if (Algebra.isclose(at_ending_2d(false), b.at_ending_2d(false)))
     {
         return(Line.TryInit(at_ending_2d(true), b.at_ending_2d(true), At(0f).y, b.At(0f).y - At(0f).y));
     }
     Debug.Assert(false);
     return(null);
 }
Пример #5
0
        public void NewTestScriptSimplePasses()
        {
            Curve c  = Arc.TryInit(roadPoints[0], roadPoints[1], Mathf.PI / 2);
            Curve cr = c.reversed();

            Assert.True(Algebra.isclose(c.At(0f), cr.At(1f)));

            float a1 = c.Angle_2d(1f);
            float a2 = cr.Angle_2d(0f);

            //float angleDiff = (a2 - a1) / Mathf.PI;
            Debug.Log(a1);
            Debug.Log(a2);
            Assert.True(Algebra.isclose(a2, Mathf.PI / 2));
        }
Пример #6
0
 public bool startof(Curve c)
 {
     return((c.At(0f) - position).magnitude < (c.At(1f) - position).magnitude);
 }
Пример #7
0
    /*if flattened, return Point on c1*/
    public static List <Vector3> curveIntersect(Curve c1, Curve c2)
    {
        List <Vector3> specialcase = new List <Vector3>();
        List <Vector3> commoncase  = new List <Vector3>();

        if (c1 is Bezeir)
        {
            if (c2 is Bezeir)
            {
                commoncase = (intersect(c1 as Bezeir, c2 as Bezeir));
            }
            else
            {
                if (c2 is Arc)
                {
                    commoncase = intersect(c1 as Bezeir, c2 as Arc);
                }
                else
                {
                    commoncase = intersect(c1 as Bezeir, c2 as Line);
                }
            }
        }
        else
        {
            if (c1 is Arc)
            {
                if (c2 is Bezeir)
                {
                    commoncase = intersect(c1 as Arc, c2 as Bezeir);
                }
                else
                {
                    if (c2 is Arc)
                    {
                        commoncase = intersect(c1 as Arc, c2 as Arc);
                    }
                    else
                    {
                        commoncase = intersect(c1 as Arc, c2 as Line);
                    }
                }
            }

            else
            {
                if (c2 is Bezeir)
                {
                    commoncase = intersect(c1 as Line, c2 as Bezeir);
                }
                else
                {
                    if (c2 is Arc)
                    {
                        commoncase = intersect(c1 as Line, c2 as Arc);
                    }
                    else
                    {
                        commoncase = intersect(c1 as Line, c2 as Line);
                    }
                }
            }
        }

        if (c1.contains(c2.At(0)))
        {
            specialcase.Add(c2.At(0));
        }
        if (c1.contains(c2.At(1)))
        {
            specialcase.Add(c2.At(1));
        }
        if (c2.contains(c1.At(0)))
        {
            specialcase.Add(c1.At(0));
        }
        if (c2.contains(c1.At(1)))
        {
            specialcase.Add(c1.At(1));
        }

        commoncase.AddRange(specialcase);
        commoncase = commoncase.Distinct(new IntersectPointComparator()).ToList();
        return(commoncase);
    }
Пример #8
0
        /// <summary>
        /// Adds a curve to the DCEL.
        /// </summary>
        /// <param name="curve">The curve to be added</param>
        public void AddCurve(Curve curve, int id1, int id2, int canonicityChange = 1)
        {
            void PairOfEdges(Vertex v1, Vertex v2, out Edge e1, out Edge e2, bool reverse = false)
            {
                var rev = curve.Reverse;

                e1 = new Edge(v1, v2, reverse ? rev : curve);
                e2 = new Edge(v2, v1, reverse ? curve : rev);

                e1.Twin = e2;
                e2.Twin = e1;
            }

            // Check if the vertices were added to the cache first
            bool found1 = FindVertex(id1, out var vertex1);
            bool found2 = FindVertex(id2, out var vertex2);

            // There are four main cases:
            // 1) The vertices are both new: we find which face they pertain and add them to the contour list
            // 2) The vertices are both existing and they connect two different contours of a face: we join those contours
            // 3) The vertices are both existing and they connect a contour of a face to itself: here, we close the face
            // 4) One of the vertices is new: here, there is not a lot of preprocessing to do

            // Cases 1) and 3) need to be treated differently if both vertices happen to be the same vertex
            // 1a) The loop will form another face, which will need to be added to the vertex's contour
            // 3a) If the loop is formed along, the edge link is set differently, and it needs to be accounted for

            // If none are found, add them individually and add a contour to the face
            if (!found1 && !found2)
            {
                var face = GetFaceFromVertex(curve.At(0));

                vertex1 = AddVertex(id1);
                if (id1 != id2) // Deal with the equal vertices case
                {
                    vertex2 = AddVertex(id2);
                }
                else
                {
                    vertex2 = vertex1;
                }

                PairOfEdges(vertex1, vertex2, out var e1, out var e2);

                e1.Canonicity += canonicityChange;

                // If the vertices are different, wire they on a loop
                if (vertex1 != vertex2)
                {
                    e1.Next = e1.Previous = e2;
                    e2.Next = e2.Previous = e1;
                    e1.Face = e2.Face = face;

                    AddEdgePair(vertex1, vertex2, e1, e2);

                    // Add the edge to the contours
                    face.Contours.Add(e1);
                }
                // If they are equal, they need to be wired differently
                else
                {
                    e1.Next = e1.Previous = e1;
                    e2.Next = e2.Previous = e2;

                    AddEdgePair(vertex1, vertex2, e1, e2);

                    // Create a new face
                    var newFace = new Face();

                    // Select the convex edge, and add it to the new face
                    var edge = e1.Winding > 0 ? e1 : e2;
                    newFace.Contours.Add(edge);
                    edge.Face = newFace;

                    // Extract all the old contours that should pertain to the new face
                    var contours = face.Contours.ExtractAll(e => newFace.ContainsVertex(e.Curve.At(0)));

                    // Add them to the new face
                    newFace.Contours.AddRange(contours);
                    foreach (var c in contours)
                    {
                        AssignFace(newFace, c);
                    }

                    // Add the concave edge to the outer face
                    face.Contours.Add(edge.Twin);
                    edge.Twin.Face = face;

                    // Add the new face to the face list
                    faces.Add(newFace);
                }
            }
            // If both of them are found, we create the edge and find out which shapes they are
            else if (found1 && found2)
            {
                PairOfEdges(vertex1, vertex2, out var e1, out var e2);

                // If a matching edge is found, we're done here
                if (vertex1.SearchOutgoingEdges(e1, out var e1lo, out var e1ro))
                {
                    // Just up the canonicity of the edge, so we can track it
                    e1lo.Canonicity += canonicityChange;
                    return;
                }

                e1.Canonicity += canonicityChange;

                // The other matching edge is guaranteed not to be found
                vertex2.SearchOutgoingEdges(e2, out var e2lo, out var e2ro);

                // Check whether the new edge will connect to different contours
                var differentContours = !e1lo.CyclicalSequence.Contains(e2lo, ReferenceEqualityComparer.Default);

                // WARNING: the operation above CANNOT be commuted with this below - leave the variable there
                // There is a special case that needs to be handled for the same vertex
                if (vertex1 == vertex2 && e1lo == e2lo && e1ro == e2ro)
                {
                    // Find the convex edge
                    var edge = e1.Winding > 0 ? e1 : e2;

                    // Create a loop on itself and link the other edge
                    edge.Next          = edge.Previous = edge;
                    e1ro.Twin.Next     = e1lo.Previous = edge.Twin;
                    edge.Twin.Next     = e1lo;
                    edge.Twin.Previous = e1ro.Twin;
                }
                else
                {
                    // Correctly create the edge links
                    e1ro.Twin.Next = e2lo.Previous = e1;
                    e2ro.Twin.Next = e1lo.Previous = e2;
                    e1.Next        = e2lo;
                    e1.Previous    = e1ro.Twin;
                    e2.Next        = e1lo;
                    e2.Previous    = e2ro.Twin;
                }

                // Add the edges to the list
                AddEdgePair(vertex1, vertex2, e1, e2);

                // If the new edges were connected to different contours, fuse the contours
                if (differentContours)
                {
                    var face = e1lo.Face;
                    e1.Face = e2.Face = face;

                    // Create a hashset to agilize things
                    var edges = new HashSet <Edge>(e1.CyclicalSequence, ReferenceEqualityComparer.Default);

                    // Remove the previous edge links and add a reference edge
                    face.Contours.RemoveAll(e => edges.Contains(e));
                    face.Contours.Add(e1);
                }
                else
                {
                    // The case where the edges connected the same contour is trickier
                    // First, create a face
                    var newFace = new Face();
                    var oldFace = e1lo.Face;

                    // Remove the contours that pertained to the old edges
                    var edges = new HashSet <Edge>(e1.CyclicalSequence.Concat(e2.CyclicalSequence), ReferenceEqualityComparer.Default);
                    oldFace.Contours.RemoveAll(e => edges.Contains(e));

                    // Pick the edge that forms a counterclockwise sequence
                    var edge = e1.CyclicalSequence.Sum(e => e.Winding) > 0 ? e1 : e2;

                    // And add it to the new face
                    newFace.Contours.Add(edge);
                    AssignFace(newFace, edge);

                    // Now, pluck all the old contours that should pertain to the new face
                    var contours = oldFace.Contours.ExtractAll(e => newFace.ContainsVertex(e.Curve.At(0)));

                    // Add them to the new face
                    newFace.Contours.AddRange(contours);
                    foreach (var c in contours)
                    {
                        AssignFace(newFace, c);
                    }

                    // Put the counterclockwise edge's twin in the old face
                    oldFace.Contours.Add(edge.Twin);
                    AssignFace(oldFace, edge.Twin);

                    // Add the new face to the list
                    faces.Add(newFace);
                }
            }
            // If only one of them is found, the case is very simple
            else
            {
                // Old cached vertex and new vertex, so we can run the first algorithms
                var oldVertex = found1 ? vertex1 : vertex2;
                var epo       = found1 ? id1 : id2;
                var epn       = found1 ? id2 : id1;
                var newVertex = AddVertex(epn);

                // Create the new pair of edges and set the right canonicity
                PairOfEdges(oldVertex, newVertex, out var e1, out var e2, found2);
                (found1 ? e1 : e2).Canonicity += canonicityChange;

                // Search for the adjacent edges of the new vertex
                oldVertex.SearchOutgoingEdges(e1, out var e1lo, out var e1ro);

                // Set the vertices correctly
                e1.Previous    = e1ro.Twin;
                e1.Next        = e2;
                e2.Previous    = e1;
                e2.Next        = e1lo;
                e1ro.Twin.Next = e1;
                e1lo.Previous  = e2;

                // The face is the outmost face
                e1.Face = e2.Face = e1lo.Face;

                // Add the edges to the list
                AddEdgePair(oldVertex, newVertex, e1, e2);
            }

            // FIN
        }
        private static void GenerateLineJoints(List <Triangle> triangles, List <CurveTriangle> curveTriangles,
                                               Curve prevCurve, Curve nextCurve, double halfWidth, StrokeLineJoin lineJoin, double miterLimit)
        {
            // First, calculate the difference between the angles to check where the joint need to be formed
            var exitAngle  = prevCurve.ExitAngle;
            var entryAngle = nextCurve.EntryAngle;
            var diff       = (exitAngle - entryAngle).WrapAngle();
            var sd         = Math.Sign(diff);

            // Skip creating the joint if the diff is small enough
            if (RoughlyZero(diff))
            {
                return;
            }

            // The common point and the offset vectors
            var p           = (prevCurve.At(1) + nextCurve.At(0)) / 2;
            var entryOffset = sd * halfWidth * Double2.FromAngle(entryAngle + Pi_2);
            var exitOffset  = sd * halfWidth * Double2.FromAngle(exitAngle + Pi_2);

            // Calculate the bisector and miter length
            var miter          = halfWidth / Math.Cos(Math.Abs(diff) / 2);
            var bisectorOffset = sd * miter * Double2.FromAngle(entryAngle + diff / 2 + Pi_2);

            // Utility function for miter and round
            Double2[] GenerateClippedTriangle(bool forRound)
            {
                var miterWidth = halfWidth * (forRound ? 1f : miterLimit);

                if (miter < miterWidth)
                {
                    return new[] { Double2.Zero, entryOffset, bisectorOffset, exitOffset }
                }
                ;
                else
                {
                    // Clip the miter
                    var p1 = entryOffset + miterWidth * (bisectorOffset - entryOffset) / miter;

                    var p2 = exitOffset + miterWidth * (bisectorOffset - exitOffset) / miter;
                    return(new[] { Double2.Zero, entryOffset, p1, p2, exitOffset });
                }
            }

            // Now, create the next triangles if necessary
            switch (lineJoin)
            {
            case StrokeLineJoin.Bevel:
                // Create the bevel triangle
                triangles.Add(new Triangle(p, p + entryOffset, p + exitOffset));
                break;

            case StrokeLineJoin.Miter:
            case StrokeLineJoin.MiterClip:
            {
                // Check the conditions for the miter (only clip if miter-clip is explicity selected)
                if (lineJoin == StrokeLineJoin.Miter && miter >= halfWidth * miterLimit)
                {
                    break;
                }

                // Generate the miter
                var polygon = GenerateClippedTriangle(false).Select(v => p + v).ToArray();
                triangles.AddRange(Triangle.MakeTriangleFan(polygon));
                break;
            }

            case StrokeLineJoin.Round:
            {
                // Generate the round triangle
                var curvePolygon = GenerateClippedTriangle(true)
                                   .Select(v => new CurveVertex(p + v, new Double4(v.X, v.Y, -v.Y, 1f))).ToArray();
                curveTriangles.AddRange(CurveVertex.MakeTriangleFan(curvePolygon));
                break;
            }

            case StrokeLineJoin.Arcs:
            {
                // Compute the curvatures of the curves
                var exitKappa  = prevCurve.ExitCurvature;
                var entryKappa = nextCurve.EntryCurvature;

                // If both of them are zero, fall back to miter
                if (RoughlyZero(exitKappa) && RoughlyZero(entryKappa))
                {
                    goto case StrokeLineJoin.MiterClip;
                }

                throw new NotImplementedException("Later i'll end it");
            }
            break;

            default: break;
            }
        }
Пример #10
0
    /*create meash for linear rendered 3D object*/
    public void CreateMesh(Curve curve, float offset, Material linearMaterial, Material crossMaterial, Polygon cross)
    {
        List <Vector2> fragments = cross.toFragments();

        int segmentCount = Mathf.CeilToInt(curve.length * curve.maximumCurvature / maxAngleDiff);
        int base_vcount  = (segmentCount + 1) * fragments.Count;
        int base_tcount  = segmentCount * 2 * 3 * fragments.Count;

        int[] crossTriangles = cross.createMeshTriangle();
        int   cross_vcount   = fragments.Count;
        int   cross_tcount   = crossTriangles.Length;

        Vector2[] crossUVs = cross.createUV();

        Vector3[] all_vertices     = new Vector3[base_vcount + 2 * cross_vcount];
        int[]     linear_triangles = new int[base_tcount];
        Vector2[] linearUVs        = new Vector2[base_vcount];

        for (int i = 0; i != segmentCount + 1; ++i)
        {
            Vector3        roadPoint      = curve.At(1.0f / segmentCount * i);
            float          direction      = curve.Angle_2d(1.0f / segmentCount * i) - Mathf.PI / 2;
            List <Vector3> localFragments = fragments.ConvertAll((input) => roadPoint +
                                                                 Algebra.toVector3(Algebra.twodRotate(Vector2.right * (offset + input.x), direction)) +
                                                                 Vector3.up * input.y);
            /*stretch Z*/
            List <float> cross_y_offset = cross.getVResizeOffset(roadPoint.y);
            for (int j = 0; j != localFragments.Count; ++j)
            {
                localFragments[j] += Vector3.up * cross_y_offset[j];
            }

            float cross_diameter   = fragments.Sum((input) => input.magnitude);
            float partial_diameter = 0f;
            for (int j = i * cross_vcount, local_j = 0; j != i * cross_vcount + cross_vcount; ++j, ++local_j)
            {
                all_vertices[j]   = localFragments[local_j];
                linearUVs[j]      = new Vector2(partial_diameter / cross_diameter, i * 1.0f / segmentCount * curve.length / cross_diameter);
                partial_diameter += fragments[local_j].magnitude;
            }
        }

        for (int i = 0; i != cross_vcount; ++i)
        {
            all_vertices[base_vcount + i] = all_vertices[i];
            all_vertices[base_vcount + cross_vcount + i] = all_vertices[base_vcount - cross_vcount + i];
        }

        for (int i = 0, triangle = 0; i != segmentCount; ++i)
        {
            for (int j = 0; j != cross_vcount; ++j, triangle += 6)
            {
                linear_triangles[triangle]     = i * cross_vcount + j;
                linear_triangles[triangle + 1] = i * cross_vcount + (j + 1) % cross_vcount;
                linear_triangles[triangle + 2] = (i + 1) * cross_vcount + j;

                linear_triangles[triangle + 3] = i * cross_vcount + (j + 1) % cross_vcount;
                linear_triangles[triangle + 4] = (i + 1) * cross_vcount + (j + 1) % cross_vcount;
                linear_triangles[triangle + 5] = (i + 1) * cross_vcount + j;
            }
        }

        int[] cross_triangles = new int[2 * cross_tcount];
        /*Add tris at start*/
        for (int j = 0; j != cross_tcount; ++j)
        {
            cross_triangles[j] = crossTriangles[j] + base_vcount;
        }

        /*Add tris at end*/
        for (int j = 0; j != cross_tcount; ++j)
        {
            if (j % 3 == 1)
            {
                cross_triangles[cross_tcount + j] = crossTriangles[j + 1];
            }
            else
            {
                if (j % 3 == 2)
                {
                    cross_triangles[cross_tcount + j] = crossTriangles[j - 1];
                }
                else
                {
                    cross_triangles[cross_tcount + j] = crossTriangles[j];
                }
            }
            cross_triangles[cross_tcount + j] += (base_vcount + cross_vcount);
        }

        Vector2[] modifiedCrossUVs = new Vector2[base_vcount + 2 * cross_vcount];
        for (int i = 0; i != base_vcount; ++i)
        {
            modifiedCrossUVs[i] = linearUVs[i];
        }
        for (int i = 0; i != cross_vcount; ++i)
        {
            modifiedCrossUVs[i + base_vcount] = modifiedCrossUVs[i + base_vcount + cross_vcount] = crossUVs[i];
        }


        GetComponent <MeshRenderer>().sharedMaterials = new Material[2] {
            crossMaterial, linearMaterial
        };

        MeshFilter meshFilter = GetComponent <MeshFilter>();

        if (meshFilter.sharedMesh == null)
        {
            meshFilter.sharedMesh = new Mesh();
        }

        meshFilter.sharedMesh.subMeshCount = 2;
        meshFilter.sharedMesh.SetVertices(all_vertices.ToList());
        meshFilter.sharedMesh.SetTriangles(cross_triangles, 0);
        meshFilter.sharedMesh.SetTriangles(linear_triangles, 1);
        meshFilter.sharedMesh.SetUVs(0, modifiedCrossUVs.ToList());
    }
Пример #11
0
        public void AddCurve(Curve curve)
        {
            var vert1 = curve.At(0);
            var vert2 = curve.At(1);

            void PairOfEdges(Vertex v1, Vertex v2, out Edge e1, out Edge e2)
            {
                e1 = new Edge(v1, v2, curve);
                e2 = new Edge(v2, v1, curve.Reverse);

                e1.Twin = e2;
                e2.Twin = e1;
            }

            // There is a lot of pesky cases. Note that, since we are adding only "whole" edges (i.e.
            // edges do not intersect other edges, we guarantee that a single edge with brand new vertices
            // is inside a single face).

            // Check if the vertices were added to the cache first
            bool found1 = FindVertex(vert1, out var vertex1);
            bool found2 = FindVertex(vert2, out var vertex2);

            // If none are found, add them individually and connect both to the same face
            if (!found1 && !found2)
            {
                // First, check if they actually don't belong to a sub-DCEL
                var face = GetFaceFromVertex(vert1);

                // Add it to the sub-DCEL
                if (face != null)
                {
                    face.SubDCEL.AddCurve(curve);
                }
                else // Add it to the outer face
                {
                    vertex1 = AddVertex(vert1);
                    vertex2 = AddVertex(vert2);

                    PairOfEdges(vertex1, vertex2, out var e1, out var e2);

                    e1.Canonicity++;
                    e1.Next = e1.Previous = e2;
                    e2.Next = e2.Previous = e1;

                    AddEdgePair(vertex1, vertex2, e1, e2);
                }
            }
            // If both of them are found, we "close" the loop, splitting the face in two and transfering the sub-DCEL
            // to one of the faces
            else if (found1 && found2)
            {
                // Discard the equal vertices case
                if (vertex1 == vertex2)
                {
                    return;
                }

                PairOfEdges(vertex1, vertex2, out var e1, out var e2);

                // If a matching edge is found, abort the operation
                if (vertex1.SearchOutgoingEdges(e1, out var e1lo, out var e1ro))
                {
                    // Just up the canonicity of the edge, so we can track it
                    e1lo.Canonicity++;
                    return;
                }

                e1.Canonicity++;

                // Other ones are guaranteed to return false
                vertex2.SearchOutgoingEdges(e2, out var e2lo, out var e2ro);

                // Create the edge links (I won't try to draw a diagram here, sorry... my ASCII art is horrible)
                e1ro.Twin.Next = e2lo.Previous = e1;
                e2ro.Twin.Next = e1lo.Previous = e2;
                e1.Next        = e2lo;
                e1.Previous    = e1ro.Twin;
                e2.Next        = e1lo;
                e2.Previous    = e2ro.Twin;

                // Add the edges to the list
                AddEdgePair(vertex1, vertex2, e1, e2);

                // Old face
                var oldface = e2lo.Face;

                // We could be joining two disjoint points of the outer face together. Don't add faces if it is the case
                if (oldface == null && e1.CyclicalSequence.Contains(e2))
                {
                    // Check to see if we need to join clusters
                    var cl1 = e1.CyclicalSequence.FirstOrDefault(e => e.Twin.Face != null)?.Twin.Face.Cluster;
                    var cl2 = e2.CyclicalSequence.FirstOrDefault(e => e.Twin.Face != null)?.Twin.Face.Cluster;

                    // Check if the clusters really exist
                    if (cl1 != null && cl1 != cl2)
                    {
                        // Join them
                        foreach (var face in cl2)
                        {
                            cl1.Add(face);
                            face.Cluster = cl1;
                        }
                    }

                    return;
                }

                // New face
                var newface = new Face();
                faces.Add(newface);

                // Go through all the new edges and assign them to the new faces, regenerating their vertex caches
                // along the way. The first flag is here to guarantee the loop will at least begin (since the condition
                // would cause the loop not even to start)
                AssignFace(oldface, e1);
                AssignFace(newface, e2);

                // There are two cases: linking an inside face and an outside face
                if (oldface != null) // In case of an inside face
                {
                    // Now, finally move the sub-DCEL (if any) to the destination face
                    var dcel = oldface.SubDCEL;
                    if (dcel.vertices.Count > 0)
                    {
                        // Pick a random point on the sub-DCEL
                        var vtx = dcel.vertices[0].Position; // <-- Ugly, looks like Unity :/
                        // Put the DCEL on the new face
                        if (newface.ContainsVertex(vtx))
                        {
                            oldface.SubDCEL = newface.SubDCEL;
                            newface.SubDCEL = dcel;
                        }
                    }

                    // Attribute the same face cluster to the new face
                    newface.Cluster = oldface.Cluster;
                    newface.Cluster.Add(newface);
                }
                else
                {
                    if (newface.Winding < 0)
                    {
                        // When an outside face, we must be sure that it is the only face that has a clockwise winding
                        AssignFace(newface, e1);
                        AssignFace(oldface, e2);

                        // Swap the edges, so e1 belongs to null and e2 belongs to the newly created face
                        Edge et = e1;
                        e1 = e2;
                        e2 = et;
                    }

                    // Find if the face pertains to a cluster
                    var neighbor = e1.CyclicalSequence.FirstOrDefault(e => e.Twin.Face != null && e.Twin.Face != newface);

                    // If found, add it to the neighbor. If not, create a new one
                    if (neighbor != null)
                    {
                        newface.Cluster = neighbor.Twin.Face.Cluster;
                    }
                    else
                    {
                        newface.Cluster = new HashSet <Face>(ReferenceEqualityComparer.Default);
                    }

                    // Add the new face to its cluster
                    newface.Cluster.Add(newface);

                    // Finally, we have to deal with the possible case that the face "closes" on a cluster already in the DCEL
                    while (true)
                    {
                        var nextFace = faces.FirstOrDefault(delegate(Face f)
                        {
                            // Avoid faces that pertain to the "structure" of the vertex being detected
                            if (newface.Cluster.Contains(f))
                            {
                                return(false);
                            }
                            return(newface.ContainsVertex(f.ReferenceVertex.Position));
                        });

                        if (nextFace != null)
                        {
                            MoveFaceClusterInwards(newface, nextFace);
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
            // If only one of them is found, we need to either add it "open" to the graph or join two DCELs (the worst part)
            else
            {
                // Old cached vertex and new vertex, so we can run the first algorithms
                var oldVertex = found1 ? vertex1 : vertex2;
                var epn       = found1 ? vert2 : vert1;

                // Check the face sub-DCEL to check if the new vertex is in it
                var face = GetFaceFromVertex(epn);

                Vertex newVertex = null;
                // Null coalescing and checking for boolean in the same line
                // We will use the hash set for performance
                // We check for != true because it will return null on the outside
                // face or false on inside faces
                if (face?.SubDCEL.FindVertex(epn, out newVertex) != true)
                {
                    // Add the vertex to the list
                    newVertex = AddVertex(epn);

                    // Generate the two pairs of edges
                    PairOfEdges(oldVertex, newVertex, out var e1, out var e2);
                    if (found1)
                    {
                        e1.Canonicity++;
                    }
                    else
                    {
                        e2.Canonicity++;
                    }

                    // Search left and right for the edge found
                    oldVertex.SearchOutgoingEdges(e1, out var elo, out var ero);

                    // Assign correctly the linked edges (again, do not ask me to draw this diagram :/)
                    e1.Next     = elo.Previous = e2;
                    e2.Previous = ero.Twin.Next = e1;
                    e1.Previous = ero.Twin;
                    e2.Next     = elo;

                    // Assign the face to the vertices
                    e1.Face = e2.Face = face;

                    // Add the vertices to the lsit
                    AddEdgePair(oldVertex, newVertex, e1, e2);

                    // Regenerate the vertices of the face
                    AssignFace(face, e1);
                }
                else // The last case... joining two DCELs
                {
                    // First thing... we join  the two DCELs
                    Join(face.SubDCEL);
                    face.SubDCEL.Clear();

                    // Since the precondition is that edges cannot intersect other edges in the graph,
                    // we are guaranteed that this vertex has an edge in the outer face
                    // Generate the two pairs of edges
                    PairOfEdges(oldVertex, newVertex, out var e1, out var e2);
                    if (found1)
                    {
                        e1.Canonicity++;
                    }
                    else
                    {
                        e2.Canonicity++;
                    }

                    // Search left and right for the nearby vertices, now it is the same case as for linking
                    // two vertices together, except there is no new face
                    oldVertex.SearchOutgoingEdges(e1, out var e1lo, out var e1ro);
                    newVertex.SearchOutgoingEdges(e2, out var e2lo, out var e2ro);

                    // Create the edge links (...)
                    e1ro.Twin.Next = e2lo.Previous = e1;
                    e2ro.Twin.Next = e1lo.Previous = e2;
                    e1.Next        = e2lo;
                    e1.Previous    = e1ro.Twin;
                    e2.Next        = e1lo;
                    e2.Previous    = e2ro.Twin;

                    // Add the edges to the list
                    AddEdgePair(oldVertex, newVertex, e1, e2);

                    // Regenerate the vertices of the face
                    AssignFace(face, e1);
                }
            }

            // All cases are done? I can't believe!
        }
Пример #12
0
    public void addRoad(Curve curve, List <string> laneConfigure)
    {
        if (laneConfigure.Count == 0)
        {
            GameObject.FindWithTag("Logger").GetComponent <MessageLogger>().LogMessage("Please configure lanes first!");
            return;
        }

        List <Road> allroadsBackup = allroads.ConvertAll(input => input);

        List <Vector3> newIntersectPoints = new List <Vector3>();

        List <Vector3> affectedPoints = new List <Vector3>();

        foreach (Road oldroad in allroads.ToList())
        {
            List <Vector3> intersectPoints = Geometry.curveIntersect(curve, oldroad.curve);

            if (intersectPoints.Count > 0)
            {
                newIntersectPoints.AddRange(intersectPoints);
                //remove oldroad
                removeRoad(oldroad);
                //Add new fragments
                List <float> intersectParams = interSectPoints2Fragments(intersectPoints, oldroad.curve);
                addAllFragments(intersectParams, oldroad.curve, oldroad.laneconfigure);

                affectedPoints.AddRange(intersectPoints);
                affectedPoints.Add(oldroad.curve.At(0f));
                affectedPoints.Add(oldroad.curve.At(1f));
            }
        }

        newIntersectPoints = newIntersectPoints.Distinct(new IntersectPointComparator()).ToList();
        List <float> intersectParamsWithBeginAndEnd1 = interSectPoints2Fragments(newIntersectPoints, curve);

        addAllFragments(intersectParamsWithBeginAndEnd1, curve, laneConfigure);

        affectedPoints.Add(curve.At(0f));
        affectedPoints.Add(curve.At(1f));
        affectedPoints = affectedPoints.Distinct(new IntersectPointComparator()).ToList();

        List <Node> affectedNodes = affectedPoints.ConvertAll(delegate(Vector3 pos)
        {
            Node n;
            findNodeAt(pos, out n);
            return(n);
        });

        try
        {
            foreach (Node n in affectedNodes)
            {
                n.updateMargins();
            }

            foreach (Node n in affectedNodes)
            {
                foreach (var r in n.connection)
                {
                    createRoadObject(r);
                }
            }
        }
        catch (Exception ex) {
            Debug.Log(ex.StackTrace);

            GameObject.FindWithTag("Logger").GetComponent <MessageLogger>().LogMessage("Intersections too close!");

            allroads = allroadsBackup;

            foreach (Node n in affectedNodes)
            {
                n.reEstablishConnections(allroads);
            }

            foreach (var item in affectedNodes.Where(n => n.connection.Count == 0).ToList())
            {
                affectedNodes.Remove(item);
                var nodepos = allnodes.First(kvp => kvp.Value == item).Key;
                Debug.Assert(allnodes.Remove(nodepos));
            }

            foreach (Node n in affectedNodes)
            {
                foreach (var r in n.connection)
                {
                    createRoadObject(r);
                }
            }
        }

        foreach (Road r in allroads)
        {
            Debug.Assert(r.marginedOutCurve != null);
        }
    }