Eclipse SUMO - Simulation of Urban MObility
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
MSTrainHelper.cpp
Go to the documentation of this file.
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/****************************************************************************/
18// A class that helps computing positions of a train's carriages.
19/****************************************************************************/
20#include <config.h>
21
22#include <microsim/MSLane.h>
24#include <microsim/MSVehicle.h>
25#include <microsim/MSEdge.h>
26#include "MSTrainHelper.h"
27
28#define MIN_SCALED_CARRIAGE_LENGTH 5
29
31
32// ===========================================================================
33// method definitions
34// ===========================================================================
35void
36MSTrainHelper::computeTrainDimensions(double exaggeration, bool secondaryShape, double scaledLength, int vehicleQuality) {
37 const MSVehicleType& vtype = myTrain->getVehicleType();
38 const double laneFactor = (myTrain->getLane() != nullptr
39 ? myTrain->getLane()->getLengthGeometryFactor(secondaryShape)
40 : (myTrain->getEdge()->getLanes().size() > 0 ? myTrain->getEdge()->getLanes()[0]->getLengthGeometryFactor(secondaryShape) : 1));
41 double totalLength = vtype.getLength();
42 const double geometryScale = scaledLength / totalLength;
43 myUpscaleLength = getUpscaleLength(exaggeration, totalLength, vtype.getWidth(), vehicleQuality);
46 if (myLocomotiveLength == 0) {
48 }
49 const double minLength = MIN2(myLocomotiveLength, myDefaultLength);
50 myUnscale = geometryScale == 1 && laneFactor != 1;
51 if (geometryScale < 1 && minLength * geometryScale < MIN_SCALED_CARRIAGE_LENGTH) {
52 const double rescaleSmall = MIN_SCALED_CARRIAGE_LENGTH / (minLength * geometryScale);
53 myLocomotiveLength *= rescaleSmall;
54 myDefaultLength *= rescaleSmall;
55 }
57 myLength = totalLength * myUpscaleLength;
58 myHalfWidth = 0.5 * vtype.getWidth() * exaggeration;
59 myNumCarriages = MAX2(1, 1 + (int)((myLength - myLocomotiveLength) / (myDefaultLength + myCarriageGap) + 0.5)); // Round to closest integer.
60 if (myUpscaleLength > 1 && vehicleQuality != 4) {
61 // at high zoom, it doesn't help to draw many carriages)
64 }
65 assert(myNumCarriages > 0);
66 if (myNumCarriages == 1) {
67 myCarriageGap = 0;
68 }
76 }
78}
79
80
81void
82MSTrainHelper::computeCarriages(bool reversed, bool secondaryShape) {
83 myCarriages.clear();
84
85 const MSLane* lane = myTrain->getLane(); // Lane on which the carriage's front is situated.
86 int furtherIndex = 0;
87 const MSLane* backLane = lane; // Lane on which the carriage's back is situated.
88 int backFurtherIndex = furtherIndex;
89 // Offsets of front and back parts of a carriage.
90 double carriageOffset = myTrain->getPositionOnLane();
92 // This still produces some artifacts when not fully on the current lane.
93 carriageOffset = MIN2(carriageOffset + myTrain->getLength(), lane->getLength());
94 }
95 const double unscale = myUnscale ? 1. / lane->getLengthGeometryFactor() : 1.;
96 double carriageBackOffset = carriageOffset - myFirstCarriageLength * unscale;
97
98 myFirstCarriageNo = 0; // default case - we're going forwards
100 if (myIsReversed) {
102 if (myNumCarriages > 1) {
103 carriageBackOffset = carriageOffset - myCarriageLength;
104 }
105 }
107 myFirstCarriageNo = -1; // don't draw locomotive
108 }
109
111 || (myTrain->getVClass() & (SVC_RAIL_ELECTRIC | SVC_RAIL_FAST | SVC_RAIL)) == 0 ? 0 : 1;
112
113 const double lateralOffset = (myTrain->isParking() && myTrain->getNextStopParameter()->posLat == INVALID_DOUBLE
114 ? (myTrain->getLane()->getWidth() * (MSGlobals::gLefthand ? -1 : 1))
116
117 for (int i = 0; i < myNumCarriages; ++i) {
118 Carriage* carriage = new Carriage();
119 if (i == myFirstCarriageNo) {
120 if (myFirstCarriageNo > 0) {
121 // Previous loop iteration has adjusted backpos for a normal carriage so have to correct
122 carriageBackOffset += myCarriageLengthWithGap;
123 carriageBackOffset -= myFirstCarriageLength + myCarriageGap;
124 }
125 }
126 while (carriageOffset < 0) {
127 const MSLane* prev = myTrain->getPreviousLane(lane, furtherIndex);
128 if (prev != lane) {
129 carriageOffset += prev->getLength();
130 } else {
131 break;
132 }
133 lane = prev;
134 }
135 while (carriageBackOffset < 0) {
136 const MSLane* prev = myTrain->getPreviousLane(backLane, backFurtherIndex);
137 if (prev != backLane) {
138 if (myUnscale) {
139 carriageBackOffset *= backLane->getLengthGeometryFactor() / prev->getLengthGeometryFactor();
140 }
141 carriageBackOffset += prev->getLength();
142 } else {
143 break;
144 }
145 backLane = prev;
146 }
147 carriage->front = lane->getShape(secondaryShape).positionAtOffset2D(carriageOffset * lane->getLengthGeometryFactor(secondaryShape), lateralOffset, true);
148 carriage->back = backLane->getShape(secondaryShape).positionAtOffset2D(carriageBackOffset * backLane->getLengthGeometryFactor(secondaryShape), lateralOffset, true);
149 myCarriages.push_back(carriage);
150 lane = backLane;
151 carriageOffset = carriageBackOffset - myCarriageGap;
152 carriageBackOffset -= myCarriageLengthWithGap * unscale;
153 }
154}
155
156
157void
159 for (Carriage* carriage : myCarriages) {
160 Position dir = carriage->front - carriage->back;
161 const double carriageLength = dir.length2D();
162 if (carriageLength > 0.0) {
163 dir.norm2D();
164 for (int j = 1; j <= myCarriageDoors; j++) {
165 const double doorOffset = j * carriageLength / (myCarriageDoors + 1);
166 carriage->doorPositions.push_back(carriage->front - dir * doorOffset);
167 }
168 }
169 }
170}
171
172
173void
174MSTrainHelper::computeUnboardingPositions(double passengerRadius, std::vector<Position>& unboardingPositions) {
175 passengerRadius += PEDESTRIAN_RADIUS_EXTRA_TOLERANCE;
176 for (Carriage* carriage : myCarriages) {
177 Position dir = carriage->front - carriage->back;
178 const double carriageLength = dir.length2D();
179 if (carriageLength > 0.0) {
180 dir.norm2D();
181 const Position perp = Position(-dir.y(), dir.x());
182 double nbrLongitudinalCells, longitudinalOffset;
183 longitudinalOffset = std::modf((carriageLength - 2.0 * passengerRadius) / (2.0 * passengerRadius), &nbrLongitudinalCells);
184 double nbrLateralCells, lateralOffset;
185 lateralOffset = std::modf((myHalfWidth * 2.0 - 2.0 * passengerRadius) / (2.0 * passengerRadius), &nbrLateralCells);
186 const Position gridOrigin = carriage->back + dir * (passengerRadius + 0.5 * longitudinalOffset) - perp * (myHalfWidth - passengerRadius - 0.5 * lateralOffset);
187 for (unsigned int i = 0; i <= (unsigned int)nbrLongitudinalCells; i++) {
188 for (unsigned int j = 0; j <= (unsigned int)nbrLateralCells; j++) {
189 carriage->unboardingPositions.push_back(gridOrigin + dir * i * 2.0 * passengerRadius + perp * j * 2.0 * passengerRadius);
190 }
191 }
192 }
193 std::copy(carriage->unboardingPositions.begin(), carriage->unboardingPositions.end(), std::back_inserter(unboardingPositions));
194 }
195 // Shuffle the positions upstream so that we don't have to sample later on, just pop the last element.
196 RandHelper::shuffle(unboardingPositions);
197}
198
199
200double
201MSTrainHelper::getUpscaleLength(double upscale, double length, double width, int vehicleQuality) {
202 if (upscale > 1 && length > 5 && width < 5 && vehicleQuality != 4) {
203 return MAX2(1.0, upscale * 5 / length);
204 } else {
205 return upscale;
206 }
207}
#define MIN_SCALED_CARRIAGE_LENGTH
@ SVC_RAIL
vehicle is a not electrified rail
@ SVC_RAIL_FAST
vehicle that is allowed to drive on high-speed rail tracks
@ SVC_RAIL_ELECTRIC
rail vehicle that requires electrified tracks
const double INVALID_DOUBLE
invalid double
Definition StdDefs.h:64
T MIN2(T a, T b)
Definition StdDefs.h:76
T MAX2(T a, T b)
Definition StdDefs.h:82
bool isReversed() const
Returns whether the logical state of the vehicle is reversed - for drawing.
const SUMOVehicleParameter::Stop * getNextStopParameter() const
return parameters for the next stop (SUMOVehicle Interface)
double getLength() const
Returns the vehicle's length.
bool isParking() const
Returns whether the vehicle is parking.
const MSEdge * getEdge() const
Returns the edge the vehicle is currently at.
SUMOVehicleClass getVClass() const
Returns the vehicle's access class.
const MSVehicleType & getVehicleType() const
Returns the vehicle's type definition.
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition MSEdge.h:168
static bool gLefthand
Whether lefthand-drive is being simulated.
Definition MSGlobals.h:174
Representation of a lane in the micro simulation.
Definition MSLane.h:84
virtual double getLengthGeometryFactor(bool) const
Definition MSLane.h:290
double getLength() const
Returns the lane's length.
Definition MSLane.h:606
virtual const PositionVector & getShape(bool) const
Definition MSLane.h:294
double getWidth() const
Returns the lane's width.
Definition MSLane.h:635
double myFirstCarriageLength
void computeTrainDimensions(double exaggeration, bool secondaryShape, double scaledLength, int vehicleQuality)
const MSVehicle * myTrain
static const double PEDESTRIAN_RADIUS_EXTRA_TOLERANCE
small extra tolerance used to avoid constraint violations
std::vector< Carriage * > myCarriages
void computeDoorPositions()
compute door positions on demand and fills the carriage structures
double myLocomotiveLength
double myCarriageLengthWithGap
void computeUnboardingPositions(double passengerRadius, std::vector< Position > &unboardingPositions)
compute unboarding positions on demand and fills the carriage structures
double myDefaultLength
int myFirstPassengerCarriage
double myCarriageLength
double getUpscaleLength() const
double myUpscaleLength
void computeCarriages(bool reversed, bool secondaryShape)
const MSLane * getPreviousLane(const MSLane *current, int &furtherIndex) const
MSAbstractLaneChangeModel & getLaneChangeModel()
const MSLane * getLane() const
Returns the lane the vehicle is on.
Definition MSVehicle.h:581
double getLateralPositionOnLane() const
Get the vehicle's lateral position on the lane.
Definition MSVehicle.h:413
double getPositionOnLane() const
Get the vehicle's position along the lane.
Definition MSVehicle.h:374
The car-following model and parameter.
double getWidth() const
Get the width which vehicles of this class shall have when being drawn.
double getLength() const
Get vehicle's length [m].
const SUMOVTypeParameter & getParameter() const
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
void norm2D()
Normalizes the given vector.
Definition Position.h:179
double x() const
Returns the x-position.
Definition Position.h:52
double length2D() const
Computes the length of the given vector neglecting the z coordinate.
Definition Position.h:174
double y() const
Returns the y-position.
Definition Position.h:57
Position positionAtOffset2D(double pos, double lateralOffset=0, bool extrapolateBeyond=false) const
Returns the position at the given length.
static void shuffle(std::vector< T > &v, SumoRNG *rng=nullptr)
Definition RandHelper.h:240
double locomotiveLength
the length of the locomotive
double carriageLength
the length of train carriages
int carriageDoors
the number of doors per carriage
double carriageGap
the length of the gap between carriages
double posLat
the lateral offset when stopping