1 package org.opentrafficsim.road.network.lane;
2
3 import java.io.Serializable;
4
5 import javax.media.j3d.Bounds;
6
7 import nl.tudelft.simulation.dsol.animation.LocatableInterface;
8 import nl.tudelft.simulation.language.d3.DirectedPoint;
9
10 import org.opentrafficsim.core.OTS_SCALAR;
11 import org.opentrafficsim.core.geometry.OTSBuffering;
12 import org.opentrafficsim.core.geometry.OTSGeometryException;
13 import org.opentrafficsim.core.geometry.OTSLine3D;
14 import org.opentrafficsim.core.geometry.OTSPoint3D;
15 import org.opentrafficsim.core.network.LateralDirectionality;
16 import org.opentrafficsim.core.network.NetworkException;
17
18
19
20
21
22
23
24
25
26
27
28
29 public abstract class CrossSectionElement implements LocatableInterface, Serializable, OTS_SCALAR
30 {
31
32 private static final long serialVersionUID = 20150826L;
33
34
35 private final String id;
36
37
38 @SuppressWarnings("checkstyle:visibilitymodifier")
39 protected final CrossSectionLink parentLink;
40
41
42 @SuppressWarnings("checkstyle:visibilitymodifier")
43 protected final Length.Rel designLineOffsetAtBegin;
44
45
46 @SuppressWarnings("checkstyle:visibilitymodifier")
47 protected final Length.Rel designLineOffsetAtEnd;
48
49
50 @SuppressWarnings("checkstyle:visibilitymodifier")
51 protected final Length.Rel beginWidth;
52
53
54 @SuppressWarnings("checkstyle:visibilitymodifier")
55 protected final Length.Rel endWidth;
56
57
58 @SuppressWarnings("checkstyle:visibilitymodifier")
59 protected final Length.Rel length;
60
61
62 private final OTSLine3D centerLine;
63
64
65 private final OTSLine3D contour;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public CrossSectionElement(final CrossSectionLink parentLink, final String id, final Length.Rel lateralOffsetAtBegin,
83 final Length.Rel lateralOffsetAtEnd, final Length.Rel beginWidth, final Length.Rel endWidth)
84 throws OTSGeometryException, NetworkException
85 {
86 super();
87 if (id == null)
88 {
89 throw new NetworkException("Constructor of CrossSectionElement -- id cannot be null");
90 }
91 for (CrossSectionElement cse : parentLink.getCrossSectionElementList())
92 {
93 if (cse.getId().equals(id))
94 {
95 throw new NetworkException("Constructor of CrossSectionElement -- id " + id + " not unique within the Link");
96 }
97 }
98 this.id = id;
99 this.parentLink = parentLink;
100 this.designLineOffsetAtBegin = lateralOffsetAtBegin;
101 this.designLineOffsetAtEnd = lateralOffsetAtEnd;
102 this.beginWidth = beginWidth;
103 this.endWidth = endWidth;
104
105 this.centerLine =
106 OTSBuffering.offsetLine(this.getParentLink().getDesignLine(), this.designLineOffsetAtBegin.getSI(),
107 this.designLineOffsetAtEnd.getSI());
108 this.length = this.centerLine.getLength();
109 this.contour = constructContour(this);
110
111 this.parentLink.addCrossSectionElement(this);
112 }
113
114
115
116
117 public final CrossSectionLink getParentLink()
118 {
119 return this.parentLink;
120 }
121
122
123
124
125
126
127 public final Length.Rel getLateralCenterPosition(final double fractionalPosition)
128 {
129 return Length.Rel.interpolate(this.designLineOffsetAtBegin, this.designLineOffsetAtEnd, fractionalPosition);
130 }
131
132
133
134
135
136
137 public final Length.Rel getLateralCenterPosition(final Length.Rel longitudinalPosition)
138 {
139 return getLateralCenterPosition(longitudinalPosition.getSI() / getLength().getSI());
140 }
141
142
143
144
145
146
147 public final Length.Rel getWidth(final Length.Rel longitudinalPosition)
148 {
149 return getWidth(longitudinalPosition.getSI() / getLength().getSI());
150 }
151
152
153
154
155
156
157
158 public final Length.Rel getWidth(final double fractionalPosition)
159 {
160 return Length.Rel.interpolate(this.beginWidth, this.endWidth, fractionalPosition);
161 }
162
163
164
165
166
167 public final Length.Rel getLength()
168 {
169 return this.length;
170 }
171
172
173
174
175 public final Length.Rel getDesignLineOffsetAtBegin()
176 {
177 return this.designLineOffsetAtBegin;
178 }
179
180
181
182
183 public final Length.Rel getDesignLineOffsetAtEnd()
184 {
185 return this.designLineOffsetAtEnd;
186 }
187
188
189
190
191 public final Length.Rel getBeginWidth()
192 {
193 return this.beginWidth;
194 }
195
196
197
198
199 public final Length.Rel getEndWidth()
200 {
201 return this.endWidth;
202 }
203
204
205
206
207 protected abstract double getZ();
208
209
210
211
212 public final OTSLine3D getCenterLine()
213 {
214 return this.centerLine;
215 }
216
217
218
219
220 public final OTSLine3D getContour()
221 {
222 return this.contour;
223 }
224
225
226
227
228 public final String getId()
229 {
230 return this.id;
231 }
232
233
234
235
236
237
238
239
240 public final Length.Rel getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
241 final double fractionalLongitudinalPosition)
242 {
243 Length.Rel designLineOffset =
244 Length.Rel.interpolate(this.designLineOffsetAtBegin, this.designLineOffsetAtEnd, fractionalLongitudinalPosition);
245 Length.Rel halfWidth =
246 Length.Rel.interpolate(this.beginWidth, this.endWidth, fractionalLongitudinalPosition).multiplyBy(0.5);
247 switch (lateralDirection)
248 {
249 case LEFT:
250 return designLineOffset.minus(halfWidth);
251 case RIGHT:
252 return designLineOffset.plus(halfWidth);
253 default:
254 throw new Error("Bad switch on LateralDirectionality " + lateralDirection);
255 }
256 }
257
258
259
260
261
262
263
264
265 public final Length.Rel getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
266 final Length.Rel longitudinalPosition)
267 {
268 return getLateralBoundaryPosition(lateralDirection, longitudinalPosition.getSI() / getLength().getSI());
269 }
270
271
272
273
274
275
276
277
278
279 public static OTSLine3D constructContour(final CrossSectionElement cse) throws OTSGeometryException
280 {
281 OTSLine3D crossSectionDesignLine =
282 OTSBuffering.offsetLine(cse.getParentLink().getDesignLine(), cse.getDesignLineOffsetAtBegin().getSI(), cse
283 .getDesignLineOffsetAtEnd().getSI());
284 OTSLine3D rightBoundary =
285 OTSBuffering.offsetLine(crossSectionDesignLine, -cse.getBeginWidth().getSI() / 2, -cse.getEndWidth().getSI() / 2);
286 OTSLine3D leftBoundary =
287 OTSBuffering.offsetLine(crossSectionDesignLine, cse.getBeginWidth().getSI() / 2, cse.getEndWidth().getSI() / 2);
288 OTSPoint3D[] result = new OTSPoint3D[rightBoundary.size() + leftBoundary.size() + 1];
289 int resultIndex = 0;
290 for (int index = 0; index < rightBoundary.size(); index++)
291 {
292 result[resultIndex++] = rightBoundary.get(index);
293 }
294 for (int index = leftBoundary.size(); --index >= 0;)
295 {
296 result[resultIndex++] = leftBoundary.get(index);
297 }
298 result[resultIndex] = rightBoundary.get(0);
299 return new OTSLine3D(result);
300 }
301
302
303 @Override
304 @SuppressWarnings("checkstyle:designforextension")
305 public DirectedPoint getLocation()
306 {
307 DirectedPoint centroid = this.contour.getLocation();
308 return new DirectedPoint(centroid.x, centroid.y, getZ());
309 }
310
311
312 @Override
313 @SuppressWarnings("checkstyle:designforextension")
314 public Bounds getBounds()
315 {
316 return this.contour.getBounds();
317 }
318
319
320 @Override
321 @SuppressWarnings("checkstyle:designforextension")
322 public String toString()
323 {
324 return String.format("CSE offset %.2fm..%.2fm, width %.2fm..%.2fm", this.designLineOffsetAtBegin.getSI(),
325 this.designLineOffsetAtEnd.getSI(), this.beginWidth.getSI(), this.endWidth.getSI());
326 }
327
328
329 @SuppressWarnings("checkstyle:designforextension")
330 @Override
331 public int hashCode()
332 {
333 final int prime = 31;
334 int result = 1;
335 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
336 result = prime * result + ((this.parentLink == null) ? 0 : this.parentLink.hashCode());
337 return result;
338 }
339
340
341 @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
342 @Override
343 public boolean equals(final Object obj)
344 {
345 if (this == obj)
346 return true;
347 if (obj == null)
348 return false;
349 if (getClass() != obj.getClass())
350 return false;
351 CrossSectionElement other = (CrossSectionElement) obj;
352 if (this.id == null)
353 {
354 if (other.id != null)
355 return false;
356 }
357 else if (!this.id.equals(other.id))
358 return false;
359 if (this.parentLink == null)
360 {
361 if (other.parentLink != null)
362 return false;
363 }
364 else if (!this.parentLink.equals(other.parentLink))
365 return false;
366 return true;
367 }
368
369 }