//public void GenHighway() { public IEnumerator GenHighwayCoroutine() { WorldManager.GenHighwayState = true; Dictionary <Vector2, float> densityLookup = new Dictionary <Vector2, float>(); // cluster nearby points with DBScan List <Vector2> features = new List <Vector2>();// = new MyFeatureDataSource().GetFeatureData(); for (int i = 0; i < patchDensityCenters.Count; i++) { (Vector2, float)cd = ((Vector2, float))patchDensityCenters[i]; features.Add(cd.Item1); densityLookup[cd.Item1] = cd.Item2; } var result = RunOfflineDbscan(features); // Build highway graph var points = new List <Vertex>(); foreach (int i in result.Clusters.Keys) { points.Add(new Vertex(result.Clusters[i][0].Feature.x, result.Clusters[i][0].Feature.y)); } // Generate a default mesher var mesher = new GenericMesher(new Dwyer()); // Generate mesh (Delaunay Triangulation) mesh = mesher.Triangulate(points); // Init edge/vertex lists for mutation foreach (Vertex v in mesh.Vertices) { vertices.Add(v); } foreach (Edge e in mesh.Edges) { edges.Add(e); // build neighbor map Vertex v0 = (Vertex)vertices[e.P0]; Vertex v1 = (Vertex)vertices[e.P1]; if (!neighbors.ContainsKey(v0)) { neighbors[v0] = new List <Vertex>(); } neighbors[v0].Add(v1); } // Remove unecessary edges on cost basis foreach (Edge e in mesh.Edges) { Vertex v0 = (Vertex)vertices[e.P0]; Vertex v1 = (Vertex)vertices[e.P1]; (Vector2, Vector2)tup1 = (Util.VertexToVector2(v0), Util.VertexToVector2(v1)); (Vector2, Vector2)tup2 = (Util.VertexToVector2(v1), Util.VertexToVector2(v0)); if (!InPatchBounds(regionIdx, tup1.Item1, tup1.Item2)) { RemoveEdge(e); continue; } if (!WorldManager.edgeState.ContainsKey(tup1)) //needs removal check { if (ShouldRemoveEdge(e, densityLookup)) { // remove edge for first time WorldManager.edgeState[tup1] = false; WorldManager.edgeState[tup2] = false; RemoveEdge(e); } else { // keep edge, register with edgeState WorldManager.edgeState[tup1] = true; WorldManager.edgeState[tup2] = true; } } else // remove if removed before { if (!WorldManager.edgeState[tup1]) { RemoveEdge(e); } } } // Generate final highway segments for each edge // Uses A* search to pathfind int hwCount = 0; foreach (Edge e in edges) { hwCount++; (Vector2Int, Vector2Int)eVec = (Util.VertexToVector2Int((Vertex)vertices[e.P0]), Util.VertexToVector2Int((Vertex)vertices[e.P1])); Vertex v0 = (Vertex)vertices[e.P0]; Vertex v1 = (Vertex)vertices[e.P1]; // Skip pathfinding for edges that have been generated/built already if (WorldBuilder.builtHighways.ContainsKey((Util.VertexToVector2(v0), Util.VertexToVector2(v0))) && WorldBuilder.builtHighways[(Util.VertexToVector2(v0), Util.VertexToVector2(v0))]) { //Debug.Log("Aborting pathfind since already built!"); continue; } // A* pathfind from v0 to v1 ArrayList segments = pathfinding.FindPath(Util.VertexToVector2(v0), Util.VertexToVector2(v1)); // Removal of redundant paths int firstIdx = -1, secondIdx = -1; List <(Vector2, (Vector2Int, Vector2Int))> vertListFirst = null, vertListSecond = null; // traverse from beginning for (int i = 0; i < segments.Count - 1; i++) { Vector2 v = (Vector2)segments[i]; if (WorldBuilder.DoesChunkContainHighway(v)) { firstIdx = i; vertListFirst = WorldBuilder.GetHighwayVertList(v); break; } } // traverse from end if (firstIdx >= 0) { for (int i = segments.Count - 1; i > 0; i--) { Vector2 v = (Vector2)segments[i]; if (WorldBuilder.DoesChunkContainHighway(v)) { secondIdx = i; vertListSecond = WorldBuilder.GetHighwayVertList(v); break; } } } // segment join logic if (vertListFirst != null && vertListSecond != null && firstIdx >= 0 && secondIdx >= 0) { // Direct connection with existing path bool done = false; // *--|____/* foreach ((Vector2, (Vector2Int, Vector2Int))tup in vertListFirst) { if (eVec.Item2 == tup.Item2.Item2 || eVec.Item2 == tup.Item2.Item1) { segments.RemoveRange(firstIdx, segments.Count - firstIdx); segments.Insert(firstIdx, tup.Item1); done = true; break; } } if (!done) { // *\____|--* foreach ((Vector2, (Vector2Int, Vector2Int))tup in vertListSecond) { if (eVec.Item1 == tup.Item2.Item2 || eVec.Item1 == tup.Item2.Item1) { segments.RemoveRange(0, secondIdx); segments.Insert(0, tup.Item1); done = true; break; } } } if (!done) { // No direct connection cases (needs paths from start and end) (bool, (Vector2Int, Vector2Int))sameEdgeRes = DoListsContainSameEdge(vertListFirst, vertListSecond); if (sameEdgeRes.Item1) { Vector2 con1 = FindVecWithEdge(sameEdgeRes.Item2, vertListFirst), con2 = FindVecWithEdge(sameEdgeRes.Item2, vertListSecond); if (con1 == con2) // same vert // *--|-----* pass through path { segments.RemoveAt(firstIdx); segments.Insert(firstIdx, con1); } else // diff vert // *--|__|--* join at existing path //Debug.Log(firstIdx + " " + secondIdx); { segments.RemoveRange(firstIdx, secondIdx - firstIdx + 1); segments.Insert(firstIdx, con2); segments.Insert(firstIdx, WorldBuilder.SignalVector); segments.Insert(firstIdx, con1); } } else// not matched so edges different! { Vector2 con1 = vertListFirst[0].Item1, con2 = vertListSecond[0].Item1; //neighbor or not if (DoListsContainNeighbors(vertListFirst, vertListSecond)) { // *--\./---* segments.RemoveRange(firstIdx, secondIdx - firstIdx + 1); segments.Insert(firstIdx, con2); segments.Insert(firstIdx, WorldBuilder.SignalVector); segments.Insert(firstIdx, con1); } else { // *--|--|--* pass through paths segments.RemoveAt(secondIdx); segments.RemoveAt(firstIdx); segments.Insert(firstIdx, con1); segments.Insert(secondIdx, con2); } } } } foreach (Vector2 v in segments) { WorldBuilder.AddHighwayVertToChunkHash(v, eVec); } if (segments != null) { highways.Add(segments); } if (hwCount % Settings.hwPathfindingIncrement == 0) { yield return(null); } } WorldManager.GenHighwayState = false; }