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.DrawRuntimeException;
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"), fixClosingPoint(point1, otherPoints));
124 }
125
126
127
128
129
130
131
132 private static Point3d[] fixClosingPoint(final Point3d point1, final Point3d[] otherPoints)
133 {
134 if (otherPoints == null || otherPoints.length == 0)
135 {
136 return otherPoints;
137 }
138 Point3d[] result = otherPoints;
139 Point3d lastPoint = result[result.length - 1];
140 if (point1.x == lastPoint.x && point1.y == lastPoint.y)
141 {
142 result = Arrays.copyOf(otherPoints, result.length - 1);
143 lastPoint = result[result.length - 1];
144 }
145 Throw.when(point1.x == lastPoint.x && point1.y == lastPoint.y, IllegalArgumentException.class,
146 "Before last point and last point are at same location");
147 return result;
148 }
149
150
151
152
153
154
155
156 public Polygon3d(final List<Point3d> points)
157 {
158 this(NO_FILTER, points);
159 }
160
161
162
163
164
165
166
167
168 public Polygon3d(final double epsilon, final List<Point3d> points)
169 {
170 super(epsilon, fixClosingPoint(true, Throw.whenNull(points, "points")));
171 }
172
173
174
175
176
177
178
179
180
181
182
183 private static List<Point3d> fixClosingPoint(final boolean doNotModifyList, final List<Point3d> points)
184 {
185 Throw.when(points.size() < 2, IllegalArgumentException.class, "Need at least two points");
186 Point3d firstPoint = points.get(0);
187 Point3d lastPoint = points.get(points.size() - 1);
188 List<Point3d> result = points;
189 if (firstPoint.x == lastPoint.x && firstPoint.y == lastPoint.y && firstPoint.z == lastPoint.z)
190 {
191 if (doNotModifyList)
192 {
193 result = new ArrayList<>(points.size() - 1);
194 for (int i = 0; i < points.size() - 1; i++)
195 {
196 result.add(points.get(i));
197 }
198 }
199 else
200 {
201 result.remove(points.size() - 1);
202 }
203 lastPoint = result.get(result.size() - 1);
204 }
205 Throw.when(firstPoint.x == lastPoint.x && firstPoint.y == lastPoint.y && firstPoint.z == lastPoint.z,
206 IllegalArgumentException.class, "Before last point and last point are at same location");
207 return result;
208 }
209
210
211
212
213
214 public Polygon3d(final Iterator<Point3d> iterator)
215 {
216 this(NO_FILTER, iterator);
217 }
218
219
220
221
222
223
224 public Polygon3d(final double epsilon, final Iterator<Point3d> iterator)
225 {
226 this(epsilon, fixClosingPoint(false, iteratorToList(Throw.whenNull(iterator, "iterator"))));
227 }
228
229
230
231
232
233
234 public Polygon3d(final Polygon3d polygon)
235 {
236 super(polygon);
237 }
238
239 @Override
240 public double getLength()
241 {
242
243 return super.getLength()
244 + Math.hypot(Math.hypot(getX(size() - 1) - getX(0), getY(size() - 1) - getY(0)), getZ(size() - 1) - getZ(0));
245 }
246
247 @Override
248 public LineSegment3d getSegment(final int index)
249 {
250 if (index < size() - 1)
251 {
252 return super.getSegment(index);
253 }
254 Throw.when(index != size() - 1, IndexOutOfBoundsException.class, "index must be in range [0, .size() - 1] (got %d)",
255 index);
256 return new LineSegment3d(getX(index), getY(index), getZ(index), getX(0), getY(0), getZ(0));
257 }
258
259 @Override
260 public Polygon2d project()
261 {
262 double[] projectedX = new double[this.size()];
263 double[] projectedY = new double[this.size()];
264
265 int nextIndex = 0;
266 for (int i = 0; i < this.size(); i++)
267 {
268 if (i > 0 && getX(i) == getX(i - 1) && getY(i) == getY(i - 1))
269 {
270 continue;
271 }
272 projectedX[nextIndex] = getX(i);
273 projectedY[nextIndex] = getY(i);
274 nextIndex++;
275 }
276 Throw.when(nextIndex < 2, DrawRuntimeException.class,
277 "projection yielded too few points to construct a valid Polygon2d");
278 if (nextIndex < projectedX.length)
279 {
280 return new Polygon2d(Arrays.copyOf(projectedX, nextIndex), Arrays.copyOf(projectedY, nextIndex));
281 }
282 return new Polygon2d(projectedX, projectedY);
283 }
284
285 @Override
286 public Polygon3d reverse()
287 {
288 return new Polygon3d(super.reverse().iterator());
289 }
290
291 @Override
292 public final String toString()
293 {
294 return toString("%f", false);
295 }
296
297 @Override
298 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
299 {
300 StringBuilder result = new StringBuilder();
301 if (!doNotIncludeClassName)
302 {
303 result.append("Polygon3d ");
304 }
305 result.append("[super=");
306 result.append(super.toString(doubleFormat, false));
307 result.append("]");
308 return result.toString();
309 }
310
311 }