LCOV - code coverage report
Current view: top level - src/microsim - MSTrainHelper.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 80.0 % 105 84
Test Date: 2025-12-06 15:35:27 Functions: 80.0 % 5 4

            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              : }
        

Generated by: LCOV version 2.0-1