Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MSTrainHelper.cpp
15 : /// @author Benjamin Coueraud
16 : /// @date Fri, 8 Feb 2024
17 : ///
18 : // A class that helps computing positions of a train's carriages.
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #include <microsim/MSLane.h>
23 : #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
24 : #include <microsim/MSVehicle.h>
25 : #include <microsim/MSEdge.h>
26 : #include "MSTrainHelper.h"
27 :
28 : #define MIN_SCALED_CARRIAGE_LENGTH 5.
29 :
30 : const double MSTrainHelper::PEDESTRIAN_RADIUS_EXTRA_TOLERANCE = 0.01;
31 :
32 : // ===========================================================================
33 : // method definitions
34 : // ===========================================================================
35 : void
36 4709 : MSTrainHelper::computeTrainDimensions(double exaggeration, bool secondaryShape, double scaledLength, int vehicleQuality) {
37 4709 : const MSVehicleType& vtype = myTrain->getVehicleType();
38 4709 : const double laneFactor = (myTrain->getLane() != nullptr
39 4709 : ? myTrain->getLane()->getLengthGeometryFactor(secondaryShape)
40 0 : : (myTrain->getEdge()->getLanes().size() > 0 ? myTrain->getEdge()->getLanes()[0]->getLengthGeometryFactor(secondaryShape) : 1));
41 : double totalLength = vtype.getLength();
42 4709 : const double geometryScale = scaledLength / totalLength;
43 4709 : myUpscaleLength = getUpscaleLength(exaggeration, totalLength, vtype.getWidth(), vehicleQuality);
44 4709 : myLocomotiveLength = vtype.getParameter().locomotiveLength * myUpscaleLength;
45 4709 : myDefaultLength = vtype.getParameter().carriageLength * myUpscaleLength;
46 4709 : if (myLocomotiveLength == 0) {
47 0 : myLocomotiveLength = myDefaultLength;
48 : }
49 4709 : const double minLength = MIN2(myLocomotiveLength, myDefaultLength);
50 : const double minScaledLength = MIN2(MIN_SCALED_CARRIAGE_LENGTH, minLength);
51 4709 : myUnscale = geometryScale == 1 && laneFactor != 1;
52 4709 : if (geometryScale < 1 && minLength * geometryScale < minScaledLength) {
53 1025 : const double rescaleSmall = minScaledLength / (minLength * geometryScale);
54 1025 : myLocomotiveLength *= rescaleSmall;
55 1025 : myDefaultLength *= rescaleSmall;
56 : }
57 4709 : myCarriageGap = vtype.getParameter().carriageGap * myUpscaleLength;
58 4709 : myLength = totalLength * myUpscaleLength;
59 4709 : myHalfWidth = 0.5 * vtype.getWidth() * exaggeration;
60 4709 : myNumCarriages = MAX2(1, 1 + (int)((myLength - myLocomotiveLength) / (myDefaultLength + myCarriageGap) + 0.5)); // Round to closest integer.
61 4709 : if (myUpscaleLength > 1 && vehicleQuality != 4) {
62 : // at high zoom, it doesn't help to draw many carriages)
63 0 : myNumCarriages = MIN2(myNumCarriages, 2);
64 0 : myLocomotiveLength = myLength / 2;
65 : }
66 : assert(myNumCarriages > 0);
67 4709 : if (myNumCarriages == 1) {
68 72 : myCarriageGap = 0;
69 : }
70 4709 : myCarriageLengthWithGap = myLength / myNumCarriages;
71 4709 : myCarriageLength = myCarriageLengthWithGap - myCarriageGap;
72 4709 : myFirstCarriageLength = myCarriageLength;
73 4709 : if (myDefaultLength != myLocomotiveLength && myNumCarriages > 1) {
74 3868 : myFirstCarriageLength = myLocomotiveLength;
75 3868 : myCarriageLengthWithGap = (myLength - myLocomotiveLength) / (myNumCarriages - 1);
76 3868 : myCarriageLength = myCarriageLengthWithGap - myCarriageGap;
77 : }
78 4709 : myCarriageDoors = vtype.getParameter().carriageDoors;
79 4709 : }
80 :
81 :
82 : void
83 4709 : MSTrainHelper::computeCarriages(bool reversed, bool secondaryShape) {
84 : myCarriages.clear();
85 :
86 4709 : const MSLane* lane = myTrain->getLane(); // Lane on which the carriage's front is situated.
87 4709 : int furtherIndex = 0;
88 : const MSLane* backLane = lane; // Lane on which the carriage's back is situated.
89 4709 : int backFurtherIndex = furtherIndex;
90 : // Offsets of front and back parts of a carriage.
91 4709 : double carriageOffset = myTrain->getPositionOnLane();
92 4709 : if (myTrain->getLaneChangeModel().isOpposite()) {
93 : // This still produces some artifacts when not fully on the current lane.
94 0 : carriageOffset = MIN2(carriageOffset + myTrain->getLength(), lane->getLength());
95 : }
96 4709 : const double unscale = myUnscale ? 1. / lane->getLengthGeometryFactor() : 1.;
97 4709 : double carriageBackOffset = carriageOffset - myFirstCarriageLength * unscale;
98 :
99 4709 : myFirstCarriageNo = 0; // default case - we're going forwards
100 4709 : myIsReversed = (myTrain->isReversed() && reversed) || myTrain->getLaneChangeModel().isOpposite();
101 4709 : if (myIsReversed) {
102 899 : myFirstCarriageNo = myNumCarriages - 1;
103 899 : if (myNumCarriages > 1) {
104 899 : carriageBackOffset = carriageOffset - myCarriageLength;
105 : }
106 : }
107 4709 : if (myTrain->getVehicleType().getParameter().locomotiveLength == 0) {
108 0 : myFirstCarriageNo = -1; // don't draw locomotive
109 : }
110 :
111 3868 : myFirstPassengerCarriage = myDefaultLength == myLocomotiveLength || myNumCarriages == 1
112 8577 : || (myTrain->getVClass() & (SVC_RAIL_ELECTRIC | SVC_RAIL_FAST | SVC_RAIL)) == 0 ? 0 : 1;
113 :
114 4709 : const double lateralOffset = (myTrain->isParking() && myTrain->getNextStopParameter()->posLat == INVALID_DOUBLE
115 9418 : ? (myTrain->getLane()->getWidth() * (MSGlobals::gLefthand ? -1 : 1))
116 4709 : : -myTrain->getLateralPositionOnLane());
117 :
118 19713 : for (int i = 0; i < myNumCarriages; ++i) {
119 15004 : Carriage* carriage = new Carriage();
120 15004 : if (i == myFirstCarriageNo) {
121 4709 : if (myFirstCarriageNo > 0) {
122 : // Previous loop iteration has adjusted backpos for a normal carriage so have to correct
123 899 : carriageBackOffset += myCarriageLengthWithGap;
124 899 : carriageBackOffset -= myFirstCarriageLength + myCarriageGap;
125 : }
126 : }
127 15192 : while (carriageOffset < 0) {
128 305 : const MSLane* prev = myTrain->getPreviousLane(lane, furtherIndex);
129 305 : if (prev != lane) {
130 188 : carriageOffset += prev->getLength();
131 : } else {
132 : break;
133 : }
134 : lane = prev;
135 : }
136 16030 : while (carriageBackOffset < 0) {
137 1286 : const MSLane* prev = myTrain->getPreviousLane(backLane, backFurtherIndex);
138 1286 : if (prev != backLane) {
139 1026 : if (myUnscale) {
140 527 : carriageBackOffset *= backLane->getLengthGeometryFactor() / prev->getLengthGeometryFactor();
141 : }
142 1026 : carriageBackOffset += prev->getLength();
143 : } else {
144 : break;
145 : }
146 : backLane = prev;
147 : }
148 15004 : carriage->front = lane->getShape(secondaryShape).positionAtOffset2D(carriageOffset * lane->getLengthGeometryFactor(secondaryShape), lateralOffset, true);
149 15004 : carriage->back = backLane->getShape(secondaryShape).positionAtOffset2D(carriageBackOffset * backLane->getLengthGeometryFactor(secondaryShape), lateralOffset, true);
150 15004 : myCarriages.push_back(carriage);
151 : lane = backLane;
152 15004 : furtherIndex = backFurtherIndex;
153 15004 : carriageOffset = carriageBackOffset - myCarriageGap;
154 15004 : carriageBackOffset -= myCarriageLengthWithGap * unscale;
155 : }
156 4709 : }
157 :
158 :
159 : void
160 336 : MSTrainHelper::computeDoorPositions() {
161 1560 : for (Carriage* carriage : myCarriages) {
162 : Position dir = carriage->front - carriage->back;
163 : const double carriageLength = dir.length2D();
164 1224 : if (carriageLength > 0.0) {
165 1224 : dir.norm2D();
166 3672 : for (int j = 1; j <= myCarriageDoors; j++) {
167 2448 : const double doorOffset = j * carriageLength / (myCarriageDoors + 1);
168 2448 : carriage->doorPositions.push_back(carriage->front - dir * doorOffset);
169 : }
170 : }
171 : }
172 336 : }
173 :
174 :
175 : void
176 0 : MSTrainHelper::computeUnboardingPositions(double passengerRadius, std::vector<Position>& unboardingPositions) {
177 0 : passengerRadius += PEDESTRIAN_RADIUS_EXTRA_TOLERANCE;
178 0 : for (Carriage* carriage : myCarriages) {
179 : Position dir = carriage->front - carriage->back;
180 : const double carriageLength = dir.length2D();
181 0 : if (carriageLength > 0.0) {
182 0 : dir.norm2D();
183 0 : const Position perp = Position(-dir.y(), dir.x());
184 : double nbrLongitudinalCells, longitudinalOffset;
185 0 : longitudinalOffset = std::modf((carriageLength - 2.0 * passengerRadius) / (2.0 * passengerRadius), &nbrLongitudinalCells);
186 : double nbrLateralCells, lateralOffset;
187 0 : lateralOffset = std::modf((myHalfWidth * 2.0 - 2.0 * passengerRadius) / (2.0 * passengerRadius), &nbrLateralCells);
188 0 : const Position gridOrigin = carriage->back + dir * (passengerRadius + 0.5 * longitudinalOffset) - perp * (myHalfWidth - passengerRadius - 0.5 * lateralOffset);
189 0 : for (unsigned int i = 0; i <= (unsigned int)nbrLongitudinalCells; i++) {
190 0 : for (unsigned int j = 0; j <= (unsigned int)nbrLateralCells; j++) {
191 0 : carriage->unboardingPositions.push_back(gridOrigin + dir * i * 2.0 * passengerRadius + perp * j * 2.0 * passengerRadius);
192 : }
193 : }
194 : }
195 : std::copy(carriage->unboardingPositions.begin(), carriage->unboardingPositions.end(), std::back_inserter(unboardingPositions));
196 : }
197 : // Shuffle the positions upstream so that we don't have to sample later on, just pop the last element.
198 0 : RandHelper::shuffle(unboardingPositions);
199 0 : }
200 :
201 :
202 : double
203 13232189 : MSTrainHelper::getUpscaleLength(double upscale, double length, double width, int vehicleQuality) {
204 13232189 : if (upscale > 1 && length > 5 && width < 5 && vehicleQuality != 4) {
205 0 : return MAX2(1.0, upscale * 5 / length);
206 : } else {
207 : return upscale;
208 : }
209 : }
|