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.image.ImageObserver;
7 import java.io.Serializable;
8 import java.rmi.RemoteException;
9 import java.util.ArrayList;
10 import java.util.Arrays;
11
12 import javax.naming.NamingException;
13
14 import org.locationtech.jts.geom.Coordinate;
15 import org.locationtech.jts.geom.Geometry;
16 import org.locationtech.jts.linearref.LengthIndexedLine;
17 import org.locationtech.jts.operation.buffer.BufferParameters;
18 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
19 import org.opentrafficsim.core.geometry.OtsGeometryException;
20 import org.opentrafficsim.core.geometry.OtsLine3d;
21 import org.opentrafficsim.core.geometry.OtsPoint3d;
22 import org.opentrafficsim.draw.core.PaintPolygons;
23 import org.opentrafficsim.road.network.lane.Stripe;
24
25 import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
26 import nl.tudelft.simulation.dsol.animation.D2.Renderable2DInterface;
27
28
29
30
31
32
33
34
35
36 public class StripeAnimation extends Renderable2D<Stripe> implements Renderable2DInterface<Stripe>, Serializable
37 {
38
39 private static final long serialVersionUID = 20141017L;
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
59 private ArrayList<OtsPoint3d> makeDashes(final LengthIndexedLine center, final double width, final double startOffset,
60 final double[] onOffLengths)
61 {
62 double period = 0;
63 for (double length : onOffLengths)
64 {
65 if (length < 0)
66 {
67 throw new Error("Bad pattern - on or off length is < 0");
68 }
69 period += length;
70 }
71 if (period <= 0)
72 {
73 throw new Error("Bad pattern - repeat period length is 0");
74 }
75 double length = center.getEndIndex();
76 double position = -startOffset;
77 int phase = 0;
78 ArrayList<OtsPoint3d> result = new ArrayList<>();
79 while (position < length)
80 {
81 double nextBoundary = position + onOffLengths[phase++ % onOffLengths.length];
82 if (nextBoundary > 0)
83 {
84 if (position < 0)
85 {
86 position = 0;
87 }
88 double endPosition = nextBoundary;
89 if (endPosition > length)
90 {
91 endPosition = length;
92 }
93 Coordinate[] oneDash = center.extractLine(position, endPosition)
94 .buffer(width / 2, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).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 private ArrayList<OtsPoint3d> makePoints(final Stripe stripe) throws NamingException
113 {
114 double width = stripe.getWidth(0.5).si;
115 switch (stripe.getType())
116 {
117 case DASHED:
118 return makeDashes(new LengthIndexedLine(stripe.getCenterLine().getLineString()), width, 3.0,
119 new double[] {3, 9});
120
121 case BLOCK:
122 return makeDashes(new LengthIndexedLine(stripe.getCenterLine().getLineString()), width, 1.0,
123 new double[] {1, 3});
124
125 case DOUBLE:
126 {
127 OtsLine3d centerLine = stripe.getCenterLine();
128 Coordinate[] leftLine = centerLine.offsetLine(width / 3).getLineString()
129 .buffer(width / 6, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
130 Coordinate[] rightLine = centerLine.offsetLine(-width / 3).getLineString()
131 .buffer(width / 6, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
132 ArrayList<OtsPoint3d> result = new ArrayList<>(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 LEFT:
145 {
146 OtsLine3d centerLine = stripe.getCenterLine();
147 Geometry rightDesignLine = centerLine.offsetLine(-width / 3).getLineString();
148 ArrayList<OtsPoint3d> result =
149 makeDashes(new LengthIndexedLine(rightDesignLine), width / 3, 0.0, new double[] {3, 9});
150 Coordinate[] leftCoordinates = centerLine.offsetLine(width / 3).getLineString()
151 .buffer(width / 6, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
152 for (int i = 0; i < leftCoordinates.length; i++)
153 {
154 result.add(new OtsPoint3d(leftCoordinates[i]));
155 }
156 result.add(PaintPolygons.NEWPATH);
157 return result;
158 }
159
160 case RIGHT:
161 {
162 OtsLine3d centerLine = stripe.getCenterLine();
163 Geometry leftDesignLine = centerLine.offsetLine(width / 3).getLineString();
164 ArrayList<OtsPoint3d> result =
165 makeDashes(new LengthIndexedLine(leftDesignLine), width / 3, 0.0, new double[] {3, 9});
166 Coordinate[] rightCoordinates = centerLine.offsetLine(-width / 3).getLineString()
167 .buffer(width / 6, QUADRANTSEGMENTS, BufferParameters.CAP_FLAT).getCoordinates();
168 for (int i = 0; i < rightCoordinates.length; i++)
169 {
170 result.add(new OtsPoint3d(rightCoordinates[i]));
171 }
172 result.add(PaintPolygons.NEWPATH);
173 return result;
174 }
175
176 case SOLID:
177 return new ArrayList<>(Arrays.asList(stripe.getContour().getPoints()));
178
179 default:
180 throw new NamingException("Unsupported stripe type: " + stripe.getType());
181 }
182
183 }
184
185
186
187
188
189
190
191
192 public StripeAnimation(final Stripe source, final OtsSimulatorInterface simulator)
193 throws NamingException, RemoteException, OtsGeometryException
194 {
195 super(source, simulator);
196 ArrayList<OtsPoint3d> list = makePoints(source);
197 if (!list.isEmpty())
198 {
199 this.line = new OtsLine3d(list);
200 }
201 else
202 {
203
204 this.line = null;
205 }
206 }
207
208
209 @Override
210 public final void paint(final Graphics2D graphics, final ImageObserver observer)
211 {
212 if (this.line != null)
213 {
214 graphics.setStroke(new BasicStroke(2.0f));
215 PaintPolygons.paintMultiPolygon(graphics, Color.WHITE, getSource().getLocation(), this.line, true);
216 }
217 }
218
219
220 @Override
221 public final String toString()
222 {
223 return "StripeAnimation [source = " + getSource().toString() + ", line=" + this.line + "]";
224 }
225 }