StreetTraversal PickNextTraversed(Intersection currentIntersection, StreetTraversal prevTraversed) { //the float as the key doesnt pose issues because the only way we access anyway is by MaxKey Dictionary <float, StreetTraversal> streetsByAngle = new Dictionary <float, StreetTraversal>(); //picking connected Streets that go counterclockwise, or straight, compared to prevTraversed foreach (Street connected in currentIntersection.Connected) { if (connected == prevTraversed.Street) { continue; } //we dont want to backtrack, so skipping the one from which we came from Vector2 prevDir = GS.GetPolar(prevTraversed.FromAToB) * prevTraversed.Street.Line.Dir; bool connectedFromAToB = (currentIntersection == connected.InterA); Vector2 connectedDir = GS.GetPolar(connectedFromAToB) * connected.Line.Dir; float angle = -Vector2.SignedAngle(prevDir, connectedDir); if (angle >= -0.01f) { streetsByAngle.Add(angle, new StreetTraversal(connected, connectedFromAToB)); } } if (streetsByAngle.Count > 0) { return(streetsByAngle[streetsByAngle.MaxKey()]); } return(null); }
public IEnumerator ExtractCityBlocks(List <Intersection> inters, List <RimStreetLine> linesWithEndOnRim) { DB.Log("EXTRACTING CITY BLOCKS", 1); Blocks = new List <CityBlock>(); //extracting city blocks looks like this: //From every Intersection the algorithm tries to traverse the Streets connected to it //and upon encountering an Intersection on the other end it makes the first clockwise //turn available, unless there is no such turn, then it continues in the same direction. //When it doesn't encounter an Intersection on the end of a Street, but instead a dead end //on the rim of the city circle, it jumps to the next point clockwise along the rim //and continues traversal. This way the alg will eventually reach the same Intersection it started //from and this way it will save all the Streets encountered as the bounds of the city block. //In the case when the traversal gets lost and after a lot of points it still hasn't come back, //it is skipped, but I doubt if getting lost is even mathematically possible, it's just an //extra precaution. for (int i = 0; i < inters.Count; i++) { foreach (Street startStreet in inters[i].Connected) { bool lost = false; bool hasRimSkip = false; Intersection current = startStreet.GetOtherSide(inters[i]); List <StreetTraversal> traversal = new List <StreetTraversal>(); bool startingTraversedFromAToB = (inters[i] == startStreet.InterA); traversal.Add(new StreetTraversal(startStreet, startingTraversedFromAToB)); while (current != inters[i]) { if (current == null) { //current Intersection is null meaning we've reached city rim on its end //need to jump along the rim clockwise hasRimSkip = true; Vector2 pointOnRim = (traversal.Last().FromAToB) ? traversal.Last().Street.B : traversal.Last().Street.A; StreetTraversal traversedAfterSkip = GetTraversalFromSkip(pointOnRim, linesWithEndOnRim); traversal.Add(traversedAfterSkip); current = traversedAfterSkip.FromAToB ? traversedAfterSkip.Street.InterB : traversedAfterSkip.Street.InterA; } else { traversal.Add(PickNextTraversed(current, traversal.Last())); current = traversal.Last().Street.GetOtherSide(current); } if (traversal.Count > 50) { lost = true; DB.Log("traversal got lost."); break; } } if (!lost) { CityBlock block = new CityBlock(traversal, hasRimSkip); if (!BlockIsADuplicate(block)) { Blocks.Add(block); block.DebugDraw(300f); //DB.Log(block.ToString()); //yield return new WaitForSeconds(1f); } } } yield return(null); } DB.Log($"{Blocks.Count} CITY BLOCKS EXTRACTED", 1); }