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.geom.Path2D;
7 import java.awt.image.ImageObserver;
8 import java.rmi.RemoteException;
9 import java.util.ArrayList;
10 import java.util.List;
11 import java.util.Set;
12
13 import javax.naming.NamingException;
14
15 import org.djunits.value.vdouble.scalar.Length;
16 import org.djutils.draw.line.PolyLine2d;
17 import org.djutils.draw.line.Polygon2d;
18 import org.djutils.draw.point.OrientedPoint2d;
19 import org.djutils.draw.point.Point2d;
20 import org.opentrafficsim.base.geometry.OtsLocatable;
21 import org.opentrafficsim.base.geometry.OtsRenderable;
22 import org.opentrafficsim.draw.DrawLevel;
23 import org.opentrafficsim.draw.PaintPolygons;
24 import org.opentrafficsim.draw.road.StripeAnimation.StripeData;
25
26 import nl.tudelft.simulation.naming.context.Contextualized;
27
28
29
30
31
32
33
34
35
36
37 public class StripeAnimation extends OtsRenderable<StripeData>
38 {
39
40 private static final long serialVersionUID = 20141017L;
41
42
43 private final Set<Path2D.Float> paths;
44
45
46
47
48
49
50
51 public StripeAnimation(final StripeData source, final Contextualized contextualized) throws NamingException, RemoteException
52 {
53 super(source, contextualized);
54 List<Point2d> list = makePoints(source);
55 if (!list.isEmpty())
56 {
57 this.paths = PaintPolygons.getPaths(getSource().getLocation(), list);
58 }
59 else
60 {
61
62 this.paths = null;
63 }
64 }
65
66
67
68
69
70
71
72
73
74
75
76
77 private ArrayList<Point2d> makeDashes(final PolyLine2d center, final double width, final double startOffset,
78 final double[] onOffLengths)
79 {
80 double period = 0;
81 for (double length : onOffLengths)
82 {
83 if (length < 0)
84 {
85 throw new Error("Bad pattern - on or off length is < 0");
86 }
87 period += length;
88 }
89 if (period <= 0)
90 {
91 throw new Error("Bad pattern - repeat period length is 0");
92 }
93 double length = center.getLength();
94 double position = -startOffset;
95 int phase = 0;
96 ArrayList<Point2d> result = new ArrayList<>();
97 while (position < length)
98 {
99 double nextBoundary = position + onOffLengths[phase++ % onOffLengths.length];
100 if (nextBoundary > 0)
101 {
102 if (position < 0)
103 {
104 position = 0;
105 }
106 double endPosition = nextBoundary;
107 if (endPosition > length)
108 {
109 endPosition = length;
110 }
111
112 PolyLine2d dashCenter;
113 dashCenter = center.extract(position, endPosition);
114 dashCenter.offsetLine(width / 2).getPoints().forEachRemaining(result::add);
115 dashCenter.offsetLine(-width / 2).reverse().getPoints().forEachRemaining(result::add);
116 result.add(PaintPolygons.NEWPATH);
117 }
118 position = nextBoundary + onOffLengths[phase++ % onOffLengths.length];
119 }
120 return result;
121 }
122
123
124
125
126
127
128
129 private List<Point2d> makePoints(final StripeData stripe) throws NamingException
130 {
131 double width = stripe.getWidth().si;
132 switch (stripe.getType())
133 {
134 case DASHED:
135 return makeDashes(stripe.getCenterLine(), width, 3.0, new double[] {3, 9});
136
137 case BLOCK:
138 return makeDashes(stripe.getCenterLine(), width, 1.0, new double[] {1, 3});
139
140 case DOUBLE:
141 {
142 PolyLine2d centerLine = stripe.getCenterLine();
143 List<Point2d> result = new ArrayList<>(centerLine.size() * 4 + 1);
144 centerLine.offsetLine(width / 2).getPoints().forEachRemaining(result::add);
145 centerLine.offsetLine(width / 6).reverse().getPoints().forEachRemaining(result::add);
146 result.add(PaintPolygons.NEWPATH);
147 centerLine.offsetLine(-width / 2).getPoints().forEachRemaining(result::add);
148 centerLine.offsetLine(-width / 6).reverse().getPoints().forEachRemaining(result::add);
149 return result;
150 }
151
152 case LEFT:
153 {
154 PolyLine2d centerLine = stripe.getCenterLine();
155 List<Point2d> result = makeDashes(centerLine.offsetLine(-width / 3), width / 3, 0.0, new double[] {3, 9});
156 result.add(PaintPolygons.NEWPATH);
157 centerLine.offsetLine(width / 2).getPoints().forEachRemaining(result::add);
158 centerLine.offsetLine(width / 6).reverse().getPoints().forEachRemaining(result::add);
159 return result;
160 }
161
162 case RIGHT:
163 {
164 PolyLine2d centerLine = stripe.getCenterLine();
165 ArrayList<Point2d> result = makeDashes(centerLine.offsetLine(width / 3), width / 3, 0.0, new double[] {3, 9});
166 result.add(PaintPolygons.NEWPATH);
167 centerLine.offsetLine(-width / 2).getPoints().forEachRemaining(result::add);
168 centerLine.offsetLine(-width / 6).reverse().getPoints().forEachRemaining(result::add);
169 return result;
170 }
171
172 case SOLID:
173 PolyLine2d centerLine = stripe.getCenterLine();
174 PolyLine2d leftEdge = centerLine.offsetLine(stripe.getWidth().si / 2.0);
175 PolyLine2d rightEdge = centerLine.offsetLine(-stripe.getWidth().si / 2.0);
176 List<Point2d> list = leftEdge.getPointList();
177 list.addAll(rightEdge.reverse().getPointList());
178 List<Point2d> result = new Polygon2d(list).getPointList();
179 return result;
180
181 default:
182 throw new NamingException("Unsupported stripe type: " + stripe.getType());
183 }
184
185 }
186
187
188 @Override
189 public final void paint(final Graphics2D graphics, final ImageObserver observer)
190 {
191 if (this.paths != null)
192 {
193 setRendering(graphics);
194 graphics.setStroke(new BasicStroke(2.0f));
195 PaintPolygons.paintPaths(graphics, Color.WHITE, this.paths, true);
196 resetRendering(graphics);
197 }
198 }
199
200
201 @Override
202 public final String toString()
203 {
204 return "StripeAnimation [source = " + getSource().toString() + ", paths=" + this.paths + "]";
205 }
206
207
208
209
210
211
212
213
214
215
216 public interface StripeData extends OtsLocatable
217 {
218
219 @Override
220 OrientedPoint2d getLocation();
221
222
223
224
225
226 PolyLine2d getCenterLine();
227
228
229
230
231
232 Type getType();
233
234
235
236
237
238 Length getWidth();
239
240
241 @Override
242 default double getZ()
243 {
244 return DrawLevel.MARKING.getZ();
245 }
246
247
248
249
250
251
252
253
254
255
256 public enum Type
257 {
258
259 SOLID,
260
261
262 LEFT,
263
264
265 RIGHT,
266
267
268 DASHED,
269
270
271 DOUBLE,
272
273
274 BLOCK;
275 }
276 }
277 }