1 package org.djutils.draw.line;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Iterator;
6 import java.util.List;
7
8 import org.djutils.draw.InvalidProjectionException;
9 import org.djutils.draw.point.Point3d;
10 import org.djutils.exceptions.Throw;
11
12
13
14
15
16
17
18
19
20
21
22
23 public class Polygon3d extends PolyLine3d
24 {
25
26 private static final long serialVersionUID = 20209999L;
27
28
29
30
31
32
33
34
35 public Polygon3d(final double[] x, final double[] y, final double[] z)
36 {
37 this(NO_FILTER, x, y, z);
38 }
39
40
41
42
43
44
45
46
47
48 public Polygon3d(final double epsilon, final double[] x, final double[] y, final double[] z)
49 {
50 super(epsilon, fixClosingPoint(Throw.whenNull(x, "x"), Throw.whenNull(y, "y"), Throw.whenNull(z, "z")),
51 fixClosingPoint(y, x, z), fixClosingPoint(z, x, y));
52 }
53
54
55
56
57
58
59
60
61 static double[] fixClosingPoint(final double[] a, final double[] b, final double[] c)
62 {
63 if (a.length > 1 && b.length == a.length && c.length == a.length && a[0] == a[a.length - 1] && b[0] == b[a.length - 1]
64 && c[0] == c[c.length - 1])
65 {
66 return Arrays.copyOf(a, a.length - 1);
67 }
68 return a;
69 }
70
71
72
73
74
75
76
77 public Polygon3d(final Point3d[] points)
78 {
79 this(NO_FILTER, points);
80 }
81
82
83
84
85
86
87
88
89 public Polygon3d(final double epsilon, final Point3d[] points)
90 {
91 this(epsilon, PolyLine3d.makeArray(Throw.whenNull(points, "points"), p -> p.x), PolyLine3d.makeArray(points, p -> p.y),
92 PolyLine3d.makeArray(points, p -> p.z));
93 }
94
95
96
97
98
99
100
101
102
103
104
105 public Polygon3d(final Point3d point1, final Point3d point2, final Point3d... otherPoints)
106 {
107 this(NO_FILTER, point1, point2, otherPoints);
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121 public Polygon3d(final double epsilon, final Point3d point1, final Point3d point2, final Point3d... otherPoints)
122 {
123 super(epsilon, Throw.whenNull(point1, "point1"), Throw.whenNull(point2, "point2"),
124 fixClosingPoint(point1, otherPoints));
125 }
126
127
128
129
130
131
132
133 private static Point3d[] fixClosingPoint(final Point3d point1, final Point3d[] otherPoints)
134 {
135 if (otherPoints == null || otherPoints.length == 0)
136 {
137 return otherPoints;
138 }
139 Point3d[] result = otherPoints;
140 Point3d lastPoint = result[result.length - 1];
141 if (point1.x == lastPoint.x && point1.y == lastPoint.y)
142 {
143 result = Arrays.copyOf(otherPoints, result.length - 1);
144 lastPoint = result[result.length - 1];
145 }
146 Throw.when(point1.x == lastPoint.x && point1.y == lastPoint.y, IllegalArgumentException.class,
147 "Before last point and last point are at same location");
148 return result;
149 }
150
151
152
153
154
155
156
157 public Polygon3d(final List<Point3d> points)
158 {
159 this(NO_FILTER, points);
160 }
161
162
163
164
165
166
167
168
169 public Polygon3d(final double epsilon, final List<Point3d> points)
170 {
171 super(epsilon, fixClosingPoint(true, Throw.whenNull(points, "points")));
172 }
173
174
175
176
177
178
179
180
181
182
183
184 private static List<Point3d> fixClosingPoint(final boolean doNotModifyList, final List<Point3d> points)
185 {
186 Throw.when(points.size() < 2, IllegalArgumentException.class, "Need at least two points");
187 Point3d firstPoint = points.get(0);
188 Point3d lastPoint = points.get(points.size() - 1);
189 List<Point3d> result = points;
190 if (firstPoint.x == lastPoint.x && firstPoint.y == lastPoint.y && firstPoint.z == lastPoint.z)
191 {
192 if (doNotModifyList)
193 {
194 result = new ArrayList<>(points.size() - 1);
195 for (int i = 0; i < points.size() - 1; i++)
196 {
197 result.add(points.get(i));
198 }
199 }
200 else
201 {
202 result.remove(points.size() - 1);
203 }
204 lastPoint = result.get(result.size() - 1);
205 }
206 Throw.when(firstPoint.x == lastPoint.x && firstPoint.y == lastPoint.y && firstPoint.z == lastPoint.z,
207 IllegalArgumentException.class, "Before last point and last point are at same location");
208 return result;
209 }
210
211
212
213
214
215 public Polygon3d(final Iterator<Point3d> iterator)
216 {
217 this(NO_FILTER, iterator);
218 }
219
220
221
222
223
224
225 public Polygon3d(final double epsilon, final Iterator<Point3d> iterator)
226 {
227 this(epsilon, fixClosingPoint(false, iteratorToList(Throw.whenNull(iterator, "iterator"))));
228 }
229
230
231
232
233
234
235 public Polygon3d(final Polygon3d polygon)
236 {
237 super(polygon);
238 }
239
240 @Override
241 public double getLength()
242 {
243
244 return super.getLength()
245 + Math.hypot(Math.hypot(getX(size() - 1) - getX(0), getY(size() - 1) - getY(0)), getZ(size() - 1) - getZ(0));
246 }
247
248 @Override
249 public LineSegment3d getSegment(final int index)
250 {
251 if (index < size() - 1)
252 {
253 return super.getSegment(index);
254 }
255 Throw.when(index != size() - 1, IndexOutOfBoundsException.class, "index must be in range [0, .size() - 1] (got %d)",
256 index);
257 return new LineSegment3d(getX(index), getY(index), getZ(index), getX(0), getY(0), getZ(0));
258 }
259
260 @Override
261 public Polygon2d project() throws InvalidProjectionException
262 {
263 double[] projectedX = new double[this.size()];
264 double[] projectedY = new double[this.size()];
265
266 int nextIndex = 0;
267 for (int i = 0; i < this.size(); i++)
268 {
269 if (i > 0 && getX(i) == getX(i - 1) && getY(i) == getY(i - 1))
270 {
271 continue;
272 }
273 projectedX[nextIndex] = getX(i);
274 projectedY[nextIndex] = getY(i);
275 nextIndex++;
276 }
277 Throw.when(nextIndex < 2, InvalidProjectionException.class,
278 "projection yielded too few points to construct a valid Polygon2d");
279 if (nextIndex < projectedX.length)
280 {
281 return new Polygon2d(Arrays.copyOf(projectedX, nextIndex), Arrays.copyOf(projectedY, nextIndex));
282 }
283 return new Polygon2d(projectedX, projectedY);
284 }
285
286 @Override
287 public Polygon3d reverse()
288 {
289 return new Polygon3d(super.reverse().iterator());
290 }
291
292 @Override
293 public final String toString()
294 {
295 return toString("%f", false);
296 }
297
298 @Override
299 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
300 {
301 StringBuilder result = new StringBuilder();
302 if (!doNotIncludeClassName)
303 {
304 result.append("Polygon3d ");
305 }
306 result.append("[super=");
307 result.append(super.toString(doubleFormat, false));
308 result.append("]");
309 return result.toString();
310 }
311
312 }