/// <summary> /// Calculates for each segment the number of cars going to this segment. /// We use integer arithmetic for better performance. /// </summary> public Dictionary <ushort, uint> GetVehicleMetricGoingToSegment(bool includeStopped = true, byte?laneIndex = null, bool debug = false) { VehicleManager vehicleManager = Singleton <VehicleManager> .instance; NetManager netManager = Singleton <NetManager> .instance; VehicleStateManager vehStateManager = VehicleStateManager.Instance; Dictionary <ushort, uint> ret = includeStopped ? numVehiclesGoingToSegmentId : numVehiclesFlowingToSegmentId; foreach (SegmentEndGeometry endGeo in NodeGeometry.Get(NodeId).SegmentEndGeometries) { if (endGeo == null) { continue; } if (!endGeo.IncomingOneWay && !ret.ContainsKey(endGeo.SegmentId)) { #if DEBUG Log._Debug($"SegmentEnd.GetVehicleMetricGoingToSegment: return dict does not contain entry for segment {endGeo.SegmentId}"); #endif } ret[endGeo.SegmentId] = 0; } #if DEBUGMETRIC if (debug) { Log._Debug($"GetVehicleMetricGoingToSegment: Segment {SegmentId}, Node {NodeId}. Target segments: {string.Join(", ", ret.Keys.Select(x => x.ToString()).ToArray())}"); } #endif ushort vehicleId = FirstRegisteredVehicleId; int numProcessed = 0; while (vehicleId != 0) { VehicleState state = vehStateManager._GetVehicleState(vehicleId); bool breakLoop = false; state.ProcessCurrentAndNextPathPosition(ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[vehicleId], delegate(ref Vehicle vehState, ref PathUnit.Position curPos, ref PathUnit.Position nextPos) { if (!state.CheckValidity(ref vehState)) { RequestCleanup(); return; } #if DEBUGMETRIC2 if (debug) { Log._Debug($" GetVehicleMetricGoingToSegment: Checking vehicle {vehicleId}"); } #endif if (!ret.ContainsKey(nextPos.m_segment)) { #if DEBUGMETRIC2 if (debug) { Log._Debug($" GetVehicleMetricGoingToSegment: ret does not contain key for target segment {nextPos.m_segment}"); } #endif return; } if (!includeStopped && vehState.GetLastFrameVelocity().sqrMagnitude < TrafficPriorityManager.MAX_SQR_STOP_VELOCITY) { #if DEBUGMETRIC2 if (debug) { Log._Debug($" GetVehicleMetricGoingToSegment: Vehicle {vehicleId}: too slow"); } #endif ++numProcessed; return; } if (laneIndex != null && curPos.m_lane != laneIndex) { #if DEBUGMETRIC2 if (debug) { Log._Debug($" GetVehicleMetricGoingToSegment: Vehicle {vehicleId}: Lane index mismatch (expected: {laneIndex}, was: {curPos.m_lane})"); } #endif return; } if (Options.simAccuracy <= 2) { uint avgSegmentLength = (uint)netManager.m_segments.m_buffer[SegmentId].m_averageLength; uint normLength = 100u; if (avgSegmentLength > 0) { normLength = Math.Min(100u, (uint)(state.TotalLength * 100u) / avgSegmentLength); } #if DEBUGMETRIC if (debug) { Log._Debug($" GetVehicleMetricGoingToSegment: NormLength of vehicle {vehicleId}: {avgSegmentLength} -> {normLength}"); } #endif ret[nextPos.m_segment] += normLength; } else { ret[nextPos.m_segment] += 10; } ++ret[nextPos.m_segment]; ++numProcessed; if ((Options.simAccuracy >= 3 && numProcessed >= 3) || (Options.simAccuracy == 2 && numProcessed >= 5) || (Options.simAccuracy == 1 && numProcessed >= 10)) { breakLoop = true; return; } #if DEBUGMETRIC2 if (debug) { Log._Debug($" GetVehicleMetricGoingToSegment: Vehicle {vehicleId}: *added*! Coming from segment {SegmentId}, lane {laneIndex}. Going to segment {nextPos.m_segment}, lane {nextPos.m_lane}"); } #endif }); if (breakLoop) { break; } vehicleId = state.NextVehicleIdOnSegment; } #if DEBUGMETRIC if (debug) { Log._Debug($"GetVehicleMetricGoingToSegment: Calculation completed. {string.Join(", ", ret.Select(x => x.Key.ToString() + "=" + x.Value.ToString()).ToArray())}"); } #endif return(ret); }
/// <summary> /// Recalculates lane arrows based on present lane connections. /// </summary> /// <param name="laneId"></param> /// <param name="nodeId"></param> private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}) called"); #endif if (!Flags.mayHaveLaneArrows(laneId, startNode)) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): lane {laneId}, startNode? {startNode} must not have lane arrows"); #endif return; } if (!HasConnections(laneId, startNode)) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): lane {laneId} does not have outgoing connections"); #endif return; } if (nodeId == 0) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid node"); #endif return; } Flags.LaneArrows arrows = Flags.LaneArrows.None; NetManager netManager = Singleton <NetManager> .instance; ushort segmentId = netManager.m_lanes.m_buffer[laneId].m_segment; if (segmentId == 0) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid segment"); #endif return; } #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): startNode? {startNode}"); #endif NodeGeometry nodeGeo = NodeGeometry.Get(nodeId); if (!nodeGeo.IsValid()) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid node geometry"); #endif return; } SegmentGeometry segmentGeo = SegmentGeometry.Get(segmentId); if (!segmentGeo.IsValid()) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid segment geometry"); #endif return; } ushort[] connectedSegmentIds = segmentGeo.GetConnectedSegments(startNode); if (connectedSegmentIds == null) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): connectedSegmentIds is null"); #endif return; } foreach (ushort connectedSegmentId in connectedSegmentIds) { if (connectedSegmentId == 0) { continue; } Direction dir = segmentGeo.GetDirection(connectedSegmentId, startNode); #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}. dir={dir}"); #endif // check if arrow has already been set for this direction switch (dir) { case Direction.Turn: default: continue; case Direction.Forward: if ((arrows & Flags.LaneArrows.Forward) != Flags.LaneArrows.None) { continue; } break; case Direction.Left: if ((arrows & Flags.LaneArrows.Left) != Flags.LaneArrows.None) { continue; } break; case Direction.Right: if ((arrows & Flags.LaneArrows.Right) != Flags.LaneArrows.None) { continue; } break; } #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: need to determine arrows"); #endif bool addArrow = false; uint curLaneId = netManager.m_segments.m_buffer[connectedSegmentId].m_lanes; while (curLaneId != 0) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: checking lane {curLaneId}"); #endif if (AreLanesConnected(laneId, curLaneId, startNode)) { #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: checking lane {curLaneId}: lanes are connected"); #endif addArrow = true; break; } curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane; } #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: finished processing lanes. addArrow={addArrow} arrows (before)={arrows}"); #endif if (addArrow) { switch (dir) { case Direction.Turn: default: continue; case Direction.Forward: arrows |= Flags.LaneArrows.Forward; break; case Direction.Left: arrows |= Flags.LaneArrows.Left; break; case Direction.Right: arrows |= Flags.LaneArrows.Right; break; } #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: arrows={arrows}"); #endif } } #if DEBUGCONN Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): setting lane arrows to {arrows}"); #endif Flags.setLaneArrowFlags(laneId, arrows, true); }