1 package org.djutils.draw.line;
2
3 import java.awt.geom.Path2D;
4 import java.awt.geom.Path2D.Double;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Iterator;
8 import java.util.List;
9
10 import org.djutils.draw.DrawRuntimeException;
11 import org.djutils.draw.bounds.Bounds2d;
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 class Polygon2d extends PolyLine2d
26 {
27
28 private static final long serialVersionUID = 20209999L;
29
30
31
32
33
34
35
36 public Polygon2d(final double[] x, final double[] y) throws DrawRuntimeException
37 {
38 super(fixClosingPointX(Throw.whenNull(x, "x"), Throw.whenNull(y, "y")), fixClosingPointY(x, y));
39 }
40
41
42
43
44
45
46
47 private static double[] fixClosingPointX(final double[] x, final double[] y)
48 {
49 if (x.length > 1 && y.length == x.length && x[0] == x[x.length - 1] && y[0] == y[x.length - 1])
50 {
51 return Arrays.copyOf(x, x.length - 1);
52 }
53 return x;
54 }
55
56
57
58
59
60
61
62 private static double[] fixClosingPointY(final double[] x, final double[] y)
63 {
64 if (x.length > 1 && y.length == x.length && x[0] == x[x.length - 1] && y[0] == y[x.length - 1])
65 {
66 return Arrays.copyOf(y, x.length - 1);
67 }
68 return y;
69 }
70
71
72
73
74
75
76
77 public Polygon2d(final Point2d[] points) throws NullPointerException, DrawRuntimeException
78 {
79 this(PolyLine2d.makeArray(points, p -> p.x), PolyLine2d.makeArray(points, p -> p.y));
80 }
81
82
83
84
85
86
87
88
89
90
91 public Polygon2d(final Point2d point1, final Point2d point2, final Point2d... otherPoints)
92 throws NullPointerException, DrawRuntimeException
93 {
94 super(Throw.whenNull(point1, "point1"), Throw.whenNull(point2, "point2"), fixClosingPoint(point1, otherPoints));
95 }
96
97
98
99
100
101
102
103 private static Point2d[] fixClosingPoint(final Point2d point1, final Point2d[] otherPoints)
104 {
105 if (otherPoints == null || otherPoints.length == 0)
106 {
107 return otherPoints;
108 }
109 Point2d[] result = otherPoints;
110 Point2d lastPoint = result[result.length - 1];
111 if (point1.x == lastPoint.x && point1.y == lastPoint.y)
112 {
113 result = Arrays.copyOf(otherPoints, result.length - 1);
114 lastPoint = result[result.length - 1];
115 }
116 Throw.when(point1.x == lastPoint.x && point1.y == lastPoint.y, DrawRuntimeException.class,
117 "Before last point and last point are at same location");
118 return result;
119 }
120
121
122
123
124
125
126
127 public Polygon2d(final List<Point2d> points) throws NullPointerException, DrawRuntimeException
128 {
129 super(fixClosingPoint(true, Throw.whenNull(points, "points")));
130
131 }
132
133
134
135
136
137
138
139
140
141
142 private static List<Point2d> fixClosingPoint(final boolean doNotModifyList, final List<Point2d> points)
143 throws DrawRuntimeException
144 {
145 Throw.when(points.size() < 2, DrawRuntimeException.class, "Need at least two points");
146 Point2d firstPoint = points.get(0);
147 Point2d lastPoint = points.get(points.size() - 1);
148 List<Point2d> result = points;
149 if (firstPoint.x == lastPoint.x && firstPoint.y == lastPoint.y)
150 {
151 if (doNotModifyList)
152 {
153 result = new ArrayList<>(points.size() - 1);
154 for (int i = 0; i < points.size() - 1; i++)
155 {
156 result.add(points.get(i));
157 }
158 }
159 else
160 {
161 result.remove(points.size() - 1);
162 }
163 lastPoint = result.get(result.size() - 1);
164 }
165 Throw.when(firstPoint.x == lastPoint.x && firstPoint.y == lastPoint.y, DrawRuntimeException.class,
166 "Before last point and last point are at same location");
167 return result;
168 }
169
170
171
172
173
174 public Polygon2d(final Iterator<Point2d> iterator)
175 {
176 this(fixClosingPoint(false, iteratorToList(Throw.whenNull(iterator, "iterator"))));
177 }
178
179
180
181
182
183
184
185 public Polygon2d(final boolean filterDuplicates, final Point2d... points) throws DrawRuntimeException
186 {
187 this(PolyLine2d.cleanPoints(filterDuplicates, Arrays.stream(points).iterator()));
188 }
189
190
191
192
193
194
195
196
197 public Polygon2d(final boolean filterDuplicates, final List<Point2d> pointList) throws DrawRuntimeException
198 {
199 this(PolyLine2d.cleanPoints(filterDuplicates, pointList.iterator()));
200 }
201
202
203
204
205
206 public Polygon2d(final Polygon2d polygon)
207 {
208 super(polygon);
209 }
210
211
212
213
214
215
216 public final boolean isConvex()
217 {
218 int flag = 0;
219 for (int i = 0; i < size(); i++)
220 {
221 int j = (i + 1) % size();
222 int k = (j + 1) % size();
223 double z = (getX(j) - getX(i)) * (getY(k) - getY(j)) - (getY(j) - getY(i)) * (getX(k) - getX(j));
224 if (z < 0)
225 {
226 flag |= 1;
227 }
228 else if (z > 0)
229 {
230 flag |= 2;
231 }
232 if (flag == 3)
233 {
234 return false;
235 }
236 }
237 return flag != 0;
238 }
239
240
241
242
243
244
245
246 public boolean contains(final Point2d point)
247 {
248 return contains(point.x, point.y);
249 }
250
251
252
253
254
255
256
257
258
259 public boolean contains(final double x, final double y)
260 {
261 if (!getBounds().contains(x, y))
262 {
263 return false;
264 }
265 int counter = 0;
266
267 double prevPointX = getX(size() - 1);
268 double prevPointY = getY(size() - 1);
269 for (int i = 0; i < size(); i++)
270 {
271 double curPointX = getX(i);
272 double curPointY = getY(i);
273
274 if (y > Math.min(prevPointY, curPointY) && y <= Math.max(prevPointY, curPointY)
275 && x <= Math.max(prevPointX, curPointX) && prevPointY != curPointY)
276 {
277 double xIntersection = (y - prevPointY) * (curPointX - prevPointX) / (curPointY - prevPointY) + prevPointX;
278 if (prevPointX == curPointX || x <= xIntersection)
279 {
280 counter++;
281 }
282 }
283 prevPointX = curPointX;
284 prevPointY = curPointY;
285 }
286 return counter % 2 != 0;
287 }
288
289
290
291
292
293
294
295 public boolean contains(final Bounds2d bounds)
296 {
297
298 if (getBounds().disjoint(bounds))
299 {
300 return false;
301 }
302
303 return toPath2D().contains(bounds.getMinX(), bounds.getMinY(), bounds.getDeltaX(), bounds.getDeltaY());
304 }
305
306
307
308
309
310
311 public boolean intersects(final Polygon2d other)
312 {
313
314 if (!getBounds().intersects(other.getBounds()))
315 {
316 return false;
317 }
318
319
320 for (Iterator<Point2d> iterator = getPoints(); iterator.hasNext();)
321 {
322 if (other.contains(iterator.next()))
323 {
324 return true;
325 }
326 }
327
328
329 for (Iterator<Point2d> iterator = other.getPoints(); iterator.hasNext();)
330 {
331 if (contains(iterator.next()))
332 {
333 return true;
334 }
335 }
336
337
338 for (int i = 0; i < this.size(); i++)
339 {
340 LineSegment2d ourSegment = getSegment(i);
341 for (int j = 0; j < other.size(); j++)
342 {
343 LineSegment2d otherSegment = other.getSegment(j);
344 Point2d intersection = Point2d.intersectionOfLineSegments(ourSegment, otherSegment);
345 if (intersection != null)
346 {
347 double p1x = ourSegment.startX, p1y = ourSegment.startY;
348 double d1x = ourSegment.endX - p1x, d1y = ourSegment.endY - p1y;
349 double p2x = otherSegment.startX, p2y = otherSegment.startY;
350 double d2x = otherSegment.endX - p2x, d2y = otherSegment.endY - p2y;
351
352 double det = d2x * d1y - d2y * d1x;
353 if (det != 0)
354 {
355 double z = (d2x * (p2y - p1y) + d2y * (p1x - p2x)) / det;
356 if (Math.abs(z) < 10.0 * Math.ulp(1.0) || Math.abs(z - 1.0) > 10.0 * Math.ulp(1.0))
357 {
358 return true;
359 }
360 }
361 }
362 }
363 }
364 return false;
365 }
366
367
368
369
370
371
372 public double surface()
373 {
374
375 Point2d midPoint = getBounds().midPoint();
376 double result = 0;
377
378 double prevX = getX(size() - 1) - midPoint.x;
379 double prevY = getY(size() - 1) - midPoint.y;
380 for (int i = 0; i < size(); i++)
381 {
382 double thisX = getX(i) - midPoint.x;
383 double thisY = getY(i) - midPoint.y;
384 result += prevX * thisY - thisX * prevY;
385 prevX = thisX;
386 prevY = thisY;
387 }
388 return result / 2;
389 }
390
391 @Override
392 public double getLength()
393 {
394
395 return super.getLength() + Math.hypot(getX(size() - 1) - getX(0), getY(size() - 1) - getY(0));
396 }
397
398 @Override
399 public LineSegment2d getSegment(final int index)
400 {
401 if (index < size() - 1)
402 {
403 return super.getSegment(index);
404 }
405 Throw.when(index != size() - 1, DrawRuntimeException.class, "index must be in range 0..size() - 1");
406 return new LineSegment2d(getX(index), getY(index), getX(0), getY(0));
407 }
408
409 @Override
410 public Polygon2d reverse()
411 {
412 return new Polygon2d(super.reverse().getPoints());
413 }
414
415
416
417
418
419 @Override
420 public Path2D toPath2D()
421 {
422 Path2D.Double result = (Double) super.toPath2D();
423 result.closePath();
424 return result;
425 }
426
427 @Override
428 public final String toString()
429 {
430 return toString("%f", false);
431 }
432
433 @Override
434 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
435 {
436 StringBuilder result = new StringBuilder();
437 if (!doNotIncludeClassName)
438 {
439 result.append("Polygon2d ");
440 }
441 result.append("[super=");
442 result.append(super.toString(doubleFormat, false));
443 result.append("]");
444 return result.toString();
445 }
446
447 }