Ejemplo n.º 1
0
        /// <summary>
        /// Performs a Depth-First traversal over the cached segment geometry structure. At each
        /// traversed segment, the given `visitor` is notified. It then can update the current `state`.
        /// </summary>
        /// <param name="initialSegmentGeometry">Specifies the segment at which the traversal
        ///     should start.</param>
        /// <param name="nextNodeIsStartNode">Specifies if the next node to traverse is the start
        ///     node of the initial segment.</param>
        /// <param name="direction">Specifies if traffic should be able to flow towards the initial
        ///     segment (Incoming) or should be able to flow from the initial segment (Outgoing) or
        ///     in both directions (Both).</param>
        /// <param name="maximumDepth">Specifies the maximum depth to explore. At a depth of 0, no
        ///     segment will be traversed (event the initial segment will be omitted).</param>
        /// <param name="visitorFun">Specifies the stateful visitor that should be notified as soon as
        ///     a traversable segment (which has not been traversed before) is found.</param>
        public static IEnumerable <ushort> Traverse(ushort initialSegmentId,
                                                    TraverseDirection direction,
                                                    TraverseSide side,
                                                    SegmentStopCriterion stopCrit,
                                                    SegmentVisitor visitorFun)
        {
            ExtSegment initialSeg = Constants.ManagerFactory.ExtSegmentManager.ExtSegments[initialSegmentId];

            if (!initialSeg.valid)
            {
                return(null);
            }

            // Log._Debug($"SegmentTraverser: Traversing initial segment {initialSegmentId}");
            if (!visitorFun(
                    new SegmentVisitData(
                        ref initialSeg,
                        ref initialSeg,
                        false,
                        false,
                        true)))
            {
                return(null);
            }

            HashSet <ushort> visitedSegmentIds = new HashSet <ushort>();

            visitedSegmentIds.Add(initialSegmentId);

            IExtSegmentEndManager extSegEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            ref NetSegment initialSegment = ref initialSegmentId.ToSegment();
Ejemplo n.º 2
0
        /// <summary>
        /// Performs a Depth-First traversal over the cached segment geometry structure. At each
        /// traversed segment, the given `visitor` is notified. It then can update the current `state`.
        /// </summary>
        /// <param name="initialSegmentGeometry">Specifies the segment at which the traversal
        ///     should start.</param>
        /// <param name="nextNodeIsStartNode">Specifies if the next node to traverse is the start
        ///     node of the initial segment.</param>
        /// <param name="direction">Specifies if traffic should be able to flow towards the initial
        ///     segment (Incoming) or should be able to flow from the initial segment (Outgoing) or
        ///     in both directions (Both).</param>
        /// <param name="maximumDepth">Specifies the maximum depth to explore. At a depth of 0, no
        ///     segment will be traversed (event the initial segment will be omitted).</param>
        /// <param name="visitorFun">Specifies the stateful visitor that should be notified as soon as
        ///     a traversable segment (which has not been traversed before) is found.</param>
        public static IEnumerable <ushort> Traverse(ushort initialSegmentId,
                                                    TraverseDirection direction,
                                                    TraverseSide side,
                                                    SegmentStopCriterion stopCrit,
                                                    SegmentVisitor visitorFun)
        {
            ExtSegment initialSeg = Constants.ManagerFactory.ExtSegmentManager.ExtSegments[initialSegmentId];

            if (!initialSeg.valid)
            {
                return(null);
            }

            // Log._Debug($"SegmentTraverser: Traversing initial segment {initialSegmentId}");
            if (!visitorFun(
                    new SegmentVisitData(
                        ref initialSeg,
                        ref initialSeg,
                        false,
                        false,
                        true)))
            {
                return(null);
            }

            HashSet <ushort> visitedSegmentIds = new HashSet <ushort>();

            visitedSegmentIds.Add(initialSegmentId);

            IExtSegmentEndManager extSegEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            ushort startNodeId = Constants.ServiceFactory.NetService.GetSegmentNodeId(initialSegmentId, true);

            TraverseRec(
                ref initialSeg,
                ref extSegEndMan.ExtSegmentEnds[extSegEndMan.GetIndex(initialSegmentId, true)],
                ref startNodeId.ToNode(),
                true,
                direction,
                side,
                stopCrit,
                visitorFun,
                visitedSegmentIds);

            ushort endNodeId = Constants.ServiceFactory.NetService.GetSegmentNodeId(initialSegmentId, false);

            TraverseRec(
                ref initialSeg,
                ref extSegEndMan.ExtSegmentEnds[extSegEndMan.GetIndex(initialSegmentId, false)],
                ref endNodeId.ToNode(),
                false,
                direction,
                side,
                stopCrit,
                visitorFun,
                visitedSegmentIds);

            // Log._Debug($"SegmentTraverser: Traversal finished.");
            return(visitedSegmentIds);
        }
Ejemplo n.º 3
0
        protected void UpdateSegment(ref ExtSegment seg)
        {
#if DEBUG
            if (DebugSwitch.TurnOnRed.Get())
            {
                Log._Debug($"TurnOnRedManager.UpdateSegment({seg.segmentId}) called.");
            }
#endif
            ResetSegment(seg.segmentId);

            IExtSegmentEndManager extSegmentEndManager =
                Constants.ManagerFactory.ExtSegmentEndManager;
            ushort startNodeId = Services.NetService.GetSegmentNodeId(seg.segmentId, true);

            if (startNodeId != 0)
            {
                int index0 = extSegmentEndManager.GetIndex(seg.segmentId, true);
                UpdateSegmentEnd(
                    ref seg,
                    ref extSegmentEndManager.ExtSegmentEnds[index0]);
            }

            ushort endNodeId = Services.NetService.GetSegmentNodeId(seg.segmentId, false);

            if (endNodeId != 0)
            {
                int index1 = extSegmentEndManager.GetIndex(seg.segmentId, false);
                UpdateSegmentEnd(
                    ref seg,
                    ref extSegmentEndManager.ExtSegmentEnds[index1]);
            }
        }
        /// <summary>
        /// Performs a Depth-First traversal over the cached segment geometry structure. At each traversed segment, the given `visitor` is notified. It then can update the current `state`.
        /// </summary>
        /// <param name="initialSegmentGeometry">Specifies the segment at which the traversal should start.</param>
        /// <param name="nextNodeIsStartNode">Specifies if the next node to traverse is the start node of the initial segment.</param>
        /// <param name="direction">Specifies if traffic should be able to flow towards the initial segment (Incoming) or should be able to flow from the initial segment (Outgoing) or in both directions (Both).</param>
        /// <param name="maximumDepth">Specifies the maximum depth to explore. At a depth of 0, no segment will be traversed (event the initial segment will be omitted).</param>
        /// <param name="visitor">Specifies the stateful visitor that should be notified as soon as a traversable segment (which has not been traversed before) is found.</param>
        public static void Traverse(ushort initialSegmentId, TraverseDirection direction, TraverseSide side, SegmentStopCriterion stopCrit, SegmentVisitor visitor)
        {
            ExtSegment initialSeg = Constants.ManagerFactory.ExtSegmentManager.ExtSegments[initialSegmentId];

            if (!initialSeg.valid)
            {
                return;
            }

            //Log._Debug($"SegmentTraverser: Traversing initial segment {initialSegmentId}");
            if (visitor(new SegmentVisitData(ref initialSeg, ref initialSeg, false, false, true)))
            {
                HashSet <ushort> visitedSegmentIds = new HashSet <ushort>();
                visitedSegmentIds.Add(initialSegmentId);

                IExtSegmentEndManager extSegEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

                ushort startNodeId = Constants.ServiceFactory.NetService.GetSegmentNodeId(initialSegmentId, true);
                Constants.ServiceFactory.NetService.ProcessNode(startNodeId, delegate(ushort nId, ref NetNode node) {
                    TraverseRec(ref initialSeg, ref extSegEndMan.ExtSegmentEnds[extSegEndMan.GetIndex(initialSegmentId, true)], ref node, true, direction, side, stopCrit, visitor, visitedSegmentIds);
                    return(true);
                });

                ushort endNodeId = Constants.ServiceFactory.NetService.GetSegmentNodeId(initialSegmentId, false);
                Constants.ServiceFactory.NetService.ProcessNode(endNodeId, delegate(ushort nId, ref NetNode node) {
                    TraverseRec(ref initialSeg, ref extSegEndMan.ExtSegmentEnds[extSegEndMan.GetIndex(initialSegmentId, false)], ref node, false, direction, side, stopCrit, visitor, visitedSegmentIds);
                    return(true);
                });
            }
            //Log._Debug($"SegmentTraverser: Traversal finished.");
        }
Ejemplo n.º 5
0
        public uint GetRegisteredVehicleCount()
        {
            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            return(segEndMan.GetRegisteredVehicleCount(
                       ref segEndMan.ExtSegmentEnds[segEndMan.GetIndex(SegmentId, StartNode)]));
        }
Ejemplo n.º 6
0
        public RoadBaseAI.TrafficLightState GetLightState(ushort toSegmentId)
        {
            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
            ArrowDirection        dir       = segEndMan.GetDirection(ref segEndMan.ExtSegmentEnds[segEndMan.GetIndex(SegmentId, StartNode)], toSegmentId);

            return(GetLightState(dir));
        }
Ejemplo n.º 7
0
        /// <summary>
        /// returns the number of all target lanes from input segment toward the secified direction.
        /// </summary>
        private static int CountTargetLanesTowardDirection(ushort segmentId, ushort nodeId, ArrowDirection dir) {
            int count = 0;
            bool startNode = segmentId.ToSegment().m_startNode == nodeId;
            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
            ExtSegmentEnd segEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)];

            LaneArrowManager.Instance.Services.NetService.IterateNodeSegments(
                nodeId,
                (ushort otherSegmentId, ref NetSegment otherSeg) => {
                    ArrowDirection dir2 = segEndMan.GetDirection(ref segEnd, otherSegmentId);
                    if (dir == dir2) {
                        int forward = 0, backward = 0;
                        otherSeg.CountLanes(
                            otherSegmentId,
                            LaneArrowManager.LANE_TYPES,
                            LaneArrowManager.VEHICLE_TYPES,
                            ref forward,
                            ref backward);
                        bool startNode2 = otherSeg.m_startNode == nodeId;
                        //xor because inverting 2 times is redundant.
                        if (startNode2) {
                            count += forward;
                        } else {
                            count += backward;
                        }
                        //Log._Debug(
                        //    $"dir={dir} startNode={startNode} segmentId={segmentId}\n" +
                        //    $"startNode2={startNode2} forward={forward} backward={backward} count={count}");
                    }
                    return true;
                });
            return count;
        }
Ejemplo n.º 8
0
        /// <summary>
        /// returns the number of all target lanes from input segment toward the secified direction.
        /// </summary>
        private static int CountTargetLanesTowardDirection(ushort segmentId, ushort nodeId, ArrowDirection dir)
        {
            int  count     = 0;
            bool startNode = segmentId.ToSegment().m_startNode == nodeId;
            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
            ExtSegmentEnd         segEnd    = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)];

            ref NetNode node = ref nodeId.ToNode();
