private bool drawSpeedLimitHandles(ushort segmentId, bool viewOnly)
        {
            if (!LoadingExtension.IsPathManagerCompatible)
            {
                return(false);
            }

            if (viewOnly && !Options.speedLimitsOverlay)
            {
                return(false);
            }

            // draw speedlimits over mean middle points of lane beziers
            if (!segmentCenterByDir.ContainsKey(segmentId))
            {
                segmentCenterByDir.Add(segmentId, new Dictionary <NetInfo.Direction, Vector3>());
                TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segmentCenterByDir[segmentId]);
            }

            bool hovered = false;

            foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segmentCenterByDir[segmentId])
            {
                Vector3 signPos   = e.Value;
                var     screenPos = Camera.main.WorldToScreenPoint(signPos);
                screenPos.y = Screen.height - screenPos.y;
                if (screenPos.z < 0)
                {
                    return(false);
                }
                var camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
                var diff   = signPos - camPos;

                if (diff.magnitude > TrafficManagerTool.PriorityCloseLod)
                {
                    return(false);                    // do not draw if too distant
                }
                ItemClass connectionClass = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].Info.GetConnectionClass();
                if (!(connectionClass.m_service == ItemClass.Service.Road ||
                      (connectionClass.m_service == ItemClass.Service.PublicTransport && connectionClass.m_subService == ItemClass.SubService.PublicTransportTrain)))
                {
                    return(false);
                }

                var  zoom          = 1.0f / diff.magnitude * 100f * MainTool.GetBaseZoom();
                var  size          = speedLimitSignSize * zoom;
                var  guiColor      = GUI.color;
                var  boundingBox   = new Rect(screenPos.x - size / 2, screenPos.y - size / 2, size, size);
                bool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);

                if (hoveredHandle)
                {
                    // mouse hovering over sign
                    hovered    = true;
                    guiColor.a = 0.8f;
                }
                else
                {
                    guiColor.a = 0.5f;
                }

                GUI.color = guiColor;

                try {
                    GUI.DrawTexture(boundingBox, TrafficLightToolTextureResources.SpeedLimitTextures[SpeedLimitManager.GetCustomSpeedLimit(segmentId, e.Key)]);
                } catch (Exception ex) {
                    Log.Error("segment " + segmentId + " limit: " + SpeedLimitManager.GetCustomSpeedLimit(segmentId, e.Key) + ", ex: " + ex.ToString());
                }

                if (hoveredHandle && Input.GetMouseButton(0))
                {
                    // change the speed limit to the selected one
                    ushort speedLimitToSet = SpeedLimitManager.AvailableSpeedLimits[curSpeedLimitIndex];
                    //Log._Debug($"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()} to {speedLimitToSet}");
                    SpeedLimitManager.SetSpeedLimit(segmentId, e.Key, speedLimitToSet);

                    // TODO use SegmentTraverser
                    if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                    {
                        NetInfo         selectedSegmentInfo = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].Info;
                        List <object[]> selectedSortedLanes = TrafficManagerTool.GetSortedVehicleLanes(segmentId, selectedSegmentInfo, null);

                        LinkedList <ushort> nodesToProcess    = new LinkedList <ushort>();
                        HashSet <ushort>    processedNodes    = new HashSet <ushort>();
                        HashSet <ushort>    processedSegments = new HashSet <ushort>();
                        processedSegments.Add(SelectedSegmentId);

                        ushort selectedStartNodeId = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_startNode;
                        ushort selectedEndNodeId   = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_endNode;

                        if (selectedStartNodeId != 0)
                        {
                            nodesToProcess.AddFirst(selectedStartNodeId);
                        }
                        if (selectedEndNodeId != 0)
                        {
                            nodesToProcess.AddFirst(selectedEndNodeId);
                        }

                        while (nodesToProcess.First != null)
                        {
                            ushort nodeId = nodesToProcess.First.Value;
                            nodesToProcess.RemoveFirst();
                            processedNodes.Add(nodeId);

                            if (Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].CountSegments() > 2)
                            {
                                continue;                                 // junction. stop.
                            }
                            // explore segments at node
                            for (var s = 0; s < 8; s++)
                            {
                                var otherSegmentId = Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].GetSegment(s);

                                if (otherSegmentId <= 0 || processedSegments.Contains(otherSegmentId))
                                {
                                    continue;
                                }
                                processedSegments.Add(otherSegmentId);

                                NetInfo         segmentInfo = Singleton <NetManager> .instance.m_segments.m_buffer[otherSegmentId].Info;
                                List <object[]> sortedLanes = TrafficManagerTool.GetSortedVehicleLanes(otherSegmentId, segmentInfo, null);

                                if (sortedLanes.Count == selectedSortedLanes.Count)
                                {
                                    // number of lanes matches selected segment
                                    for (int i = 0; i < sortedLanes.Count; ++i)
                                    {
                                        object[] selectedLaneData = selectedSortedLanes[i];
                                        object[] laneData         = sortedLanes[i];

                                        uint         selectedLaneId    = (uint)selectedLaneData[0];
                                        uint         selectedLaneIndex = (uint)selectedLaneData[2];
                                        NetInfo.Lane selectedLaneInfo  = segmentInfo.m_lanes[selectedLaneIndex];

                                        uint         laneId    = (uint)laneData[0];
                                        uint         laneIndex = (uint)laneData[2];
                                        NetInfo.Lane laneInfo  = segmentInfo.m_lanes[laneIndex];

                                        if (laneInfo.m_finalDirection == e.Key)
                                        {
                                            SpeedLimitManager.SetSpeedLimit(otherSegmentId, laneInfo.m_finalDirection, speedLimitToSet);
                                        }

                                        // apply restrictions of selected segment & lane
                                        //VehicleRestrictionsManager.SetAllowedVehicleTypes(otherSegmentId, segmentInfo, laneIndex, laneInfo, laneId, VehicleRestrictionsManager.GetAllowedVehicleTypes(SelectedSegmentId, selectedSegmentInfo, selectedLaneIndex, selectedLaneInfo));
                                    }

                                    // add nodes to explore
                                    ushort startNodeId = Singleton <NetManager> .instance.m_segments.m_buffer[otherSegmentId].m_startNode;
                                    ushort endNodeId   = Singleton <NetManager> .instance.m_segments.m_buffer[otherSegmentId].m_endNode;

                                    if (startNodeId != 0 && !processedNodes.Contains(startNodeId))
                                    {
                                        nodesToProcess.AddFirst(startNodeId);
                                    }
                                    if (endNodeId != 0 && !processedNodes.Contains(endNodeId))
                                    {
                                        nodesToProcess.AddFirst(endNodeId);
                                    }
                                }
                            }
                        }
                    }
                    //mouseClickProcessed = true;
                }

                guiColor.a = 1f;
                GUI.color  = guiColor;
            }
            return(hovered);
        }
        /// <summary>
        /// Displays lane ids over lanes
        /// </summary>
        private void _guiLanes(ushort segmentId, ref NetSegment segment, ref NetInfo segmentInfo)
        {
            GUIStyle _counterStyle = new GUIStyle();
            Vector3  centerPos     = segment.m_bounds.center;
            var      screenPos     = Camera.main.WorldToScreenPoint(centerPos);

            screenPos.y = Screen.height - screenPos.y - 200;

            if (screenPos.z < 0)
            {
                return;
            }

            var camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
            var diff   = centerPos - camPos;

            if (diff.magnitude > DebugCloseLod)
            {
                return;                 // do not draw if too distant
            }
            var zoom = 1.0f / diff.magnitude * 150f;

            _counterStyle.fontSize         = (int)(11f * zoom);
            _counterStyle.normal.textColor = new Color(1f, 1f, 0f);

            uint totalDensity = 0u;
            uint curLaneId    = segment.m_lanes;

            for (int i = 0; i < segmentInfo.m_lanes.Length; ++i)
            {
                if (curLaneId == 0)
                {
                    break;
                }

                if (CustomRoadAI.currentLaneDensities[segmentId] != null && i < CustomRoadAI.currentLaneDensities[segmentId].Length)
                {
                    totalDensity += CustomRoadAI.currentLaneDensities[segmentId][i];
                }

                curLaneId = Singleton <NetManager> .instance.m_lanes.m_buffer[curLaneId].m_nextLane;
            }

            curLaneId = segment.m_lanes;
            String labelStr = "";

            for (int i = 0; i < segmentInfo.m_lanes.Length; ++i)
            {
                if (curLaneId == 0)
                {
                    break;
                }

                NetInfo.Lane laneInfo = segmentInfo.m_lanes[i];

                labelStr += "Lane idx " + i + ", id " + curLaneId;
#if DEBUG
                labelStr += ", flags: " + ((NetLane.Flags)Singleton <NetManager> .instance.m_lanes.m_buffer[curLaneId].m_flags).ToString() + ", limit: " + SpeedLimitManager.GetCustomSpeedLimit(curLaneId) + " km/h, dir: " + laneInfo.m_direction + ", final: " + laneInfo.m_finalDirection + ", pos: " + String.Format("{0:0.##}", laneInfo.m_position) + ", sim. idx: " + laneInfo.m_similarLaneIndex + " for " + laneInfo.m_vehicleType + "/" + laneInfo.m_laneType;
#endif

                /*if (CustomRoadAI.InStartupPhase)
                 *      labelStr += ", in start-up phase";
                 * else*/
                labelStr += ", avg. speed: " + (CustomRoadAI.laneMeanSpeeds[segmentId] != null && i < CustomRoadAI.laneMeanSpeeds[segmentId].Length ? "" + CustomRoadAI.laneMeanSpeeds[segmentId][i] : "?") + " %";
                labelStr += ", rel. density: " + (CustomRoadAI.laneMeanRelDensities[segmentId] != null && i < CustomRoadAI.laneMeanRelDensities[segmentId].Length ? "" + CustomRoadAI.laneMeanRelDensities[segmentId][i] : "?") + " %";
#if ABSDENSITY
                labelStr += ", abs. density: " + (CustomRoadAI.laneMeanAbsDensities[segmentId] != null && i < CustomRoadAI.laneMeanAbsDensities[segmentId].Length ? "" + CustomRoadAI.laneMeanAbsDensities[segmentId][i] : "?") + " %";
#endif
#if DEBUG
                labelStr += " (" + (CustomRoadAI.currentLaneDensities[segmentId] != null && i < CustomRoadAI.currentLaneDensities[segmentId].Length ? "" + CustomRoadAI.currentLaneDensities[segmentId][i] : "?") + "/" + totalDensity + ")";
#endif
                labelStr += "\n";

                curLaneId = Singleton <NetManager> .instance.m_lanes.m_buffer[curLaneId].m_nextLane;
            }

            Vector2 dim       = _counterStyle.CalcSize(new GUIContent(labelStr));
            Rect    labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y, dim.x, dim.y);

            GUI.Label(labelRect, labelStr, _counterStyle);
        }
        private bool drawSpeedLimitHandles(ushort segmentId, bool viewOnly)
        {
            if (!LoadingExtension.IsPathManagerCompatible)
            {
                return(false);
            }

            if (viewOnly && !Options.speedLimitsOverlay)
            {
                return(false);
            }

            // draw speedlimits over mean middle points of lane beziers
            if (!segmentCenterByDir.ContainsKey(segmentId))
            {
                segmentCenterByDir.Add(segmentId, new Dictionary <NetInfo.Direction, Vector3>());
                TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segmentCenterByDir[segmentId]);
            }

            bool hovered = false;

            foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segmentCenterByDir[segmentId])
            {
                Vector3 signPos   = e.Value;
                var     screenPos = Camera.main.WorldToScreenPoint(signPos);
                screenPos.y = Screen.height - screenPos.y;
                if (screenPos.z < 0)
                {
                    return(false);
                }
                var camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
                var diff   = signPos - camPos;

                if (diff.magnitude > TrafficManagerTool.PriorityCloseLod)
                {
                    return(false);                    // do not draw if too distant
                }
                ItemClass connectionClass = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].Info.GetConnectionClass();
                if (!(connectionClass.m_service == ItemClass.Service.Road ||
                      (connectionClass.m_service == ItemClass.Service.PublicTransport && connectionClass.m_subService == ItemClass.SubService.PublicTransportTrain)))
                {
                    return(false);
                }

                var  zoom          = 1.0f / diff.magnitude * 100f * MainTool.GetBaseZoom();
                var  size          = speedLimitSignSize * zoom;
                var  guiColor      = GUI.color;
                var  boundingBox   = new Rect(screenPos.x - size / 2, screenPos.y - size / 2, size, size);
                bool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);

                if (hoveredHandle)
                {
                    // mouse hovering over sign
                    hovered    = true;
                    guiColor.a = 0.8f;
                }
                else
                {
                    guiColor.a = 0.5f;
                }

                GUI.color = guiColor;

                try {
                    GUI.DrawTexture(boundingBox, TrafficLightToolTextureResources.SpeedLimitTextures[SpeedLimitManager.GetCustomSpeedLimit(segmentId, e.Key)]);
                } catch (Exception ex) {
                    Log.Error("segment " + segmentId + " limit: " + SpeedLimitManager.GetCustomSpeedLimit(segmentId, e.Key) + ", ex: " + ex.ToString());
                }

                if (hoveredHandle && Input.GetMouseButton(0))
                {
                    // change the speed limit to the selected one
                    ushort speedLimitToSet = SpeedLimitManager.AvailableSpeedLimits[curSpeedLimitIndex];
                    //Log._Debug($"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()} to {speedLimitToSet}");
                    SpeedLimitManager.SetSpeedLimit(segmentId, e.Key, speedLimitToSet);
                    //mouseClickProcessed = true;
                }

                guiColor.a = 1f;
                GUI.color  = guiColor;
            }
            return(hovered);
        }