View Javadoc
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.List;
7   
8   import javax.media.j3d.Bounds;
9   
10  import org.djunits.value.vdouble.scalar.Length;
11  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
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.geometry.OTSShape;
16  import org.opentrafficsim.core.network.LateralDirectionality;
17  import org.opentrafficsim.core.network.NetworkException;
18  
19  import nl.tudelft.simulation.dsol.animation.Locatable;
20  import nl.tudelft.simulation.event.EventProducer;
21  import nl.tudelft.simulation.language.d3.DirectedPoint;
22  
23  /**
24   * Cross section elements are used to compose a CrossSectionLink.
25   * <p>
26   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
27   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
28   * <p>
29   * $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
30   * initial version Aug 19, 2014 <br>
31   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
32   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
33   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
34   */
35  public abstract class CrossSectionElement extends EventProducer implements Locatable, Serializable
36  {
37      /** */
38      private static final long serialVersionUID = 20150826L;
39  
40      /** The id. Should be unique within the parentLink. */
41      private final String id;
42  
43      /** Cross Section Link to which the element belongs. */
44      @SuppressWarnings("checkstyle:visibilitymodifier")
45      protected final CrossSectionLink parentLink;
46  
47      /** The offsets and widths at positions along the line, relative to the design line of the parent link. */
48      @SuppressWarnings("checkstyle:visibilitymodifier")
49      protected final List<CrossSectionSlice> crossSectionSlices;
50  
51      /** The length of the line. Calculated once at the creation. */
52      @SuppressWarnings("checkstyle:visibilitymodifier")
53      protected final Length length;
54  
55      /** The center line of the element. Calculated once at the creation. */
56      private final OTSLine3D centerLine;
57  
58      /** The contour of the element. Calculated once at the creation. */
59      private final OTSShape contour;
60  
61      /**
62       * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction, with the direction from
63       * the StartNode towards the EndNode as the longitudinal direction.
64       * @param id String; The id of the CrosssSectionElement. Should be unique within the parentLink.
65       * @param parentLink CrossSectionLink; Link to which the element belongs.
66       * @param crossSectionSlices List&lt;CrossSectionSlice&gt;; the offsets and widths at positions along the line, relative to
67       *            the design line of the parent link. If there is just one with and offset, there should just be one element in
68       *            the list with Length = 0. If there are more slices, the last one should be at the length of the design line.
69       *            If not, a NetworkException is thrown.
70       * @throws OTSGeometryException when creation of the geometry fails
71       * @throws NetworkException when id equal to null or not unique, or there are multiple slices and the last slice does not
72       *             end at the length of the design line.
73       */
74      public CrossSectionElement(final CrossSectionLink parentLink, final String id,
75              final List<CrossSectionSlice> crossSectionSlices) throws OTSGeometryException, NetworkException
76      {
77          if (parentLink == null)
78          {
79              throw new NetworkException("Constructor of CrossSectionElement for id " + id + ", parentLink cannot be null");
80          }
81          if (id == null)
82          {
83              throw new NetworkException("Constructor of CrossSectionElement -- id cannot be null");
84          }
85          for (CrossSectionElement cse : parentLink.getCrossSectionElementList())
86          {
87              if (cse.getId().equals(id))
88              {
89                  throw new NetworkException("Constructor of CrossSectionElement -- id " + id + " not unique within the Link");
90              }
91          }
92          this.id = id;
93          this.parentLink = parentLink;
94  
95          this.crossSectionSlices = new ArrayList<>(crossSectionSlices); // copy of list with immutable slices
96          if (this.crossSectionSlices.size() == 0)
97          {
98              throw new NetworkException("CrossSectionElement " + id + " is created with zero slices for " + parentLink);
99          }
100         if (this.crossSectionSlices.get(0).getRelativeLength().si != 0.0)
101         {
102             throw new NetworkException("CrossSectionElement " + id + " for " + parentLink
103                     + " has a first slice with relativeLength is not equal to 0.0");
104         }
105         if (this.crossSectionSlices.size() > 1 && this.crossSectionSlices.get(this.crossSectionSlices.size() - 1)
106                 .getRelativeLength().ne(this.parentLink.getLength()))
107         {
108             throw new NetworkException("CrossSectionElement " + id + " for " + parentLink
109                     + " has a last slice with relativeLength is not equal to the length of the parent link");
110         }
111 
112         if (this.crossSectionSlices.size() <= 2)
113         {
114             this.centerLine = this.getParentLink().getDesignLine().offsetLine(getDesignLineOffsetAtBegin().getSI(),
115                     getDesignLineOffsetAtEnd().getSI());
116         }
117         else
118         {
119             double[] relativeFractions = new double[this.crossSectionSlices.size()];
120             double[] offsets = new double[this.crossSectionSlices.size()];
121             for (int i = 0; i < this.crossSectionSlices.size(); i++)
122             {
123                 relativeFractions[i] = this.crossSectionSlices.get(i).getRelativeLength().si / this.parentLink.getLength().si;
124                 offsets[i] = this.crossSectionSlices.get(i).getDesignLineOffset().si;
125             }
126             this.centerLine = this.getParentLink().getDesignLine().offsetLine(relativeFractions, offsets);
127         }
128 
129         this.length = this.centerLine.getLength();
130         this.contour = constructContour(this);
131 
132         this.parentLink.addCrossSectionElement(this);
133     }
134 
135     // TODO use throwIf
136 
137     /**
138      * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction, with the direction from
139      * the StartNode towards the EndNode as the longitudinal direction.
140      * @param id String; The id of the CrosssSectionElement. Should be unique within the parentLink.
141      * @param parentLink CrossSectionLink; Link to which the element belongs.
142      * @param lateralOffsetAtBegin Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
143      *            design line of the parent Link at the start of the parent Link
144      * @param lateralOffsetAtEnd Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
145      *            design line of the parent Link at the end of the parent Link
146      * @param beginWidth Length; width at start, positioned <i>symmetrically around</i> the design line
147      * @param endWidth Length; width at end, positioned <i>symmetrically around</i> the design line
148      * @throws OTSGeometryException when creation of the geometry fails
149      * @throws NetworkException when id equal to null or not unique
150      */
151     public CrossSectionElement(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtBegin,
152             final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth)
153             throws OTSGeometryException, NetworkException
154     {
155         this(parentLink, id,
156                 Arrays.asList(new CrossSectionSlice[] { new CrossSectionSlice(Length.ZERO, lateralOffsetAtBegin, beginWidth),
157                         new CrossSectionSlice(parentLink.getLength(), lateralOffsetAtEnd, endWidth) }));
158     }
159 
160     /**
161      * Clone a CrossSectionElement for a new network.
162      * @param newCrossSectionLink the new link to which the clone belongs
163      * @param newSimulator the new simulator for this network
164      * @param animation whether to (re)create animation or not
165      * @param cse the element to clone from
166      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
167      *             or the end node of the link are not registered in the network.
168      */
169     protected CrossSectionElement(final CrossSectionLink newCrossSectionLink, final OTSSimulatorInterface newSimulator,
170             final boolean animation, final CrossSectionElement cse) throws NetworkException
171     {
172         this.id = cse.id;
173         this.parentLink = newCrossSectionLink;
174         this.centerLine = cse.centerLine;
175         this.length = this.centerLine.getLength();
176         this.contour = cse.contour;
177         this.crossSectionSlices = new ArrayList<CrossSectionSlice>(cse.crossSectionSlices);
178         newCrossSectionLink.addCrossSectionElement(this);
179     }
180 
181     /**
182      * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction, with the direction from
183      * the StartNode towards the EndNode as the longitudinal direction.
184      * @param id String; The id of the CrosssSectionElement. Should be unique within the parentLink.
185      * @param parentLink CrossSectionLink; Link to which the element belongs.
186      * @param lateralOffset Length; the lateral offset of the design line of the new CrossSectionLink with respect to the design
187      *            line of the parent Link
188      * @param width Length; width, positioned <i>symmetrically around</i> the design line
189      * @throws OTSGeometryException when creation of the geometry fails
190      * @throws NetworkException when id equal to null or not unique
191      */
192     public CrossSectionElement(final CrossSectionLink parentLink, final String id, final Length lateralOffset,
193             final Length width) throws OTSGeometryException, NetworkException
194     {
195         this(parentLink, id,
196                 Arrays.asList(new CrossSectionSlice[] { new CrossSectionSlice(Length.ZERO, lateralOffset, width) }));
197     }
198 
199     /**
200      * @return parentLink.
201      */
202     public final CrossSectionLink getParentLink()
203     {
204         return this.parentLink;
205     }
206 
207     /**
208      * Calculate the slice the fractional position is in.
209      * @param fractionalPosition the fractional position between 0 and 1 compared to the design line
210      * @return int; the lower slice number between 0 and number of slices - 1.
211      */
212     private int calculateSliceNumber(final double fractionalPosition)
213     {
214         double linkLength = this.parentLink.getLength().si;
215         for (int i = 0; i < this.crossSectionSlices.size() - 1; i++)
216         {
217             if (fractionalPosition >= this.crossSectionSlices.get(i).getRelativeLength().si / linkLength
218                     && fractionalPosition <= this.crossSectionSlices.get(i + 1).getRelativeLength().si / linkLength)
219             {
220                 return i;
221             }
222         }
223         return this.crossSectionSlices.size() - 2;
224     }
225 
226     /**
227      * Retrieve the lateral offset from the Link design line at the specified longitudinal position.
228      * @param fractionalPosition double; fractional longitudinal position on this Lane
229      * @return Length; the lateralCenterPosition at the specified longitudinal position
230      */
231     public final Length getLateralCenterPosition(final double fractionalPosition)
232     {
233         if (this.crossSectionSlices.size() == 1)
234         {
235             return this.getDesignLineOffsetAtBegin();
236         }
237         if (this.crossSectionSlices.size() == 2)
238         {
239             return Length.interpolate(this.getDesignLineOffsetAtBegin(), this.getDesignLineOffsetAtEnd(), fractionalPosition);
240         }
241         int sliceNr = calculateSliceNumber(fractionalPosition);
242         return Length.interpolate(this.crossSectionSlices.get(sliceNr).getDesignLineOffset(),
243                 this.crossSectionSlices.get(sliceNr + 1).getDesignLineOffset(), fractionalPosition
244                         - this.crossSectionSlices.get(sliceNr).getRelativeLength().si / this.parentLink.getLength().si);
245     }
246 
247     /**
248      * Retrieve the lateral offset from the Link design line at the specified longitudinal position.
249      * @param longitudinalPosition Length; the longitudinal position on this Lane
250      * @return Length; the lateralCenterPosition at the specified longitudinal position
251      */
252     public final Length getLateralCenterPosition(final Length longitudinalPosition)
253     {
254         return getLateralCenterPosition(longitudinalPosition.getSI() / getLength().getSI());
255     }
256 
257     /**
258      * Return the width of this CrossSectionElement at a specified longitudinal position.
259      * @param longitudinalPosition DoubleScalar&lt;LengthUnit&gt;; the longitudinal position
260      * @return Length; the width of this CrossSectionElement at the specified longitudinal position.
261      */
262     public final Length getWidth(final Length longitudinalPosition)
263     {
264         return getWidth(longitudinalPosition.getSI() / getLength().getSI());
265     }
266 
267     /**
268      * Return the width of this CrossSectionElement at a specified fractional longitudinal position.
269      * @param fractionalPosition double; the fractional longitudinal position
270      * @return Length; the width of this CrossSectionElement at the specified fractional longitudinal position.
271      */
272     public final Length getWidth(final double fractionalPosition)
273     {
274         if (this.crossSectionSlices.size() == 1)
275         {
276             return this.getBeginWidth();
277         }
278         if (this.crossSectionSlices.size() == 2)
279         {
280             return Length.interpolate(this.getBeginWidth(), this.getEndWidth(), fractionalPosition);
281         }
282         int sliceNr = calculateSliceNumber(fractionalPosition);
283         return Length.interpolate(this.crossSectionSlices.get(sliceNr).getWidth(),
284                 this.crossSectionSlices.get(sliceNr + 1).getWidth(), fractionalPosition
285                         - this.crossSectionSlices.get(sliceNr).getRelativeLength().si / this.parentLink.getLength().si);
286     }
287 
288     /**
289      * Return the length of this CrossSectionElement as measured along the design line (which equals the center line).
290      * @return Length; the length of this CrossSectionElement
291      */
292     public final Length getLength()
293     {
294         return this.length;
295     }
296 
297     /**
298      * Retrieve the offset from the design line at the begin of the parent link.
299      * @return Length; the offset of this CrossSectionElement at the begin of the parent link
300      */
301     public final Length getDesignLineOffsetAtBegin()
302     {
303         return this.crossSectionSlices.get(0).getDesignLineOffset();
304     }
305 
306     /**
307      * Retrieve the offset from the design line at the end of the parent link.
308      * @return Length; the offset of this CrossSectionElement at the end of the parent link
309      */
310     public final Length getDesignLineOffsetAtEnd()
311     {
312         return this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getDesignLineOffset();
313     }
314 
315     /**
316      * Retrieve the width at the begin of the parent link.
317      * @return Length; the width of this CrossSectionElement at the begin of the parent link
318      */
319     public final Length getBeginWidth()
320     {
321         return this.crossSectionSlices.get(0).getWidth();
322     }
323 
324     /**
325      * Retrieve the width at the end of the parent link.
326      * @return Length; the width of this CrossSectionElement at the end of the parent link
327      */
328     public final Length getEndWidth()
329     {
330         return this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getWidth();
331     }
332 
333     /**
334      * Retrieve the Z offset (used to determine what covers what when drawing).
335      * @return double; the Z-offset for drawing (what's on top, what's underneath).
336      */
337     protected abstract double getZ();
338 
339     /**
340      * Retrieve the center line of this CrossSectionElement.
341      * @return OTSLine3D; the center line of this CrossSectionElement
342      */
343     public final OTSLine3D getCenterLine()
344     {
345         return this.centerLine;
346     }
347 
348     /**
349      * Retrieve the contour of this CrossSectionElement.
350      * @return OTSShape; the contour of this CrossSectionElement
351      */
352     public final OTSShape getContour()
353     {
354         return this.contour;
355     }
356 
357     /**
358      * Retrieve the id of this CrossSectionElement.
359      * @return String; the id of this CrossSectionElement
360      */
361     public final String getId()
362     {
363         return this.id;
364     }
365 
366     /**
367      * Return the lateral offset from the design line of the parent Link of the Left or Right boundary of this
368      * CrossSectionElement at the specified fractional longitudinal position.
369      * @param lateralDirection LateralDirectionality; LEFT, or RIGHT
370      * @param fractionalLongitudinalPosition double; ranges from 0.0 (begin of parentLink) to 1.0 (end of parentLink)
371      * @return Length
372      */
373     public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
374             final double fractionalLongitudinalPosition)
375     {
376         Length designLineOffset;
377         Length halfWidth;
378         if (this.crossSectionSlices.size() <= 2)
379         {
380             designLineOffset = Length.interpolate(getDesignLineOffsetAtBegin(), getDesignLineOffsetAtEnd(),
381                     fractionalLongitudinalPosition);
382             halfWidth = Length.interpolate(getBeginWidth(), getEndWidth(), fractionalLongitudinalPosition).multiplyBy(0.5);
383         }
384         else
385         {
386             int sliceNr = calculateSliceNumber(fractionalLongitudinalPosition);
387             double startFractionalPosition =
388                     this.crossSectionSlices.get(sliceNr).getRelativeLength().si / this.parentLink.getLength().si;
389             designLineOffset = Length.interpolate(this.crossSectionSlices.get(sliceNr).getDesignLineOffset(),
390                     this.crossSectionSlices.get(sliceNr + 1).getDesignLineOffset(),
391                     fractionalLongitudinalPosition - startFractionalPosition);
392             halfWidth = Length.interpolate(this.crossSectionSlices.get(sliceNr).getWidth(),
393                     this.crossSectionSlices.get(sliceNr + 1).getWidth(),
394                     fractionalLongitudinalPosition - startFractionalPosition).multiplyBy(0.5);
395         }
396 
397         switch (lateralDirection)
398         {
399             case LEFT:
400                 return designLineOffset.minus(halfWidth);
401             case RIGHT:
402                 return designLineOffset.plus(halfWidth);
403             default:
404                 throw new Error("Bad switch on LateralDirectionality " + lateralDirection);
405         }
406     }
407 
408     /**
409      * Return the lateral offset from the design line of the parent Link of the Left or Right boundary of this
410      * CrossSectionElement at the specified longitudinal position.
411      * @param lateralDirection LateralDirectionality; LEFT, or RIGHT
412      * @param longitudinalPosition Length; the position along the length of this CrossSectionElement
413      * @return Length
414      */
415     public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
416             final Length longitudinalPosition)
417     {
418         return getLateralBoundaryPosition(lateralDirection, longitudinalPosition.getSI() / getLength().getSI());
419     }
420 
421     /**
422      * Construct a buffer geometry by offsetting the linear geometry line with a distance and constructing a so-called "buffer"
423      * around it.
424      * @param cse CrossSectionElement; the cross section element to construct the contour for
425      * @return OTSShape; the geometry belonging to this CrossSectionElement.
426      * @throws OTSGeometryException when construction of the geometry fails
427      * @throws NetworkException when the resulting contour is degenerate (cannot happen; we hope)
428      */
429     public static OTSShape constructContour(final CrossSectionElement cse) throws OTSGeometryException, NetworkException
430     {
431         OTSPoint3D[] result = null;
432 
433         if (cse.crossSectionSlices.size() <= 2)
434         {
435             OTSLine3D crossSectionDesignLine = cse.getParentLink().getDesignLine()
436                     .offsetLine(cse.getDesignLineOffsetAtBegin().getSI(), cse.getDesignLineOffsetAtEnd().getSI());
437             OTSLine3D rightBoundary =
438                     crossSectionDesignLine.offsetLine(-cse.getBeginWidth().getSI() / 2, -cse.getEndWidth().getSI() / 2);
439             OTSLine3D leftBoundary =
440                     crossSectionDesignLine.offsetLine(cse.getBeginWidth().getSI() / 2, cse.getEndWidth().getSI() / 2);
441             result = new OTSPoint3D[rightBoundary.size() + leftBoundary.size() + 1];
442             int resultIndex = 0;
443             for (int index = 0; index < rightBoundary.size(); index++)
444             {
445                 result[resultIndex++] = rightBoundary.get(index);
446             }
447             for (int index = leftBoundary.size(); --index >= 0;)
448             {
449                 result[resultIndex++] = leftBoundary.get(index);
450             }
451             result[resultIndex] = rightBoundary.get(0); // close the contour
452         }
453 
454         else
455 
456         {
457             List<OTSPoint3D> resultList = new ArrayList<>();
458             for (int i = 0; i < cse.crossSectionSlices.size() - 1; i++)
459             {
460                 double plLength = cse.getParentLink().getLength().si;
461                 double so = cse.crossSectionSlices.get(i).getDesignLineOffset().si;
462                 double eo = cse.crossSectionSlices.get(i + 1).getDesignLineOffset().si;
463                 double sw2 = cse.crossSectionSlices.get(i).getWidth().si / 2.0;
464                 double ew2 = cse.crossSectionSlices.get(i + 1).getWidth().si / 2.0;
465                 double sf = cse.crossSectionSlices.get(i).getRelativeLength().si / plLength;
466                 double ef = cse.crossSectionSlices.get(i + 1).getRelativeLength().si / plLength;
467                 OTSLine3D crossSectionDesignLine =
468                         cse.getParentLink().getDesignLine().extractFractional(sf, ef).offsetLine(so, eo);
469                 OTSLine3D rightBoundary = crossSectionDesignLine.offsetLine(-sw2, -ew2);
470                 OTSLine3D leftBoundary = crossSectionDesignLine.offsetLine(sw2, ew2);
471                 for (int index = 0; index < rightBoundary.size(); index++)
472                 {
473                     resultList.add(rightBoundary.get(index));
474                 }
475                 for (int index = leftBoundary.size(); --index >= 0;)
476                 {
477                     resultList.add(leftBoundary.get(index));
478                 }
479             }
480             // close the contour if needed
481             resultList.add(resultList.get(0));
482             result = resultList.toArray(new OTSPoint3D[] {});
483         }
484 
485         return OTSShape.createAndCleanOTSShape(result);
486     }
487 
488     /** {@inheritDoc} */
489     @Override
490     @SuppressWarnings("checkstyle:designforextension")
491     public DirectedPoint getLocation()
492     {
493         DirectedPoint centroid = this.contour.getLocation();
494         return new DirectedPoint(centroid.x, centroid.y, getZ());
495     }
496 
497     /** {@inheritDoc} */
498     @Override
499     @SuppressWarnings("checkstyle:designforextension")
500     public Bounds getBounds()
501     {
502         return this.contour.getBounds();
503     }
504 
505     /** {@inheritDoc} */
506     @Override
507     @SuppressWarnings("checkstyle:designforextension")
508     public String toString()
509     {
510         return String.format("CSE offset %.2fm..%.2fm, width %.2fm..%.2fm", getDesignLineOffsetAtBegin().getSI(),
511                 getDesignLineOffsetAtEnd().getSI(), getBeginWidth().getSI(), getEndWidth().getSI());
512     }
513 
514     /** {@inheritDoc} */
515     @SuppressWarnings("checkstyle:designforextension")
516     @Override
517     public int hashCode()
518     {
519         final int prime = 31;
520         int result = 1;
521         result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
522         result = prime * result + ((this.parentLink == null) ? 0 : this.parentLink.hashCode());
523         return result;
524     }
525 
526     /** {@inheritDoc} */
527     @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
528     @Override
529     public boolean equals(final Object obj)
530     {
531         if (this == obj)
532             return true;
533         if (obj == null)
534             return false;
535         if (getClass() != obj.getClass())
536             return false;
537         CrossSectionElement other = (CrossSectionElement) obj;
538         if (this.id == null)
539         {
540             if (other.id != null)
541                 return false;
542         }
543         else if (!this.id.equals(other.id))
544             return false;
545         if (this.parentLink == null)
546         {
547             if (other.parentLink != null)
548                 return false;
549         }
550         else if (!this.parentLink.equals(other.parentLink))
551             return false;
552         return true;
553     }
554 
555     /**
556      * Clone the CrossSectionElement for e.g., copying a network.
557      * @param newParentLink the new link to which the clone belongs
558      * @param newSimulator the new simulator for this network
559      * @param animation whether to (re)create animation or not
560      * @return a clone of this object
561      * @throws NetworkException in case the cloning fails
562      */
563     @SuppressWarnings("checkstyle:designforextension")
564     public abstract CrossSectionElement clone(final CrossSectionLink newParentLink,
565             final OTSSimulatorInterface newSimulator, final boolean animation) throws NetworkException;
566 }