Ejemplo n.º 9
0
        private void ToggleMode(ref ExtSegmentEnd segEnd, ref NetNode node)
        {
            IExtSegmentManager    extSegMan    = Constants.ManagerFactory.ExtSegmentManager;
            IExtSegmentEndManager extSegEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
            bool startNode = lights.StartNode;

            bool hasLeftSegment;
            bool hasForwardSegment;
            bool hasRightSegment;

            extSegEndMan.CalculateOutgoingLeftStraightRightSegments(ref segEnd, ref node, out hasLeftSegment, out hasForwardSegment, out hasRightSegment);

#if DEBUG
            Log._Debug($"ChangeMode. segment {SegmentId} @ node {NodeId}, hasLeftSegment={hasLeftSegment}, hasForwardSegment={hasForwardSegment}, hasRightSegment={hasRightSegment}");
#endif

            LightMode newMode = LightMode.Simple;
            if (CurrentMode == LightMode.Simple)
            {
                if (!hasLeftSegment)
                {
                    newMode = LightMode.SingleRight;
                }
                else
                {
                    newMode = LightMode.SingleLeft;
                }
            }
            else if (CurrentMode == LightMode.SingleLeft)
            {
                if (!hasForwardSegment || !hasRightSegment)
                {
                    newMode = LightMode.Simple;
                }
                else
                {
                    newMode = LightMode.SingleRight;
                }
            }
            else if (CurrentMode == LightMode.SingleRight)
            {
                if (!hasLeftSegment)
                {
                    newMode = LightMode.Simple;
                }
                else
                {
                    newMode = LightMode.All;
                }
            }
            else
            {
                newMode = LightMode.Simple;
            }

            CurrentMode = newMode;
        }
Ejemplo n.º 10
0
        private void RebuildVehicleNumDicts(ref NetNode node)
        {
            numVehiclesMovingToSegmentId = new TinyDictionary <ushort, uint> [numLanes];
            numVehiclesGoingToSegmentId  = new TinyDictionary <ushort, uint> [numLanes];

            Constants.ServiceFactory.NetService.IterateSegmentLanes(
                SegmentId,
                (uint laneId,
                 ref NetLane lane,
                 NetInfo.Lane laneInfo,
                 ushort segmentId,
                 ref NetSegment segment,
                 byte laneIndex) =>
            {
                IDictionary <ushort, uint> numVehicleMoving = new TinyDictionary <ushort, uint>();
                IDictionary <ushort, uint> numVehicleGoing  = new TinyDictionary <ushort, uint>();

                numVehiclesMovingToSegmentId[laneIndex] = numVehicleMoving;
                numVehiclesGoingToSegmentId[laneIndex]  = numVehicleGoing;

                return(true);
            });

            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            for (int i = 0; i < 8; ++i)
            {
                ushort segId = node.GetSegment(i);
                if (segId == 0)
                {
                    continue;
                }

                int index0 = segEndMan.GetIndex(
                    segId,
                    (bool)Constants.ServiceFactory.NetService.IsStartNode(segId, NodeId));

                if (!segEndMan.ExtSegmentEnds[index0].outgoing)
                {
                    continue;
                }

                foreach (TinyDictionary <ushort, uint> numVehiclesMovingToSegId in numVehiclesMovingToSegmentId)
                {
                    numVehiclesMovingToSegId[segId] = 0;
                }

                foreach (TinyDictionary <ushort, uint> numVehiclesGoingToSegId in numVehiclesGoingToSegmentId)
                {
                    numVehiclesGoingToSegId[segId] = 0;
                }
            }
        } // end RebuildVehicleNumDicts
Ejemplo n.º 11
0
        private void Recalculate(ref ExtSegment extSegment)
        {
            IExtSegmentEndManager extSegEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
            ushort segmentId = extSegment.segmentId;

#if DEBUG
            bool logGeometry = DebugSwitch.GeometryDebug.Get();
#else
            const bool logGeometry = false;
#endif
            if (logGeometry)
            {
                Log._Debug($">>> ExtSegmentManager.Recalculate({segmentId}) called.");
            }

            if (!IsValid(segmentId))
            {
                if (extSegment.valid)
                {
                    Reset(ref extSegment);
                    extSegment.valid = false;

                    extSegEndMan.Recalculate(segmentId);
                    Constants.ManagerFactory.GeometryManager.OnUpdateSegment(ref extSegment);
                }

                return;
            }

            if (logGeometry)
            {
                Log.Info($"Recalculating geometries of segment {segmentId} STARTED");
            }

            Reset(ref extSegment);
            extSegment.valid = true;

            extSegment.oneWay  = CalculateIsOneWay(segmentId);
            extSegment.highway = CalculateIsHighway(segmentId);
            extSegment.buslane = CalculateHasBusLane(segmentId);

            extSegEndMan.Recalculate(segmentId);

            if (logGeometry)
            {
                NetSegment[] segmentsBuffer = Singleton <NetManager> .instance.m_segments.m_buffer;
                Log.Info(
                    $"Recalculated ext. segment {segmentId} (flags={segmentsBuffer[segmentId].m_flags}): " +
                    $"{extSegment}");
            }

            Constants.ManagerFactory.GeometryManager.OnUpdateSegment(ref extSegment);
        }
Ejemplo n.º 12
0
        protected void UpdateSegmentEnd(ref ExtSegment seg, ref ExtSegmentEnd end)
        {
#if DEBUG
            bool logTurnOnRed = DebugSwitch.TurnOnRed.Get();
#else
            const bool logTurnOnRed = false;
#endif
            if (logTurnOnRed)
            {
                Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}) called.");
            }

            IExtSegmentManager    segmentManager    = Constants.ManagerFactory.ExtSegmentManager;
            IExtSegmentEndManager segmentEndManager = Constants.ManagerFactory.ExtSegmentEndManager;

            ushort segmentId          = seg.segmentId;
            ushort nodeId             = end.nodeId;
            bool   hasOutgoingSegment = false;

            Services.NetService.IterateNodeSegments(
                end.nodeId,
                (ushort otherSegId, ref NetSegment otherSeg) => {
                int index0 = segmentEndManager.GetIndex(otherSegId, otherSeg.m_startNode == nodeId);

                if (otherSegId != segmentId &&
                    segmentEndManager.ExtSegmentEnds[index0].outgoing)
                {
                    hasOutgoingSegment = true;
                    return(false);
                }

                return(true);
            });

            // check if traffic can flow to the node and that there is at least one left segment
            if (!end.incoming || !hasOutgoingSegment)
            {
                if (logTurnOnRed)
                {
                    Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                               "outgoing one-way or insufficient number of outgoing segments.");
                }

                return;
            }

            bool lht = Services.SimulationService.TrafficDrivesOnLeft;

            // check node
            // note that we must not check for the `TrafficLights` flag here because the flag might not be loaded yet
            ref NetNode node      = ref nodeId.ToNode();
Ejemplo n.º 13
0
        public void ToggleMode()
        {
            if (!Constants.ServiceFactory.NetService.IsSegmentValid(SegmentId))
            {
                Log.Error($"CustomSegmentLight.ToggleMode: Segment {SegmentId} is invalid.");
                return;
            }

            IExtSegmentEndManager extSegMan = Constants.ManagerFactory.ExtSegmentEndManager;

            Constants.ServiceFactory.NetService.ProcessNode(NodeId, delegate(ushort nId, ref NetNode node) {
                ToggleMode(ref extSegMan.ExtSegmentEnds[extSegMan.GetIndex(SegmentId, StartNode)], ref node);
                return(true);
            });
        }
Ejemplo n.º 14
0
        protected void UpdateSegmentEnd(ref ExtSegment seg, ref ExtSegmentEnd end)
        {
#if DEBUG
            bool logTurnOnRed = DebugSwitch.TurnOnRed.Get();
#else
            const bool logTurnOnRed = false;
#endif
            if (logTurnOnRed)
            {
                Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}) called.");
            }

            IExtSegmentManager    segmentManager    = Constants.ManagerFactory.ExtSegmentManager;
            IExtSegmentEndManager segmentEndManager = Constants.ManagerFactory.ExtSegmentEndManager;

            ushort segmentId          = seg.segmentId;
            ushort nodeId             = end.nodeId;
            bool   hasOutgoingSegment = false;

            ref NetNode endNode = ref end.nodeId.ToNode();
