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);
            }
        }
Пример #2
0
        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
            {
            }
        }
Пример #4
0
        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);
                    }
                }
            }
        }
Пример #5
0
        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;
            }
        }
Пример #6
0
        public bool Match(int mySolution, Ets2NavigationSegment prefabSegment)
        {

            throw new NotImplementedException();
        }
Пример #7
0
        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();

            Roads = routeRoads;
            Prefabs = routeNodes.Select(x => new Tuple<Ets2Item, int, int>(x, 0, 0)).ToList();
            Loading = false;

            // TODO: find routes on prefabs
            // The following code is a start to that, but not finished yet.
            // It divides the route into segments, that describe the path(s) on a road or prefab to get from A to B.

            // 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;
        }
        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 bool Match(int mySolution, Ets2NavigationSegment prefabSegment)
 {
     throw new NotImplementedException();
 }
Пример #10
0
        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();

            Roads   = routeRoads;
            Prefabs = routeNodes.Select(x => new Tuple <Ets2Item, int, int>(x, 0, 0)).ToList();
            Loading = false;

            // TODO: find routes on prefabs
            // The following code is a start to that, but not finished yet.
            // It divides the route into segments, that describe the path(s) on a road or prefab to get from A to B.

            // 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;
        }