1 package org.opentrafficsim.road.network.lane;
2
3 import java.io.Serializable;
4 import java.rmi.RemoteException;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Iterator;
8 import java.util.List;
9
10 import javax.media.j3d.Bounds;
11
12 import org.djunits.value.vdouble.scalar.Length;
13 import org.djutils.event.EventProducer;
14 import org.djutils.exceptions.Throw;
15 import org.djutils.exceptions.Try;
16 import org.djutils.logger.CategoryLogger;
17 import org.opentrafficsim.base.Identifiable;
18 import org.opentrafficsim.core.animation.Drawable;
19 import org.opentrafficsim.core.geometry.Bezier;
20 import org.opentrafficsim.core.geometry.OTSGeometryException;
21 import org.opentrafficsim.core.geometry.OTSLine3D;
22 import org.opentrafficsim.core.geometry.OTSPoint3D;
23 import org.opentrafficsim.core.geometry.OTSShape;
24 import org.opentrafficsim.core.network.LateralDirectionality;
25 import org.opentrafficsim.core.network.NetworkException;
26 import org.opentrafficsim.road.network.RoadNetwork;
27
28 import nl.tudelft.simulation.dsol.animation.Locatable;
29 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
30 import nl.tudelft.simulation.language.d3.DirectedPoint;
31
32
33
34
35
36
37
38
39
40
41
42
43
44 public abstract class CrossSectionElement extends EventProducer implements Locatable, Serializable, Identifiable, Drawable
45 {
46
47 private static final long serialVersionUID = 20150826L;
48
49
50 private final String id;
51
52
53 @SuppressWarnings("checkstyle:visibilitymodifier")
54 protected final CrossSectionLink parentLink;
55
56
57 @SuppressWarnings("checkstyle:visibilitymodifier")
58 protected final List<CrossSectionSlice> crossSectionSlices;
59
60
61 @SuppressWarnings("checkstyle:visibilitymodifier")
62 protected final Length length;
63
64
65 private final OTSLine3D centerLine;
66
67
68 private final OTSShape contour;
69
70
71 public static final double MAXIMUMDIRECTIONERROR = Math.toRadians(0.1);
72
73
74
75
76
77 public static final double FIXUPPOINTPROPORTION = 1.0 / 3;
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 public CrossSectionElement(final CrossSectionLink parentLink, final String id,
93 final List<CrossSectionSlice> crossSectionSlices) throws OTSGeometryException, NetworkException
94 {
95 Throw.when(parentLink == null, NetworkException.class,
96 "Constructor of CrossSectionElement for id %s, parentLink cannot be null", id);
97 Throw.when(id == null, NetworkException.class, "Constructor of CrossSectionElement -- id cannot be null");
98 for (CrossSectionElement cse : parentLink.getCrossSectionElementList())
99 {
100 Throw.when(cse.getId().equals(id), NetworkException.class,
101 "Constructor of CrossSectionElement -- id %s not unique within the Link", id);
102 }
103 Throw.whenNull(crossSectionSlices, "crossSectionSlices may not be null");
104 this.id = id;
105 this.parentLink = parentLink;
106
107 this.crossSectionSlices = new ArrayList<>(crossSectionSlices);
108 Throw.when(this.crossSectionSlices.size() == 0, NetworkException.class,
109 "CrossSectionElement %s is created with zero slices for %s", id, parentLink);
110 Throw.when(this.crossSectionSlices.get(0).getRelativeLength().si != 0.0, NetworkException.class,
111 "CrossSectionElement %s for %s has a first slice with relativeLength is not equal to 0.0", id, parentLink);
112 Throw.when(
113 this.crossSectionSlices.size() > 1 && this.crossSectionSlices.get(this.crossSectionSlices.size() - 1)
114 .getRelativeLength().ne(this.parentLink.getLength()),
115 NetworkException.class, "CrossSectionElement %s for %s has a last slice with relativeLength is not equal "
116 + "to the length of the parent link",
117 id, parentLink);
118 OTSLine3D proposedCenterLine = null;
119 if (this.crossSectionSlices.size() <= 2)
120 {
121 proposedCenterLine = fixTightInnerCurve(new double[] { 0.0, 1.0 },
122 new double[] { getDesignLineOffsetAtBegin().getSI(), getDesignLineOffsetAtEnd().getSI() });
123 }
124 else
125 {
126 double[] fractions = new double[this.crossSectionSlices.size()];
127 double[] offsets = new double[this.crossSectionSlices.size()];
128 for (int i = 0; i < this.crossSectionSlices.size(); i++)
129 {
130 fractions[i] = this.crossSectionSlices.get(i).getRelativeLength().si / this.parentLink.getLength().si;
131 offsets[i] = this.crossSectionSlices.get(i).getDesignLineOffset().si;
132 }
133 proposedCenterLine = fixTightInnerCurve(fractions, offsets);
134 }
135
136 List<OTSPoint3D> points = new ArrayList<OTSPoint3D>(Arrays.asList(proposedCenterLine.getPoints()));
137
138 DirectedPoint linkFrom = Try.assign(() -> parentLink.getStartNode().getLocation(), "Cannot happen");
139 double fromDirection = linkFrom.getRotZ();
140 points.remove(0);
141 points.add(0, new OTSPoint3D(linkFrom.x + getDesignLineOffsetAtBegin().getSI() * Math.cos(fromDirection + Math.PI / 2),
142 linkFrom.y + getDesignLineOffsetAtBegin().getSI() * Math.sin(fromDirection + Math.PI / 2)));
143
144 DirectedPoint linkTo = Try.assign(() -> parentLink.getEndNode().getLocation(), "Cannot happen");
145 double toDirection = linkTo.getRotZ();
146 points.remove(points.size() - 1);
147 points.add(new OTSPoint3D(linkTo.x + getDesignLineOffsetAtEnd().getSI() * Math.cos(toDirection + Math.PI / 2),
148 linkTo.y + getDesignLineOffsetAtEnd().getSI() * Math.sin(toDirection + Math.PI / 2)));
149
150 double direction = points.get(0).horizontalDirectionSI(points.get(1));
151 OTSPoint3D extraPointAfterStart = null;
152 if (Math.abs(direction - fromDirection) > MAXIMUMDIRECTIONERROR)
153 {
154
155 OTSPoint3D from = points.get(0);
156 OTSPoint3D next = points.get(1);
157 double distance =
158 Math.min(from.horizontalDistanceSI(next) * FIXUPPOINTPROPORTION, crossSectionSlices.get(0).getWidth().si);
159 extraPointAfterStart = new OTSPoint3D(from.x + Math.cos(fromDirection) * distance,
160 from.y + Math.sin(fromDirection) * distance, from.z + FIXUPPOINTPROPORTION * (next.z - from.z));
161
162 }
163
164 int pointCount = points.size();
165 direction = points.get(pointCount - 2).horizontalDirectionSI(points.get(pointCount - 1));
166 if (Math.abs(direction - toDirection) > MAXIMUMDIRECTIONERROR)
167 {
168
169 OTSPoint3D to = points.get(pointCount - 1);
170 OTSPoint3D before = points.get(pointCount - 2);
171 double distance = Math.min(before.horizontalDistanceSI(to) * FIXUPPOINTPROPORTION,
172 crossSectionSlices.get(Math.max(0, crossSectionSlices.size() - 2)).getWidth().si);
173 points.add(pointCount - 1, new OTSPoint3D(to.x - Math.cos(toDirection) * distance,
174 to.y - Math.sin(toDirection) * distance, to.z - FIXUPPOINTPROPORTION * (before.z - to.z)));
175 }
176 if (null != extraPointAfterStart)
177 {
178 points.add(1, extraPointAfterStart);
179 }
180 this.centerLine = new OTSLine3D(points);
181 this.length = this.centerLine.getLength();
182 this.contour = constructContour(this);
183 this.parentLink.addCrossSectionElement(this);
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201 public CrossSectionElement(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtBegin,
202 final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth,
203 final boolean fixGradualLateralOffset) throws OTSGeometryException, NetworkException
204 {
205 this(parentLink, id, fixLateralOffset(parentLink, lateralOffsetAtBegin, lateralOffsetAtEnd, beginWidth, endWidth,
206 fixGradualLateralOffset));
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220
221 private static List<CrossSectionSlice> fixLateralOffset(final CrossSectionLink parentLink,
222 final Length lateralOffsetAtBegin, final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth,
223 final boolean fixGradualLateralOffset)
224 {
225 List<CrossSectionSlice> result = new ArrayList<>();
226 int numPoints = !fixGradualLateralOffset ? 2 : lateralOffsetAtBegin.equals(lateralOffsetAtEnd) ? 2 : 16;
227 Length parentLength = parentLink.getLength();
228 for (int index = 0; index < numPoints; index++)
229 {
230 double fraction = index * 1.0 / (numPoints - 1);
231 Length lengthAtCrossSection = parentLength.times(fraction);
232 double relativeOffsetAtFraction = (1 + Math.sin((fraction - 0.5) * Math.PI)) / 2;
233 Length offsetAtFraction = Length.interpolate(lateralOffsetAtBegin, lateralOffsetAtEnd, relativeOffsetAtFraction);
234 result.add(new CrossSectionSlice(lengthAtCrossSection, offsetAtFraction,
235 Length.interpolate(beginWidth, endWidth, fraction)));
236 }
237 return result;
238 }
239
240
241
242
243
244
245
246
247
248
249 private OTSLine3D fixTightInnerCurve(final double[] fractions, final double[] offsets) throws OTSGeometryException
250 {
251 OTSLine3D linkCenterLine = getParentLink().getDesignLine();
252 for (int i = 1; i < linkCenterLine.size() - 1; i++)
253 {
254 double fraction = linkCenterLine.getVertexFraction(i);
255 int index = 0;
256 while (index < fractions.length - 2 && fraction > fractions[index + 1])
257 {
258 index++;
259 }
260 double w = (fraction - fractions[index]) / (fractions[index + 1] - fractions[index]);
261 double offset = (1.0 - w) * offsets[index] + w * offsets[index + 1];
262 double radius = 1.0;
263 try
264 {
265 radius = linkCenterLine.getProjectedVertexRadius(i).si;
266 }
267 catch (Exception e)
268 {
269 CategoryLogger.always().error(e, "fixTightInnerCurve.getVertexFraction for " + linkCenterLine);
270 }
271 if ((!Double.isNaN(radius))
272 && ((radius < 0.0 && offset < 0.0 && offset < radius) || (radius > 0.0 && offset > 0.0 && offset > radius)))
273 {
274 double offsetStart = getDesignLineOffsetAtBegin().getSI();
275 double offsetEnd = getDesignLineOffsetAtEnd().getSI();
276 DirectedPoint start = linkCenterLine.getLocationFraction(0.0);
277 DirectedPoint end = linkCenterLine.getLocationFraction(1.0);
278 start = new DirectedPoint(start.x - Math.sin(start.getRotZ()) * offsetStart,
279 start.y + Math.cos(start.getRotZ()) * offsetStart, start.z, start.getRotX(), start.getRotY(),
280 start.getRotZ());
281 end = new DirectedPoint(end.x - Math.sin(end.getRotZ()) * offsetEnd,
282 end.y + Math.cos(end.getRotZ()) * offsetEnd, end.z, end.getRotX(), end.getRotY(), end.getRotZ());
283 while (this.crossSectionSlices.size() > 2)
284 {
285 this.crossSectionSlices.remove(1);
286 }
287 return Bezier.cubic(start, end);
288 }
289 }
290 if (this.crossSectionSlices.size() <= 2)
291 {
292 OTSLine3D designLine = this.getParentLink().getDesignLine();
293 if (designLine.size() > 2)
294 {
295
296
297 OTSLine3D line =
298 designLine.offsetLine(getDesignLineOffsetAtBegin().getSI(), getDesignLineOffsetAtEnd().getSI());
299 List<OTSPoint3D> points = new ArrayList<>(Arrays.asList(line.getPoints()));
300 Iterator<OTSPoint3D> it = points.iterator();
301 OTSPoint3D prevPoint = null;
302 while (it.hasNext())
303 {
304 OTSPoint3D point = it.next();
305 if (prevPoint != null && prevPoint.distance(point).si < 1e-4)
306 {
307 it.remove();
308 }
309 prevPoint = point;
310 }
311 return new OTSLine3D(points);
312 }
313 else
314 {
315 try
316 {
317 DirectedPoint refStart = getParentLink().getStartNode().getLocation();
318 double startRot = refStart.getRotZ();
319 double startOffset = this.crossSectionSlices.get(0).getDesignLineOffset().si;
320 OTSPoint3D start = new OTSPoint3D(refStart.x - Math.sin(startRot) * startOffset,
321 refStart.y + Math.cos(startRot) * startOffset, refStart.z);
322 DirectedPoint refEnd = getParentLink().getEndNode().getLocation();
323 double endRot = refEnd.getRotZ();
324 double endOffset = this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getDesignLineOffset().si;
325 OTSPoint3D end = new OTSPoint3D(refEnd.x - Math.sin(endRot) * endOffset,
326 refEnd.y + Math.cos(endRot) * endOffset, refEnd.z);
327 return new OTSLine3D(start, end);
328 }
329 catch (RemoteException exception)
330 {
331 throw new OTSGeometryException(exception);
332 }
333 }
334 }
335 else
336 {
337 for (int i = 0; i < this.crossSectionSlices.size(); i++)
338 {
339 fractions[i] = this.crossSectionSlices.get(i).getRelativeLength().si / this.parentLink.getLength().si;
340 offsets[i] = this.crossSectionSlices.get(i).getDesignLineOffset().si;
341 }
342 return this.getParentLink().getDesignLine().offsetLine(fractions, offsets);
343 }
344 }
345
346
347
348
349
350
351
352
353
354 protected CrossSectionElement(final CrossSectionLink newCrossSectionLink,
355 final SimulatorInterface.TimeDoubleUnit newSimulator, final CrossSectionElement cse) throws NetworkException
356 {
357 this.id = cse.id;
358 this.parentLink = newCrossSectionLink;
359 this.centerLine = cse.centerLine;
360 this.length = this.centerLine.getLength();
361 this.contour = cse.contour;
362 this.crossSectionSlices = new ArrayList<>(cse.crossSectionSlices);
363 newCrossSectionLink.addCrossSectionElement(this);
364 }
365
366
367
368
369
370
371
372
373
374
375
376
377 public CrossSectionElement(final CrossSectionLink parentLink, final String id, final Length lateralOffset,
378 final Length width) throws OTSGeometryException, NetworkException
379 {
380 this(parentLink, id,
381 Arrays.asList(new CrossSectionSlice/lane/CrossSectionSlice.html#CrossSectionSlice">CrossSectionSlice[] { new CrossSectionSlice(Length.ZERO, lateralOffset, width) }));
382 }
383
384
385
386
387 public final CrossSectionLink getParentLink()
388 {
389 return this.parentLink;
390 }
391
392
393
394
395 public final RoadNetwork getNetwork()
396 {
397 return this.parentLink.getNetwork();
398 }
399
400
401
402
403
404
405 private int calculateSliceNumber(final double fractionalPosition)
406 {
407 double linkLength = this.parentLink.getLength().si;
408 for (int i = 0; i < this.crossSectionSlices.size() - 1; i++)
409 {
410 if (fractionalPosition >= this.crossSectionSlices.get(i).getRelativeLength().si / linkLength
411 && fractionalPosition <= this.crossSectionSlices.get(i + 1).getRelativeLength().si / linkLength)
412 {
413 return i;
414 }
415 }
416 return this.crossSectionSlices.size() - 2;
417 }
418
419
420
421
422
423
424 public final Length getLateralCenterPosition(final double fractionalPosition)
425 {
426 if (this.crossSectionSlices.size() == 1)
427 {
428 return this.getDesignLineOffsetAtBegin();
429 }
430 if (this.crossSectionSlices.size() == 2)
431 {
432 return Length.interpolate(this.getDesignLineOffsetAtBegin(), this.getDesignLineOffsetAtEnd(), fractionalPosition);
433 }
434 int sliceNr = calculateSliceNumber(fractionalPosition);
435 return Length.interpolate(this.crossSectionSlices.get(sliceNr).getDesignLineOffset(),
436 this.crossSectionSlices.get(sliceNr + 1).getDesignLineOffset(), fractionalPosition
437 - this.crossSectionSlices.get(sliceNr).getRelativeLength().si / this.parentLink.getLength().si);
438 }
439
440
441
442
443
444
445 public final Length getLateralCenterPosition(final Length longitudinalPosition)
446 {
447 return getLateralCenterPosition(longitudinalPosition.getSI() / getLength().getSI());
448 }
449
450
451
452
453
454
455 public final Length getWidth(final Length longitudinalPosition)
456 {
457 return getWidth(longitudinalPosition.getSI() / getLength().getSI());
458 }
459
460
461
462
463
464
465 public final Length getWidth(final double fractionalPosition)
466 {
467 if (this.crossSectionSlices.size() == 1)
468 {
469 return this.getBeginWidth();
470 }
471 if (this.crossSectionSlices.size() == 2)
472 {
473 return Length.interpolate(this.getBeginWidth(), this.getEndWidth(), fractionalPosition);
474 }
475 int sliceNr = calculateSliceNumber(fractionalPosition);
476 return Length.interpolate(this.crossSectionSlices.get(sliceNr).getWidth(),
477 this.crossSectionSlices.get(sliceNr + 1).getWidth(), fractionalPosition
478 - this.crossSectionSlices.get(sliceNr).getRelativeLength().si / this.parentLink.getLength().si);
479 }
480
481
482
483
484
485 public final Length getLength()
486 {
487 return this.length;
488 }
489
490
491
492
493
494 public final Length getDesignLineOffsetAtBegin()
495 {
496 return this.crossSectionSlices.get(0).getDesignLineOffset();
497 }
498
499
500
501
502
503 public final Length getDesignLineOffsetAtEnd()
504 {
505 return this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getDesignLineOffset();
506 }
507
508
509
510
511
512 public final Length getBeginWidth()
513 {
514 return this.crossSectionSlices.get(0).getWidth();
515 }
516
517
518
519
520
521 public final Length getEndWidth()
522 {
523 return this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getWidth();
524 }
525
526
527
528
529
530 protected abstract double getZ();
531
532
533
534
535
536 public final OTSLine3D getCenterLine()
537 {
538 return this.centerLine;
539 }
540
541
542
543
544
545 public final OTSShape getContour()
546 {
547 return this.contour;
548 }
549
550
551
552
553
554 @Override
555 public final String getId()
556 {
557 return this.id;
558 }
559
560
561
562
563
564 public final String getFullId()
565 {
566 return getParentLink().getId() + "." + this.id;
567 }
568
569
570
571
572
573
574
575
576 public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
577 final double fractionalLongitudinalPosition)
578 {
579 Length designLineOffset;
580 Length halfWidth;
581 if (this.crossSectionSlices.size() <= 2)
582 {
583 designLineOffset = Length.interpolate(getDesignLineOffsetAtBegin(), getDesignLineOffsetAtEnd(),
584 fractionalLongitudinalPosition);
585 halfWidth = Length.interpolate(getBeginWidth(), getEndWidth(), fractionalLongitudinalPosition).times(0.5);
586 }
587 else
588 {
589 int sliceNr = calculateSliceNumber(fractionalLongitudinalPosition);
590 double startFractionalPosition =
591 this.crossSectionSlices.get(sliceNr).getRelativeLength().si / this.parentLink.getLength().si;
592 designLineOffset = Length.interpolate(this.crossSectionSlices.get(sliceNr).getDesignLineOffset(),
593 this.crossSectionSlices.get(sliceNr + 1).getDesignLineOffset(),
594 fractionalLongitudinalPosition - startFractionalPosition);
595 halfWidth = Length.interpolate(this.crossSectionSlices.get(sliceNr).getWidth(),
596 this.crossSectionSlices.get(sliceNr + 1).getWidth(),
597 fractionalLongitudinalPosition - startFractionalPosition).times(0.5);
598 }
599
600 switch (lateralDirection)
601 {
602 case LEFT:
603 return designLineOffset.minus(halfWidth);
604 case RIGHT:
605 return designLineOffset.plus(halfWidth);
606 default:
607 throw new Error("Bad switch on LateralDirectionality " + lateralDirection);
608 }
609 }
610
611
612
613
614
615
616
617
618 public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
619 final Length longitudinalPosition)
620 {
621 return getLateralBoundaryPosition(lateralDirection, longitudinalPosition.getSI() / getLength().getSI());
622 }
623
624
625
626
627
628
629
630
631
632 public static OTSShape constructContour(final CrossSectionElement cse) throws OTSGeometryException, NetworkException
633 {
634 OTSPoint3D[] result = null;
635
636 if (cse.crossSectionSlices.size() <= 2)
637 {
638 OTSLine3D crossSectionDesignLine = cse.centerLine;
639 OTSLine3D rightBoundary =
640 crossSectionDesignLine.offsetLine(-cse.getBeginWidth().getSI() / 2, -cse.getEndWidth().getSI() / 2);
641 OTSLine3D leftBoundary =
642 crossSectionDesignLine.offsetLine(cse.getBeginWidth().getSI() / 2, cse.getEndWidth().getSI() / 2);
643 result = new OTSPoint3D[rightBoundary.size() + leftBoundary.size() + 1];
644 int resultIndex = 0;
645 for (int index = 0; index < rightBoundary.size(); index++)
646 {
647 result[resultIndex++] = rightBoundary.get(index);
648 }
649 for (int index = leftBoundary.size(); --index >= 0;)
650 {
651 result[resultIndex++] = leftBoundary.get(index);
652 }
653 result[resultIndex] = rightBoundary.get(0);
654 }
655 else
656 {
657 List<OTSPoint3D> resultList = new ArrayList<>();
658 List<OTSPoint3D> rightBoundary = new ArrayList<>();
659 for (int i = 0; i < cse.crossSectionSlices.size() - 1; i++)
660 {
661 double plLength = cse.getParentLink().getLength().si;
662 double so = cse.crossSectionSlices.get(i).getDesignLineOffset().si;
663 double eo = cse.crossSectionSlices.get(i + 1).getDesignLineOffset().si;
664 double sw2 = cse.crossSectionSlices.get(i).getWidth().si / 2.0;
665 double ew2 = cse.crossSectionSlices.get(i + 1).getWidth().si / 2.0;
666 double sf = cse.crossSectionSlices.get(i).getRelativeLength().si / plLength;
667 double ef = cse.crossSectionSlices.get(i + 1).getRelativeLength().si / plLength;
668 OTSLine3D crossSectionDesignLine =
669 cse.getParentLink().getDesignLine().extractFractional(sf, ef).offsetLine(so, eo);
670 resultList.addAll(Arrays.asList(crossSectionDesignLine.offsetLine(-sw2, -ew2).getPoints()));
671 rightBoundary.addAll(Arrays.asList(crossSectionDesignLine.offsetLine(sw2, ew2).getPoints()));
672 }
673 for (int index = rightBoundary.size(); --index >= 0;)
674 {
675 resultList.add(rightBoundary.get(index));
676 }
677
678 resultList.add(resultList.get(0));
679 result = resultList.toArray(new OTSPoint3D[] {});
680 }
681 return OTSShape.createAndCleanOTSShape(result);
682 }
683
684
685 @Override
686 @SuppressWarnings("checkstyle:designforextension")
687 public DirectedPoint getLocation()
688 {
689 DirectedPoint centroid = this.contour.getLocation();
690 return new DirectedPoint(centroid.x, centroid.y, getZ());
691 }
692
693
694 @Override
695 @SuppressWarnings("checkstyle:designforextension")
696 public Bounds getBounds()
697 {
698 return this.contour.getBounds();
699 }
700
701
702 @Override
703 public Serializable getSourceId()
704 {
705 return this;
706 }
707
708
709 @Override
710 @SuppressWarnings("checkstyle:designforextension")
711 public String toString()
712 {
713 return String.format("CSE offset %.2fm..%.2fm, width %.2fm..%.2fm", getDesignLineOffsetAtBegin().getSI(),
714 getDesignLineOffsetAtEnd().getSI(), getBeginWidth().getSI(), getEndWidth().getSI());
715 }
716
717
718 @SuppressWarnings("checkstyle:designforextension")
719 @Override
720 public int hashCode()
721 {
722 final int prime = 31;
723 int result = 1;
724 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
725 result = prime * result + ((this.parentLink == null) ? 0 : this.parentLink.hashCode());
726 return result;
727 }
728
729
730 @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
731 @Override
732 public boolean equals(final Object obj)
733 {
734 if (this == obj)
735 return true;
736 if (obj == null)
737 return false;
738 if (getClass() != obj.getClass())
739 return false;
740 CrossSectionElement../org/opentrafficsim/road/network/lane/CrossSectionElement.html#CrossSectionElement">CrossSectionElement other = (CrossSectionElement) obj;
741 if (this.id == null)
742 {
743 if (other.id != null)
744 return false;
745 }
746 else if (!this.id.equals(other.id))
747 return false;
748 if (this.parentLink == null)
749 {
750 if (other.parentLink != null)
751 return false;
752 }
753 else if (!this.parentLink.equals(other.parentLink))
754 return false;
755 return true;
756 }
757
758
759
760
761
762
763
764
765 @SuppressWarnings("checkstyle:designforextension")
766 public abstract CrossSectionElement clone(CrossSectionLink newParentLink, SimulatorInterface.TimeDoubleUnit newSimulator)
767 throws NetworkException;
768 }