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 private ArrayList<OTSPoint3D> makeDashes(final LengthIndexedLine center, final double width, final double startOffset,
64 final double[] onOffLengths)
65 {
66 double period = 0;
67 for (double length : onOffLengths)
68 {
69 if (length < 0)
70 {
71 throw new Error("Bad pattern - on or off length is < 0");
72 }
73 period += length;
74 }
75 if (period <= 0)
76 {
77 throw new Error("Bad pattern - repeat period length is 0");
78 }
79 double length = center.getEndIndex();
80 double position = -startOffset;
81 int phase = 0;
82 ArrayList<OTSPoint3D> result = new ArrayList<>();
83 while (position < length)
84 {
85 double nextBoundary = position + onOffLengths[phase++ % onOffLengths.length];
86 if (nextBoundary > 0)
87 {
88 if (position < 0)
89 {
90 position = 0;
91 }
92 double endPosition = nextBoundary;
93 if (endPosition > length)
94 {
95 endPosition = length;
96 }
97 Coordinate[] oneDash = center.extractLine(position, endPosition)
98 .buffer(width / 2, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
99 for (int i = 0; i < oneDash.length; i++)
100 {
101 result.add(new OTSPoint3D(oneDash[i]));
102 }
103 result.add(PaintPolygons.NEWPATH);
104 }
105 position = nextBoundary + onOffLengths[phase++ % onOffLengths.length];
106 }
107 return result;
108 }
109
110
111
112
113
114
115
116
117 private ArrayList<OTSPoint3D> makePoints(final Stripe stripe, final TYPE stripeType) throws NamingException
118 {
119 switch (this.type)
120 {
121 case DASHED:
122 return makeDashes(new LengthIndexedLine(stripe.getCenterLine().getLineString()), 0.2, 0, new double[] { 3, 9 });
123
124 case DOUBLE:
125 {
126 OTSLine3D centerLine = stripe.getCenterLine();
127 Coordinate[] leftLine = centerLine.offsetLine(0.2).getLineString()
128 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
129 Coordinate[] rightLine = centerLine.offsetLine(-0.2).getLineString()
130 .buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
131 ArrayList<OTSPoint3D> result = new ArrayList<>(leftLine.length + rightLine.length);
132 for (int i = 0; i < leftLine.length; i++)
133 {
134 result.add(new OTSPoint3D(leftLine[i]));
135 }
136 for (int i = 0; i < rightLine.length; i++)
137 {
138 result.add(new OTSPoint3D(rightLine[i]));
139 }
140 return result;
141 }
142
143 case LEFTONLY:
144 {
145 OTSLine3D centerLine = stripe.getCenterLine();
146 Geometry rightDesignLine = centerLine.offsetLine(-0.2).getLineString();
147 ArrayList<OTSPoint3D> result =
148 makeDashes(new LengthIndexedLine(rightDesignLine), 0.2, 0, new double[] { 3, 9 });
149 Geometry leftDesignLine =
150 centerLine.offsetLine(0.2).getLineString().buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT);
151 Coordinate[] leftCoordinates =
152 leftDesignLine.buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
153 for (int i = 0; i < leftCoordinates.length; i++)
154 {
155 result.add(new OTSPoint3D(leftCoordinates[i]));
156 }
157 result.add(PaintPolygons.NEWPATH);
158 return result;
159 }
160
161 case RIGHTONLY:
162 {
163 OTSLine3D centerLine = stripe.getCenterLine();
164 Geometry leftDesignLine = centerLine.offsetLine(0.2).getLineString();
165 ArrayList<OTSPoint3D> result = makeDashes(new LengthIndexedLine(leftDesignLine), 0.2, 0, new double[] { 3, 9 });
166 Geometry rightDesignLine =
167 centerLine.offsetLine(-0.2).getLineString().buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT);
168 Coordinate[] rightCoordinates =
169 rightDesignLine.buffer(0.1, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
170 for (int i = 0; i < rightCoordinates.length; i++)
171 {
172 result.add(new OTSPoint3D(rightCoordinates[i]));
173 }
174 result.add(PaintPolygons.NEWPATH);
175 return result;
176 }
177
178 case SOLID:
179 return new ArrayList<>(Arrays.asList(stripe.getContour().getPoints()));
180
181 default:
182 throw new NamingException("Unsupported stripe type: " + stripeType);
183 }
184
185 }
186
187
188
189
190
191
192
193
194
195 public StripeAnimation(final Stripe source, final OTSSimulatorInterface simulator, final TYPE type)
196 throws NamingException, RemoteException, OTSGeometryException
197 {
198 super(source, simulator);
199 this.type = type;
200 this.line = new OTSLine3D(makePoints(source, type));
201 }
202
203
204 @Override
205 public final void paint(final Graphics2D graphics, final ImageObserver observer)
206 {
207 PaintPolygons.paintMultiPolygon(graphics, Color.WHITE, getSource().getLocation(), this.line, true);
208 }
209
210
211
212
213
214
215
216
217 public enum TYPE
218 {
219
220 SOLID,
221
222
223 LEFTONLY,
224
225
226 RIGHTONLY,
227
228
229 DASHED,
230
231
232 DOUBLE
233 }
234
235
236 @Override
237 @SuppressWarnings("checkstyle:designforextension")
238 public ClonableRenderable2DInterface<Stripe> clone(final Stripe newSource, final OTSSimulatorInterface newSimulator)
239 throws NamingException, RemoteException
240 {
241 try
242 {
243 return new StripeAnimation(newSource, newSimulator, this.type);
244 }
245 catch (OTSGeometryException exception)
246 {
247 throw new RemoteException("Stripe animation clone failed", exception);
248 }
249 }
250
251
252 @Override
253 public final String toString()
254 {
255 return "StripeAnimation [source = " + getSource().toString() + ", type=" + this.type + ", line=" + this.line + "]";
256 }
257 }