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.dsol.OTSSimulatorInterface;
19 import org.opentrafficsim.core.geometry.OTSGeometryException;
20 import org.opentrafficsim.core.geometry.OTSLine3D;
21 import org.opentrafficsim.core.geometry.OTSPoint3D;
22 import org.opentrafficsim.draw.core.ClonableRenderable2DInterface;
23 import org.opentrafficsim.draw.core.PaintPolygons;
24 import org.opentrafficsim.road.network.lane.Stripe;
25
26 import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
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, new double[] {3, 9});
124
125 case BLOCK:
126 return makeDashes(new LengthIndexedLine(stripe.getCenterLine().getLineString()), 0.45, 1.0,
127 new double[] {1, 3});
128
129 case DOUBLE:
130 {
131 OTSLine3D centerLine = stripe.getCenterLine();
132 Coordinate[] leftLine = centerLine.offsetLine(0.2).getLineString()
133 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
134 Coordinate[] rightLine = centerLine.offsetLine(-0.2).getLineString()
135 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
136 ArrayList<OTSPoint3D> result = new ArrayList<>(leftLine.length + rightLine.length);
137 for (int i = 0; i < leftLine.length; i++)
138 {
139 result.add(new OTSPoint3D(leftLine[i]));
140 }
141 for (int i = 0; i < rightLine.length; i++)
142 {
143 result.add(new OTSPoint3D(rightLine[i]));
144 }
145 return result;
146 }
147
148 case LEFTONLY:
149 {
150 OTSLine3D centerLine = stripe.getCenterLine();
151 Geometry rightDesignLine = centerLine.offsetLine(-0.2).getLineString();
152 ArrayList<OTSPoint3D> result =
153 makeDashes(new LengthIndexedLine(rightDesignLine), 0.2, 0.0, new double[] {3, 9});
154 Coordinate[] leftCoordinates = centerLine.offsetLine(0.2).getLineString()
155 .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:
165 {
166 OTSLine3D centerLine = stripe.getCenterLine();
167 Geometry leftDesignLine = centerLine.offsetLine(0.2).getLineString();
168 ArrayList<OTSPoint3D> result = makeDashes(new LengthIndexedLine(leftDesignLine), 0.2, 0.0, new double[] {3, 9});
169 Coordinate[] rightCoordinates = centerLine.offsetLine(-0.2).getLineString()
170 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
171 for (int i = 0; i < rightCoordinates.length; i++)
172 {
173 result.add(new OTSPoint3D(rightCoordinates[i]));
174 }
175 result.add(PaintPolygons.NEWPATH);
176 return result;
177 }
178
179 case SOLID:
180 return new ArrayList<>(Arrays.asList(stripe.getContour().getPoints()));
181
182 default:
183 throw new NamingException("Unsupported stripe type: " + stripeType);
184 }
185
186 }
187
188
189
190
191
192
193
194
195
196 public StripeAnimation(final Stripe source, final OTSSimulatorInterface simulator, final TYPE type)
197 throws NamingException, RemoteException, OTSGeometryException
198 {
199 super(source, simulator);
200 this.type = type;
201 ArrayList<OTSPoint3D> list = makePoints(source, type);
202 if (!list.isEmpty())
203 {
204 this.line = new OTSLine3D(list);
205 }
206 else
207 {
208
209 this.line = null;
210 }
211 }
212
213
214 @Override
215 public final void paint(final Graphics2D graphics, final ImageObserver observer)
216 {
217 if (this.line != null)
218 {
219 graphics.setStroke(new BasicStroke(2.0f));
220 PaintPolygons.paintMultiPolygon(graphics, Color.WHITE, getSource().getLocation(), this.line, true);
221 }
222 }
223
224
225
226
227
228
229
230
231 public enum TYPE
232 {
233
234 SOLID,
235
236
237 LEFTONLY,
238
239
240 RIGHTONLY,
241
242
243 DASHED,
244
245
246 DOUBLE,
247
248
249 BLOCK
250 }
251
252
253 @Override
254 @SuppressWarnings("checkstyle:designforextension")
255 public ClonableRenderable2DInterface<Stripe> clone(final Stripe newSource,
256 final OTSSimulatorInterface newSimulator) throws NamingException, RemoteException
257 {
258 try
259 {
260 return new StripeAnimation(newSource, newSimulator, this.type);
261 }
262 catch (OTSGeometryException exception)
263 {
264 throw new RemoteException("Stripe animation clone failed", exception);
265 }
266 }
267
268
269 @Override
270 public final String toString()
271 {
272 return "StripeAnimation [source = " + getSource().toString() + ", type=" + this.type + ", line=" + this.line + "]";
273 }
274 }