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