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