View Javadoc
1   package org.opentrafficsim.road.network.lane;
2   
3   import java.util.List;
4   
5   import org.djunits.value.vdouble.scalar.Length;
6   import org.opentrafficsim.core.network.LateralDirectionality;
7   
8   /**
9    * Slice info. Can be used {@code Lane} but also the editor.
10   * <p>
11   * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
12   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
13   * </p>
14   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
15   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
16   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
17   */
18  public class SliceInfo
19  {
20  
21      /** The offsets and widths at positions along the line, relative to the design line of the parent link. */
22      private final List<CrossSectionSlice> crossSectionSlices;
23  
24      /** Length of element for which slices are defined. */
25      private final Length length;
26  
27      /**
28       * Constructor.
29       * @param crossSectionSlices List&lt;CrossSectionSlice&gt;; slices.
30       * @param length Length; length of element for which slices are defined.
31       */
32      public SliceInfo(final List<CrossSectionSlice> crossSectionSlices, final Length length)
33      {
34          this.crossSectionSlices = crossSectionSlices;
35          this.length = length;
36      }
37  
38      /**
39       * Retrieve the lateral offset from the Link design line at the specified longitudinal position.
40       * @param fractionalPosition double; fractional longitudinal position on this Lane
41       * @return Length; the lateralCenterPosition at the specified longitudinal position
42       */
43      public final Length getLateralCenterPosition(final double fractionalPosition)
44      {
45          if (this.crossSectionSlices.size() == 1)
46          {
47              return this.getOffsetAtBegin();
48          }
49          if (this.crossSectionSlices.size() == 2)
50          {
51              return Length.interpolate(this.getOffsetAtBegin(), this.getOffsetAtEnd(), fractionalPosition);
52          }
53          int sliceNr = calculateSliceNumber(fractionalPosition);
54          double segmentPosition = fractionalPositionSegment(fractionalPosition, sliceNr);
55          return Length.interpolate(this.crossSectionSlices.get(sliceNr).getOffset(),
56                  this.crossSectionSlices.get(sliceNr + 1).getOffset(), segmentPosition);
57      }
58  
59      /**
60       * Return the width of this CrossSectionElement at a specified fractional longitudinal position.
61       * @param fractionalPosition double; the fractional longitudinal position
62       * @return Length; the width of this CrossSectionElement at the specified fractional longitudinal position.
63       */
64      public final Length getWidth(final double fractionalPosition)
65      {
66          if (this.crossSectionSlices.size() == 1)
67          {
68              return this.getBeginWidth();
69          }
70          if (this.crossSectionSlices.size() == 2)
71          {
72              return Length.interpolate(this.getBeginWidth(), this.getEndWidth(), fractionalPosition);
73          }
74          int sliceNr = calculateSliceNumber(fractionalPosition);
75          double segmentPosition = fractionalPositionSegment(fractionalPosition, sliceNr);
76          return Length.interpolate(this.crossSectionSlices.get(sliceNr).getWidth(),
77                  this.crossSectionSlices.get(sliceNr + 1).getWidth(), segmentPosition);
78      }
79  
80      /**
81       * Calculate the slice the fractional position is in.
82       * @param fractionalPosition double; the fractional position between 0 and 1 compared to the design line
83       * @return int; the lower slice number between 0 and number of slices - 1.
84       */
85      private int calculateSliceNumber(final double fractionalPosition)
86      {
87          for (int i = 0; i < this.crossSectionSlices.size() - 1; i++)
88          {
89              if (fractionalPosition >= this.crossSectionSlices.get(i).getRelativeLength().si / this.length.si
90                      && fractionalPosition <= this.crossSectionSlices.get(i + 1).getRelativeLength().si / this.length.si)
91              {
92                  return i;
93              }
94          }
95          return this.crossSectionSlices.size() - 2;
96      }
97  
98      /**
99       * Returns the fractional position along the segment between two cross-section slices.
100      * @param fractionalPosition double; fractional position on the whole link.
101      * @param sliceNumber int; slice number at the start of the segment.
102      * @return double; fractional position along the segment between two cross-section slices.
103      */
104     private double fractionalPositionSegment(final double fractionalPosition, final int sliceNumber)
105     {
106         double startPos = this.crossSectionSlices.get(sliceNumber).getRelativeLength().si / this.length.si;
107         double endPos = this.crossSectionSlices.get(sliceNumber + 1).getRelativeLength().si / this.length.si;
108         return (fractionalPosition - startPos) / (endPos - startPos);
109     }
110 
111     /**
112      * Retrieve the offset from the design line at the begin of the parent link.
113      * @return Length; the offset of this CrossSectionElement at the begin of the parent link
114      */
115     public final Length getOffsetAtBegin()
116     {
117         return this.crossSectionSlices.get(0).getOffset();
118     }
119 
120     /**
121      * Retrieve the offset from the design line at the end of the parent link.
122      * @return Length; the offset of this CrossSectionElement at the end of the parent link
123      */
124     public final Length getOffsetAtEnd()
125     {
126         return this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getOffset();
127     }
128 
129     /**
130      * Retrieve the width at the begin of the parent link.
131      * @return Length; the width of this CrossSectionElement at the begin of the parent link
132      */
133     public final Length getBeginWidth()
134     {
135         return this.crossSectionSlices.get(0).getWidth();
136     }
137 
138     /**
139      * Retrieve the width at the end of the parent link.
140      * @return Length; the width of this CrossSectionElement at the end of the parent link
141      */
142     public final Length getEndWidth()
143     {
144         return this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getWidth();
145     }
146 
147     /**
148      * Return the lateral offset from the design line of the parent Link of the Left or Right boundary of this
149      * CrossSectionElement at the specified fractional longitudinal position.
150      * @param lateralDirection LateralDirectionality; LEFT, or RIGHT
151      * @param fractionalLongitudinalPosition double; ranges from 0.0 (begin of parentLink) to 1.0 (end of parentLink)
152      * @return Length
153      */
154     public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
155             final double fractionalLongitudinalPosition)
156     {
157         Length offset;
158         Length halfWidth;
159         if (this.crossSectionSlices.size() <= 2)
160         {
161             offset = Length.interpolate(getOffsetAtBegin(), getOffsetAtEnd(), fractionalLongitudinalPosition);
162             halfWidth = Length.interpolate(getBeginWidth(), getEndWidth(), fractionalLongitudinalPosition).times(0.5);
163         }
164         else
165         {
166             int sliceNr = calculateSliceNumber(fractionalLongitudinalPosition);
167             double segmentPosition = fractionalPositionSegment(fractionalLongitudinalPosition, sliceNr);
168             offset = Length.interpolate(this.crossSectionSlices.get(sliceNr).getOffset(),
169                     this.crossSectionSlices.get(sliceNr + 1).getOffset(), segmentPosition);
170             halfWidth = Length.interpolate(this.crossSectionSlices.get(sliceNr).getWidth(),
171                     this.crossSectionSlices.get(sliceNr + 1).getWidth(), segmentPosition).times(0.5);
172         }
173 
174         switch (lateralDirection)
175         {
176             case LEFT:
177                 return offset.minus(halfWidth);
178             case RIGHT:
179                 return offset.plus(halfWidth);
180             default:
181                 throw new Error("Bad switch on LateralDirectionality " + lateralDirection);
182         }
183     }
184 
185 }