Ejemplo n.º 15
0
        private void UpdateVehiclePosition(ref Vehicle vehicleData, ref ExtVehicle extVehicle)
        {
#if DEBUG
            if (DebugSwitch.VehicleLinkingToSegmentEnd.Get())
            {
                Log._Debug($"ExtVehicleManager.UpdateVehiclePosition({extVehicle.vehicleId}) called");
            }
#endif

            if (vehicleData.m_path == 0 || (vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0 ||
                (extVehicle.lastPathId == vehicleData.m_path &&
                 extVehicle.lastPathPositionIndex == vehicleData.m_pathPositionIndex))
            {
                return;
            }

            PathManager           pathManager   = Singleton <PathManager> .instance;
            IExtSegmentEndManager segmentEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            // update vehicle position for timed traffic lights and priority signs
            int coarsePathPosIndex       = vehicleData.m_pathPositionIndex >> 1;
            PathUnit.Position curPathPos = pathManager
                                           .m_pathUnits.m_buffer[vehicleData.m_path]
                                           .GetPosition(coarsePathPosIndex);

            pathManager.m_pathUnits.m_buffer[vehicleData.m_path]
            .GetNextPosition(coarsePathPosIndex, out PathUnit.Position nextPathPos);

            bool startNode = IsTransitNodeCurStartNode(ref curPathPos, ref nextPathPos);
            UpdatePosition(
                ref extVehicle,
                ref vehicleData,
                ref segmentEndMan.ExtSegmentEnds[
                    segmentEndMan.GetIndex(curPathPos.m_segment, startNode)
                ],
                ref curPathPos,
                ref nextPathPos);
        }
Ejemplo n.º 16
0
        public static IRecordable FixPrioritySigns(
            PrioritySignsTool.PrioritySignsMassEditMode massEditMode,
            List <ushort> segmentList)
        {
            if (segmentList == null || segmentList.Count == 0)
            {
                return(null);
            }

            IRecordable record = RecordRoad(segmentList);

            var primaryPrioType   = PriorityType.None;
            var secondaryPrioType = PriorityType.None;

            switch (massEditMode)
            {
            case PrioritySignsTool.PrioritySignsMassEditMode.MainYield: {
                primaryPrioType   = PriorityType.Main;
                secondaryPrioType = PriorityType.Yield;
                break;
            }

            case PrioritySignsTool.PrioritySignsMassEditMode.MainStop: {
                primaryPrioType   = PriorityType.Main;
                secondaryPrioType = PriorityType.Stop;
                break;
            }

            case PrioritySignsTool.PrioritySignsMassEditMode.YieldMain: {
                primaryPrioType   = PriorityType.Yield;
                secondaryPrioType = PriorityType.Main;
                break;
            }

            case PrioritySignsTool.PrioritySignsMassEditMode.StopMain: {
                primaryPrioType   = PriorityType.Stop;
                secondaryPrioType = PriorityType.Main;
                break;
            }
            }

            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            void ApplyPrioritySigns(ushort segmentId, bool startNode)
            {
                ushort nodeId = netService.GetSegmentNodeId(
                    segmentId,
                    startNode);

                TrafficPriorityManager.Instance.SetPrioritySign(
                    segmentId,
                    startNode,
                    primaryPrioType);

                for (int i = 0; i < 8; ++i)
                {
                    ushort otherSegmentId = nodeId.ToNode().GetSegment(i);
                    if (otherSegmentId == 0 ||
                        otherSegmentId == segmentId ||
                        segmentList.Contains(otherSegmentId))
                    {
                        continue;
                    }

                    TrafficPriorityManager.Instance.SetPrioritySign(
                        otherSegmentId,
                        (bool)netService.IsStartNode(otherSegmentId, nodeId),
                        secondaryPrioType);
                }
            }

            // TODO avoid settin up the same node two times.
            foreach (ushort segId in segmentList)
            {
                ApplyPrioritySigns(segId, true);
                ApplyPrioritySigns(segId, false);
            }

            return(record);
        }
Ejemplo n.º 17
0
        public override void OnPrimaryClickOverlay()
        {
            bool ctrlDown  = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
            bool shiftDown = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);

            if (ctrlDown || shiftDown)
            {
                if (HoveredSegmentId == 0)
                {
                    return;
                }
                SelectedNodeId = 0;
            }

            // TODO provide revert/clear mode issue #568
            if (ctrlDown && shiftDown)
            {
                bool isRAbout = RoundaboutMassEdit.Instance.FixRabout(HoveredSegmentId);
                if (!isRAbout)
                {
                    PriorityRoad.FixRoad(HoveredSegmentId);
                }
                RefreshMassEditOverlay();
                return;
            }
            else if (ctrlDown)
            {
                PriorityRoad.FixJunction(HoveredNodeId);
                RefreshMassEditOverlay();
                return;
            }
            if (shiftDown)
            {
                var primaryPrioType   = PriorityType.None;
                var secondaryPrioType = PriorityType.None;

                switch (massEditMode)
                {
                case PrioritySignsMassEditMode.MainYield: {
                    primaryPrioType   = PriorityType.Main;
                    secondaryPrioType = PriorityType.Yield;
                    break;
                }

                case PrioritySignsMassEditMode.MainStop: {
                    primaryPrioType   = PriorityType.Main;
                    secondaryPrioType = PriorityType.Stop;
                    break;
                }

                case PrioritySignsMassEditMode.YieldMain: {
                    primaryPrioType   = PriorityType.Yield;
                    secondaryPrioType = PriorityType.Main;
                    break;
                }

                case PrioritySignsMassEditMode.StopMain: {
                    primaryPrioType   = PriorityType.Stop;
                    secondaryPrioType = PriorityType.Main;
                    break;
                }
                }

                IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

                bool VisitorFun(SegmentVisitData data)
                {
                    foreach (bool startNode in Constants.ALL_BOOL)
                    {
                        TrafficPriorityManager.Instance.SetPrioritySign(
                            data.CurSeg.segmentId,
                            startNode,
                            primaryPrioType);
                        ushort nodeId = Constants.ServiceFactory.NetService.GetSegmentNodeId(
                            data.CurSeg.segmentId,
                            startNode);
                        ExtSegmentEnd curEnd = segEndMan.ExtSegmentEnds[
                            segEndMan.GetIndex(data.CurSeg.segmentId, startNode)];

                        for (int i = 0; i < 8; ++i)
                        {
                            ushort otherSegmentId = Singleton <NetManager> .instance.m_nodes
                                                    .m_buffer[nodeId]
                                                    .GetSegment(i);

                            if (otherSegmentId == 0 || otherSegmentId == data.CurSeg.segmentId)
                            {
                                continue;
                            }

                            ArrowDirection dir = segEndMan.GetDirection(
                                ref curEnd,
                                otherSegmentId);

                            if (dir != ArrowDirection.Forward)
                            {
                                TrafficPriorityManager.Instance.SetPrioritySign(
                                    otherSegmentId,
                                    (bool)Constants.ServiceFactory.NetService.IsStartNode(
                                        otherSegmentId,
                                        nodeId),
                                    secondaryPrioType);
                            }
                        }
                    }

                    return(true);
                }

                bool isRAbout = RoundaboutMassEdit.Instance.TraverseLoop(HoveredSegmentId, out var segmentList);
                if (isRAbout)
                {
                    SegmentTraverser.Traverse(segmentList, VisitorFun);
                }
                else
                {
                    SegmentTraverser.Traverse(
                        HoveredSegmentId,
                        TraverseDirection.AnyDirection,
                        TraverseSide.Straight,
                        SegmentStopCriterion.None,
                        VisitorFun);
                }

                // cycle mass edit mode
                massEditMode =
                    (PrioritySignsMassEditMode)(((int)massEditMode + 1) %
                                                Enum.GetValues(typeof(PrioritySignsMassEditMode))
                                                .GetLength(0));
            }
            else
            {
                if (TrafficPriorityManager.Instance.HasNodePrioritySign(HoveredNodeId))
                {
                    return;
                }

                if (!MayNodeHavePrioritySigns(HoveredNodeId))
                {
                    return;
                }

                SelectedNodeId = HoveredNodeId;
                Log._Debug($"PrioritySignsTool.OnPrimaryClickOverlay: SelectedNodeId={SelectedNodeId}");
            }

            // update priority node cache
            RefreshCurrentPriorityNodeIds();
        }
