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