private static void InsertNodeSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments) { foreach (var node in topology.Nodes) { foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template)) { var template = edgesPerTemplate.Key; // possible optimization: only compute angles if necessary? var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate); switch (edgesPerTemplate.Count()) { // TODO support end caps case 1: // end point break; case 2: CurvedRoadSegment.CreateCurve(incomingRoadData, node.Position, template, edgeSegments); break; case 3: case 4: CrossingRoadSegment.CreateCrossing(incomingRoadData, node.Position, template, edgeSegments); break; } } } }
internal RoadCollection(RoadTopology topology, AssetLoadContext loadContext, HeightMap heightMap) : this() { // The map stores road segments with no connectivity: // - a segment is from point A to point B // - with a road type name // - and start and end curve types (angled, tight curve, broad curve). // The goal is to create road networks of connected road segments, // where a network has only a single road type. // A road network is composed of 2 or more nodes. // A network is a (potentially) cyclic graph. // A road node has > 1 and <= 4 edges connected to it. // A node can be part of multiple networks. // An edge can only exist in one network. var roadTemplateList = new RoadTemplateList(loadContext.AssetStore.RoadTemplates); var networks = RoadNetwork.BuildNetworks(topology, roadTemplateList); foreach (var network in networks) { _roads.Add(AddDisposable(new Road( loadContext, heightMap, network))); } }
private static void InsertNodeSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments) { foreach (var node in topology.Nodes) { foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template)) { switch (edgesPerTemplate.Count()) { // TODO support end caps case 1: // end point break; case 2: // TODO normal road, create segments for tight/broad curves break; case 3: case 4: var template = edgesPerTemplate.Key; var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate); CrossingRoadSegment.CreateCrossing(incomingRoadData, node.Position, template, edgeSegments); break; } } } }
public static IList <RoadNetwork> BuildNetworks(RoadTopology topology) { var edgeSegments = BuildEdgeSegments(topology); InsertNodeSegments(topology, edgeSegments); var networks = BuildNetworks(topology, edgeSegments); return(networks); }
private static void InsertNodeSegments(RoadTopology topology, IDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments) { foreach (var node in topology.Nodes) { foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template)) { switch (edgesPerTemplate.Count()) { // TODO support end caps case 1: // end point break; case 2: // TODO normal road, create segments for tight/broad curves break; case 3: // TODO figure out orientation and endpoints // TODO support Y segments //var halfWidth = edgesPerTemplate.Key.RoadWidth / 2; //var segment = new TRoadSegment( // node.Position, // new RoadSegmentEndPoint(node.Position + new Vector3(0, halfWidth, 0)), // new RoadSegmentEndPoint(node.Position + new Vector3(halfWidth, 0, 0)), // new RoadSegmentEndPoint(node.Position + new Vector3(0, -halfWidth, 0))); //// TODO consider ordering of edges //Connect(edgesPerTemplate.ElementAt(0), segment.Top, Vector3.UnitY); //Connect(edgesPerTemplate.ElementAt(1), segment.Right, Vector3.UnitX); //Connect(edgesPerTemplate.ElementAt(2), segment.Bottom, -Vector3.UnitY); //void Connect(RoadTopologyEdge edge, RoadSegmentEndPoint endPoint, in Vector3 direction) //{ // var edgeSegment = edgeSegments[edge]; // if (edge.Start.Position == node.Position) // { // edgeSegment.Start.Position = endPoint.Position; // edgeSegment.Start.ConnectTo(segment, direction); // endPoint.ConnectTo(edgeSegment, edge.Start.Position - edge.End.Position); // } // else // { // edgeSegment.End.Position = endPoint.Position; // edgeSegment.End.ConnectTo(segment, direction); // endPoint.ConnectTo(edgeSegment, edge.End.Position - edge.Start.Position); // } //} break; } } } }
private static void InsertCurveSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments) { foreach (var node in topology.Nodes) { foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template)) { var connectedEdges = edgesPerTemplate.Count(); if (connectedEdges == 2) { var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate, edgeSegments); CurvedRoadSegment.CreateCurve(incomingRoadData, node.Position, edgesPerTemplate.Key, edgeSegments); } } } }
private static IList <RoadNetwork> BuildNetworks(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments) { var networks = new List <RoadNetwork>(); // Create one network for each connected set of segments of a specific type. foreach (var templateEdges in topology.Edges.GroupBy(e => e.Template)) { var edgesToProcess = new HashSet <IRoadSegment>(templateEdges.Select(e => edgeSegments[e])); while (edgesToProcess.Any()) { var edgeSegment = edgesToProcess.First(); edgesToProcess.Remove(edgeSegment); var seenSegments = new HashSet <IRoadSegment>(); seenSegments.Add(edgeSegment); var network = new RoadNetwork(templateEdges.Key); networks.Add(network); network._segments.Add(edgeSegment); foreach (var endPoint in edgeSegment.EndPoints) { FollowPath(endPoint); } void FollowPath(RoadSegmentEndPoint endPoint) { if (endPoint.To == null || seenSegments.Contains(endPoint.To)) { return; } edgesToProcess.Remove(endPoint.To); network._segments.Add(endPoint.To); seenSegments.Add(endPoint.To); foreach (var nextEndPoint in endPoint.To.EndPoints) { FollowPath(nextEndPoint); } } } } return(networks); }
private static void InsertCrossingSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments) { foreach (var node in topology.Nodes) { foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template)) { if (edgesPerTemplate.Count() >= 3) { var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate, edgeSegments); if (incomingRoadData.Count == 3 || incomingRoadData.Count == 4) { CrossingRoadSegment.CreateCrossing(incomingRoadData, node.Position, edgesPerTemplate.Key, edgeSegments); } } } } }
internal RoadCollection(RoadTopology topology, AssetLoadContext loadContext, HeightMap heightMap) : this() { // The map stores road segments with no connectivity: // - a segment is from point A to point B // - with a road type name // - and start and end curve types (angled, tight curve, broad curve). // The goal is to create road networks of connected road segments, // where a network has only a single road type. // A road network is composed of 2 or more nodes. // A network is a (potentially) cyclic graph. // A road node has > 1 and <= 4 edges connected to it. // A node can be part of multiple networks. // An edge can only exist in one network. // TODO: If a node stored in the map has > 4 edges, the extra edges // are put into a separate network. var networks = RoadNetwork.BuildNetworks(topology); // Roads of different types are rendered in reverse template order: // the first template has the lowest z-index, the last one the highest. // Since we don't know the index here we start with the templates, // join them with the networks and reverse the result. var sortedNetworks = loadContext.AssetStore.RoadTemplates .Join( networks, t => t.InstanceId, n => n.Template.InstanceId, (t, n) => n) .Reverse(); foreach (var network in sortedNetworks) { _roads.Add(AddDisposable(new Road( loadContext, heightMap, network))); } }
public static IEnumerable <RoadNetwork> BuildNetworks(RoadTopology topology, RoadTemplateList roadTemplateList) { topology.AlignOrientation(); var edgeSegments = BuildEdgeSegments(topology); InsertNodeSegments(topology, edgeSegments); InsertEndCapSegments(edgeSegments, roadTemplateList); var networks = BuildNetworks(topology, edgeSegments); // sort networks in the order specified by roadTemplateList var sortedNetworks = roadTemplateList .Join( networks, t => t.InstanceId, n => n.Template.InstanceId, (t, n) => n); return(sortedNetworks); }
private static void InsertNodeSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments) { foreach (var node in topology.Nodes) { foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template)) { var template = edgesPerTemplate.Key; var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate); switch (edgesPerTemplate.Count()) { case 2: CurvedRoadSegment.CreateCurve(incomingRoadData, node.Position, template, edgeSegments); break; case 3: case 4: CrossingRoadSegment.CreateCrossing(incomingRoadData, node.Position, template, edgeSegments); break; } } } }
private static IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> BuildEdgeSegments(RoadTopology topology) { // create a dictionary from edges to segments var edgeSegments = topology.Edges.ToDictionary(e => e, e => new StraightRoadSegment(e.Start.Position, e.End.Position)); // create end points and connect them to the neighbour edges foreach (var edge in topology.Edges) { var edgeSegment = edgeSegments[edge]; Connect(edge.Start, edge.Start.Position - edge.End.Position); Connect(edge.End, edge.End.Position - edge.Start.Position); void Connect(RoadTopologyNode node, in Vector3 direction) { foreach (var connectedEdge in node.Edges) { if (connectedEdge == edge || connectedEdge.Template != edge.Template) { continue; } var connectedEdgeSegment = edgeSegments[connectedEdge]; if (connectedEdge.Start.Position == node.Position) { connectedEdgeSegment.Start.ConnectTo(edgeSegment, Vector3.Normalize(direction)); } else { connectedEdgeSegment.End.ConnectTo(edgeSegment, Vector3.Normalize(direction)); } } } } return(edgeSegments); }