Ejemplo n.º 18
0
        public static IRecordable FixPrioritySigns(
            PrioritySignsTool.PrioritySignsMassEditMode massEditMode,
            List <ushort> segmentList)
        {
            if (segmentList == null || segmentList.Count == 0)
            {
                return(null);
            }

            IRecordable record = RecordRoad(segmentList);

            var primaryPrioType   = PriorityType.None;
            var secondaryPrioType = PriorityType.None;

            switch (massEditMode)
            {
            case PrioritySignsTool.PrioritySignsMassEditMode.MainYield: {
                primaryPrioType   = PriorityType.Main;
                secondaryPrioType = PriorityType.Yield;
                break;
            }

            case PrioritySignsTool.PrioritySignsMassEditMode.MainStop: {
                primaryPrioType   = PriorityType.Main;
                secondaryPrioType = PriorityType.Stop;
                break;
            }

            case PrioritySignsTool.PrioritySignsMassEditMode.YieldMain: {
                primaryPrioType   = PriorityType.Yield;
                secondaryPrioType = PriorityType.Main;
                break;
            }

            case PrioritySignsTool.PrioritySignsMassEditMode.StopMain: {
                primaryPrioType   = PriorityType.Stop;
                secondaryPrioType = PriorityType.Main;
                break;
            }
            }

            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            void ApplyPrioritySigns(ushort segmentId, bool startNode)
            {
                ref NetSegment netSegment = ref segmentId.ToSegment();
                ushort         nodeId     = startNode ? netSegment.m_startNode : netSegment.m_endNode;

                TrafficPriorityManager.Instance.SetPrioritySign(
                    segmentId,
                    startNode,
                    primaryPrioType);

                ExtSegmentManager extSegmentManager = ExtSegmentManager.Instance;

                for (int i = 0; i < 8; ++i)
                {
                    ushort otherSegmentId = nodeId.ToNode().GetSegment(i);
                    if (otherSegmentId == 0 ||
                        otherSegmentId == segmentId ||
                        segmentList.Contains(otherSegmentId))
                    {
                        continue;
                    }

                    TrafficPriorityManager.Instance.SetPrioritySign(
                        otherSegmentId,
                        (bool)extSegmentManager.IsStartNode(otherSegmentId, nodeId),
                        secondaryPrioType);
                }
            }
Ejemplo n.º 19
0
        public void Unlink(ref ExtVehicle extVehicle)
        {
#if DEBUG
            if (DebugSwitch.VehicleLinkingToSegmentEnd.Get())
            {
                Log._Debug(
                    $"ExtVehicleManager.Unlink({extVehicle.vehicleId}) called: Unlinking vehicle " +
                    $"from all segment ends\nstate:{extVehicle}");
            }

            ushort prevSegmentId = extVehicle.currentSegmentId;
            bool   prevStartNode = extVehicle.currentStartNode;
#endif
            extVehicle.lastPositionUpdate = Now();

            if (extVehicle.previousVehicleIdOnSegment != 0)
            {
                ExtVehicles[extVehicle.previousVehicleIdOnSegment].nextVehicleIdOnSegment =
                    extVehicle.nextVehicleIdOnSegment;
            }
            else if (extVehicle.currentSegmentId != 0)
            {
                IExtSegmentEndManager segmentEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
                int endIndex = segmentEndMan.GetIndex(
                    extVehicle.currentSegmentId,
                    extVehicle.currentStartNode);
                if (segmentEndMan.ExtSegmentEnds[endIndex].firstVehicleId == extVehicle.vehicleId)
                {
                    segmentEndMan.ExtSegmentEnds[endIndex].firstVehicleId =
                        extVehicle.nextVehicleIdOnSegment;
                }
                else
                {
                    Log.Error(
                        $"ExtVehicleManager.Unlink({extVehicle.vehicleId}): Unexpected first " +
                        $"vehicle on segment {extVehicle.currentSegmentId}: " +
                        $"{segmentEndMan.ExtSegmentEnds[endIndex].firstVehicleId}");
                }
            }

            if (extVehicle.nextVehicleIdOnSegment != 0)
            {
                ExtVehicles[extVehicle.nextVehicleIdOnSegment].previousVehicleIdOnSegment =
                    extVehicle.previousVehicleIdOnSegment;
            }

            extVehicle.nextVehicleIdOnSegment     = 0;
            extVehicle.previousVehicleIdOnSegment = 0;

            extVehicle.currentSegmentId = 0;
            extVehicle.currentStartNode = false;
            extVehicle.currentLaneIndex = 0;

            extVehicle.lastPathId            = 0;
            extVehicle.lastPathPositionIndex = 0;

#if DEBUG
            if (DebugSwitch.PedestrianPathfinding.Get())
            {
                string vehicleChainDebugInfo =
                    ExtSegmentEndManager.Instance.GenerateVehicleChainDebugInfo(
                        prevSegmentId,
                        prevStartNode);
                Log._Debug(
                    $"ExtVehicleManager.Unlink({extVehicle.vehicleId}) finished: Unlinked vehicle " +
                    $"from all segment ends\nstate:{extVehicle}\nold segment end vehicle chain: " +
                    vehicleChainDebugInfo);
            }
#endif
        }
        public void OnUpdate(GeometryUpdate update)
        {
#if DEBUG
            bool logGeometry = DebugSwitch.GeometryDebug.Get();
#else
            const bool logGeometry = false;
#endif
            if (update.segment != null)
            {
                // Handle a segment update
                ExtSegment seg = (ExtSegment)update.segment;

                if (!seg.valid)
                {
                    if (logGeometry)
                    {
                        Log._Debug($"{GetType().Name}.HandleInvalidSegment({seg.segmentId})");
                    }

                    HandleInvalidSegment(ref seg);
                }
                else
                {
                    if (logGeometry)
                    {
                        Log._Debug($"{GetType().Name}.HandleValidSegment({seg.segmentId})");
                    }

                    HandleValidSegment(ref seg);
                }
            }
            else if (update.nodeId != null)
            {
                // Handle a node update
                ushort nodeId = (ushort)update.nodeId;
                Services.NetService.ProcessNode(
                    nodeId,
                    (ushort nId, ref NetNode node) => {
                    if ((node.m_flags &
                         (NetNode.Flags.Created | NetNode.Flags.Deleted)) ==
                        NetNode.Flags.Created)
                    {
                        if (logGeometry)
                        {
                            Log._Debug($"{GetType().Name}.HandleValidNode({nodeId})");
                        }

                        HandleValidNode(nodeId, ref node);
                    }
                    else
                    {
                        if (logGeometry)
                        {
                            Log._Debug($"{GetType().Name}.HandleInvalidNode({nodeId})");
                        }

                        HandleInvalidNode(nodeId, ref node);
                    }

                    return(true);
                });
            }
            else
            {
                // Handle a segment end replacement
                IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

                if (logGeometry)
                {
                    Log._Debug(
                        $"{GetType().Name}.HandleSegmentReplacement({update.replacement.oldSegmentEndId} " +
                        $"-> {update.replacement.newSegmentEndId})");
                }

                int index0 = segEndMan.GetIndex(
                    update.replacement.newSegmentEndId.SegmentId,
                    update.replacement.newSegmentEndId.StartNode);

                HandleSegmentEndReplacement(
                    update.replacement,
                    ref segEndMan.ExtSegmentEnds[index0]);
            }
        }
Ejemplo n.º 21
0
        private static void TraverseRec(ref ExtSegment prevSeg,
                                        ref ExtSegmentEnd prevSegEnd,
                                        ref NetNode node,
                                        bool viaInitialStartNode,
                                        TraverseDirection direction,
                                        TraverseSide side,
                                        SegmentStopCriterion stopCrit,
                                        SegmentVisitor visitorFun,
                                        HashSet <ushort> visitedSegmentIds)
        {
            // Log._Debug($"SegmentTraverser: Traversing segment {prevSegEnd.segmentId}");
            // collect next segment ids to traverse
            if (direction == TraverseDirection.None)
            {
                throw new ArgumentException($"Invalid direction {direction} given.");
            }

            if (side == TraverseSide.None)
            {
                throw new ArgumentException($"Invalid side {side} given.");
            }

            IExtSegmentManager    extSegMan    = Constants.ManagerFactory.ExtSegmentManager;
            IExtSegmentEndManager extSegEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            HashSet <ushort> nextSegmentIds = new HashSet <ushort>();

            for (int i = 0; i < 8; ++i)
            {
                ushort nextSegmentId = node.GetSegment(i);
                if (nextSegmentId == 0 || nextSegmentId == prevSegEnd.segmentId)
                {
                    continue;
                }

                bool nextIsStartNode =
                    (bool)Constants.ServiceFactory.NetService.IsStartNode(
                        nextSegmentId,
                        prevSegEnd.nodeId);
                ExtSegmentEnd nextSegEnd =
                    extSegEndMan.ExtSegmentEnds[extSegEndMan.GetIndex(nextSegmentId, nextIsStartNode)];

                if (direction == TraverseDirection.AnyDirection ||
                    (direction == TraverseDirection.Incoming && nextSegEnd.incoming) ||
                    (direction == TraverseDirection.Outgoing && nextSegEnd.outgoing))
                {
                    if (side == TraverseSide.AnySide)
                    {
                        nextSegmentIds.Add(nextSegmentId);
                    }
                    else
                    {
                        ArrowDirection dir = extSegEndMan.GetDirection(
                            ref prevSegEnd,
                            nextSegmentId);
                        if (((side & TraverseSide.Left) != TraverseSide.None &&
                             dir == ArrowDirection.Left) ||
                            ((side & TraverseSide.Straight) != TraverseSide.None &&
                             dir == ArrowDirection.Forward) ||
                            ((side & TraverseSide.Right) != TraverseSide.None &&
                             dir == ArrowDirection.Right))
                        {
                            nextSegmentIds.Add(nextSegmentId);
                        }
                    }
                }
            }

            nextSegmentIds.Remove(0);
            // Log._Debug($"SegmentTraverser: Fetched next segments to traverse:
            //     {nextSegmentIds.CollectionToString()}");
            if (nextSegmentIds.Count >= 2 && (stopCrit & SegmentStopCriterion.Junction) !=
                SegmentStopCriterion.None)
            {
                // Log._Debug($"SegmentTraverser: Stop criterion reached @ {prevSegEnd.segmentId}:
                //    {nextSegmentIds.Count} connected segments");
                return;
            }

            // explore next segments
            foreach (ushort nextSegmentId in nextSegmentIds)
            {
                if (visitedSegmentIds.Contains(nextSegmentId))
                {
                    continue;
                }

                visitedSegmentIds.Add(nextSegmentId);
                // Log._Debug($"SegmentTraverser: Traversing segment {nextSegmentId}");
                ushort nextStartNodeId =
                    Constants.ServiceFactory.NetService.GetSegmentNodeId(nextSegmentId, true);

                if (!visitorFun(
                        new SegmentVisitData(
                            ref prevSeg,
                            ref extSegMan.ExtSegments[nextSegmentId],
                            viaInitialStartNode,
                            prevSegEnd.nodeId == nextStartNodeId,
                            false)))
                {
                    continue;
                }

                bool nextNodeIsStartNode = nextStartNodeId != prevSegEnd.nodeId;

                ExtSegmentEnd nextSegEnd
                    = extSegEndMan.ExtSegmentEnds[extSegEndMan.GetIndex(nextSegmentId, nextNodeIsStartNode)];

                Constants.ServiceFactory.NetService.ProcessNode(
                    nextSegEnd.nodeId,
                    (ushort nId, ref NetNode nextNode) => {
                    TraverseRec(
                        ref extSegMan.ExtSegments[nextSegmentId],
                        ref nextSegEnd,
                        ref nextNode,
                        viaInitialStartNode,
                        direction,
                        side,
                        stopCrit,
                        visitorFun,
                        visitedSegmentIds);
                    return(true);
                });
            } // end foreach
        }
        /// <summary>
        /// Calculates for each segment the number of cars going to this segment.
        /// We use integer arithmetic for better performance.
        /// </summary>
        public IDictionary <ushort, uint>[] MeasureOutgoingVehicles(bool includeStopped = true, bool debug = false)
        {
            //VehicleManager vehicleManager = Singleton<VehicleManager>.instance;
            //NetManager netManager = Singleton<NetManager>.instance;
            ExtVehicleManager     vehStateManager = ExtVehicleManager.Instance;
            IExtSegmentEndManager segEndMan       = Constants.ManagerFactory.ExtSegmentEndManager;

            // TODO pre-calculate this
            uint avgSegLen = 0;

            Constants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate(ushort segmentId, ref NetSegment segment) {
                avgSegLen = (uint)segment.m_averageLength;
                return(true);
            });

            IDictionary <ushort, uint>[] ret = includeStopped ? numVehiclesGoingToSegmentId : numVehiclesMovingToSegmentId;

            // reset
            for (byte laneIndex = 0; laneIndex < ret.Length; ++laneIndex)
            {
                IDictionary <ushort, uint> laneMetrics = ret[laneIndex];
                foreach (KeyValuePair <ushort, uint> e in laneMetrics)
                {
                    laneMetrics[e.Key] = 0;
                }
            }

#if DEBUGMETRIC
            if (debug)
            {
                Log._Debug($"GetVehicleMetricGoingToSegment: Segment {SegmentId}, Node {NodeId}, includeStopped={includeStopped}.");
            }
#endif

            int    endIndex     = segEndMan.GetIndex(SegmentId, StartNode);
            ushort vehicleId    = segEndMan.ExtSegmentEnds[endIndex].firstVehicleId;
            int    numProcessed = 0;
            int    numIter      = 0;
            while (vehicleId != 0)
            {
                Constants.ServiceFactory.VehicleService.ProcessVehicle(vehicleId, delegate(ushort vId, ref Vehicle veh) {
                    MeasureOutgoingVehicle(debug, ret, includeStopped, avgSegLen, vehicleId, ref veh, ref vehStateManager.ExtVehicles[vehicleId], ref numProcessed);
                    return(true);
                });
                vehicleId = vehStateManager.ExtVehicles[vehicleId].nextVehicleIdOnSegment;

                if (++numIter > Constants.ServiceFactory.VehicleService.MaxVehicleCount)
                {
                    CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);

                    break;
                }
            }

#if DEBUGMETRIC
            if (debug)
            {
                Log._Debug($"GetVehicleMetricGoingToSegment: Calculation completed. {string.Join(", ", ret.Select(e => "[" + string.Join(", ", e.Select(x => x.Key.ToString() + "=" + x.Value.ToString()).ToArray()) + "]").ToArray())}");
            }
