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