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