#endif
            return(ret);
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Calculates for each segment the number of cars going to this segment.
        /// We use integer arithmetic for better performance.
        /// </summary>
        public IDictionary <ushort, uint>[] MeasureOutgoingVehicles(
            bool includeStopped = true,
            bool logDebug       = false)
        {
            // VehicleManager vehicleManager = Singleton<VehicleManager>.instance;
            // NetManager netManager = Singleton<NetManager>.instance;
            ExtVehicleManager     vehStateManager = ExtVehicleManager.Instance;
            IExtSegmentEndManager segEndMan       = Constants.ManagerFactory.ExtSegmentEndManager;

            // TODO pre-calculate this
            uint avgSegLen = (uint)SegmentId.ToSegment().m_averageLength;

            IDictionary <ushort, uint>[] ret =
                includeStopped ? numVehiclesGoingToSegmentId : numVehiclesMovingToSegmentId;

            // reset
            for (byte laneIndex = 0; laneIndex < ret.Length; ++laneIndex)
            {
                IDictionary <ushort, uint> laneMetrics = ret[laneIndex];
                foreach (KeyValuePair <ushort, uint> e in laneMetrics)
                {
                    laneMetrics[e.Key] = 0;
                }
            }

            Log._DebugIf(
                logDebug,
                () => $"GetVehicleMetricGoingToSegment: Segment {SegmentId}, Node {NodeId}, " +
                $"includeStopped={includeStopped}.");

            int    endIndex     = segEndMan.GetIndex(SegmentId, StartNode);
            ushort vehicleId    = segEndMan.ExtSegmentEnds[endIndex].firstVehicleId;
            int    numProcessed = 0;
            int    numIter      = 0;

            while (vehicleId != 0)
            {
                Constants.ServiceFactory.VehicleService.ProcessVehicle(
                    vehicleId,
                    (ushort vId, ref Vehicle veh) => {
                    MeasureOutgoingVehicle(
                        logDebug,
                        ret,
                        includeStopped,
                        avgSegLen,
                        vehicleId,
                        ref veh,
                        ref vehStateManager.ExtVehicles[vehicleId],
                        ref numProcessed);
                    return(true);
                });

                if ((Options.simulationAccuracy <= SimulationAccuracy.Low && numProcessed >= 3) ||
                    (Options.simulationAccuracy == SimulationAccuracy.Medium && numProcessed >= 5) ||
                    (Options.simulationAccuracy == SimulationAccuracy.High && numProcessed >= 10))
                {
                    break;
                }

                vehicleId = vehStateManager.ExtVehicles[vehicleId].nextVehicleIdOnSegment;

                if (++numIter > Constants.ServiceFactory.VehicleService.MaxVehicleCount)
                {
                    CODebugBase <LogChannel> .Error(
                        LogChannel.Core,
                        $"Invalid list detected!\n{Environment.StackTrace}");

                    break;
                }
            }

            if (logDebug)
            {
                string SelectFun(IDictionary <ushort, uint> e) =>
                "[" + string.Join(
                    ", ",
                    e.Select(x => x.Key.ToString() + "=" + x.Value.ToString()).ToArray())
                + "]";

                string result = string.Join(", ", ret.Select(SelectFun).ToArray());
                Log._Debug("GetVehicleMetricGoingToSegment: Calculation completed. " + result);
            }

            return(ret);
        }
Ejemplo n.º 24
0
        public override void OnToolGUI(Event e)
        {
            IExtSegmentManager    segMan    = Constants.ManagerFactory.ExtSegmentManager;
            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
            var hoveredSegment = false;

            if (SelectedNodeId != 0)
            {
                CustomSegmentLightsManager    customTrafficLightsManager = CustomSegmentLightsManager.Instance;
                TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;
                JunctionRestrictionsManager   junctionRestrictionsManager = JunctionRestrictionsManager.Instance;

                if (!tlsMan.HasManualSimulation(SelectedNodeId))
                {
                    return;
                }

                tlsMan.TrafficLightSimulations[SelectedNodeId].Housekeeping();

                // TODO check
                // if (Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNode].CountSegments() == 2) {
                //     _guiManualTrafficLightsCrosswalk(
                //         ref Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNode]);
                //     return;
                // }
                NetNode[]    nodesBuffer    = Singleton <NetManager> .instance.m_nodes.m_buffer;
                NetSegment[] segmentsBuffer = Singleton <NetManager> .instance.m_segments.m_buffer;

                for (int i = 0; i < 8; ++i)
                {
                    ushort segmentId = nodesBuffer[SelectedNodeId].GetSegment(i);
                    if (segmentId == 0)
                    {
                        continue;
                    }

                    bool startNode =
                        (bool)Constants.ServiceFactory.NetService.IsStartNode(
                            segmentId,
                            SelectedNodeId);
                    Vector3 position = CalculateNodePositionForSegment(
                        nodesBuffer[SelectedNodeId],
                        ref segmentsBuffer[segmentId]);
                    ICustomSegmentLights segmentLights =
                        customTrafficLightsManager.GetSegmentLights(segmentId, startNode, false);

                    if (segmentLights == null)
                    {
                        continue;
                    }

                    bool showPedLight = segmentLights.PedestrianLightState != null &&
                                        junctionRestrictionsManager.IsPedestrianCrossingAllowed(
                        segmentLights.SegmentId,
                        segmentLights.StartNode);
                    bool visible = MainTool.WorldToScreenPoint(position, out Vector3 screenPos);

                    if (!visible)
                    {
                        continue;
                    }

                    Vector3 diff = position - Camera.main.transform.position;
                    float   zoom = 1.0f / diff.magnitude * 100f;

                    // original / 2.5
                    float lightWidth  = 41f * zoom;
                    float lightHeight = 97f * zoom;

                    float pedestrianWidth  = 36f * zoom;
                    float pedestrianHeight = 61f * zoom;

                    // SWITCH MODE BUTTON
                    float modeWidth  = 41f * zoom;
                    float modeHeight = 38f * zoom;

                    Color guiColor = GUI.color;

                    if (showPedLight)
                    {
                        // pedestrian light

                        // SWITCH MANUAL PEDESTRIAN LIGHT BUTTON
                        hoveredSegment = RenderManualPedestrianLightSwitch(
                            zoom,
                            segmentId,
                            screenPos,
                            lightWidth,
                            segmentLights,
                            hoveredSegment);

                        // SWITCH PEDESTRIAN LIGHT
                        guiColor.a = TrafficManagerTool.GetHandleAlpha(
                            hoveredButton[0] == segmentId && hoveredButton[1] == 2 &&
                            segmentLights.ManualPedestrianMode);
                        GUI.color = guiColor;

                        var myRect3 = new Rect(
                            screenPos.x - pedestrianWidth / 2 - lightWidth + 5f * zoom,
                            screenPos.y - pedestrianHeight / 2 + 22f * zoom,
                            pedestrianWidth,
                            pedestrianHeight);

                        switch (segmentLights.PedestrianLightState)
                        {
                        case RoadBaseAI.TrafficLightState.Green: {
                            GUI.DrawTexture(
                                myRect3,
                                TrafficLightTextures.PedestrianGreenLight);
                            break;
                        }

                        // also: case RoadBaseAI.TrafficLightState.Red:
                        default: {
                            GUI.DrawTexture(
                                myRect3,
                                TrafficLightTextures.PedestrianRedLight);
                            break;
                        }
                        }

                        hoveredSegment = IsPedestrianLightHovered(
                            myRect3,
                            segmentId,
                            hoveredSegment,
                            segmentLights);
                    }

                    int lightOffset = -1;

                    foreach (ExtVehicleType vehicleType in segmentLights.VehicleTypes)
                    {
                        ++lightOffset;
                        ICustomSegmentLight segmentLight = segmentLights.GetCustomLight(vehicleType);

                        Vector3 offsetScreenPos = screenPos;
                        offsetScreenPos.y -= (lightHeight + 10f * zoom) * lightOffset;

                        SetAlpha(segmentId, -1);

                        var myRect1 = new Rect(
                            offsetScreenPos.x - modeWidth / 2,
                            offsetScreenPos.y - modeHeight / 2 + modeHeight - 7f * zoom,
                            modeWidth,
                            modeHeight);

                        GUI.DrawTexture(myRect1, TrafficLightTextures.LightMode);

                        hoveredSegment = GetHoveredSegment(
                            myRect1,
                            segmentId,
                            hoveredSegment,
                            segmentLight);

                        // COUNTER
                        hoveredSegment = RenderCounter(
                            segmentId,
                            offsetScreenPos,
                            modeWidth,
                            modeHeight,
                            zoom,
                            segmentLights,
                            hoveredSegment);

                        if (vehicleType != ExtVehicleType.None)
                        {
                            // Info sign
                            float infoWidth  = 56.125f * zoom;
                            float infoHeight = 51.375f * zoom;

                            int numInfos = 0;

                            for (int k = 0; k < TrafficManagerTool.InfoSignsToDisplay.Length; ++k)
                            {
                                if ((TrafficManagerTool.InfoSignsToDisplay[k] & vehicleType) ==
                                    ExtVehicleType.None)
                                {
                                    continue;
                                }

                                var infoRect = new Rect(
                                    offsetScreenPos.x + modeWidth / 2f +
                                    (7f * zoom * (float)(numInfos + 1)) + (infoWidth * (float)numInfos),
                                    offsetScreenPos.y - (infoHeight / 2f),
                                    infoWidth,
                                    infoHeight);
                                guiColor.a = TrafficManagerTool.GetHandleAlpha(false);

                                GUI.DrawTexture(
                                    infoRect,
                                    RoadUITextures.VehicleInfoSignTextures[TrafficManagerTool.InfoSignsToDisplay[k]]);

                                ++numInfos;
                            }
                        }

                        ExtSegment    seg    = segMan.ExtSegments[segmentId];
                        ExtSegmentEnd segEnd =
                            segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)];
                        if (seg.oneWay && segEnd.outgoing)
                        {
                            continue;
                        }

                        segEndMan.CalculateOutgoingLeftStraightRightSegments(
                            ref segEnd,
                            ref nodesBuffer[segmentId],
                            out bool hasLeftSegment,
                            out bool hasForwardSegment,
                            out bool hasRightSegment);

                        switch (segmentLight.CurrentMode)
                        {
                        case LightMode.Simple: {
                            hoveredSegment = SimpleManualSegmentLightMode(
                                segmentId,
                                offsetScreenPos,
                                lightWidth,
                                pedestrianWidth,
                                zoom,
                                lightHeight,
                                segmentLight,
                                hoveredSegment);
                            break;
                        }

                        case LightMode.SingleLeft: {
                            hoveredSegment = LeftForwardRManualSegmentLightMode(
                                hasLeftSegment,
                                segmentId,
                                offsetScreenPos,
                                lightWidth,
                                pedestrianWidth,
                                zoom,
                                lightHeight,
                                segmentLight,
                                hoveredSegment,
                                hasForwardSegment,
                                hasRightSegment);
                            break;
                        }

                        case LightMode.SingleRight: {
                            hoveredSegment = RightForwardLSegmentLightMode(
                                segmentId,
                                offsetScreenPos,
                                lightWidth,
                                pedestrianWidth,
                                zoom,
                                lightHeight,
                                hasForwardSegment,
                                hasLeftSegment,
                                segmentLight,
                                hasRightSegment,
                                hoveredSegment);
                            break;
                        }

                        default: {
                            // left arrow light
                            if (hasLeftSegment)
                            {
                                hoveredSegment = LeftArrowLightMode(
                                    segmentId,
                                    lightWidth,
                                    hasRightSegment,
                                    hasForwardSegment,
                                    offsetScreenPos,
                                    pedestrianWidth,
                                    zoom,
                                    lightHeight,
                                    segmentLight,
                                    hoveredSegment);
                            }

                            // forward arrow light
                            if (hasForwardSegment)
                            {
                                hoveredSegment = ForwardArrowLightMode(
                                    segmentId,
                                    lightWidth,
                                    hasRightSegment,
                                    offsetScreenPos,
                                    pedestrianWidth,
                                    zoom,
                                    lightHeight,
                                    segmentLight,
                                    hoveredSegment);
                            }

                            // right arrow light
                            if (hasRightSegment)
                            {
                                hoveredSegment = RightArrowLightMode(
                                    segmentId,
                                    offsetScreenPos,
                                    lightWidth,
                                    pedestrianWidth,
                                    zoom,
                                    lightHeight,
                                    segmentLight,
                                    hoveredSegment);
                            }

                            break;
                        }
                        } // end switch
                    }     // end foreach all vehicle type
                }         // end for all 8 segments
            }             // end if a node is selected

            if (hoveredSegment)
            {
                return;
            }

            hoveredButton[0] = 0;
            hoveredButton[1] = 0;
        }
        public override void OnPrimaryClickOverlay()
        {
            if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
            {
                if (HoveredSegmentId != 0)
                {
                    SelectedNodeId = 0;

                    PriorityType primaryPrioType   = PriorityType.None;
                    PriorityType secondaryPrioType = PriorityType.None;
                    switch (massEditMode)
                    {
                    case PrioritySignsMassEditMode.MainYield:
                        primaryPrioType   = PriorityType.Main;
                        secondaryPrioType = PriorityType.Yield;
                        break;

                    case PrioritySignsMassEditMode.MainStop:
                        primaryPrioType   = PriorityType.Main;
                        secondaryPrioType = PriorityType.Stop;
                        break;

                    case PrioritySignsMassEditMode.YieldMain:
                        primaryPrioType   = PriorityType.Yield;
                        secondaryPrioType = PriorityType.Main;
                        break;

                    case PrioritySignsMassEditMode.StopMain:
                        primaryPrioType   = PriorityType.Stop;
                        secondaryPrioType = PriorityType.Main;
                        break;

                    case PrioritySignsMassEditMode.Delete:
                    default:
                        break;
                    }

                    IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
                    SegmentTraverser.Traverse(HoveredSegmentId, TraverseDirection.AnyDirection, TraverseSide.Straight, SegmentStopCriterion.None, delegate(SegmentVisitData data) {
                        foreach (bool startNode in Constants.ALL_BOOL)
                        {
                            TrafficPriorityManager.Instance.SetPrioritySign(data.curSeg.segmentId, startNode, primaryPrioType);
                            ushort nodeId        = Constants.ServiceFactory.NetService.GetSegmentNodeId(data.curSeg.segmentId, startNode);
                            ExtSegmentEnd curEnd = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(data.curSeg.segmentId, startNode)];

                            for (int i = 0; i < 8; ++i)
                            {
                                ushort otherSegmentId = Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].GetSegment(i);
                                if (otherSegmentId == 0 || otherSegmentId == data.curSeg.segmentId)
                                {
                                    continue;
                                }

                                ArrowDirection dir = segEndMan.GetDirection(ref curEnd, otherSegmentId);
                                if (dir != ArrowDirection.Forward)
                                {
                                    TrafficPriorityManager.Instance.SetPrioritySign(otherSegmentId, (bool)Constants.ServiceFactory.NetService.IsStartNode(otherSegmentId, nodeId), secondaryPrioType);
                                }
                            }
                        }

                        return(true);
                    });

                    // cycle mass edit mode
                    massEditMode = (PrioritySignsMassEditMode)(((int)massEditMode + 1) % Enum.GetValues(typeof(PrioritySignsMassEditMode)).GetLength(0));

                    // update priority node cache
                    RefreshCurrentPriorityNodeIds();
                }
                return;
            }

            if (TrafficPriorityManager.Instance.HasNodePrioritySign(HoveredNodeId))
            {
                return;
            }

            if (!MayNodeHavePrioritySigns(HoveredNodeId))
            {
                return;
            }

            SelectedNodeId = HoveredNodeId;
            Log._Debug($"PrioritySignsTool.OnPrimaryClickOverlay: SelectedNodeId={SelectedNodeId}");
            // update priority node cache
            RefreshCurrentPriorityNodeIds();
        }
