From 37c5d0200111690d3f2fa89e7e9f6a7c5ed4db70 Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Sat, 14 Nov 2020 18:53:49 +0100 Subject: [PATCH] Apply Shift+Click speed limit to entire roundabout (#920) #869: Apply Shift+Click speed limit to entire roundabout (cherry picked from commit ec3227dcdce6a7df9dc8f8cee280e399280625e3) --- TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs | 58 +++++---- .../SubTools/SpeedLimits/SpeedLimitsTool.cs | 122 ++++++++++++++---- 2 files changed, 130 insertions(+), 50 deletions(-) diff --git a/TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs b/TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs index 6e5a9b207..1254a841d 100644 --- a/TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs +++ b/TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs @@ -12,17 +12,10 @@ public class ExtSegmentEndManager : AbstractCustomManager, IExtSegmentEndManager { - public static ExtSegmentEndManager Instance { get; } - static ExtSegmentEndManager() { Instance = new ExtSegmentEndManager(); } - /// - /// All additional data for segment ends - /// - public ExtSegmentEnd[] ExtSegmentEnds { get; } - private ExtSegmentEndManager() { ExtSegmentEnds = new ExtSegmentEnd[NetManager.MAX_SEGMENT_COUNT * 2]; for (uint i = 0; i < NetManager.MAX_SEGMENT_COUNT; ++i) { @@ -31,6 +24,13 @@ private ExtSegmentEndManager() { } } + public static ExtSegmentEndManager Instance { get; } + + /// + /// All additional data for segment ends + /// + public ExtSegmentEnd[] ExtSegmentEnds { get; } + #if DEBUG public string GenerateVehicleChainDebugInfo(ushort segmentId, bool startNode) { int index = GetIndex(segmentId, startNode); @@ -298,23 +298,33 @@ private void Recalculate(ref ExtSegmentEnd segEnd) { public void CalculateCorners(ushort segmentId, bool startNode) { if (!Shortcuts.netService.IsSegmentValid(segmentId)) return; - ref ExtSegmentEnd segEnd = ref ExtSegmentEnds[GetIndex(segmentId, startNode)]; - segmentId.ToSegment().CalculateCorner( - segmentID: segmentId, - heightOffset: true, - start: startNode, - leftSide: false, - cornerPos: out segEnd.RightCorner, - cornerDirection: out segEnd.RightCornerDir, - smooth: out _); - segmentId.ToSegment().CalculateCorner( - segmentID: segmentId, - heightOffset: true, - start: startNode, - leftSide: true, - cornerPos: out segEnd.LeftCorner, - cornerDirection: out segEnd.LeftCornerDir, - smooth: out _); + if (!segmentId.ToSegment().Info) { + Log.Warning($"segment {segmentId} has null info"); + return; + } + + try { + ref ExtSegmentEnd segEnd = ref ExtSegmentEnds[GetIndex(segmentId, startNode)]; + segmentId.ToSegment().CalculateCorner( + segmentID: segmentId, + heightOffset: true, + start: startNode, + leftSide: false, + cornerPos: out segEnd.RightCorner, + cornerDirection: out segEnd.RightCornerDir, + smooth: out _); + segmentId.ToSegment().CalculateCorner( + segmentID: segmentId, + heightOffset: true, + start: startNode, + leftSide: true, + cornerPos: out segEnd.LeftCorner, + cornerDirection: out segEnd.LeftCornerDir, + smooth: out _); + } catch (Exception e) { + Log.Error($"failed calculating corner for segment:{segmentId}, info={segmentId.ToSegment().Info}\n" + + e.Message); + } } private void CalculateIncomingOutgoing(ushort segmentId, diff --git a/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs b/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs index 9ff9e56f8..c152c5173 100644 --- a/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs +++ b/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs @@ -43,7 +43,7 @@ public const int /// 0 = no limit /// null = default /// - private SpeedValue ?currentPaletteSpeedLimit = new SpeedValue(-1f); + private SpeedValue? currentPaletteSpeedLimit = new SpeedValue(-1f); private readonly Dictionary> segmentCenterByDir = new Dictionary>(); @@ -96,12 +96,21 @@ private struct RenderData { private RenderData renderData_; public SpeedLimitsTool(TrafficManagerTool mainTool) - : base(mainTool) - { + : base(mainTool) { CachedVisibleSegmentIds = new GenericArrayCache(NetManager.MAX_SEGMENT_COUNT); LastCachedCamera = new CameraTransformValue(); } + internal static void SetSpeedLimit(LanePos lane, SpeedValue? speed) { + ushort segmentId = lane.laneId.ToLane().m_segment; + SpeedLimitManager.Instance.SetSpeedLimit( + segmentId: segmentId, + laneIndex: lane.laneIndex, + laneInfo: segmentId.ToSegment().Info.m_lanes[lane.laneIndex], + laneId: lane.laneId, + speedLimit: speed?.GameUnits); + } + public override bool IsCursorInPanel() { return base.IsCursorInPanel() || cursorInSecondaryPanel; } @@ -156,17 +165,21 @@ public override void OnToolGUI(Event e) { private void RenderLaneOverlay(RenderManager.CameraInfo cameraInfo, uint laneId) { var marker = new SegmentLaneMarker(laneBuffer[laneId].m_bezier); bool pressed = Input.GetMouseButton(0); - Color color = MainTool.GetToolColor(pressed,false); + Color color = MainTool.GetToolColor(pressed, false); if (!ShowLimitsPerLane) { marker.Size = 3f; // lump the lanes together. } marker.RenderOverlay(cameraInfo, color, pressed); } + /// + /// Renders all lanes with the given + /// if NetInfo.Direction.None, all lanes are rendered. + /// private int RenderSegmentSideOverlay( RenderManager.CameraInfo cameraInfo, ushort segmentId, - NetInfo.Direction finalDirection) { + NetInfo.Direction finalDirection = NetInfo.Direction.None) { int count = 0; bool pressed = Input.GetMouseButton(0); Color color = MainTool.GetToolColor(pressed, false); @@ -180,7 +193,7 @@ private int RenderSegmentSideOverlay( byte laneIndex) => { bool render = (laneInfo.m_laneType & SpeedLimitManager.LANE_TYPES) != 0; render &= (laneInfo.m_vehicleType & SpeedLimitManager.VEHICLE_TYPES) != 0; - render &= laneInfo.m_finalDirection == finalDirection; + render &= laneInfo.m_finalDirection == finalDirection || finalDirection == NetInfo.Direction.None; if (render) { RenderLaneOverlay(cameraInfo, laneId); count++; @@ -193,6 +206,10 @@ private int RenderSegmentSideOverlay( private void RenderLanes(RenderManager.CameraInfo cameraInfo) { if (!MultiSegmentMode) { RenderLaneOverlay(cameraInfo, renderData_.LaneId); + } else if (RoundaboutMassEdit.Instance.TraverseLoop(renderData_.SegmentId, out var segmentList)) { + var lanes = FollowRoundaboutLane(segmentList, renderData_.SegmentId, renderData_.SortedLaneIndex); + foreach (var lane in lanes) + RenderLaneOverlay(cameraInfo, lane.laneId); } else { bool LaneVisitorFun(SegmentLaneTraverser.SegmentLaneVisitData data) { if (data.SortedLaneIndex == renderData_.SortedLaneIndex) { @@ -200,6 +217,7 @@ bool LaneVisitorFun(SegmentLaneTraverser.SegmentLaneVisitData data) { } return true; } + { SegmentLaneTraverser.Traverse( renderData_.SegmentId, SegmentTraverser.TraverseDirection.AnyDirection, @@ -211,10 +229,53 @@ bool LaneVisitorFun(SegmentLaneTraverser.SegmentLaneVisitData data) { LaneVisitorFun); } } + } + + /// + /// iterates through the given roundabout returning an enumeration + /// of all lanes with a matching based on + /// + /// input list of roundabout segments (must be oneway, and in the same direction). + /// The segment to match lane agaisnt + /// + private IEnumerable FollowRoundaboutLane(List segmentList, ushort segmentId0, int sortedLaneIndex) { + bool invert0 = segmentId0.ToSegment().m_flags.IsFlagSet(NetSegment.Flags.Invert); + int count0 = netService.GetSortedLanes( + segmentId: segmentId0, + segment: ref segmentId0.ToSegment(), + startNode: null, + laneTypeFilter: SpeedLimitManager.LANE_TYPES, + vehicleTypeFilter: SpeedLimitManager.VEHICLE_TYPES, + sort: false).Count; + foreach (ushort segmentId in segmentList) { + bool invert = segmentId.ToSegment().m_flags.IsFlagSet(NetSegment.Flags.Invert); + var lanes = netService.GetSortedLanes( + segmentId: segmentId, + segment: ref segmentId.ToSegment(), + startNode: null, + laneTypeFilter: SpeedLimitManager.LANE_TYPES, + vehicleTypeFilter: SpeedLimitManager.VEHICLE_TYPES, + reverse: invert != invert0, + sort: true); + int index = sortedLaneIndex; + + // if lane count does not match, assume segments are connected from outer side of the roundabout. + if (invert0) { + int diff = lanes.Count - count0; + index += diff; + } + if (0 <= index && index < lanes.Count) { + yield return lanes[index]; + } + } // foreach + } private void RenderSegmentsSide(RenderManager.CameraInfo cameraInfo) { if (!MultiSegmentMode) { RenderSegmentSideOverlay(cameraInfo, renderData_.SegmentId, renderData_.FinalDirection); + } else if (RoundaboutMassEdit.Instance.TraverseLoop(renderData_.SegmentId, out var segmentList)) { + foreach (ushort segmentId in segmentList) + RenderSegmentSideOverlay(cameraInfo, segmentId); } else { SegmentTraverser.Traverse( renderData_.SegmentId, @@ -305,8 +366,7 @@ private void ShowSigns(bool viewOnly) { bool hover = false; for (int segmentIdIndex = CachedVisibleSegmentIds.Size - 1; segmentIdIndex >= 0; - segmentIdIndex--) - { + segmentIdIndex--) { ushort segmentId = CachedVisibleSegmentIds.Values[segmentIdIndex]; // draw speed limits if ((MainTool.GetToolMode() == ToolMode.VehicleRestrictions) && @@ -315,7 +375,7 @@ private void ShowSigns(bool viewOnly) { } // no speed limit overlay on selected segment when in vehicle restrictions mode - hover|= DrawSpeedLimitHandles( + hover |= DrawSpeedLimitHandles( segmentId, ref netManager.m_segments.m_buffer[segmentId], viewOnly, @@ -485,8 +545,7 @@ private void GuiDefaultsWindow(int num) { GUILayout.FlexibleSpace(); if (GUILayout.Button(Translation.SpeedLimits.Get("Button:Save"), - GUILayout.Width(70))) - { + GUILayout.Width(70))) { SpeedLimitManager.Instance.FixCurrentSpeedLimits(info); SpeedLimitManager.Instance.SetCustomNetInfoSpeedLimit(info, currentSpeedLimit.GameUnits); } @@ -512,8 +571,7 @@ private void UpdateRoadTex(NetInfo info) { if (info != null) { if ((info.m_Atlas != null) && (info.m_Atlas.material != null) && (info.m_Atlas.material.mainTexture != null) && - info.m_Atlas.material.mainTexture is Texture2D mainTex) - { + info.m_Atlas.material.mainTexture is Texture2D mainTex) { UITextureAtlas.SpriteInfo spriteInfo = info.m_Atlas[info.m_Thumbnail]; if ((spriteInfo != null) && (spriteInfo.texture != null) && @@ -569,7 +627,7 @@ private void GuiSpeedLimitsWindow(int num) { foreach (SpeedValue speedLimit in allSpeedLimits) { // Highlight palette item if it is very close to its float speed - if (currentPaletteSpeedLimit !=null && + if (currentPaletteSpeedLimit != null && FloatUtil.NearlyEqual( (float)currentPaletteSpeedLimit?.GameUnits, speedLimit.GameUnits)) { @@ -589,7 +647,7 @@ private void GuiSpeedLimitsWindow(int num) { } } { - if (currentPaletteSpeedLimit == null ) { + if (currentPaletteSpeedLimit == null) { GUI.color = Color.gray; } GuiSpeedLimitsWindow_AddClearButton(); @@ -606,7 +664,7 @@ private void GuiSpeedLimitsWindow(int num) { if (GUILayout.Button(Translation.SpeedLimits.Get("Window.Title:Default speed limits"), GUILayout.Width(200))) { - TrafficManagerTool.ShowAdvisor(this.GetType().Name + "_Defaults"); + TrafficManagerTool.ShowAdvisor(GetType().Name + "_Defaults"); defaultsWindowVisible = true; } @@ -728,7 +786,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, Vector3 center = segment.m_bounds.center; NetManager netManager = Singleton.instance; - SpeedValue ?speedLimitToSet = viewOnly + SpeedValue? speedLimitToSet = viewOnly ? new SpeedValue(-1f) : currentPaletteSpeedLimit; @@ -812,8 +870,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, if (!viewOnly && !onlyMonorailLanes && ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) != - VehicleInfo.VehicleType.None)) - { + VehicleInfo.VehicleType.None)) { Texture2D tex1 = RoadUI.VehicleInfoSignTextures[ LegacyExtVehicleType.ToNew(ExtVehicleType.PassengerTrain)]; MainTool.DrawStaticSquareOverlayGridTexture( @@ -845,8 +902,15 @@ private bool DrawSpeedLimitHandles(ushort segmentId, speedLimitToSet?.GameUnits); if (MultiSegmentMode) { + if (new RoundaboutMassEdit().TraverseLoop(segmentId, out var segmentList)) { + var lanes = FollowRoundaboutLane(segmentList, segmentId, sortedLaneIndex); + foreach (var lane in lanes) { + if (lane.laneId == laneId) // the speed limit for this lane has already been set. + continue; + SetSpeedLimit(lane, speedLimitToSet); + } + } else { int slIndexCopy = sortedLaneIndex; - SegmentLaneTraverser.Traverse( segmentId, SegmentTraverser.TraverseDirection.AnyDirection, @@ -883,6 +947,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, }); } } + } ++x; } @@ -897,7 +962,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, GeometryUtil.CalculateSegmentCenterByDir( segmentId, segCenter, - speedLimitSignSize*TrafficManagerTool.MAX_ZOOM); + speedLimitSignSize * TrafficManagerTool.MAX_ZOOM); } foreach (KeyValuePair e in segCenter) { @@ -941,6 +1006,13 @@ private bool DrawSpeedLimitHandles(ushort segmentId, currentPaletteSpeedLimit?.GameUnits); if (MultiSegmentMode) { + if (new RoundaboutMassEdit().TraverseLoop(segmentId, out var segmentList)) { + foreach (ushort segId in segmentList) { + SpeedLimitManager.Instance.SetSpeedLimit( + segId, + currentPaletteSpeedLimit?.GameUnits); + } + } else { NetInfo.Direction normDir = e.Key; if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) { normDir = NetInfo.InvertDirection(normDir); @@ -954,8 +1026,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, SegmentTraverser.SegmentStopCriterion.Junction, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES, - data => - { + data => { if (data.SegVisitData.Initial) { return true; } @@ -974,8 +1045,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, if (((netManager.m_segments.m_buffer[otherSegmentId].m_flags & NetSegment.Flags.Invert) - != NetSegment.Flags.None) ^ reverse) - { + != NetSegment.Flags.None) ^ reverse) { otherNormDir = NetInfo.InvertDirection(otherNormDir); } @@ -988,6 +1058,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, return true; }); + } } } @@ -1124,6 +1195,5 @@ public static float GetVerticalTextureScale() { ? 1.25f : 1.0f; } - } // end class }