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