1 package org.opentrafficsim.base.geometry;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.Locale;
6 import java.util.stream.Collectors;
7 import java.util.stream.DoubleStream;
8
9 import org.djunits.value.vdouble.scalar.Angle;
10 import org.djutils.draw.line.PolyLine2d;
11 import org.djutils.draw.point.DirectedPoint2d;
12 import org.djutils.draw.point.Point2d;
13 import org.djutils.exceptions.Throw;
14
15
16
17
18
19
20
21
22
23
24
25 public final class OtsGeometryUtil
26 {
27
28 private OtsGeometryUtil()
29 {
30
31 }
32
33
34
35
36
37
38
39 public static String printCoordinate(final String prefix, final Point2d point)
40 {
41 return String.format(Locale.US, "%s %8.3f,%8.3f ", prefix, point.x, point.y);
42 }
43
44
45
46
47
48
49
50
51 public static int getNumSegmentsForRadius(final double maxSpatialError, final Angle angle, final double r)
52 {
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 return (int) Math.ceil(angle.si / (2.0 * Math.acos(1.0 - maxSpatialError / r)));
68 }
69
70
71
72
73
74
75
76
77 public static DirectedPoint2d offsetPoint(final DirectedPoint2d point, final double offset)
78 {
79 return new DirectedPoint2d(point.x - Math.sin(point.dirZ) * offset, point.y + Math.cos(point.dirZ) * offset,
80 point.dirZ);
81 }
82
83
84
85
86
87
88
89
90 public static DirectedPoint2d translatePoint(final DirectedPoint2d point, final double distance)
91 {
92 return new DirectedPoint2d(point.x + distance * Math.cos(point.dirZ), point.y + distance * Math.sin(point.dirZ),
93 point.dirZ);
94 }
95
96
97
98
99
100
101
102
103
104
105
106 public static PolyLine2d offsetLine(final PolyLine2d line, final double[] relativeFractions, final double[] offsets)
107 throws OtsGeometryException
108 {
109 Throw.whenNull(relativeFractions, "relativeFraction may not be null");
110 Throw.whenNull(offsets, "offsets may not be null");
111 Throw.when(relativeFractions.length < 2, OtsGeometryException.class, "size of relativeFractions must be >= 2");
112 Throw.when(relativeFractions.length != offsets.length, OtsGeometryException.class,
113 "size of relativeFractions must be equal to size of offsets");
114 Throw.when(relativeFractions[0] < 0, OtsGeometryException.class, "relativeFractions may not start before 0");
115 Throw.when(relativeFractions[relativeFractions.length - 1] > 1, OtsGeometryException.class,
116 "relativeFractions may not end beyond 1");
117 List<Double> fractionsList = DoubleStream.of(relativeFractions).boxed().collect(Collectors.toList());
118 List<Double> offsetsList = DoubleStream.of(offsets).boxed().collect(Collectors.toList());
119 if (relativeFractions[0] != 0)
120 {
121 fractionsList.add(0, 0.0);
122 offsetsList.add(0, 0.0);
123 }
124 if (relativeFractions[relativeFractions.length - 1] < 1.0)
125 {
126 fractionsList.add(1.0);
127 offsetsList.add(0.0);
128 }
129 PolyLine2d[] offsetLine = new PolyLine2d[fractionsList.size()];
130 for (int i = 0; i < fractionsList.size(); i++)
131 {
132 offsetLine[i] = line.offsetLine(offsetsList.get(i));
133 }
134 List<Point2d> out = new ArrayList<>();
135 Point2d prevCoordinate = null;
136 final double tooClose = 0.05;
137 for (int i = 0; i < offsetsList.size() - 1; i++)
138 {
139 Throw.when(fractionsList.get(i + 1) <= fractionsList.get(i), OtsGeometryException.class,
140 "fractions must be in ascending order");
141 PolyLine2d startGeometry = offsetLine[i].extractFractional(fractionsList.get(i), fractionsList.get(i + 1));
142 PolyLine2d endGeometry = offsetLine[i + 1].extractFractional(fractionsList.get(i), fractionsList.get(i + 1));
143 double firstLength = startGeometry.getLength();
144 double secondLength = endGeometry.getLength();
145 int firstIndex = 0;
146 int secondIndex = 0;
147 while (firstIndex < startGeometry.size() && secondIndex < endGeometry.size())
148 {
149 double firstRatio = firstIndex < startGeometry.size() ? startGeometry.lengthAtIndex(firstIndex) / firstLength
150 : Double.MAX_VALUE;
151 double secondRatio = secondIndex < endGeometry.size() ? endGeometry.lengthAtIndex(secondIndex) / secondLength
152 : Double.MAX_VALUE;
153 double ratio;
154 if (firstRatio < secondRatio)
155 {
156 ratio = firstRatio;
157 firstIndex++;
158 }
159 else
160 {
161 ratio = secondRatio;
162 secondIndex++;
163 }
164 Point2d firstCoordinate = startGeometry.getLocation(ratio * firstLength);
165 Point2d secondCoordinate = endGeometry.getLocation(ratio * secondLength);
166 Point2d resultCoordinate = new Point2d((1 - ratio) * firstCoordinate.x + ratio * secondCoordinate.x,
167 (1 - ratio) * firstCoordinate.y + ratio * secondCoordinate.y);
168 if (null == prevCoordinate || resultCoordinate.distance(prevCoordinate) > tooClose)
169 {
170 out.add(resultCoordinate);
171 prevCoordinate = resultCoordinate;
172 }
173 }
174 }
175 return new PolyLine2d(0.0, out.toArray(new Point2d[out.size()]));
176 }
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197 @SuppressWarnings("checkstyle:parameternumber")
198 public static Point2d intersectionOfLinesEps(final double line1P1X, final double line1P1Y, final double line1P2X,
199 final double line1P2Y, final boolean lowLimitLine1, final boolean highLimitLine1, final double line2P1X,
200 final double line2P1Y, final double line2P2X, final double line2P2Y, final boolean lowLimitLine2,
201 final boolean highLimitLine2, final double eps)
202 {
203 Throw.when(eps < 0.0, IllegalArgumentException.class, "eps may not be negative");
204 double line1DX = line1P2X - line1P1X;
205 double line1DY = line1P2Y - line1P1Y;
206 double l2p1x = line2P1X - line1P1X;
207 double l2p1y = line2P1Y - line1P1Y;
208 double l2p2x = line2P2X - line1P1X;
209 double l2p2y = line2P2Y - line1P1Y;
210 double denominator = (l2p2y - l2p1y) * line1DX - (l2p2x - l2p1x) * line1DY;
211 Throw.whenNaN(denominator, "none of the parameters may be NaN");
212 if (Math.abs(denominator) < eps)
213 {
214 return null;
215 }
216 double uA = ((l2p2x - l2p1x) * (-l2p1y) - (l2p2y - l2p1y) * (-l2p1x)) / denominator;
217
218 if (uA < -eps && lowLimitLine1 || uA > 1.0 + eps && highLimitLine1)
219 {
220 return null;
221 }
222 double uB = (line1DY * l2p1x - line1DX * l2p1y) / denominator;
223
224 if (uB < -eps && lowLimitLine2 || uB > 1.0 + eps && highLimitLine2)
225 {
226 return null;
227 }
228 if (Math.abs(uA - 1.0) < eps)
229 {
230 return new Point2d(line1P2X, line1P2Y);
231 }
232 if (Math.abs(uB) < eps)
233 {
234 return new Point2d(line2P1X, line2P1Y);
235 }
236 if (Math.abs(uB - 1.0) < eps)
237 {
238 return new Point2d(line2P2X, line2P2Y);
239 }
240 return new Point2d(line1P1X + uA * line1DX, line1P1Y + uA * line1DY);
241 }
242
243 }