Example #1
0
        public override void OnToolGUI(Event e)
        {
            //base.OnToolGUI(e);

            _cursorInSecondaryPanel = false;

            if (SelectedNodeId == 0 || SelectedSegmentId == 0)
            {
                return;
            }

            int numDirections;
            int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(SelectedSegmentId, SelectedNodeId, out numDirections);

            if (numLanes <= 0)
            {
                SelectedNodeId    = 0;
                SelectedSegmentId = 0;
                return;
            }

            var style = new GUIStyle {
                normal    = { background = SecondPanelTexture },
                alignment = TextAnchor.MiddleCenter,
                border    =
                {
                    bottom = 2,
                    top    = 2,
                    right  = 2,
                    left   = 2
                }
            };

            Vector3 nodePos   = Singleton <NetManager> .instance.m_nodes.m_buffer[SelectedNodeId].m_position;
            var     screenPos = Camera.main.WorldToScreenPoint(nodePos);

            screenPos.y = Screen.height - screenPos.y;
            //Log._Debug($"node pos of {SelectedNodeId}: {nodePos.ToString()} {screenPos.ToString()}");
            if (screenPos.z < 0)
            {
                return;
            }
            var camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
            var diff   = nodePos - camPos;

            if (diff.magnitude > TrafficManagerTool.PriorityCloseLod)
            {
                return;                 // do not draw if too distant
            }
            int width       = numLanes * 128;
            var windowRect3 = new Rect(screenPos.x - width / 2, screenPos.y - 70, width, 50);

            GUILayout.Window(250, windowRect3, _guiLaneChangeWindow, "", style);
            _cursorInSecondaryPanel = windowRect3.Contains(Event.current.mousePosition);
        }
Example #2
0
        public override void OnToolGUI(Event e)
        {
            //base.OnToolGUI(e);
            _cursorInSecondaryPanel = false;

            if (SelectedNodeId == 0 || SelectedSegmentId == 0)
            {
                return;
            }

            int numDirections;
            int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(SelectedSegmentId, SelectedNodeId, out numDirections, LaneArrowManager.VEHICLE_TYPES);

            if (numLanes <= 0)
            {
                SelectedNodeId    = 0;
                SelectedSegmentId = 0;
                return;
            }

            var style = new GUIStyle {
                normal    = { background = SecondPanelTexture },
                alignment = TextAnchor.MiddleCenter,
                border    =
                {
                    bottom = 2,
                    top    = 2,
                    right  = 2,
                    left   = 2
                }
            };

            Vector3 nodePos = Singleton <NetManager> .instance.m_nodes.m_buffer[SelectedNodeId].m_position;

            Vector3 screenPos;
            bool    visible = MainTool.WorldToScreenPoint(nodePos, out screenPos);

            if (!visible)
            {
                return;
            }

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

            if (diff.magnitude > TrafficManagerTool.MaxOverlayDistance)
            {
                return;                 // do not draw if too distant
            }
            int width       = numLanes * 128;
            var windowRect3 = new Rect(screenPos.x - width / 2, screenPos.y - 70, width, 50);

            GUILayout.Window(250, windowRect3, _guiLaneChangeWindow, "", style);
            _cursorInSecondaryPanel = windowRect3.Contains(Event.current.mousePosition);
        }
