public bool MatchExit(int solI, Ets2NavigationSegment next) { if (this.Type == Ets2NavigationSegmentType.Road) { var exitPoint = Options[solI].Points.LastOrDefault(); bool res = false; foreach (var route in next.Options) { var first = route.Points.FirstOrDefault(); if (first.CloseTo(exitPoint)) { route.ExitLane = this.Options[solI].EntryLane; if (route.ExitLane >= 0 && route.EntryLane >= 0) { route.Valid = true; } res = true; } } return(res); } else { return(false); } }
public Ets2NavigationSegment(IEnumerable<Ets2Item> roadPath, Ets2NavigationSegment prevSeg) { Type = Ets2NavigationSegmentType.Road; Roads = roadPath.ToList(); // Generate entry/exit noads & road item order var firstRoad = Roads.FirstOrDefault(); var lastRoad = Roads.LastOrDefault(); if (prevSeg.Prefab.NodesList.ContainsValue(firstRoad.StartNode)) { Entry = firstRoad.StartNode; Exit = lastRoad.EndNode; ReversedRoadChain = false; ReversedRoadElements = false; } else if (prevSeg.Prefab.NodesList.ContainsValue(firstRoad.EndNode)) { Entry = firstRoad.EndNode; Exit = lastRoad.StartNode; ReversedRoadChain = false; ReversedRoadElements = true; } else if (prevSeg.Prefab.NodesList.ContainsValue(lastRoad.StartNode)) { Entry = lastRoad.StartNode; Exit = firstRoad.EndNode; ReversedRoadChain = true; ReversedRoadElements = false; } else if ( prevSeg.Prefab.NodesList.ContainsValue(lastRoad.EndNode)) { Entry = lastRoad.EndNode; Exit = firstRoad.StartNode; ReversedRoadChain = true; ReversedRoadElements = true; } else { } }
public Ets2NavigationSegment(IEnumerable <Ets2Item> roadPath, Ets2NavigationSegment prevSeg) { Type = Ets2NavigationSegmentType.Road; Roads = roadPath.ToList(); // Generate entry/exit noads & road item order var firstRoad = Roads.FirstOrDefault(); var lastRoad = Roads.LastOrDefault(); if (prevSeg.Prefab.NodesList.ContainsValue(firstRoad.StartNode)) { Entry = firstRoad.StartNode; Exit = lastRoad.EndNode; ReversedRoadChain = false; ReversedRoadElements = false; } else if (prevSeg.Prefab.NodesList.ContainsValue(firstRoad.EndNode)) { Entry = firstRoad.EndNode; Exit = lastRoad.StartNode; ReversedRoadChain = false; ReversedRoadElements = true; } else if (prevSeg.Prefab.NodesList.ContainsValue(lastRoad.StartNode)) { Entry = lastRoad.StartNode; Exit = firstRoad.EndNode; ReversedRoadChain = true; ReversedRoadElements = false; } else if (prevSeg.Prefab.NodesList.ContainsValue(lastRoad.EndNode)) { Entry = lastRoad.EndNode; Exit = firstRoad.StartNode; ReversedRoadChain = true; ReversedRoadElements = true; } else { } }
public bool MatchEntry(int solI, Ets2NavigationSegment prev) { if (this.Type == Ets2NavigationSegmentType.Road) { var entryPoint = Options[solI].Points.FirstOrDefault(); bool res = false; foreach (var route in prev.Options) { var last = route.Points.LastOrDefault(); if (last.CloseTo(entryPoint)) { route.EntryLane = this.Options[solI].ExitLane; if (route.ExitLane >= 0 && route.EntryLane >= 0) route.Valid = true; res = true; } } return res; } else { return false; } }
public bool MatchExit(int solI, Ets2NavigationSegment next) { if (this.Type == Ets2NavigationSegmentType.Road) { var exitPoint = Options[solI].Points.LastOrDefault(); bool res = false; foreach (var route in next.Options) { var first = route.Points.FirstOrDefault(); if (first.CloseTo(exitPoint)) { route.ExitLane = this.Options[solI].EntryLane; if (route.ExitLane >= 0 && route.EntryLane >= 0) route.Valid = true; res = true; } } return res; } else { return false; } }
public bool Match(int mySolution, Ets2NavigationSegment prefabSegment) { throw new NotImplementedException(); }
public void GenerateOptions(Ets2NavigationSegment prevSeg, Ets2NavigationSegment nextSeg) { if (Type == Ets2NavigationSegmentType.Prefab) { var entryNode = -1; var exitNode = -1; var i = 0; // find node id's foreach (var kvp in Prefab.NodesList) { if (Entry != null && kvp.Value.NodeUID == Entry.NodeUID) entryNode = i; if (Exit != null && kvp.Value.NodeUID == Exit.NodeUID) exitNode = i; i++; } //var routes = Prefab.Prefab.GetRoute(entryNode, exitNode); var routes = Prefab.Prefab.GetAllRoutes(); if (routes == null || !routes.Any()) { return; } // Create options (we do this by just saving paths) foreach (var route in routes) { var option = new Ets2NavigationSegmentOption(); option.EntryLane = -1; option.ExitLane = -1; option.Points = Prefab.Prefab.GeneratePolygonForRoute(route, Prefab.NodesList.FirstOrDefault().Value, Prefab.Origin).ToList(); Options.Add(option); } } var curveSize = 32; if (Type == Ets2NavigationSegmentType.Road) { var firstRoad = Roads.FirstOrDefault(); // TODO: support UK // We have x number of lanes for (int startLane = 0; startLane < firstRoad.RoadLook.LanesRight; startLane++) { var curve1 = new List<Ets2Point>(); foreach (var rd in Roads) { var rdc = rd.GenerateRoadCurve(curveSize, false, startLane); if (!curve1.Any()) { if (!Entry.Point.CloseTo(rdc.FirstOrDefault())) rdc = rdc.Reverse(); } else { var lp = curve1.LastOrDefault(); if (!rdc.FirstOrDefault().CloseTo(lp)) rdc = rdc.Reverse(); } curve1.AddRange(rdc); } for (int endLane = 0; endLane < firstRoad.RoadLook.LanesRight; endLane++) { var curve2 = new List<Ets2Point>(); foreach (var rd in Roads) { var rdc = rd.GenerateRoadCurve(curveSize, false, endLane); if (!curve2.Any()) { if (!Entry.Point.CloseTo(rdc.FirstOrDefault())) rdc = rdc.Reverse(); } else { var lp = curve2.LastOrDefault(); if (!rdc.FirstOrDefault().CloseTo(lp)) rdc = rdc.Reverse(); } curve2.AddRange(rdc); } var curve = new List<Ets2Point>(); curve.AddRange(curve1.Skip(0).Take(curve2.Count/2)); curve.AddRange(curve2.Skip(curve2.Count / 2).Take(curve2.Count / 2)); if (ReversedRoadChain) curve.Reverse(); var option = new Ets2NavigationSegmentOption(); option.LeftLane = false; option.EntryLane = startLane; option.ExitLane = endLane; option.Points = curve; option.LaneCrossOver = (startLane != endLane); Options.Add(option); } } for (int startLane = 0; startLane < firstRoad.RoadLook.LanesLeft; startLane++) { var curve1 = new List<Ets2Point>(); foreach (var rd in Roads) { var rdc = rd.GenerateRoadCurve(curveSize, true, startLane); if (!curve1.Any()) { if (!Entry.Point.CloseTo(rdc.FirstOrDefault())) rdc = rdc.Reverse(); } else { var lp = curve1.LastOrDefault(); if (!rdc.FirstOrDefault().CloseTo(lp)) rdc = rdc.Reverse(); } curve1.AddRange(rdc); } for (int endLane = 0; endLane < firstRoad.RoadLook.LanesLeft; endLane++) { var curve2 = new List<Ets2Point>(); foreach (var rd in Roads) { var rdc = rd.GenerateRoadCurve(curveSize, true, endLane); if (!curve2.Any()) { if (!Entry.Point.CloseTo(rdc.FirstOrDefault())) rdc = rdc.Reverse(); } else { var lp = curve2.LastOrDefault(); if (!rdc.FirstOrDefault().CloseTo(lp)) rdc = rdc.Reverse(); } curve2.AddRange(rdc); } var curve = new List<Ets2Point>(); curve.AddRange(curve1.Skip(0).Take(curve2.Count / 2)); curve.AddRange(curve2.Skip(curve2.Count / 2).Take(curve2.Count / 2)); if (!ReversedRoadChain) curve.Reverse(); var option = new Ets2NavigationSegmentOption(); option.LeftLane = true; option.EntryLane = startLane; option.ExitLane = endLane; option.Points = curve; option.LaneCrossOver = (startLane != endLane); Options.Add(option); } } } }
public void GenerateOptions(Ets2NavigationSegment prevSeg, Ets2NavigationSegment nextSeg) { if (Type == Ets2NavigationSegmentType.Prefab) { var entryNode = -1; var exitNode = -1; var i = 0; // find node id's foreach (var kvp in Prefab.NodesList) { if (Entry != null && kvp.Value.NodeUID == Entry.NodeUID) { entryNode = i; } if (Exit != null && kvp.Value.NodeUID == Exit.NodeUID) { exitNode = i; } i++; } //var routes = Prefab.Prefab.GetRoute(entryNode, exitNode); var routes = Prefab.Prefab.GetAllRoutes(); if (routes == null || !routes.Any()) { return; } // Create options (we do this by just saving paths) foreach (var route in routes) { var option = new Ets2NavigationSegmentOption(); option.EntryLane = -1; option.ExitLane = -1; option.Points = Prefab.Prefab.GeneratePolygonForRoute(route, Prefab.NodesList.FirstOrDefault().Value, Prefab.Origin).ToList(); Options.Add(option); } } var curveSize = 32; if (Type == Ets2NavigationSegmentType.Road) { var firstRoad = Roads.FirstOrDefault(); // TODO: support UK // We have x number of lanes for (int startLane = 0; startLane < firstRoad.RoadLook.LanesRight; startLane++) { var curve1 = new List <Ets2Point>(); foreach (var rd in Roads) { var rdc = rd.GenerateRoadCurve(curveSize, false, startLane); if (!curve1.Any()) { if (!Entry.Point.CloseTo(rdc.FirstOrDefault())) { rdc = rdc.Reverse(); } } else { var lp = curve1.LastOrDefault(); if (!rdc.FirstOrDefault().CloseTo(lp)) { rdc = rdc.Reverse(); } } curve1.AddRange(rdc); } for (int endLane = 0; endLane < firstRoad.RoadLook.LanesRight; endLane++) { var curve2 = new List <Ets2Point>(); foreach (var rd in Roads) { var rdc = rd.GenerateRoadCurve(curveSize, false, endLane); if (!curve2.Any()) { if (!Entry.Point.CloseTo(rdc.FirstOrDefault())) { rdc = rdc.Reverse(); } } else { var lp = curve2.LastOrDefault(); if (!rdc.FirstOrDefault().CloseTo(lp)) { rdc = rdc.Reverse(); } } curve2.AddRange(rdc); } var curve = new List <Ets2Point>(); curve.AddRange(curve1.Skip(0).Take(curve2.Count / 2)); curve.AddRange(curve2.Skip(curve2.Count / 2).Take(curve2.Count / 2)); if (ReversedRoadChain) { curve.Reverse(); } var option = new Ets2NavigationSegmentOption(); option.LeftLane = false; option.EntryLane = startLane; option.ExitLane = endLane; option.Points = curve; option.LaneCrossOver = (startLane != endLane); Options.Add(option); } } for (int startLane = 0; startLane < firstRoad.RoadLook.LanesLeft; startLane++) { var curve1 = new List <Ets2Point>(); foreach (var rd in Roads) { var rdc = rd.GenerateRoadCurve(curveSize, true, startLane); if (!curve1.Any()) { if (!Entry.Point.CloseTo(rdc.FirstOrDefault())) { rdc = rdc.Reverse(); } } else { var lp = curve1.LastOrDefault(); if (!rdc.FirstOrDefault().CloseTo(lp)) { rdc = rdc.Reverse(); } } curve1.AddRange(rdc); } for (int endLane = 0; endLane < firstRoad.RoadLook.LanesLeft; endLane++) { var curve2 = new List <Ets2Point>(); foreach (var rd in Roads) { var rdc = rd.GenerateRoadCurve(curveSize, true, endLane); if (!curve2.Any()) { if (!Entry.Point.CloseTo(rdc.FirstOrDefault())) { rdc = rdc.Reverse(); } } else { var lp = curve2.LastOrDefault(); if (!rdc.FirstOrDefault().CloseTo(lp)) { rdc = rdc.Reverse(); } } curve2.AddRange(rdc); } var curve = new List <Ets2Point>(); curve.AddRange(curve1.Skip(0).Take(curve2.Count / 2)); curve.AddRange(curve2.Skip(curve2.Count / 2).Take(curve2.Count / 2)); if (!ReversedRoadChain) { curve.Reverse(); } var option = new Ets2NavigationSegmentOption(); option.LeftLane = true; option.EntryLane = startLane; option.ExitLane = endLane; option.Points = curve; option.LaneCrossOver = (startLane != endLane); Options.Add(option); } } } }
private void FindRoute(object state) { Loading = true; Dictionary<ulong, Tuple<float, Ets2Item>> nodeMap = new Dictionary<ulong, Tuple<float, Ets2Item>>(); List<Ets2Item> nodesToWalk = new List<Ets2Item>(); // Fill node map foreach (var node in Mapper.Items.Values.Where(x => x.Type == Ets2ItemType.Prefab && x.HideUI == false)) { nodesToWalk.Add(node); nodeMap.Add(node.ItemUID, new Tuple<float, Ets2Item>(float.MaxValue, null)); } // Walk first node (START) if (nodeMap.ContainsKey(Start.ItemUID) == false) { // Nope return; } if (nodeMap.ContainsKey(End.ItemUID) == false) { // Nope return; } // <Weight, LastItem> nodeMap[Start.ItemUID] = new Tuple<float, Ets2Item>(0, null); while (nodesToWalk.Any()) { float distanceWalked = float.MaxValue; Ets2Item toWalk = null; foreach (var node in nodesToWalk) { var dTmp = nodeMap[node.ItemUID].Item1; if (dTmp != float.MaxValue && distanceWalked > dTmp) { distanceWalked = dTmp; toWalk = node; } } if (toWalk == null) break; nodesToWalk.Remove(toWalk); var currentWeight = nodeMap[toWalk.ItemUID].Item1; // Iterate all destination nodes from this node foreach (var jump in toWalk.Navigation) { var newWeight = jump.Value.Item1 + currentWeight; var newNode = jump.Key; if (nodeMap.ContainsKey(newNode.ItemUID) && nodeMap[newNode.ItemUID].Item1 > newWeight) nodeMap[newNode.ItemUID] = new Tuple<float, Ets2Item>(newWeight, toWalk); // add route with weight + previous node } } List<Ets2Item> routeNodes = new List<Ets2Item>(); List<Ets2Item> routeRoads = new List<Ets2Item>(); List<Ets2NavigationSegment> segments = new List<Ets2NavigationSegment>(); var goingViaNode = (ulong) 0; var route = End; while (route != null) { // we add this prefab to the route list routeNodes.Add(route); var prefabSeg = new Ets2NavigationSegment(route); segments.Add(prefabSeg); // find the next prefab in the route description var gotoNew = nodeMap[route.ItemUID].Item2; if (gotoNew == null) break; // get a path from the current prefab to the new one var path = route.Navigation[gotoNew]; var roadPath = path.Item3; // add the path to road list routeRoads.AddRange(roadPath); segments.Add(new Ets2NavigationSegment(roadPath, prefabSeg)); // Set the new prefab as route route = gotoNew; } routeNodes.Add(Start); segments.Reverse(); // Find entry/exit of start/end segment var foundDst = float.MaxValue; var foundNode = default(Ets2Node); // Find the closest road to startpoint foreach (var node in segments[0].Prefab.NodesList) { var dst = node.Value.Point.DistanceTo(From); if (foundDst > dst) { foundDst = dst; foundNode = node.Value; } } // We found the node; find the road that exists at this point segments[0].Entry = foundNode; foundDst = float.MaxValue; foundNode = default(Ets2Node); // Find the closest road to startpoint foreach (var node in segments[segments.Count - 1].Prefab.NodesList) { var dst = node.Value.Point.DistanceTo(To); if (foundDst > dst) { foundDst = dst; foundNode = node.Value; } } // We found the node; find the road that exists at this point segments[segments.Count - 1].Exit = foundNode; // Iterate all segments for (int seg = 0; seg < segments.Count; seg++) { // Generate prefab routes if (segments[seg].Type == Ets2NavigationSegmentType.Prefab) { var prevRoad = seg > 0 ? segments[seg - 1] : null; var nextRoad = seg + 1 < segments.Count ? segments[seg + 1] : null; // Link segments together if (prevRoad != null) segments[seg].Entry = prevRoad.Entry; if (nextRoad != null) segments[seg].Exit = nextRoad.Exit; segments[seg].GenerateOptions(prevRoad,nextRoad); } // Generate lane data if (segments[seg].Type == Ets2NavigationSegmentType.Road) { var prefFab = seg > 0 ? segments[seg - 1] : null; var nextFab = seg + 1 < segments.Count ? segments[seg + 1] : null; segments[seg].GenerateOptions(prefFab, nextFab); } } //for (int seg = 1; seg < segments.Count - 1; seg++) for (int seg = 0; seg < segments.Count; seg++) { // Validate routes if (segments[seg].Type == Ets2NavigationSegmentType.Prefab) { var prevRoad = seg > 0 ? segments[seg - 1] : null; var nextRoad = seg + 1 < segments.Count ? segments[seg + 1] : null; foreach (var opt in segments[seg].Options) { if (prevRoad == null) { // start point; validate if it is close to our start node if (opt.Points.FirstOrDefault().DistanceTo(segments[seg].Entry.Point) < 10) { opt.EntryLane = 0; // yep; sure } } else { var entryPoint = opt.Points.FirstOrDefault(); foreach (var roadOpt in prevRoad.Options) { if (roadOpt.Points.Any(x => x.CloseTo(entryPoint))) { // We've got a match ! :D opt.EntryLane = roadOpt.ExitLane; roadOpt.Valid = roadOpt.EntryLane >= 0 && roadOpt.ExitLane >= 0; } } } if (nextRoad == null) { // last point. if (opt.Points.FirstOrDefault().DistanceTo(segments[seg].Exit.Point) < 10) { opt.ExitLane = 0; // yep; sure } } else { var exitPoint = opt.Points.LastOrDefault(); foreach (var roadOpt in nextRoad.Options) { if (roadOpt.Points.Any(x => x.CloseTo(exitPoint))) { // We've got a match ! :D opt.ExitLane= roadOpt.EntryLane; roadOpt.Valid = roadOpt.EntryLane >= 0 && roadOpt.ExitLane >= 0; } } } opt.Valid = opt.EntryLane >= 0 && opt.ExitLane >= 0; } } // Generate prefab routes if (segments[seg].Type == Ets2NavigationSegmentType.Road && false) { var nextPrefab = segments[seg + 1]; var prevPrefab = segments[seg - 1]; if (nextPrefab.Type != Ets2NavigationSegmentType.Prefab || prevPrefab.Type != Ets2NavigationSegmentType.Prefab) continue; // Deduct road options by matching entry/exits for (int solI = 0; solI < segments[seg].Options.Count; solI++) { // Find if there is any route in prefab that matches our entry lane var okStart = prevPrefab.Options.Any(x => segments[seg].MatchEntry(solI, prevPrefab)); var okEnd = nextPrefab.Options.Any(x => segments[seg].MatchExit(solI, nextPrefab)); // This road has a valid end & Start if (okStart && okEnd) { segments[seg].Options[solI].Valid = true; } } } } // There is probably only 1 valid solution for entry/exit //if (segments[0].Options.Any()) segments[0].Options[0].Valid = true; //if (segments[segments.Count - 1].Options.Any()) segments[segments.Count - 1].Options[0].Valid = true; Segments = segments; var pts = Segments.SelectMany(x => x.Solutions).SelectMany(x => x.Points).ToList(); Roads = routeRoads; Prefabs = routeNodes.Select(x => new Tuple<Ets2Item, int, int>(x, 0, 0)).ToList(); Loading = false; }
private void FindRoute(object state) { Loading = true; Dictionary <ulong, Tuple <float, Ets2Item> > nodeMap = new Dictionary <ulong, Tuple <float, Ets2Item> >(); List <Ets2Item> nodesToWalk = new List <Ets2Item>(); // Fill node map foreach (var node in Mapper.Items.Values.Where(x => x.Type == Ets2ItemType.Prefab && x.HideUI == false)) { nodesToWalk.Add(node); nodeMap.Add(node.ItemUID, new Tuple <float, Ets2Item>(float.MaxValue, null)); } // Walk first node (START) if (nodeMap.ContainsKey(Start.ItemUID) == false) { // Nope return; } if (nodeMap.ContainsKey(End.ItemUID) == false) { // Nope return; } // <Weight, LastItem> nodeMap[Start.ItemUID] = new Tuple <float, Ets2Item>(0, null); while (nodesToWalk.Any()) { float distanceWalked = float.MaxValue; Ets2Item toWalk = null; foreach (var node in nodesToWalk) { var dTmp = nodeMap[node.ItemUID].Item1; if (dTmp != float.MaxValue && distanceWalked > dTmp) { distanceWalked = dTmp; toWalk = node; } } if (toWalk == null) { break; } nodesToWalk.Remove(toWalk); var currentWeight = nodeMap[toWalk.ItemUID].Item1; // Iterate all destination nodes from this node foreach (var jump in toWalk.Navigation) { var newWeight = jump.Value.Item1 + currentWeight; var newNode = jump.Key; if (nodeMap.ContainsKey(newNode.ItemUID) && nodeMap[newNode.ItemUID].Item1 > newWeight) { nodeMap[newNode.ItemUID] = new Tuple <float, Ets2Item>(newWeight, toWalk); } // add route with weight + previous node } } List <Ets2Item> routeNodes = new List <Ets2Item>(); List <Ets2Item> routeRoads = new List <Ets2Item>(); List <Ets2NavigationSegment> segments = new List <Ets2NavigationSegment>(); var goingViaNode = (ulong)0; var route = End; while (route != null) { // we add this prefab to the route list routeNodes.Add(route); var prefabSeg = new Ets2NavigationSegment(route); segments.Add(prefabSeg); // find the next prefab in the route description var gotoNew = nodeMap[route.ItemUID].Item2; if (gotoNew == null) { break; } // get a path from the current prefab to the new one var path = route.Navigation[gotoNew]; var roadPath = path.Item3; // add the path to road list routeRoads.AddRange(roadPath); segments.Add(new Ets2NavigationSegment(roadPath, prefabSeg)); // Set the new prefab as route route = gotoNew; } routeNodes.Add(Start); segments.Reverse(); // Find entry/exit of start/end segment var foundDst = float.MaxValue; var foundNode = default(Ets2Node); // Find the closest road to startpoint foreach (var node in segments[0].Prefab.NodesList) { var dst = node.Value.Point.DistanceTo(From); if (foundDst > dst) { foundDst = dst; foundNode = node.Value; } } // We found the node; find the road that exists at this point segments[0].Entry = foundNode; foundDst = float.MaxValue; foundNode = default(Ets2Node); // Find the closest road to startpoint foreach (var node in segments[segments.Count - 1].Prefab.NodesList) { var dst = node.Value.Point.DistanceTo(To); if (foundDst > dst) { foundDst = dst; foundNode = node.Value; } } // We found the node; find the road that exists at this point segments[segments.Count - 1].Exit = foundNode; // Iterate all segments for (int seg = 0; seg < segments.Count; seg++) { // Generate prefab routes if (segments[seg].Type == Ets2NavigationSegmentType.Prefab) { var prevRoad = seg > 0 ? segments[seg - 1] : null; var nextRoad = seg + 1 < segments.Count ? segments[seg + 1] : null; // Link segments together if (prevRoad != null) { segments[seg].Entry = prevRoad.Entry; } if (nextRoad != null) { segments[seg].Exit = nextRoad.Exit; } segments[seg].GenerateOptions(prevRoad, nextRoad); } // Generate lane data if (segments[seg].Type == Ets2NavigationSegmentType.Road) { var prefFab = seg > 0 ? segments[seg - 1] : null; var nextFab = seg + 1 < segments.Count ? segments[seg + 1] : null; segments[seg].GenerateOptions(prefFab, nextFab); } } //for (int seg = 1; seg < segments.Count - 1; seg++) for (int seg = 0; seg < segments.Count; seg++) { // Validate routes if (segments[seg].Type == Ets2NavigationSegmentType.Prefab) { var prevRoad = seg > 0 ? segments[seg - 1] : null; var nextRoad = seg + 1 < segments.Count ? segments[seg + 1] : null; foreach (var opt in segments[seg].Options) { if (prevRoad == null) { // start point; validate if it is close to our start node if (opt.Points.FirstOrDefault().DistanceTo(segments[seg].Entry.Point) < 10) { opt.EntryLane = 0; // yep; sure } } else { var entryPoint = opt.Points.FirstOrDefault(); foreach (var roadOpt in prevRoad.Options) { if (roadOpt.Points.Any(x => x.CloseTo(entryPoint))) { // We've got a match ! :D opt.EntryLane = roadOpt.ExitLane; roadOpt.Valid = roadOpt.EntryLane >= 0 && roadOpt.ExitLane >= 0; } } } if (nextRoad == null) { // last point. if (opt.Points.FirstOrDefault().DistanceTo(segments[seg].Exit.Point) < 10) { opt.ExitLane = 0; // yep; sure } } else { var exitPoint = opt.Points.LastOrDefault(); foreach (var roadOpt in nextRoad.Options) { if (roadOpt.Points.Any(x => x.CloseTo(exitPoint))) { // We've got a match ! :D opt.ExitLane = roadOpt.EntryLane; roadOpt.Valid = roadOpt.EntryLane >= 0 && roadOpt.ExitLane >= 0; } } } opt.Valid = opt.EntryLane >= 0 && opt.ExitLane >= 0; } } // Generate prefab routes if (segments[seg].Type == Ets2NavigationSegmentType.Road && false) { var nextPrefab = segments[seg + 1]; var prevPrefab = segments[seg - 1]; if (nextPrefab.Type != Ets2NavigationSegmentType.Prefab || prevPrefab.Type != Ets2NavigationSegmentType.Prefab) { continue; } // Deduct road options by matching entry/exits for (int solI = 0; solI < segments[seg].Options.Count; solI++) { // Find if there is any route in prefab that matches our entry lane var okStart = prevPrefab.Options.Any(x => segments[seg].MatchEntry(solI, prevPrefab)); var okEnd = nextPrefab.Options.Any(x => segments[seg].MatchExit(solI, nextPrefab)); // This road has a valid end & Start if (okStart && okEnd) { segments[seg].Options[solI].Valid = true; } } } } // There is probably only 1 valid solution for entry/exit //if (segments[0].Options.Any()) segments[0].Options[0].Valid = true; //if (segments[segments.Count - 1].Options.Any()) segments[segments.Count - 1].Options[0].Valid = true; Segments = segments; var pts = Segments.SelectMany(x => x.Solutions).SelectMany(x => x.Points).ToList(); Roads = routeRoads; Prefabs = routeNodes.Select(x => new Tuple <Ets2Item, int, int>(x, 0, 0)).ToList(); Loading = false; }