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 org.opentrafficsim.core.animation.ClonableRenderable2DInterface;
14 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
15 import org.opentrafficsim.core.geometry.OTSGeometryException;
16 import org.opentrafficsim.core.geometry.OTSLine3D;
17 import org.opentrafficsim.core.geometry.OTSPoint3D;
18 import org.opentrafficsim.core.network.animation.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
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, 2.99,
124 new double[] { 3, 9 });
125
126 case DOUBLE:
127 {
128 OTSLine3D centerLine = stripe.getCenterLine();
129 Coordinate[] leftLine = centerLine.offsetLine(0.2).getLineString()
130 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
131 Coordinate[] rightLine = centerLine.offsetLine(-0.2).getLineString()
132 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
133 ArrayList<OTSPoint3D> result = new ArrayList<>(leftLine.length + rightLine.length);
134 for (int i = 0; i < leftLine.length; i++)
135 {
136 result.add(new OTSPoint3D(leftLine[i]));
137 }
138 for (int i = 0; i < rightLine.length; i++)
139 {
140 result.add(new OTSPoint3D(rightLine[i]));
141 }
142 return result;
143 }
144
145 case LEFTONLY:
146 {
147 OTSLine3D centerLine = stripe.getCenterLine();
148 Geometry rightDesignLine = centerLine.offsetLine(-0.2).getLineString();
149 ArrayList<OTSPoint3D> result =
150 makeDashes(new LengthIndexedLine(rightDesignLine), 0.2, 2.99, new double[] { 3, 9 });
151 Geometry leftDesignLine =
152 centerLine.offsetLine(0.2).getLineString().buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT);
153 Coordinate[] leftCoordinates =
154 leftDesignLine.buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
155 for (int i = 0; i < leftCoordinates.length; i++)
156 {
157 result.add(new OTSPoint3D(leftCoordinates[i]));
158 }
159 result.add(PaintPolygons.NEWPATH);
160 return result;
161 }
162
163 case RIGHTONLY:
164 {
165 OTSLine3D centerLine = stripe.getCenterLine();
166 Geometry leftDesignLine = centerLine.offsetLine(0.2).getLineString();
167 ArrayList<OTSPoint3D> result =
168 makeDashes(new LengthIndexedLine(leftDesignLine), 0.2, 2.99, new double[] { 3, 9 });
169 Geometry rightDesignLine =
170 centerLine.offsetLine(-0.2).getLineString().buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT);
171 Coordinate[] rightCoordinates =
172 rightDesignLine.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 OTSSimulatorInterface simulator, final TYPE type)
199 throws NamingException, RemoteException, OTSGeometryException
200 {
201 super(source, simulator);
202 this.type = type;
203 this.line = new OTSLine3D(makePoints(source, type));
204 }
205
206
207 @Override
208 public final void paint(final Graphics2D graphics, final ImageObserver observer)
209 {
210 PaintPolygons.paintMultiPolygon(graphics, Color.WHITE, getSource().getLocation(), this.line, true);
211 }
212
213
214
215
216
217
218
219
220 public enum TYPE
221 {
222
223 SOLID,
224
225
226 LEFTONLY,
227
228
229 RIGHTONLY,
230
231
232 DASHED,
233
234
235 DOUBLE
236 }
237
238
239 @Override
240 @SuppressWarnings("checkstyle:designforextension")
241 public ClonableRenderable2DInterface<Stripe> clone(final Stripe newSource, final OTSSimulatorInterface newSimulator)
242 throws NamingException, RemoteException
243 {
244 try
245 {
246 return new StripeAnimation(newSource, newSimulator, this.type);
247 }
248 catch (OTSGeometryException exception)
249 {
250 throw new RemoteException("Stripe animation clone failed", exception);
251 }
252 }
253
254
255 @Override
256 public final String toString()
257 {
258 return "StripeAnimation [source = " + getSource().toString() + ", type=" + this.type + ", line=" + this.line + "]";
259 }
260 }