Example #3
0
        public override void OnToolGUI(Event e)
        {
            // base.OnToolGUI(e);
            cursorInSecondaryPanel_ = false;

            if ((SelectedNodeId == 0) || (SelectedSegmentId == 0))
            {
                return;
            }

            int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(
                SelectedSegmentId,
                SelectedNodeId,
                out int numDirections,
                LaneArrowManager.VEHICLE_TYPES);

            if (numLanes <= 0)
            {
                SelectedNodeId    = 0;
                SelectedSegmentId = 0;
                return;
            }

            Vector3 nodePos = Singleton <NetManager>
                              .instance.m_nodes.m_buffer[SelectedNodeId].m_position;

            bool visible = MainTool.WorldToScreenPoint(nodePos, out Vector3 screenPos);

            if (!visible)
            {
                return;
            }

            Vector3 camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
            Vector3 diff   = nodePos - camPos;

            if (diff.sqrMagnitude > TrafficManagerTool.MAX_OVERLAY_DISTANCE_SQR)
            {
                return; // do not draw if too distant
            }

            int  width     = numLanes * 128;
            int  height    = 50;
            bool startNode = (bool)netService.IsStartNode(SelectedSegmentId, SelectedNodeId);

            if (CanReset(SelectedSegmentId, startNode))
            {
                height += 40;
            }

            var windowRect3 = new Rect(screenPos.x - (width / 2), screenPos.y - 70, width, height);

            GUILayout.Window(250, windowRect3, GuiLaneChangeWindow, string.Empty, BorderlessStyle);
            cursorInSecondaryPanel_ = windowRect3.Contains(Event.current.mousePosition);
        }
        private void DrawSpeedLimitHandles(ushort segmentId,
                                           ref NetSegment segment,
                                           bool viewOnly,
                                           ref Vector3 camPos)
        {
            if (viewOnly && !Options.speedLimitsOverlay)
            {
                return;
            }

            Vector3    center          = segment.m_bounds.center;
            NetManager netManager      = Singleton <NetManager> .instance;
            SpeedValue speedLimitToSet = viewOnly
                                             ? new SpeedValue(-1f)
                                             : currentPaletteSpeedLimit;
            bool showPerLane = showLimitsPerLane;

            if (!viewOnly)
            {
                showPerLane = showLimitsPerLane ^
                              (Input.GetKey(KeyCode.LeftControl) ||
                               Input.GetKey(KeyCode.RightControl));
            }

            // US signs are rectangular, all other are round
            float speedLimitSignVerticalScale = GetVerticalTextureScale();

            if (showPerLane)
            {
                // show individual speed limit handle per lane
                int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(
                    segmentId,
                    null,
                    out int numDirections,
                    SpeedLimitManager.VEHICLE_TYPES);

                NetInfo segmentInfo = segment.Info;
                Vector3 yu          = (segment.m_endDirection - segment.m_startDirection).normalized;
                Vector3 xu          = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;

                // if ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) {
                //        xu = -xu; }
                float   f    = viewOnly ? 4f : 7f; // reserved sign size in game coordinates
                Vector3 zero = center - (0.5f * (((numLanes - 1) + numDirections) - 1) * f * xu);

                uint            x           = 0;
                IList <LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(
                    segmentId,
                    ref segment,
                    null,
                    SpeedLimitManager.LANE_TYPES,
                    SpeedLimitManager.VEHICLE_TYPES);
                bool onlyMonorailLanes = sortedLanes.Count > 0;

                if (!viewOnly)
                {
                    foreach (LanePos laneData in sortedLanes)
                    {
                        byte         laneIndex = laneData.laneIndex;
                        NetInfo.Lane laneInfo  = segmentInfo.m_lanes[laneIndex];

                        if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) ==
                            VehicleInfo.VehicleType.None)
                        {
                            onlyMonorailLanes = false;
                            break;
                        }
                    }
                }

                var directions      = new HashSet <NetInfo.Direction>();
                int sortedLaneIndex = -1;

                foreach (LanePos laneData in sortedLanes)
                {
                    ++sortedLaneIndex;
                    uint laneId    = laneData.laneId;
                    byte laneIndex = laneData.laneIndex;

                    NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];
                    if (!directions.Contains(laneInfo.m_finalDirection))
                    {
                        if (directions.Count > 0)
                        {
                            ++x; // space between different directions
                        }

                        directions.Add(laneInfo.m_finalDirection);
                    }

                    SpeedValue laneSpeedLimit = new SpeedValue(
                        SpeedLimitManager.Instance.GetCustomSpeedLimit(laneId));

                    bool hoveredHandle = MainTool.DrawGenericOverlayGridTexture(
                        SpeedLimitTextures.GetSpeedLimitTexture(laneSpeedLimit),
                        camPos,
                        zero,
                        f,
                        f,
                        xu,
                        yu,
                        x,
                        0,
                        speedLimitSignSize,
                        speedLimitSignSize * speedLimitSignVerticalScale,
                        !viewOnly);

                    if (!viewOnly &&
                        !onlyMonorailLanes &&
                        ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) !=
                         VehicleInfo.VehicleType.None))
                    {
                        Texture2D tex1 = RoadUITextures.VehicleInfoSignTextures[
                            LegacyExtVehicleType.ToNew(ExtVehicleType.PassengerTrain)];
                        MainTool.DrawStaticSquareOverlayGridTexture(
                            tex1,
                            camPos,
                            zero,
                            f,
                            xu,
                            yu,
                            x,
                            1,
                            speedLimitSignSize);
                    }

                    if (hoveredHandle)
                    {
                    }

                    if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel())
                    {
                        SpeedLimitManager.Instance.SetSpeedLimit(
                            segmentId,
                            laneIndex,
                            laneInfo,
                            laneId,
                            speedLimitToSet.GameUnits);

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            int slIndexCopy = sortedLaneIndex;

                            SegmentLaneTraverser.Traverse(
                                segmentId,
                                SegmentTraverser.TraverseDirection.AnyDirection,
                                SegmentTraverser.TraverseSide.AnySide,
                                SegmentLaneTraverser.LaneStopCriterion.LaneCount,
                                SegmentTraverser.SegmentStopCriterion.Junction,
                                SpeedLimitManager.LANE_TYPES,
                                SpeedLimitManager.VEHICLE_TYPES,
                                data => {
                                if (data.SegVisitData.Initial)
                                {
                                    return(true);
                                }

                                if (slIndexCopy != data.SortedLaneIndex)
                                {
                                    return(true);
                                }

                                Constants.ServiceFactory.NetService.ProcessSegment(
                                    data.SegVisitData.CurSeg.segmentId,
                                    (ushort curSegmentId, ref NetSegment curSegment) =>
                                {
                                    NetInfo.Lane curLaneInfo = curSegment.Info.m_lanes[
                                        data.CurLanePos.laneIndex];

                                    SpeedLimitManager.Instance.SetSpeedLimit(
                                        curSegmentId,
                                        data.CurLanePos.laneIndex,
                                        curLaneInfo,
                                        data.CurLanePos.laneId,
                                        speedLimitToSet.GameUnits);
                                    return(true);
                                });

                                return(true);
                            });
                        }
                    }

                    ++x;
                }
            }
            else
            {
                // draw speedlimits over mean middle points of lane beziers

                if (!segmentCenterByDir.TryGetValue(
                        segmentId,
                        out Dictionary <NetInfo.Direction, Vector3> segCenter))
                {
                    segCenter = new Dictionary <NetInfo.Direction, Vector3>();
                    segmentCenterByDir.Add(segmentId, segCenter);
                    TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segCenter);
                }

                foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segCenter)
                {
                    bool visible = MainTool.WorldToScreenPoint(e.Value, out Vector3 screenPos);

                    if (!visible)
                    {
                        continue;
                    }

                    float zoom        = (1.0f / (e.Value - camPos).magnitude) * 100f * MainTool.GetBaseZoom();
                    float size        = (viewOnly ? 0.8f : 1f) * speedLimitSignSize * zoom;
                    Color guiColor    = GUI.color;
                    var   boundingBox = new Rect(screenPos.x - (size / 2),
                                                 screenPos.y - (size / 2),
                                                 size,
                                                 size * speedLimitSignVerticalScale);
                    bool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);

                    guiColor.a = TrafficManagerTool.GetHandleAlpha(hoveredHandle);

                    if (hoveredHandle)
                    {
                        // mouse hovering over sign
                    }

                    // Draw something right here, the road sign texture
                    GUI.color = guiColor;
                    SpeedValue displayLimit = new SpeedValue(
                        SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key));
                    Texture2D tex = SpeedLimitTextures.GetSpeedLimitTexture(displayLimit);

                    GUI.DrawTexture(boundingBox, tex);

                    if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel())
                    {
                        // change the speed limit to the selected one
                        // Log._Debug($"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()}
                        //     to {speedLimitToSet}");
                        SpeedLimitManager.Instance.SetSpeedLimit(segmentId,
                                                                 e.Key,
                                                                 currentPaletteSpeedLimit.GameUnits);

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            NetInfo.Direction normDir = e.Key;
                            if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None)
                            {
                                normDir = NetInfo.InvertDirection(normDir);
                            }

                            SegmentLaneTraverser.Traverse(
                                segmentId,
                                SegmentTraverser.TraverseDirection.AnyDirection,
                                SegmentTraverser.TraverseSide.AnySide,
                                SegmentLaneTraverser.LaneStopCriterion.LaneCount,
                                SegmentTraverser.SegmentStopCriterion.Junction,
                                SpeedLimitManager.LANE_TYPES,
                                SpeedLimitManager.VEHICLE_TYPES,
                                data =>
                            {
                                if (data.SegVisitData.Initial)
                                {
                                    return(true);
                                }

                                bool reverse =
                                    data.SegVisitData.ViaStartNode
                                    == data.SegVisitData.ViaInitialStartNode;

                                ushort otherSegmentId    = data.SegVisitData.CurSeg.segmentId;
                                NetInfo otherSegmentInfo =
                                    netManager.m_segments.m_buffer[otherSegmentId].Info;
                                byte laneIndex        = data.CurLanePos.laneIndex;
                                NetInfo.Lane laneInfo = otherSegmentInfo.m_lanes[laneIndex];

                                NetInfo.Direction otherNormDir = laneInfo.m_finalDirection;

                                if (((netManager.m_segments.m_buffer[otherSegmentId].m_flags
                                      & NetSegment.Flags.Invert)
                                     != NetSegment.Flags.None) ^ reverse)
                                {
                                    otherNormDir = NetInfo.InvertDirection(otherNormDir);
                                }

                                if (otherNormDir == normDir)
                                {
                                    SpeedLimitManager.Instance.SetSpeedLimit(
                                        otherSegmentId,
                                        laneInfo.m_finalDirection,
                                        speedLimitToSet.GameUnits);
                                }

                                return(true);
                            });
                        }
                    }

                    guiColor.a = 1f;
                    GUI.color  = guiColor;
                }
            }
        }
        private bool drawVehicleRestrictionHandles(ushort segmentId, bool viewOnly, out bool stateUpdated)
        {
            stateUpdated = false;

            if (viewOnly && !Options.vehicleRestrictionsOverlay && TrafficManagerTool.GetToolMode() != ToolMode.VehicleRestrictions)
            {
                return(false);
            }

            Vector3 center = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_bounds.center;

            var screenPos = Camera.main.WorldToScreenPoint(center);

            screenPos.y = Screen.height - screenPos.y;
            if (screenPos.z < 0)
            {
                return(false);
            }
            var camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
            var diff   = center - camPos;

            if (diff.magnitude > TrafficManagerTool.PriorityCloseLod)
            {
                return(false);                // do not draw if too distant
            }
            int numDirections;
            // TODO refactor vehicle mask
            int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(segmentId, null, out numDirections, VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train);

            // draw vehicle restrictions over each lane
            NetInfo segmentInfo = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].Info;
            Vector3 yu          = (Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_endDirection - Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_startDirection).normalized;

            if ((Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None)
            {
                yu = -yu;
            }
            Vector3   xu = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;
            float     f  = viewOnly ? 4f : 7f;        // reserved sign size in game coordinates
            ItemClass connectionClass = segmentInfo.GetConnectionClass();
            int       maxNumSigns     = 0;

            if (connectionClass.m_service == ItemClass.Service.Road)
            {
                maxNumSigns = roadVehicleTypes.Length;
            }
            else if (connectionClass.m_service == ItemClass.Service.PublicTransport && connectionClass.m_subService == ItemClass.SubService.PublicTransportTrain)
            {
                maxNumSigns = railVehicleTypes.Length;
            }
            //Vector3 zero = center - 0.5f * (float)(numLanes + numDirections - 1) * f * (xu + yu); // "bottom left"
            Vector3 zero = center - 0.5f * (float)(numLanes - 1 + numDirections - 1) * f * xu - 0.5f * (float)maxNumSigns * f * yu;             // "bottom left"

            /*if (!viewOnly)
             *      Log._Debug($"xu: {xu.ToString()} yu: {yu.ToString()} center: {center.ToString()} zero: {zero.ToString()} numLanes: {numLanes} numDirections: {numDirections}");*/

            uint x        = 0;
            var  guiColor = GUI.color;
            // TODO refactor vehicle mask
            List <object[]>             sortedLanes = TrafficManagerTool.GetSortedVehicleLanes(segmentId, segmentInfo, null, VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train);
            bool                        hovered     = false;
            HashSet <NetInfo.Direction> directions  = new HashSet <NetInfo.Direction>();
            int sortedLaneIndex = -1;

            foreach (object[] laneData in sortedLanes)
            {
                ++sortedLaneIndex;
                uint laneId    = (uint)laneData[0];
                uint laneIndex = (uint)laneData[2];

                NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];
                if (!directions.Contains(laneInfo.m_direction))
                {
                    if (directions.Count > 0)
                    {
                        ++x;                         // space between different directions
                    }
                    directions.Add(laneInfo.m_direction);
                }

                ExtVehicleType[] possibleVehicleTypes = null;
                if (VehicleRestrictionsManager.Instance.IsRoadLane(laneInfo))
                {
                    possibleVehicleTypes = roadVehicleTypes;
                }
                else if (VehicleRestrictionsManager.Instance.IsRailLane(laneInfo))
                {
                    possibleVehicleTypes = railVehicleTypes;
                }
                else
                {
                    ++x;
                    continue;
                }

                ExtVehicleType allowedTypes = VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo);

                uint y = 0;