Ejemplo n.º 26
0
        protected void UpdateSegmentEnd(ref ExtSegment seg, ref ExtSegmentEnd end)
        {
#if DEBUG
            bool logTurnOnRed = DebugSwitch.TurnOnRed.Get();
#else
            const bool logTurnOnRed = false;
#endif
            if (logTurnOnRed)
            {
                Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}) called.");
            }

            IExtSegmentManager    segmentManager    = Constants.ManagerFactory.ExtSegmentManager;
            IExtSegmentEndManager segmentEndManager = Constants.ManagerFactory.ExtSegmentEndManager;

            ushort segmentId          = seg.segmentId;
            ushort nodeId             = end.nodeId;
            bool   hasOutgoingSegment = false;

            Services.NetService.IterateNodeSegments(
                end.nodeId,
                (ushort otherSegId, ref NetSegment otherSeg) => {
                int index0 = segmentEndManager.GetIndex(otherSegId, otherSeg.m_startNode == nodeId);

                if (otherSegId != segmentId &&
                    segmentEndManager.ExtSegmentEnds[index0].outgoing)
                {
                    hasOutgoingSegment = true;
                    return(false);
                }

                return(true);
            });

            // check if traffic can flow to the node and that there is at least one left segment
            if (!end.incoming || !hasOutgoingSegment)
            {
                if (logTurnOnRed)
                {
                    Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                               "outgoing one-way or insufficient number of outgoing segments.");
                }

                return;
            }

            bool lhd = Services.SimulationService.LeftHandDrive;

            // check node
            // note that we must not check for the `TrafficLights` flag here because the flag might not be loaded yet
            bool nodeValid = false;
            Services.NetService.ProcessNode(
                nodeId,
                (ushort _, ref NetNode node) => {
                nodeValid =
                    (node.m_flags & NetNode.Flags.LevelCrossing) ==
                    NetNode.Flags.None &&
                    node.Info?.m_class?.m_service != ItemClass.Service.Beautification;
                return(true);
            });

            if (!nodeValid)
            {
                if (logTurnOnRed)
                {
                    Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): node invalid");
                }

                return;
            }

            // get left/right segments
            ushort leftSegmentId  = 0;
            ushort rightSegmentId = 0;
            Services.NetService.ProcessSegment(
                end.segmentId,
                (ushort _, ref NetSegment segment) => {
                segment.GetLeftAndRightSegments(
                    nodeId,
                    out leftSegmentId,
                    out rightSegmentId);
                return(true);
            });

            if (logTurnOnRed)
            {
                Log._Debug(
                    $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                    $"got left/right segments: {leftSegmentId}/{rightSegmentId}");
            }

            // validate left/right segments according to geometric properties
            if (leftSegmentId != 0 &&
                segmentEndManager.GetDirection(ref end, leftSegmentId) != ArrowDirection.Left)
            {
                if (logTurnOnRed)
                {
                    Log._Debug(
                        $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                        "left segment is not geometrically left");
                }

                leftSegmentId = 0;
            }

            if (rightSegmentId != 0 &&
                segmentEndManager.GetDirection(ref end, rightSegmentId) != ArrowDirection.Right)
            {
                if (logTurnOnRed)
                {
                    Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                               "right segment is not geometrically right");
                }

                rightSegmentId = 0;
            }

            // check for incoming one-ways
            if (leftSegmentId != 0 &&
                !segmentEndManager.ExtSegmentEnds[segmentEndManager.GetIndex(leftSegmentId, nodeId)].outgoing)
            {
                if (logTurnOnRed)
                {
                    Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                               "left segment is incoming one-way");
                }

                leftSegmentId = 0;
            }

            if (rightSegmentId != 0 &&
                !segmentEndManager.ExtSegmentEnds[segmentEndManager.GetIndex(rightSegmentId, nodeId)].outgoing)
            {
                if (logTurnOnRed)
                {
                    Log._Debug($"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                               "right segment is incoming one-way");
                }

                rightSegmentId = 0;
            }

            if (seg.oneWay)
            {
                if ((lhd && rightSegmentId != 0) || (!lhd && leftSegmentId != 0))
                {
                    // special case: one-way to one-way in non-preferred direction
                    if (logTurnOnRed)
                    {
                        Log._Debug(
                            $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                            "source is incoming one-way. checking for one-way in non-preferred direction");
                    }

                    ushort targetSegmentId = lhd ? rightSegmentId : leftSegmentId;

                    if (!segmentManager.ExtSegments[targetSegmentId].oneWay)
                    {
                        // disallow turn in non-preferred direction
                        if (logTurnOnRed)
                        {
                            Log._Debug(
                                $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                                $"turn in non-preferred direction {(lhd ? "right" : "left")} disallowed");
                        }

                        if (lhd)
                        {
                            rightSegmentId = 0;
                        }
                        else
                        {
                            leftSegmentId = 0;
                        }
                    }
                }
            }
            else if (lhd)
            {
                // default case (LHD): turn in preferred direction
                rightSegmentId = 0;
            }
            else
            {
                // default case (RHD): turn in preferred direction
                leftSegmentId = 0;
            }

            int index = GetIndex(end.segmentId, end.startNode);
            TurnOnRedSegments[index].leftSegmentId  = leftSegmentId;
            TurnOnRedSegments[index].rightSegmentId = rightSegmentId;

            if (logTurnOnRed)
            {
                Log._Debug(
                    $"TurnOnRedManager.UpdateSegmentEnd({end.segmentId}, {end.startNode}): " +
                    $"Finished calculation. leftSegmentId={leftSegmentId}, rightSegmentId={rightSegmentId}");
            }
        }
        public void ShowGUI(bool viewOnly)
        {
            try {
                IExtSegmentManager            segMan    = Constants.ManagerFactory.ExtSegmentManager;
                IExtSegmentEndManager         segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
                TrafficLightSimulationManager tlsMan    = TrafficLightSimulationManager.Instance;
                TrafficPriorityManager        prioMan   = TrafficPriorityManager.Instance;
                TrafficLightManager           tlm       = TrafficLightManager.Instance;

                Vector3 camPos = Constants.ServiceFactory.SimulationService.CameraPosition;

                bool clicked = !viewOnly?MainTool.CheckClicked() : false;

                ushort removedNodeId    = 0;
                bool   showRemoveButton = false;
                foreach (ushort nodeId in currentPriorityNodeIds)
                {
                    if (!Constants.ServiceFactory.NetService.IsNodeValid(nodeId))
                    {
                        continue;
                    }

                    if (!MainTool.IsNodeWithinViewDistance(nodeId))
                    {
                        continue;
                    }

                    Vector3 nodePos = default(Vector3);
                    Constants.ServiceFactory.NetService.ProcessNode(nodeId, delegate(ushort nId, ref NetNode node) {
                        nodePos = node.m_position;
                        return(true);
                    });

                    for (int i = 0; i < 8; ++i)
                    {
                        ushort segmentId = 0;
                        Constants.ServiceFactory.NetService.ProcessNode(nodeId, delegate(ushort nId, ref NetNode node) {
                            segmentId = node.GetSegment(i);
                            return(true);
                        });

                        if (segmentId == 0)
                        {
                            continue;
                        }

                        bool          startNode = (bool)Constants.ServiceFactory.NetService.IsStartNode(segmentId, nodeId);
                        ExtSegment    seg       = segMan.ExtSegments[segmentId];
                        ExtSegmentEnd segEnd    = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)];

                        if (seg.oneWay && segEnd.outgoing)
                        {
                            continue;
                        }

                        // calculate sign position
                        Vector3 signPos = nodePos;

                        Constants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate(ushort sId, ref NetSegment segment) {
                            signPos += 10f * (startNode ? segment.m_startDirection : segment.m_endDirection);
                            return(true);
                        });

                        Vector3 signScreenPos;
                        if (!MainTool.WorldToScreenPoint(signPos, out signScreenPos))
                        {
                            continue;
                        }

                        // draw sign and handle input
                        PriorityType sign = prioMan.GetPrioritySign(segmentId, startNode);
                        if (viewOnly && sign == PriorityType.None)
                        {
                            continue;
                        }
                        if (!viewOnly && sign != PriorityType.None)
                        {
                            showRemoveButton = true;
                        }

                        if (MainTool.DrawGenericSquareOverlayTexture(TextureResources.PrioritySignTextures[sign], camPos, signPos, 90f, !viewOnly) && clicked)
                        {
                            PriorityType?newSign = null;
                            switch (sign)
                            {
                            case PriorityType.Main:
                                newSign = PriorityType.Yield;
                                break;

                            case PriorityType.Yield:
                                newSign = PriorityType.Stop;
                                break;

                            case PriorityType.Stop:
                                newSign = PriorityType.Main;
                                break;

                            case PriorityType.None:
                            default:
                                newSign = prioMan.CountPrioritySignsAtNode(nodeId, PriorityType.Main) >= 2 ? PriorityType.Yield : PriorityType.Main;
                                break;
                            }

                            if (newSign != null)
                            {
                                SetPrioritySign(segmentId, startNode, (PriorityType)newSign);
                            }
                        }                 // draw sign
                    }                     // foreach segment end

                    if (viewOnly)
                    {
                        continue;
                    }

                    // draw remove button and handle click
                    if (showRemoveButton && MainTool.DrawHoverableSquareOverlayTexture(TextureResources.SignRemoveTexture2D, camPos, nodePos, 90f) && clicked)
                    {
                        prioMan.RemovePrioritySignsFromNode(nodeId);
                        Log._Debug($"PrioritySignsTool.ShowGUI: Removed priority signs from node {nodeId}");
                        removedNodeId = nodeId;
                    }
                }                 // foreach node

                if (removedNodeId != 0)
                {
                    currentPriorityNodeIds.Remove(removedNodeId);
                    SelectedNodeId = 0;
                }
            } catch (Exception e) {
                Log.Error(e.ToString());
            }
        }
        /// <summary>
        /// Calculates for each segment the number of cars going to this segment.
        /// We use integer arithmetic for better performance.
        /// </summary>
        public IDictionary <ushort, uint>[] MeasureOutgoingVehicles(
            bool includeStopped = true,
            bool logDebug       = false)
        {
            // VehicleManager vehicleManager = Singleton<VehicleManager>.instance;
            // NetManager netManager = Singleton<NetManager>.instance;
            ExtVehicleManager     vehStateManager = ExtVehicleManager.Instance;
            IExtSegmentEndManager segEndMan       = Constants.ManagerFactory.ExtSegmentEndManager;

            // TODO pre-calculate this
            uint avgSegLen = (uint)SegmentId.ToSegment().m_averageLength;

            Dictionary <ushort, uint>[] ret =
                includeStopped ? numVehiclesGoingToSegmentId : numVehiclesMovingToSegmentId;

            // reset
            for (byte laneIndex = 0; laneIndex < ret.Length; ++laneIndex)
            {
                IDictionary <ushort, uint> laneMetrics = ret[laneIndex];
                foreach (ushort key in laneMetrics.Keys.ToList())
                {
                    laneMetrics[key] = 0;
                }
            }

            Log._DebugIf(
                logDebug,
                () => $"GetVehicleMetricGoingToSegment: Segment {SegmentId}, Node {NodeId}, " +
                $"includeStopped={includeStopped}.");

            int    endIndex        = segEndMan.GetIndex(SegmentId, StartNode);
            ushort vehicleId       = segEndMan.ExtSegmentEnds[endIndex].firstVehicleId;
            int    numProcessed    = 0;
            int    numIter         = 0;
            var    maxVehicleCount = VehicleManager.instance.m_vehicles.m_buffer.Length;

            while (vehicleId != 0)
            {
                ref Vehicle vehicle = ref vehicleId.ToVehicle();

                MeasureOutgoingVehicle(
                    logDebug,
                    ret,
                    includeStopped,
                    avgSegLen,
                    vehicleId,
                    ref vehicle,
                    ref vehStateManager.ExtVehicles[vehicleId],
                    ref numProcessed);

                if ((Options.simulationAccuracy <= SimulationAccuracy.Low && numProcessed >= 3) ||
                    (Options.simulationAccuracy == SimulationAccuracy.Medium && numProcessed >= 5) ||
                    (Options.simulationAccuracy == SimulationAccuracy.High && numProcessed >= 10))
                {
                    break;
                }

                vehicleId = vehStateManager.ExtVehicles[vehicleId].nextVehicleIdOnSegment;

                if (++numIter > maxVehicleCount)
                {
                    CODebugBase <LogChannel> .Error(
                        LogChannel.Core,
                        $"Invalid list detected!\n{Environment.StackTrace}");

                    break;
                }
            }
        /// <summary>
        /// Recalculates lane arrows based on present lane connections.
        /// </summary>
        /// <param name="laneId">Affected lane</param>
        /// <param name="nodeId">Affected node</param>
        private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode)
        {
#if DEBUG
            bool logLaneConnections = DebugSwitch.LaneConnections.Get();
#else
            const bool logLaneConnections = false;
#endif
            if (logLaneConnections)
            {
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}) called");
            }

            if (!Options.laneConnectorEnabled)
            {
                return;
            }

            if (!Flags.CanHaveLaneArrows(laneId, startNode))
            {
                if (logLaneConnections)
                {
                    Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                               $"lane {laneId}, startNode? {startNode} must not have lane arrows");
                }

                return;
            }

            if (!HasConnections(laneId, startNode))
            {
                if (logLaneConnections)
                {
                    Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                               $"lane {laneId} does not have outgoing connections");
                }

                return;
            }

            if (nodeId == 0)
            {
                if (logLaneConnections)
                {
                    Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                               "invalid node");
                }

                return;
            }

            var        arrows     = LaneArrows.None;
            NetManager netManager = Singleton <NetManager> .instance;
            ushort     segmentId  = netManager.m_lanes.m_buffer[laneId].m_segment;

            if (segmentId == 0)
            {
                if (logLaneConnections)
                {
                    Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                               "invalid segment");
                }

                return;
            }

            if (logLaneConnections)
            {
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                           $"startNode? {startNode}");
            }

            if (!Services.NetService.IsNodeValid(nodeId))
            {
                if (logLaneConnections)
                {
                    Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                               "Node is invalid");
                }

                return;
            }

            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;
            ExtSegmentEnd         segEnd    = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)];

            Services.NetService.IterateNodeSegments(
                nodeId,
                (ushort otherSegmentId, ref NetSegment otherSeg) => {
                ArrowDirection dir = segEndMan.GetDirection(ref segEnd, otherSegmentId);

                if (logLaneConnections)
                {
                    Log._Debug(
                        $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                        $"processing connected segment {otherSegmentId}. dir={dir}");
                }

                // check if arrow has already been set for this direction
                switch (dir)
                {
                case ArrowDirection.Turn: {
                    if (Constants.ServiceFactory.SimulationService.TrafficDrivesOnLeft)
                    {
                        if ((arrows & LaneArrows.Right) != LaneArrows.None)
                        {
                            return(true);
                        }
                    }
                    else
                    {
                        if ((arrows & LaneArrows.Left) != LaneArrows.None)
                        {
                            return(true);
                        }
                    }

                    break;
                }

                case ArrowDirection.Forward: {
                    if ((arrows & LaneArrows.Forward) != LaneArrows.None)
                    {
                        return(true);
                    }

                    break;
                }

                case ArrowDirection.Left: {
                    if ((arrows & LaneArrows.Left) != LaneArrows.None)
                    {
                        return(true);
                    }

                    break;
                }

                case ArrowDirection.Right: {
                    if ((arrows & LaneArrows.Right) != LaneArrows.None)
                    {
                        return(true);
                    }

                    break;
                }

                default: {
                    return(true);
                }
                }

                if (logLaneConnections)
                {
                    Log._Debug(
                        $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                        $"processing connected segment {otherSegmentId}: need to determine arrows");
                }

                bool addArrow  = false;
                uint curLaneId = netManager.m_segments.m_buffer[otherSegmentId].m_lanes;

                while (curLaneId != 0)
                {
                    if (logLaneConnections)
                    {
                        Log._Debug(
                            $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                            $"processing connected segment {otherSegmentId}: checking lane {curLaneId}");
                    }

                    if (AreLanesConnected(laneId, curLaneId, startNode))
                    {
                        if (logLaneConnections)
                        {
                            Log._Debug(
                                $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                                $"processing connected segment {otherSegmentId}: checking lane " +
                                $"{curLaneId}: lanes are connected");
                        }

                        addArrow = true;
                        break;
                    }

                    curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;
                }

                if (logLaneConnections)
                {
                    Log._Debug(
                        $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                        $"processing connected segment {otherSegmentId}: finished processing " +
                        $"lanes. addArrow={addArrow} arrows (before)={arrows}");
                }

                if (!addArrow)
                {
                    return(true);
                }

                switch (dir)
                {
                case ArrowDirection.Turn: {
                    if (Constants.ServiceFactory.SimulationService.TrafficDrivesOnLeft)
                    {
                        arrows |= LaneArrows.Right;
                    }
                    else
                    {
                        arrows |= LaneArrows.Left;
                    }

                    break;
                }

                case ArrowDirection.Forward: {
                    arrows |= LaneArrows.Forward;
                    break;
                }

                case ArrowDirection.Left: {
                    arrows |= LaneArrows.Left;
                    break;
                }

                case ArrowDirection.Right: {
                    arrows |= LaneArrows.Right;
                    break;
                }

                default: {
                    return(true);
                }
                }

                if (logLaneConnections)
                {
                    Log._Debug(
                        $"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                        $"processing connected segment {otherSegmentId}: arrows={arrows}");
                }

                return(true);
            });

            if (logLaneConnections)
            {
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): " +
                           $"setting lane arrows to {arrows}");
            }

            LaneArrowManager.Instance.SetLaneArrows(laneId, arrows, true);
        }
        private bool drawSignHandles(bool debug,
                                     ushort nodeId,
                                     ref NetNode node,
                                     bool viewOnly,
                                     bool handleClick,
                                     ref Vector3 camPos,
                                     out bool stateUpdated)
        {
            bool hovered = false;

            stateUpdated = false;

            if (viewOnly && !(Options.junctionRestrictionsOverlay || PrioritySignsTool.showMassEditOverlay) &&
                (MainTool.GetToolMode() != ToolMode.JunctionRestrictions))
            {
                return(false);
            }

            // NetManager netManager = Singleton<NetManager>.instance;
            Color   guiColor = GUI.color;
            Vector3 nodePos  = Singleton <NetManager> .instance.m_nodes.m_buffer[nodeId].m_position;
            IExtSegmentEndManager segEndMan = Constants.ManagerFactory.ExtSegmentEndManager;

            for (int i = 0; i < 8; ++i)
            {
                ushort segmentId = node.GetSegment(i);

                if (segmentId == 0)
                {
                    continue;
                }

                bool startNode = (bool)Constants.ServiceFactory.NetService.IsStartNode(segmentId, nodeId);
                bool incoming  = segEndMan.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)].incoming;

                int numSignsPerRow = incoming ? 2 : 1;

                NetInfo segmentInfo = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId].Info;

                ItemClass connectionClass = segmentInfo.GetConnectionClass();

                if (connectionClass.m_service != ItemClass.Service.Road)
                {
                    continue; // only for road junctions
                }

                // draw all junction restriction signs
                Vector3 segmentCenterPos = Singleton <NetManager> .instance.m_segments.m_buffer[segmentId]
                                           .m_bounds.center;
                Vector3 yu = (segmentCenterPos - nodePos).normalized;
                Vector3 xu = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;
                float   f  = viewOnly ? 6f : 7f; // reserved sign size in game coordinates

                Vector3 centerStart = nodePos + (yu * (viewOnly ? 5f : 14f));
                Vector3 zero        = centerStart - (0.5f * (numSignsPerRow - 1) * f * xu); // "top left"
                if (viewOnly)
                {
                    if (Constants.ServiceFactory.SimulationService.TrafficDrivesOnLeft)
                    {
                        zero -= xu * 8f;
                    }
                    else
                    {
                        zero += xu * 8f;
                    }
                }

                bool signHovered;
                int  x = 0;
                int  y = 0;
                bool hasSignInPrevRow = false;

                // draw "lane-changing when going straight allowed" sign at (0; 0)
                bool allowed =
                    JunctionRestrictionsManager.Instance.IsLaneChangingAllowedWhenGoingStraight(
                        segmentId,
                        startNode);

                bool configurable =
                    Constants.ManagerFactory.JunctionRestrictionsManager
                    .IsLaneChangingAllowedWhenGoingStraightConfigurable(
                        segmentId,
                        startNode,
                        ref node);

                if (debug ||
                    (configurable &&
                     (!viewOnly ||
                      (allowed != Constants.ManagerFactory
                       .JunctionRestrictionsManager
                       .GetDefaultLaneChangingAllowedWhenGoingStraight(
                           segmentId,
                           startNode,
                           ref node)))))
                {
                    DrawSign(
                        viewOnly,
                        !configurable,
                        ref camPos,
                        ref xu,
                        ref yu,
                        f,
                        ref zero,
                        x,
                        y,
                        guiColor,
                        allowed
                            ? JunctionRestrictions.LaneChangeAllowed
                            : JunctionRestrictions.LaneChangeForbidden,
                        out signHovered);

                    if (signHovered && handleClick)
                    {
                        hovered = true;
                        if (MainTool.CheckClicked())
                        {
                            JunctionRestrictionsManager.Instance.ToggleLaneChangingAllowedWhenGoingStraight(
                                segmentId,
                                startNode);
                            stateUpdated = true;
                        }
                    }

                    ++x;
                    hasSignInPrevRow = true;
                }

                // draw "u-turns allowed" sign at (1; 0)
                allowed      = JunctionRestrictionsManager.Instance.IsUturnAllowed(segmentId, startNode);
                configurable =
                    Constants.ManagerFactory.JunctionRestrictionsManager.IsUturnAllowedConfigurable(
                        segmentId,
                        startNode,
                        ref node);
                if (debug ||
                    (configurable &&
                     (!viewOnly ||
                      (allowed != Constants.ManagerFactory
                       .JunctionRestrictionsManager
                       .GetDefaultUturnAllowed(
                           segmentId,
                           startNode,
                           ref node)))))
                {
                    DrawSign(
                        viewOnly,
                        !configurable,
                        ref camPos,
                        ref xu,
                        ref yu,
                        f,
                        ref zero,
                        x,
                        y,
                        guiColor,
                        allowed
                            ? JunctionRestrictions.UturnAllowed
                            : JunctionRestrictions.UturnForbidden,
                        out signHovered);

                    if (signHovered && handleClick)
                    {
                        hovered = true;

                        if (MainTool.CheckClicked())
                        {
                            if (!JunctionRestrictionsManager.Instance.ToggleUturnAllowed(
                                    segmentId,
                                    startNode))
                            {
                                // TODO MainTool.ShowTooltip(Translation.GetString("..."), Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_position);
                            }
                            else
                            {
                                stateUpdated = true;
                            }
                        }
                    }

                    x++;
                    hasSignInPrevRow = true;
                }

                x = 0;
                if (hasSignInPrevRow)
                {
                    ++y;
                    hasSignInPrevRow = false;
                }

                // draw "entering blocked junctions allowed" sign at (0; 1)
                allowed = JunctionRestrictionsManager.Instance.IsEnteringBlockedJunctionAllowed(
                    segmentId,
                    startNode);
                configurable =
                    Constants.ManagerFactory.JunctionRestrictionsManager
                    .IsEnteringBlockedJunctionAllowedConfigurable(
                        segmentId,
                        startNode,
                        ref node);

                if (debug ||
                    (configurable &&
                     (!viewOnly ||
                      (allowed != Constants.ManagerFactory
                       .JunctionRestrictionsManager
                       .GetDefaultEnteringBlockedJunctionAllowed(
                           segmentId,
                           startNode,
                           ref node)))))
                {
                    DrawSign(
                        viewOnly,
                        !configurable,
                        ref camPos,
                        ref xu,
                        ref yu,
                        f,
                        ref zero,
                        x,
                        y,
                        guiColor,
                        allowed
                            ? JunctionRestrictions.EnterBlockedJunctionAllowed
                            : JunctionRestrictions.EnterBlockedJunctionForbidden,
                        out signHovered);

                    if (signHovered && handleClick)
                    {
                        hovered = true;

                        if (MainTool.CheckClicked())
                        {
                            JunctionRestrictionsManager
                            .Instance
                            .ToggleEnteringBlockedJunctionAllowed(segmentId, startNode);
                            stateUpdated = true;
                        }
                    }

                    ++x;
                    hasSignInPrevRow = true;
                }

                // draw "pedestrian crossing allowed" sign at (1; 1)
                allowed = JunctionRestrictionsManager.Instance.IsPedestrianCrossingAllowed(
                    segmentId,
                    startNode);
                configurable =
                    Constants.ManagerFactory.JunctionRestrictionsManager
                    .IsPedestrianCrossingAllowedConfigurable(
                        segmentId,
                        startNode,
                        ref node);

                if (debug ||
                    (configurable &&
                     (!viewOnly || !allowed)))
                {
                    DrawSign(
                        viewOnly,
                        !configurable,
                        ref camPos,
                        ref xu,
                        ref yu,
                        f,
                        ref zero,
                        x,
                        y,
                        guiColor,
                        allowed
                            ? JunctionRestrictions.PedestrianCrossingAllowed
                            : JunctionRestrictions.PedestrianCrossingForbidden,
                        out signHovered);

                    if (signHovered && handleClick)
                    {
                        hovered = true;

                        if (MainTool.CheckClicked())
                        {
                            JunctionRestrictionsManager.Instance.TogglePedestrianCrossingAllowed(segmentId, startNode);
                            stateUpdated = true;
                        }
                    }

                    x++;
                    hasSignInPrevRow = true;
                }

                x = 0;

                if (hasSignInPrevRow)
                {
                    ++y;
                    hasSignInPrevRow = false;
                }

                if (!Options.turnOnRedEnabled)
                {
                    continue;
                }

                //--------------------------------
                // TURN ON RED ENABLED
                //--------------------------------
                IJunctionRestrictionsManager junctionRestrictionsManager =
                    Constants.ManagerFactory.JunctionRestrictionsManager;
                bool lht = Constants.ServiceFactory.SimulationService.TrafficDrivesOnLeft;

                // draw "turn-left-on-red allowed" sign at (2; 0)
                allowed      = junctionRestrictionsManager.IsTurnOnRedAllowed(lht, segmentId, startNode);
                configurable = junctionRestrictionsManager.IsTurnOnRedAllowedConfigurable(
                    lht,
                    segmentId,
                    startNode,
                    ref node);

                if (debug ||
                    (configurable &&
                     (!viewOnly ||
                      (allowed != junctionRestrictionsManager
                       .GetDefaultTurnOnRedAllowed(
                           lht,
                           segmentId,
                           startNode,
                           ref node)))))
                {
                    DrawSign(
                        viewOnly,
                        !configurable,
                        ref camPos,
                        ref xu,
                        ref yu,
                        f,
                        ref zero,
                        x,
                        y,
                        guiColor,
                        allowed
                            ? JunctionRestrictions.LeftOnRedAllowed
                            : JunctionRestrictions.LeftOnRedForbidden,
                        out signHovered);

                    if (signHovered && handleClick)
                    {
                        hovered = true;

                        if (MainTool.CheckClicked())
                        {
                            junctionRestrictionsManager.ToggleTurnOnRedAllowed(
                                lht,
                                segmentId,
                                startNode);
                            stateUpdated = true;
                        }
                    }

                    hasSignInPrevRow = true;
                }

                x++;

                // draw "turn-right-on-red allowed" sign at (2; 1)
                allowed = junctionRestrictionsManager.IsTurnOnRedAllowed(
                    !lht,
                    segmentId,
                    startNode);
                configurable = junctionRestrictionsManager.IsTurnOnRedAllowedConfigurable(
                    !lht,
                    segmentId,
                    startNode,
                    ref node);

                if (debug ||
                    (configurable &&
                     (!viewOnly ||
                      (allowed != junctionRestrictionsManager
                       .GetDefaultTurnOnRedAllowed(
                           !lht,
                           segmentId,
                           startNode,
                           ref node)))))
                {
                    DrawSign(
                        viewOnly,
                        !configurable,
                        ref camPos,
                        ref xu,
                        ref yu,
                        f,
                        ref zero,
                        x,
                        y,
                        guiColor,
                        allowed
                            ? JunctionRestrictions.RightOnRedAllowed
                            : JunctionRestrictions.RightOnRedForbidden,
                        out signHovered);

                    if (signHovered && handleClick)
                    {
                        hovered = true;

                        if (MainTool.CheckClicked())
                        {
                            junctionRestrictionsManager.ToggleTurnOnRedAllowed(
                                !lht,
                                segmentId,
                                startNode);
                            stateUpdated = true;
                        }
                    }

                    hasSignInPrevRow = true;
                }
            }

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

            return(hovered);
        }