View Javadoc
1   package org.opentrafficsim.road.network.animation;
2   
3   import java.awt.Color;
4   import java.awt.Graphics2D;
5   import java.awt.image.ImageObserver;
6   import java.rmi.RemoteException;
7   import java.util.ArrayList;
8   import java.util.Arrays;
9   
10  import javax.naming.NamingException;
11  
12  import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
13  
14  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
15  import org.opentrafficsim.core.geometry.OTSBuffering;
16  import org.opentrafficsim.core.geometry.OTSGeometryException;
17  import org.opentrafficsim.core.geometry.OTSLine3D;
18  import org.opentrafficsim.core.geometry.OTSPoint3D;
19  import org.opentrafficsim.core.network.animation.PaintPolygons;
20  import org.opentrafficsim.road.network.lane.Stripe;
21  
22  import com.vividsolutions.jts.geom.Coordinate;
23  import com.vividsolutions.jts.geom.Geometry;
24  import com.vividsolutions.jts.linearref.LengthIndexedLine;
25  import com.vividsolutions.jts.operation.buffer.BufferParameters;
26  
27  /**
28   * <p>
29   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
30   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
31   * <p>
32   * $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
33   * initial version Oct 17, 2014 <br>
34   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
35   */
36  public class StripeAnimation extends Renderable2D
37  {
38      /** the line type. */
39      private final TYPE type;
40  
41      /** The points for the outline of the Stripe. */
42      private final OTSLine3D line;
43  
44      /** Precision of buffer operations. */
45      private static final int QUADRANTSEGMENTS = 8;
46  
47      /**
48       * Generate the drawing commands for a dash pattern.
49       * @param center LengthIndexedLine; the design line of the striped pattern
50       * @param width double; width of the stripes in meters
51       * @param startOffset double; shift the starting point in the pattern by this length in meters
52       * @param onOffLengths double[]; one or more lengths of the dashes and the gaps between those dashes. If the number of
53       *            values in <cite>onOffLengths</cite> is odd, the pattern repeats inverted. The first value in
54       *            <cite>onOffLengths</cite> is the length of a dash.
55       * @return ArrayList&lt;Coordinate&gt;; the coordinates of the dashes separated and terminated by a <cite>NEWPATH</cite>
56       *         Coordinate
57       */
58      private ArrayList<OTSPoint3D> makeDashes(final LengthIndexedLine center, final double width, final double startOffset,
59          final double[] onOffLengths)
60      {
61          double period = 0;
62          for (double length : onOffLengths)
63          {
64              if (length < 0)
65              {
66                  throw new Error("Bad pattern - on or off length is < 0");
67              }
68              period += length;
69          }
70          if (period <= 0)
71          {
72              throw new Error("Bad pattern - repeat period length is 0");
73          }
74          double length = center.getEndIndex();
75          double position = -startOffset;
76          int phase = 0;
77          ArrayList<OTSPoint3D> result = new ArrayList<>();
78          while (position < length)
79          {
80              double nextBoundary = position + onOffLengths[phase++ % onOffLengths.length];
81              if (nextBoundary > 0) // Skip this one; this entire dash lies within the startOffset
82              {
83                  if (position < 0)
84                  {
85                      position = 0; // Draw a partial dash, starting at 0 (begin of the center line)
86                  }
87                  double endPosition = nextBoundary;
88                  if (endPosition > length)
89                  {
90                      endPosition = length; // Draw a partial dash, ending at length (end of the center line)
91                  }
92                  Coordinate[] oneDash =
93                      center.extractLine(position, endPosition).buffer(width / 2, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT)
94                          .getCoordinates();
95                  for (int i = 0; i < oneDash.length; i++)
96                  {
97                      result.add(new OTSPoint3D(oneDash[i]));
98                  }
99                  result.add(PaintPolygons.NEWPATH);
100             }
101             position = nextBoundary + onOffLengths[phase++ % onOffLengths.length];
102         }
103         return result;
104     }
105 
106     /**
107      * Generate the points needed to draw the stripe pattern.
108      * @param stripe Stripe; the stripe
109      * @param stripeType TYPE; the stripe type
110      * @return Coordinate[]; array of Coordinate
111      * @throws NamingException when <cite>type</cite> is not supported
112      */
113     private ArrayList<OTSPoint3D> makePoints(final Stripe stripe, final TYPE stripeType) throws NamingException
114     {
115         switch (this.type)
116         {
117             case DASHED:// : - Draw a 3-9 dash pattern on the center line
118                 return makeDashes(new LengthIndexedLine(stripe.getCenterLine().getLineString()), 0.2, 0, new double[]{3, 9});
119 
120             case DOUBLE:// ||- Draw two solid lines
121                 try
122                 {
123                     OTSLine3D centerLine = stripe.getCenterLine();
124                     Coordinate[] leftLine =
125                         OTSBuffering.offsetGeometry(centerLine, 0.2).getLineString().buffer(0.1, QUADRANTSEGMENTS,
126                             BufferParameters.CAP_FLAT).getCoordinates();
127                     Coordinate[] rightLine =
128                         OTSBuffering.offsetGeometry(centerLine, -0.2).getLineString().buffer(0.1, QUADRANTSEGMENTS,
129                             BufferParameters.CAP_FLAT).getCoordinates();
130                     ArrayList<OTSPoint3D> result = new ArrayList<OTSPoint3D>(leftLine.length + rightLine.length);
131                     for (int i = 0; i < leftLine.length; i++)
132                     {
133                         result.add(new OTSPoint3D(leftLine[i]));
134                     }
135                     for (int i = 0; i < rightLine.length; i++)
136                     {
137                         result.add(new OTSPoint3D(rightLine[i]));
138                     }
139                     return result;
140                 }
141                 catch (OTSGeometryException exception)
142                 {
143                     exception.printStackTrace();
144                 }
145                 return new ArrayList<OTSPoint3D>(); // Return an empty ArrayList after an error occurred
146 
147             case LEFTONLY: // |: - Draw left solid, right 3-9 dashed
148                 try
149                 {
150                     OTSLine3D centerLine = stripe.getCenterLine();
151                     Geometry rightDesignLine = OTSBuffering.offsetGeometry(centerLine, -0.2).getLineString();
152                     ArrayList<OTSPoint3D> result =
153                         makeDashes(new LengthIndexedLine(rightDesignLine), 0.2, 0, new double[]{3, 9});
154                     Geometry leftDesignLine =
155                         OTSBuffering.offsetGeometry(centerLine, 0.2).getLineString().buffer(0.1, QUADRANTSEGMENTS,
156                             BufferParameters.CAP_FLAT);
157                     Coordinate[] leftCoordinates =
158                         leftDesignLine.buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
159                     for (int i = 0; i < leftCoordinates.length; i++)
160                     {
161                         result.add(new OTSPoint3D(leftCoordinates[i]));
162                     }
163                     result.add(PaintPolygons.NEWPATH);
164                     return result;
165                 }
166                 catch (OTSGeometryException exception)
167                 {
168                     exception.printStackTrace();
169                 }
170                 return new ArrayList<OTSPoint3D>(); // Return an empty ArrayList after an error occurred
171 
172             case RIGHTONLY: // :| - Draw left 3-9 dashed, right solid
173                 try
174                 {
175                     OTSLine3D centerLine = stripe.getCenterLine();
176                     Geometry leftDesignLine = OTSBuffering.offsetGeometry(centerLine, 0.2).getLineString();
177                     ArrayList<OTSPoint3D> result =
178                         makeDashes(new LengthIndexedLine(leftDesignLine), 0.2, 0, new double[]{3, 9});
179                     Geometry rightDesignLine =
180                         OTSBuffering.offsetGeometry(centerLine, -0.2).getLineString().buffer(0.1, QUADRANTSEGMENTS,
181                             BufferParameters.CAP_FLAT);
182                     Coordinate[] rightCoordinates =
183                         rightDesignLine.buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
184                     for (int i = 0; i < rightCoordinates.length; i++)
185                     {
186                         result.add(new OTSPoint3D(rightCoordinates[i]));
187                     }
188                     result.add(PaintPolygons.NEWPATH);
189                     return result;
190                 }
191                 catch (OTSGeometryException exception)
192                 {
193                     exception.printStackTrace();
194                 }
195                 return new ArrayList<OTSPoint3D>(); // Return an empty ArrayList after an error occurred
196 
197             case SOLID:// | - Draw single solid line. This (regretfully) involves copying everything twice...
198                 return new ArrayList<OTSPoint3D>(Arrays.asList(stripe.getContour().getPoints()));
199 
200             default:
201                 throw new NamingException("Unsupported stripe type: " + stripeType);
202         }
203 
204     }
205 
206     /**
207      * @param source s
208      * @param simulator s
209      * @param type t
210      * @throws NamingException ne
211      * @throws RemoteException on communication failure
212      */
213     public StripeAnimation(final Stripe source, final OTSSimulatorInterface simulator, final TYPE type)
214         throws NamingException, RemoteException
215     {
216         super(source, simulator);
217         this.type = type;
218         this.line = new OTSLine3D(makePoints(source, type));
219     }
220 
221     /** {@inheritDoc} */
222     @Override
223     public final void paint(final Graphics2D graphics, final ImageObserver observer) 
224     {
225         PaintPolygons.paintMultiPolygon(graphics, Color.WHITE, ((Stripe) getSource()).getLocation(), this.line);
226     }
227 
228     /**
229      * Stripe type.
230      * <p>
231      * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands.<br>
232      * All rights reserved. <br>
233      * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
234      */
235     public static enum TYPE
236     {
237         /** Single solid line. */
238         SOLID,
239 
240         /** Line |: allow to go to left, but not to right. */
241         LEFTONLY,
242 
243         /** Line :| allow to go to right, but not to left. */
244         RIGHTONLY,
245 
246         /** Dashes : allow to cross in both directions. */
247         DASHED,
248 
249         /** Double solid line ||, don't cross. */
250         DOUBLE
251     }
252 }