#if DEBUGx
                Vector3 labelCenter = zero + f * (float)x * xu + f * (float)y * yu;                 // in game coordinates

                var labelScreenPos = Camera.main.WorldToScreenPoint(labelCenter);
                labelScreenPos.y = Screen.height - labelScreenPos.y;
                diff             = labelCenter - camPos;

                var labelZoom = 1.0f / diff.magnitude * 100f;
                _counterStyle.fontSize         = (int)(11f * labelZoom);
                _counterStyle.normal.textColor = new Color(1f, 1f, 0f);

                string  labelStr  = $"Idx {laneIndex}";
                Vector2 dim       = _counterStyle.CalcSize(new GUIContent(labelStr));
                Rect    labelRect = new Rect(labelScreenPos.x - dim.x / 2f, labelScreenPos.y, dim.x, dim.y);
                GUI.Label(labelRect, labelStr, _counterStyle);

                ++y;
#endif
                foreach (ExtVehicleType vehicleType in possibleVehicleTypes)
                {
                    bool allowed = VehicleRestrictionsManager.Instance.IsAllowed(allowedTypes, vehicleType);
                    if (allowed && viewOnly)
                    {
                        continue;                         // do not draw allowed vehicles in view-only mode
                    }
                    bool hoveredHandle;
                    DrawRestrictionsSign(viewOnly, camPos, out diff, xu, yu, f, zero, x, y, ref guiColor, TrafficLightToolTextureResources.VehicleRestrictionTextures[vehicleType][allowed], out hoveredHandle);
                    if (hoveredHandle)
                    {
                        hovered = true;
                    }

                    if (hoveredHandle && MainTool.CheckClicked())
                    {
                        // toggle vehicle restrictions
                        //Log._Debug($"Setting vehicle restrictions of segment {segmentId}, lane idx {laneIndex}, {vehicleType.ToString()} to {!allowed}");
                        VehicleRestrictionsManager.Instance.ToggleAllowedType(segmentId, segmentInfo, laneIndex, laneId, laneInfo, vehicleType, !allowed);
                        stateUpdated = true;

                        // TODO use SegmentTraverser
                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            ApplyRestrictionsToAllSegments(sortedLaneIndex);
                        }
                    }

                    ++y;
                }

                ++x;
            }

            guiColor.a = 1f;
            GUI.color  = guiColor;

            return(hovered);
        }
        private bool DrawVehicleRestrictionHandles(ushort segmentId,
                                                   ref NetSegment segment,
                                                   bool viewOnly,
                                                   out bool stateUpdated)
        {
            stateUpdated = false;

            if (viewOnly && !Options.vehicleRestrictionsOverlay &&
                MainTool.GetToolMode() != ToolMode.VehicleRestrictions)
            {
                return(false);
            }

            Vector3 center = segment.m_bounds.center;

            bool visible = MainTool.WorldToScreenPoint(center, out Vector3 _);

            if (!visible)
            {
                return(false);
            }

            Vector3 camPos = Singleton <SimulationManager> .instance.m_simulationView.m_position;
            Vector3 diff   = center - camPos;

            if (diff.sqrMagnitude > TrafficManagerTool.MAX_OVERLAY_DISTANCE_SQR)
            {
                return(false); // do not draw if too distant
            }

            int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(
                segmentId,
                null,
                out int numDirections,
                VehicleRestrictionsManager.VEHICLE_TYPES);

            // draw vehicle restrictions over each lane
            NetInfo segmentInfo = segment.Info;
            Vector3 yu          = (segment.m_endDirection - segment.m_startDirection).normalized;

            // if ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None)
            //        yu = -yu;
            Vector3 xu          = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;
            float   f           = viewOnly ? 4f : 7f; // reserved sign size in game coordinates
            int     maxNumSigns = 0;

            if (VehicleRestrictionsManager.Instance.IsRoadSegment(segmentInfo))
            {
                maxNumSigns = RoadVehicleTypes.Length;
            }
            else if (VehicleRestrictionsManager.Instance.IsRailSegment(segmentInfo))
            {
                maxNumSigns = RailVehicleTypes.Length;
            }

            // Vector3 zero = center - 0.5f * (float)(numLanes + numDirections - 1) * f * (xu + yu); // "bottom left"
            Vector3 zero = center - (0.5f * (numLanes - 1 + numDirections - 1) * f * xu)
                           - (0.5f * maxNumSigns * f * yu);        // "bottom left"

            // if (!viewOnly)
            //     Log._Debug($"xu: {xu.ToString()} yu: {yu.ToString()} center: {center.ToString()}
            //     zero: {zero.ToString()} numLanes: {numLanes} numDirections: {numDirections}");*/

            uint            x           = 0;
            Color           guiColor    = GUI.color;
            IList <LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(
                segmentId,
                ref segment,
                null,
                VehicleRestrictionsManager.LANE_TYPES,
                VehicleRestrictionsManager.VEHICLE_TYPES);
            bool hovered = false;
            HashSet <NetInfo.Direction> directions = new HashSet <NetInfo.Direction>();
            int sortedLaneIndex = -1;

            foreach (LanePos laneData in sortedLanes)
            {
                ++sortedLaneIndex;
                uint laneId    = laneData.laneId;
                byte laneIndex = laneData.laneIndex;

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

                if (!directions.Contains(laneInfo.m_finalDirection))
                {
                    if (directions.Count > 0)
                    {
                        ++x; // space between different directions
                    }

                    directions.Add(laneInfo.m_finalDirection);
                }

                ExtVehicleType[] possibleVehicleTypes;

                if (VehicleRestrictionsManager.Instance.IsRoadLane(laneInfo))
                {
                    possibleVehicleTypes = RoadVehicleTypes;
                }
                else if (VehicleRestrictionsManager.Instance.IsRailLane(laneInfo))
                {
                    possibleVehicleTypes = RailVehicleTypes;
                }
                else
                {
                    ++x;
                    continue;
                }

                ExtVehicleType allowedTypes =
                    VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(
                        segmentId,
                        segmentInfo,
                        laneIndex,
                        laneInfo,
                        VehicleRestrictionsMode.Configured);

                uint y = 0;
#if DEBUG_disabled_xxx
                Vector3 labelCenter = zero + f * (float)x * xu + f * (float)y * yu; // in game coordinates

                Vector3 labelScreenPos;
                bool    visible = MainTool.WorldToScreenPoint(labelCenter, out labelScreenPos);
                labelScreenPos.y = Screen.height - labelScreenPos.y;
                diff             = labelCenter - camPos;

                var labelZoom = 1.0f / diff.magnitude * 100f;
                _counterStyle.fontSize         = (int)(11f * labelZoom);
                _counterStyle.normal.textColor = new Color(1f, 1f, 0f);

                string  labelStr  = $"Idx {laneIndex}";
                Vector2 dim       = _counterStyle.CalcSize(new GUIContent(labelStr));
                Rect    labelRect = new Rect(labelScreenPos.x - dim.x / 2f, labelScreenPos.y, dim.x, dim.y);
                GUI.Label(labelRect, labelStr, _counterStyle);

                ++y;
#endif
                foreach (ExtVehicleType vehicleType in possibleVehicleTypes)
                {
                    bool allowed = VehicleRestrictionsManager.Instance.IsAllowed(allowedTypes, vehicleType);

                    if (allowed && viewOnly)
                    {
                        continue; // do not draw allowed vehicles in view-only mode
                    }

                    bool hoveredHandle = MainTool.DrawGenericSquareOverlayGridTexture(
                        RoadUITextures.VehicleRestrictionTextures[vehicleType][allowed],
                        camPos,
                        zero,
                        f,
                        xu,
                        yu,
                        x,
                        y,
                        vehicleRestrictionsSignSize,
                        !viewOnly);

                    if (hoveredHandle)
                    {
                        hovered = true;
                    }

                    if (hoveredHandle && MainTool.CheckClicked())
                    {
                        // toggle vehicle restrictions
                        // Log._Debug($"Setting vehicle restrictions of segment {segmentId}, lane
                        //     idx {laneIndex}, {vehicleType.ToString()} to {!allowed}");
                        VehicleRestrictionsManager.Instance.ToggleAllowedType(
                            segmentId,
                            segmentInfo,
                            laneIndex,
                            laneId,
                            laneInfo,
                            vehicleType,
                            !allowed);
                        stateUpdated = true;

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            ApplyRestrictionsToAllSegments(sortedLaneIndex);
                        }
                    }

                    ++y;
                }

                ++x;
            }

            guiColor.a = 1f;
            GUI.color  = guiColor;

            return(hovered);
        }
        private bool drawSpeedLimitHandles(ushort segmentId, ref NetSegment segment, bool viewOnly, ref Vector3 camPos)
        {
            if (viewOnly && !Options.speedLimitsOverlay)
            {
                return(false);
            }

            Vector3    center     = segment.m_bounds.center;
            NetManager netManager = Singleton <NetManager> .instance;

            bool   hovered         = false;
            ushort speedLimitToSet = viewOnly ? (ushort)0 : SpeedLimitManager.Instance.AvailableSpeedLimits[curSpeedLimitIndex];

            bool showPerLane = showLimitsPerLane;

            if (!viewOnly)
            {
                showPerLane = showLimitsPerLane ^ (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl));
            }
            if (showPerLane)
            {
                // show individual speed limit handle per lane
                int numDirections;
                int numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(segmentId, null, out numDirections, SpeedLimitManager.VEHICLE_TYPES);

                NetInfo segmentInfo = segment.Info;
                Vector3 yu          = (segment.m_endDirection - segment.m_startDirection).normalized;
                Vector3 xu          = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;

                /*if ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) {
                 *      xu = -xu;
                 * }*/
                float   f    = viewOnly ? 4f : 7f;            // reserved sign size in game coordinates
                Vector3 zero = center - 0.5f * (float)(numLanes - 1 + numDirections - 1) * f * xu;

                uint            x                 = 0;
                var             guiColor          = GUI.color;
                IList <LanePos> sortedLanes       = Constants.ServiceFactory.NetService.GetSortedLanes(segmentId, ref segment, null, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES);
                bool            onlyMonorailLanes = sortedLanes.Count > 0;
                if (!viewOnly)
                {
                    foreach (LanePos laneData in sortedLanes)
                    {
                        byte         laneIndex = laneData.laneIndex;
                        NetInfo.Lane laneInfo  = segmentInfo.m_lanes[laneIndex];

                        if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None)
                        {
                            onlyMonorailLanes = false;
                            break;
                        }
                    }
                }

                HashSet <NetInfo.Direction> directions = new HashSet <NetInfo.Direction>();
                int sortedLaneIndex = -1;
                foreach (LanePos laneData in sortedLanes)
                {
                    ++sortedLaneIndex;
                    uint laneId    = laneData.laneId;
                    byte laneIndex = laneData.laneIndex;

                    NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];
                    if (!directions.Contains(laneInfo.m_finalDirection))
                    {
                        if (directions.Count > 0)
                        {
                            ++x;                             // space between different directions
                        }
                        directions.Add(laneInfo.m_finalDirection);
                    }

                    bool hoveredHandle = MainTool.DrawGenericSquareOverlayGridTexture(TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(laneId)], camPos, zero, f, xu, yu, x, 0, speedLimitSignSize, !viewOnly, 0.5f, 0.8f);
                    if (!viewOnly && !onlyMonorailLanes && (laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) != VehicleInfo.VehicleType.None)
                    {
                        MainTool.DrawStaticSquareOverlayGridTexture(TextureResources.VehicleInfoSignTextures[ExtVehicleType.PassengerTrain], camPos, zero, f, xu, yu, x, 1, speedLimitSignSize, 0.5f);
                    }
                    if (hoveredHandle)
                    {
                        hovered = true;
                    }

                    if (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel())
                    {
                        SpeedLimitManager.Instance.SetSpeedLimit(segmentId, laneIndex, laneInfo, laneId, speedLimitToSet);

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            SegmentLaneTraverser.Traverse(segmentId, SegmentTraverser.TraverseDirection.AnyDirection, SegmentTraverser.TraverseSide.AnySide, SegmentLaneTraverser.LaneStopCriterion.LaneCount, SegmentTraverser.SegmentStopCriterion.Junction, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES, delegate(SegmentLaneVisitData data) {
                                if (data.segVisitData.initial)
                                {
                                    return(true);
                                }

                                if (sortedLaneIndex != data.sortedLaneIndex)
                                {
                                    return(true);
                                }

                                Constants.ServiceFactory.NetService.ProcessSegment(data.segVisitData.curGeo.SegmentId, delegate(ushort curSegmentId, ref NetSegment curSegment) {
                                    NetInfo.Lane curLaneInfo = curSegment.Info.m_lanes[data.curLanePos.laneIndex];
                                    SpeedLimitManager.Instance.SetSpeedLimit(curSegmentId, data.curLanePos.laneIndex, curLaneInfo, data.curLanePos.laneId, speedLimitToSet);
                                    return(true);
                                });

                                return(true);
                            });
                        }
                    }

                    ++x;
                }
            }
            else
            {
                // draw speedlimits over mean middle points of lane beziers
                Dictionary <NetInfo.Direction, Vector3> segCenter;
                if (!segmentCenterByDir.TryGetValue(segmentId, out segCenter))
                {
                    segCenter = new Dictionary <NetInfo.Direction, Vector3>();
                    segmentCenterByDir.Add(segmentId, segCenter);
                    TrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segCenter);
                }

                foreach (KeyValuePair <NetInfo.Direction, Vector3> e in segCenter)
                {
                    Vector3 screenPos;
                    bool    visible = MainTool.WorldToScreenPoint(e.Value, out screenPos);

                    if (!visible)
                    {
                        continue;
                    }

                    float zoom          = 1.0f / (e.Value - camPos).magnitude * 100f * MainTool.GetBaseZoom();
                    float size          = (viewOnly ? 0.8f : 1f) * speedLimitSignSize * zoom;
                    Color guiColor      = GUI.color;
                    Rect  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;
                    GUI.DrawTexture(boundingBox, TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key)]);

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

                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            NetInfo.Direction normDir = e.Key;
                            if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None)
                            {
                                normDir = NetInfo.InvertDirection(normDir);
                            }

                            SegmentLaneTraverser.Traverse(segmentId, SegmentTraverser.TraverseDirection.AnyDirection, SegmentTraverser.TraverseSide.AnySide, SegmentLaneTraverser.LaneStopCriterion.LaneCount, SegmentTraverser.SegmentStopCriterion.Junction, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES, delegate(SegmentLaneVisitData data) {
                                if (data.segVisitData.initial)
                                {
                                    return(true);
                                }
                                bool reverse = data.segVisitData.viaStartNode == data.segVisitData.viaInitialStartNode;

                                ushort otherSegmentId    = data.segVisitData.curGeo.SegmentId;
                                NetInfo otherSegmentInfo = netManager.m_segments.m_buffer[otherSegmentId].Info;
                                uint laneId           = data.curLanePos.laneId;
                                byte laneIndex        = data.curLanePos.laneIndex;
                                NetInfo.Lane laneInfo = otherSegmentInfo.m_lanes[laneIndex];

                                NetInfo.Direction otherNormDir = laneInfo.m_finalDirection;
                                if ((netManager.m_segments.m_buffer[otherSegmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None ^
                                    reverse)
                                {
                                    otherNormDir = NetInfo.InvertDirection(otherNormDir);
                                }

                                if (otherNormDir == normDir)
                                {
                                    SpeedLimitManager.Instance.SetSpeedLimit(otherSegmentId, laneInfo.m_finalDirection, speedLimitToSet);
                                }

                                return(true);
                            });
                        }
                    }

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