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); }
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()); }
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); }
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); }
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)); }
public bool startof(Curve c) { return((c.At(0f) - position).magnitude < (c.At(1f) - position).magnitude); }
/*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); }
/// <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; } }
/*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()); }
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! }
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); } }