Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 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 "MSTrainHelper.h"
26 :
27 :
28 : const double MSTrainHelper::CARRIAGE_DOOR_WIDTH = 1.5;
29 : const double MSTrainHelper::PEDESTRIAN_RADIUS_EXTRA_TOLERANCE = 0.01;
30 :
31 : // ===========================================================================
32 : // method definitions
33 : // ===========================================================================
34 : void
35 4694 : MSTrainHelper::computeTrainDimensions(double exaggeration, int vehicleQuality) {
36 4694 : const MSVehicleType& vtype = myTrain->getVehicleType();
37 : const double totalLength = vtype.getLength();
38 4694 : myUpscaleLength = getUpscaleLength(exaggeration, totalLength, vtype.getWidth(), vehicleQuality);
39 4694 : myLocomotiveLength = vtype.getParameter().locomotiveLength * myUpscaleLength;
40 4694 : myDefaultLength = vtype.getParameter().carriageLength * myUpscaleLength;
41 4694 : if (myLocomotiveLength == 0) {
42 0 : myLocomotiveLength = myDefaultLength;
43 : }
44 4694 : myCarriageGap = vtype.getParameter().carriageGap * myUpscaleLength;
45 4694 : myLength = totalLength * myUpscaleLength;
46 4694 : myHalfWidth = 0.5 * vtype.getWidth() * exaggeration;
47 4694 : myNumCarriages = MAX2(1, 1 + (int)((myLength - myLocomotiveLength) / (myDefaultLength + myCarriageGap) + 0.5)); // Round to closest integer.
48 4694 : if (myUpscaleLength > 1 && vehicleQuality != 4) {
49 : // at high zoom, it doesn't help to draw many carriages)
50 0 : myNumCarriages = MIN2(myNumCarriages, 2);
51 0 : myLocomotiveLength = myLength / 2;
52 : }
53 : assert(myNumCarriages > 0);
54 4694 : if (myNumCarriages == 1) {
55 69 : myCarriageGap = 0;
56 : }
57 4694 : myCarriageLengthWithGap = myLength / myNumCarriages;
58 4694 : myCarriageLength = myCarriageLengthWithGap - myCarriageGap;
59 4694 : myFirstCarriageLength = myCarriageLength;
60 4694 : if (myDefaultLength != myLocomotiveLength && myNumCarriages > 1) {
61 3856 : myFirstCarriageLength = myLocomotiveLength;
62 3856 : myCarriageLengthWithGap = (myLength - myLocomotiveLength) / (myNumCarriages - 1);
63 3856 : myCarriageLength = myCarriageLengthWithGap - myCarriageGap;
64 : }
65 4694 : myCarriageDoors = vtype.getParameter().carriageDoors;
66 4694 : }
67 :
68 :
69 : void
70 4694 : MSTrainHelper::computeCarriages(bool reversed, bool secondaryShape) {
71 : myCarriages.clear();
72 :
73 4694 : const MSLane* lane = myTrain->getLane(); // Lane on which the carriage's front is situated.
74 4694 : int furtherIndex = 0;
75 : const MSLane* backLane = lane; // Lane on which the carriage's back is situated.
76 4694 : int backFurtherIndex = furtherIndex;
77 : // Offsets of front and back parts of a carriage.
78 4694 : double carriageOffset = myTrain->getPositionOnLane();
79 4694 : if (myTrain->getLaneChangeModel().isOpposite()) {
80 : // This still produces some artifacts when not fully on the current lane.
81 0 : carriageOffset = MIN2(carriageOffset + myTrain->getLength(), lane->getLength());
82 : }
83 4694 : double carriageBackOffset = carriageOffset - myFirstCarriageLength;
84 :
85 : double curCLength = myFirstCarriageLength;
86 4694 : myFirstCarriageNo = 0; // default case - we're going forwards
87 4694 : myIsReversed = (myTrain->isReversed() && reversed) || myTrain->getLaneChangeModel().isOpposite();
88 4694 : if (myIsReversed) {
89 893 : myFirstCarriageNo = myNumCarriages - 1;
90 893 : if (myNumCarriages > 1) {
91 893 : carriageBackOffset = carriageOffset - myCarriageLength;
92 : }
93 : }
94 4694 : if (myTrain->getVehicleType().getParameter().locomotiveLength == 0) {
95 0 : myFirstCarriageNo = -1; // don't draw locomotive
96 : }
97 :
98 3856 : myFirstPassengerCarriage = myDefaultLength == myLocomotiveLength || myNumCarriages == 1
99 8550 : || (myTrain->getVClass() & (SVC_RAIL_ELECTRIC | SVC_RAIL_FAST | SVC_RAIL)) == 0 ? 0 : 1;
100 :
101 4694 : const double lateralOffset = (myTrain->isParking() && myTrain->getNextStopParameter()->posLat == INVALID_DOUBLE
102 9388 : ? (myTrain->getLane()->getWidth() * (MSGlobals::gLefthand ? -1 : 1))
103 4694 : : -myTrain->getLateralPositionOnLane());
104 :
105 19647 : for (int i = 0; i < myNumCarriages; ++i) {
106 14953 : Carriage* carriage = new Carriage();
107 14953 : if (i == myFirstCarriageNo) {
108 4694 : curCLength = myFirstCarriageLength;
109 4694 : if (myFirstCarriageNo > 0) {
110 : // Previous loop iteration has adjusted backpos for a normal carriage so have to correct
111 893 : carriageBackOffset += myCarriageLengthWithGap;
112 893 : carriageBackOffset -= myFirstCarriageLength + myCarriageGap;
113 : }
114 : } else {
115 10259 : curCLength = myCarriageLength;
116 : }
117 15878 : while (carriageOffset < 0) {
118 925 : const MSLane* prev = myTrain->getPreviousLane(lane, furtherIndex);
119 925 : if (prev != lane) {
120 808 : carriageOffset += prev->getLength();
121 : } else {
122 : // No lane available.
123 : carriageOffset = 0;
124 : }
125 : lane = prev;
126 : }
127 16213 : while (carriageBackOffset < 0) {
128 1260 : const MSLane* prev = myTrain->getPreviousLane(backLane, backFurtherIndex);
129 1260 : if (prev != backLane) {
130 1000 : carriageBackOffset += prev->getLength();
131 : } else {
132 : // No lane available.
133 : carriageBackOffset = 0;
134 : }
135 : backLane = prev;
136 : }
137 14953 : carriage->front = lane->getShape(secondaryShape).positionAtOffset(carriageOffset * lane->getLengthGeometryFactor(secondaryShape), lateralOffset);
138 14953 : carriage->back = backLane->getShape(secondaryShape).positionAtOffset(carriageBackOffset * backLane->getLengthGeometryFactor(secondaryShape), lateralOffset);
139 14953 : myCarriages.push_back(carriage);
140 :
141 14953 : carriageOffset -= (curCLength + myCarriageGap);
142 14953 : carriageBackOffset -= myCarriageLengthWithGap;
143 : }
144 4694 : }
145 :
146 :
147 : void
148 336 : MSTrainHelper::computeDoorPositions() {
149 1560 : for (Carriage* carriage : myCarriages) {
150 : Position dir = carriage->front - carriage->back;
151 : const double carriageLength = dir.length2D();
152 1224 : if (carriageLength > 0.0) {
153 1224 : dir.norm2D();
154 3672 : for (int j = 1; j <= myCarriageDoors; j++) {
155 2448 : const double doorOffset = j * carriageLength / (myCarriageDoors + 1);
156 2448 : carriage->doorPositions.push_back(carriage->front - dir * doorOffset);
157 : }
158 : }
159 : }
160 336 : }
161 :
162 :
163 : void
164 0 : MSTrainHelper::computeUnboardingPositions(double passengerRadius, std::vector<Position>& unboardingPositions) {
165 0 : passengerRadius += PEDESTRIAN_RADIUS_EXTRA_TOLERANCE;
166 0 : for (Carriage* carriage : myCarriages) {
167 : Position dir = carriage->front - carriage->back;
168 : const double carriageLength = dir.length2D();
169 0 : if (carriageLength > 0.0) {
170 0 : dir.norm2D();
171 0 : const Position perp = Position(-dir.y(), dir.x());
172 : double nbrLongitudinalCells, longitudinalOffset;
173 0 : longitudinalOffset = std::modf((carriageLength - 2.0 * passengerRadius) / (2.0 * passengerRadius), &nbrLongitudinalCells);
174 : double nbrLateralCells, lateralOffset;
175 0 : lateralOffset = std::modf((myHalfWidth * 2.0 - 2.0 * passengerRadius) / (2.0 * passengerRadius), &nbrLateralCells);
176 0 : const Position gridOrigin = carriage->back + dir * (passengerRadius + 0.5 * longitudinalOffset) - perp * (myHalfWidth - passengerRadius - 0.5 * lateralOffset);
177 0 : for (unsigned int i = 0; i <= (unsigned int)nbrLongitudinalCells; i++) {
178 0 : for (unsigned int j = 0; j <= (unsigned int)nbrLateralCells; j++) {
179 0 : carriage->unboardingPositions.push_back(gridOrigin + dir * i * 2.0 * passengerRadius + perp * j * 2.0 * passengerRadius);
180 : }
181 : }
182 : }
183 : std::copy(carriage->unboardingPositions.begin(), carriage->unboardingPositions.end(), std::back_inserter(unboardingPositions));
184 : }
185 : // Shuffle the positions upstream so that we don't have to sample later on, just pop the last element.
186 0 : RandHelper::shuffle(unboardingPositions);
187 0 : }
188 :
189 :
190 : double
191 8954065 : MSTrainHelper::getUpscaleLength(double upscale, double length, double width, int vehicleQuality) {
192 8954065 : if (upscale > 1 && length > 5 && width < 5 && vehicleQuality != 4) {
193 0 : return MAX2(1.0, upscale * 5 / length);
194 : } else {
195 : return upscale;
196 : }
197 : }
|