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