1 package org.opentrafficsim.draw.road;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Graphics2D;
6 import java.awt.image.ImageObserver;
7 import java.io.Serializable;
8 import java.rmi.RemoteException;
9 import java.util.ArrayList;
10 import java.util.Arrays;
11
12 import javax.naming.NamingException;
13
14 import org.locationtech.jts.geom.Coordinate;
15 import org.locationtech.jts.geom.Geometry;
16 import org.locationtech.jts.linearref.LengthIndexedLine;
17 import org.locationtech.jts.operation.buffer.BufferParameters;
18 import org.opentrafficsim.core.geometry.OTSGeometryException;
19 import org.opentrafficsim.core.geometry.OTSLine3D;
20 import org.opentrafficsim.core.geometry.OTSPoint3D;
21 import org.opentrafficsim.draw.core.ClonableRenderable2DInterface;
22 import org.opentrafficsim.draw.core.PaintPolygons;
23 import org.opentrafficsim.road.network.lane.Stripe;
24
25 import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
26 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
27
28
29
30
31
32
33
34
35
36
37
38 public class StripeAnimation extends Renderable2D<Stripe> implements ClonableRenderable2DInterface<Stripe>, Serializable
39 {
40
41 private static final long serialVersionUID = 20141017L;
42
43
44 private final TYPE type;
45
46
47 private final OTSLine3D line;
48
49
50 private static final int QUADRANTSEGMENTS = 8;
51
52
53
54
55
56
57
58
59
60
61
62
63
64 private ArrayList<OTSPoint3D> makeDashes(final LengthIndexedLine center, final double width, final double startOffset,
65 final double[] onOffLengths)
66 {
67 double period = 0;
68 for (double length : onOffLengths)
69 {
70 if (length < 0)
71 {
72 throw new Error("Bad pattern - on or off length is < 0");
73 }
74 period += length;
75 }
76 if (period <= 0)
77 {
78 throw new Error("Bad pattern - repeat period length is 0");
79 }
80 double length = center.getEndIndex();
81 double position = -startOffset;
82 int phase = 0;
83 ArrayList<OTSPoint3D> result = new ArrayList<>();
84 while (position < length)
85 {
86 double nextBoundary = position + onOffLengths[phase++ % onOffLengths.length];
87 if (nextBoundary > 0)
88 {
89 if (position < 0)
90 {
91 position = 0;
92 }
93 double endPosition = nextBoundary;
94 if (endPosition > length)
95 {
96 endPosition = length;
97 }
98 Coordinate[] oneDash = center.extractLine(position, endPosition)
99 .buffer(width / 2, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
100 for (int i = 0; i < oneDash.length; i++)
101 {
102 result.add(new OTSPoint3D(oneDash[i]));
103 }
104 result.add(PaintPolygons.NEWPATH);
105 }
106 position = nextBoundary + onOffLengths[phase++ % onOffLengths.length];
107 }
108 return result;
109 }
110
111
112
113
114
115
116
117
118 private ArrayList<OTSPoint3D> makePoints(final Stripe stripe, final TYPE stripeType) throws NamingException
119 {
120 switch (this.type)
121 {
122 case DASHED:
123 return makeDashes(new LengthIndexedLine(stripe.getCenterLine().getLineString()), 0.2, 3.0,
124 new double[] { 3, 9 });
125
126 case BLOCK:
127 return makeDashes(new LengthIndexedLine(stripe.getCenterLine().getLineString()), 0.45, 1.0,
128 new double[] { 1, 3 });
129
130 case DOUBLE:
131 {
132 OTSLine3D centerLine = stripe.getCenterLine();
133 Coordinate[] leftLine = centerLine.offsetLine(0.2).getLineString()
134 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
135 Coordinate[] rightLine = centerLine.offsetLine(-0.2).getLineString()
136 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
137 ArrayList<OTSPoint3D> result = new ArrayList<>(leftLine.length + rightLine.length);
138 for (int i = 0; i < leftLine.length; i++)
139 {
140 result.add(new OTSPoint3D(leftLine[i]));
141 }
142 for (int i = 0; i < rightLine.length; i++)
143 {
144 result.add(new OTSPoint3D(rightLine[i]));
145 }
146 return result;
147 }
148
149 case LEFTONLY:
150 {
151 OTSLine3D centerLine = stripe.getCenterLine();
152 Geometry rightDesignLine = centerLine.offsetLine(-0.2).getLineString();
153 ArrayList<OTSPoint3D> result =
154 makeDashes(new LengthIndexedLine(rightDesignLine), 0.2, 0.0, new double[] { 3, 9 });
155 Coordinate[] leftCoordinates = centerLine.offsetLine(0.2).getLineString()
156 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
157 for (int i = 0; i < leftCoordinates.length; i++)
158 {
159 result.add(new OTSPoint3D(leftCoordinates[i]));
160 }
161 result.add(PaintPolygons.NEWPATH);
162 return result;
163 }
164
165 case RIGHTONLY:
166 {
167 OTSLine3D centerLine = stripe.getCenterLine();
168 Geometry leftDesignLine = centerLine.offsetLine(0.2).getLineString();
169 ArrayList<OTSPoint3D> result =
170 makeDashes(new LengthIndexedLine(leftDesignLine), 0.2, 0.0, new double[] { 3, 9 });
171 Coordinate[] rightCoordinates = centerLine.offsetLine(-0.2).getLineString()
172 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
173 for (int i = 0; i < rightCoordinates.length; i++)
174 {
175 result.add(new OTSPoint3D(rightCoordinates[i]));
176 }
177 result.add(PaintPolygons.NEWPATH);
178 return result;
179 }
180
181 case SOLID:
182 return new ArrayList<>(Arrays.asList(stripe.getContour().getPoints()));
183
184 default:
185 throw new NamingException("Unsupported stripe type: " + stripeType);
186 }
187
188 }
189
190
191
192
193
194
195
196
197
198 public StripeAnimation(final Stripe source, final SimulatorInterface.TimeDoubleUnit simulator, final TYPE type)
199 throws NamingException, RemoteException, OTSGeometryException
200 {
201 super(source, simulator);
202 this.type = type;
203 ArrayList<OTSPoint3D> list = makePoints(source, type);
204 if (!list.isEmpty())
205 {
206 this.line = new OTSLine3D(list);
207 }
208 else
209 {
210
211 this.line = null;
212 }
213 }
214
215
216 @Override
217 public final void paint(final Graphics2D graphics, final ImageObserver observer)
218 {
219 if (this.line != null)
220 {
221 graphics.setStroke(new BasicStroke(2.0f));
222 PaintPolygons.paintMultiPolygon(graphics, Color.WHITE, getSource().getLocation(), this.line, true);
223 }
224 }
225
226
227
228
229
230
231
232
233 public enum TYPE
234 {
235
236 SOLID,
237
238
239 LEFTONLY,
240
241
242 RIGHTONLY,
243
244
245 DASHED,
246
247
248 DOUBLE,
249
250
251 BLOCK
252 }
253
254
255 @Override
256 @SuppressWarnings("checkstyle:designforextension")
257 public ClonableRenderable2DInterface<Stripe> clone(final Stripe newSource,
258 final SimulatorInterface.TimeDoubleUnit newSimulator) throws NamingException, RemoteException
259 {
260 try
261 {
262 return new StripeAnimation(newSource, newSimulator, this.type);
263 }
264 catch (OTSGeometryException exception)
265 {
266 throw new RemoteException("Stripe animation clone failed", exception);
267 }
268 }
269
270
271 @Override
272 public final String toString()
273 {
274 return "StripeAnimation [source = " + getSource().toString() + ", type=" + this.type + ", line=" + this.line + "]";
275 }
276 }