1 package org.djutils.draw.bounds;
2
3 import java.io.Serializable;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.Iterator;
7
8 import org.djutils.draw.Drawable3d;
9 import org.djutils.draw.Space3d;
10 import org.djutils.draw.point.Point3d;
11 import org.djutils.exceptions.Throw;
12
13
14
15
16
17
18
19
20
21
22 public class Bounds3d implements Serializable, Drawable3d, Bounds<Bounds3d, Space3d>
23 {
24
25 private static final long serialVersionUID = 2020829L;
26
27
28 private final double minX;
29
30
31 private final double minY;
32
33
34 private final double minZ;
35
36
37 private final double maxX;
38
39
40 private final double maxY;
41
42
43 private final double maxZ;
44
45
46
47
48
49
50
51
52
53
54
55 public Bounds3d(final double minX, final double maxX, final double minY, final double maxY, final double minZ,
56 final double maxZ)
57 {
58 Throw.when(Double.isNaN(minX) || Double.isNaN(maxX) || Double.isNaN(minY) || Double.isNaN(maxY) || Double.isNaN(minZ)
59 || Double.isNaN(maxZ), IllegalArgumentException.class, "Nan boundary value not permitted");
60 Throw.when(minX > maxX || minY > maxY || minZ > maxZ, IllegalArgumentException.class,
61 "lower bound for each dimension should be less than or equal to its upper bound");
62 this.minX = minX;
63 this.minY = minY;
64 this.minZ = minZ;
65 this.maxX = maxX;
66 this.maxY = maxY;
67 this.maxZ = maxZ;
68 }
69
70
71
72
73
74
75
76
77 public Bounds3d(final double deltaX, final double deltaY, final double deltaZ)
78 {
79 this(-0.5 * deltaX, 0.5 * deltaX, -0.5 * deltaY, 0.5 * deltaY, -0.5 * deltaZ, 0.5 * deltaZ);
80 }
81
82
83
84
85
86
87
88 public Bounds3d(final Iterator<? extends Point3d> points)
89 {
90 Throw.whenNull(points, "points may not be null");
91 Throw.when(!points.hasNext(), IllegalArgumentException.class, "need at least one point");
92 Point3d point = points.next();
93 double tempMinX = point.x;
94 double tempMaxX = point.x;
95 double tempMinY = point.y;
96 double tempMaxY = point.y;
97 double tempMinZ = point.z;
98 double tempMaxZ = point.z;
99 while (points.hasNext())
100 {
101 point = points.next();
102 tempMinX = Math.min(tempMinX, point.x);
103 tempMaxX = Math.max(tempMaxX, point.x);
104 tempMinY = Math.min(tempMinY, point.y);
105 tempMaxY = Math.max(tempMaxY, point.y);
106 tempMinZ = Math.min(tempMinZ, point.z);
107 tempMaxZ = Math.max(tempMaxZ, point.z);
108 }
109 this.minX = tempMinX;
110 this.maxX = tempMaxX;
111 this.minY = tempMinY;
112 this.maxY = tempMaxY;
113 this.minZ = tempMinZ;
114 this.maxZ = tempMaxZ;
115 }
116
117
118
119
120
121
122
123 public Bounds3d(final Point3d[] points) throws NullPointerException, IllegalArgumentException
124 {
125 this(Arrays.stream(Throw.whenNull(points, "points may not be null")).iterator());
126 }
127
128
129
130
131
132
133 public Bounds3d(final Drawable3d drawable3d) throws NullPointerException
134 {
135 this(Throw.whenNull(drawable3d, "drawable3d may not be null").getPoints());
136 }
137
138
139
140
141
142
143
144 public Bounds3d(final Collection<Point3d> points)
145 {
146 this(Throw.whenNull(points, "points may not be null").iterator());
147 }
148
149
150 @Override
151 public Iterator<Point3d> getPoints()
152 {
153 Point3d[] array =
154 new PoPoint3da>Point3d.html#Point3d">Point3d[] {new PoPoint3da>(getMinX(), getMinY(), getMinZ()), new Point3d(getMinX(), getMinY(), getMaxZ()),
155 new PoPoint3da>(getMinX(), getMaxY(), getMinZ()), new Point3d(getMinX(), getMaxY(), getMaxZ()),
156 new PoPoint3da>(getMaxX(), getMinY(), getMinZ()), new Point3d(getMaxX(), getMinY(), getMaxZ()),
157 new PoPoint3da>(getMaxX(), getMaxY(), getMinZ()), new Point3d(getMaxX(), getMaxY(), getMaxZ())};
158 return Arrays.stream(array).iterator();
159 }
160
161
162 @Override
163 public int size()
164 {
165 return 8;
166 }
167
168
169
170
171
172
173
174 public boolean contains(final Point3d point)
175 {
176 Throw.whenNull(point, "point cannot be null");
177 return contains(point.x, point.y, point.z);
178 }
179
180
181
182
183
184
185
186
187
188 public boolean contains(final double x, final double y, final double z) throws IllegalArgumentException
189 {
190 Throw.when(Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z), IllegalArgumentException.class,
191 "coordinates must be numbers (not NaN)");
192 return x > this.minX && x < this.maxX && y > this.minY && y < this.maxY && z > this.minZ && z < this.maxZ;
193 }
194
195
196
197
198
199
200
201
202 public boolean contains(final Drawable3d drawable)
203 {
204 Throw.whenNull(drawable, "drawable cannot be null");
205 for (Iterator<? extends Point3d> iterator = drawable.getPoints(); iterator.hasNext();)
206 {
207 if (!contains(iterator.next()))
208 {
209 return false;
210 }
211 }
212 return true;
213 }
214
215
216 @Override
217 public Bounds2d project()
218 {
219 return new Bounds2d(getMinX(), getMaxX(), getMinY(), getMaxY());
220 }
221
222
223
224
225
226
227
228
229
230 public boolean covers(final double x, final double y, final double z) throws IllegalArgumentException
231 {
232 Throw.when(Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z), IllegalArgumentException.class,
233 "coordinates must be numbers (not NaN)");
234 return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY && z >= this.minZ && z <= this.maxZ;
235 }
236
237
238
239
240
241
242
243 public boolean covers(final Point3d point)
244 {
245 Throw.whenNull(point, "point cannot be null");
246 return covers(point.x, point.y, point.z);
247 }
248
249
250 @Override
251 public boolean covers(final Bounds3d otherBounds3d)
252 {
253 Throw.whenNull(otherBounds3d, "otherBounds3d cannot be null");
254 return covers(otherBounds3d.getMinX(), otherBounds3d.getMinY(), otherBounds3d.getMinZ())
255 && covers(otherBounds3d.getMaxX(), otherBounds3d.getMaxY(), otherBounds3d.getMaxZ());
256 }
257
258
259 @Override
260 public boolean disjoint(final Bounds3d otherBounds3d)
261 {
262 Throw.whenNull(otherBounds3d, "otherBounds3d cannot be null");
263 return otherBounds3d.minX >= this.maxX || otherBounds3d.maxX <= this.minX || otherBounds3d.minY >= this.maxY
264 || otherBounds3d.maxY <= this.minY || otherBounds3d.minZ >= this.maxZ || otherBounds3d.maxZ <= this.minZ;
265 }
266
267
268 @Override
269 public boolean intersects(final Bounds3d otherBounds3d)
270 {
271 return !disjoint(otherBounds3d);
272 }
273
274
275 @Override
276 public Bounds3dl#Bounds3d">Bounds3d intersection(final Bounds3d otherBounds3d)
277 {
278 Throw.whenNull(otherBounds3d, "otherBounds3d cannot be null");
279 if (disjoint(otherBounds3d))
280 {
281 return null;
282 }
283 return new Bounds3d(Math.max(this.minX, otherBounds3d.minX), Math.min(this.maxX, otherBounds3d.maxX),
284 Math.max(this.minY, otherBounds3d.minY), Math.min(this.maxY, otherBounds3d.maxY),
285 Math.max(this.minZ, otherBounds3d.minZ), Math.min(this.maxZ, otherBounds3d.maxZ));
286 }
287
288
289
290
291
292 public double getDeltaZ()
293 {
294 return getMaxZ() - getMinZ();
295 }
296
297
298
299
300
301 public double getVolume()
302 {
303 return getDeltaX() * getDeltaY() * getDeltaZ();
304 }
305
306
307 @Override
308 public double getMinX()
309 {
310 return this.minX;
311 }
312
313
314 @Override
315 public double getMaxX()
316 {
317 return this.maxX;
318 }
319
320
321 @Override
322 public double getMinY()
323 {
324 return this.minY;
325 }
326
327
328 @Override
329 public double getMaxY()
330 {
331 return this.maxY;
332 }
333
334
335
336
337
338 public double getMinZ()
339 {
340 return this.minZ;
341 }
342
343
344
345
346
347 public double getMaxZ()
348 {
349 return this.maxZ;
350 }
351
352
353
354
355
356 public Point3d midPoint()
357 {
358 return new Point3d((this.minX + this.maxX) / 2, (this.minY + this.maxY) / 2, (this.minZ + this.maxZ) / 2);
359 }
360
361
362 @Override
363 public Bounds3d getBounds()
364 {
365 return this;
366 }
367
368
369 @Override
370 public String toString()
371 {
372 return "Bounds3d [x[" + this.minX + " : " + this.maxX + "], y[" + this.minY + " : " + this.maxY + "], z[" + this.minZ
373 + " : " + this.maxZ + "]]";
374 }
375
376
377 @Override
378 public int hashCode()
379 {
380 final int prime = 31;
381 int result = 1;
382 long temp;
383 temp = Double.doubleToLongBits(this.maxX);
384 result = prime * result + (int) (temp ^ (temp >>> 32));
385 temp = Double.doubleToLongBits(this.maxY);
386 result = prime * result + (int) (temp ^ (temp >>> 32));
387 temp = Double.doubleToLongBits(this.maxZ);
388 result = prime * result + (int) (temp ^ (temp >>> 32));
389 temp = Double.doubleToLongBits(this.minX);
390 result = prime * result + (int) (temp ^ (temp >>> 32));
391 temp = Double.doubleToLongBits(this.minY);
392 result = prime * result + (int) (temp ^ (temp >>> 32));
393 temp = Double.doubleToLongBits(this.minZ);
394 result = prime * result + (int) (temp ^ (temp >>> 32));
395 return result;
396 }
397
398
399 @SuppressWarnings("checkstyle:needbraces")
400 @Override
401 public boolean equals(final Object obj)
402 {
403 if (this == obj)
404 return true;
405 if (obj == null)
406 return false;
407 if (getClass() != obj.getClass())
408 return false;
409 Bounds3d./../../../org/djutils/draw/bounds/Bounds3d.html#Bounds3d">Bounds3d other = (Bounds3d) obj;
410 if (Double.doubleToLongBits(this.maxX) != Double.doubleToLongBits(other.maxX))
411 return false;
412 if (Double.doubleToLongBits(this.maxY) != Double.doubleToLongBits(other.maxY))
413 return false;
414 if (Double.doubleToLongBits(this.maxZ) != Double.doubleToLongBits(other.maxZ))
415 return false;
416 if (Double.doubleToLongBits(this.minX) != Double.doubleToLongBits(other.minX))
417 return false;
418 if (Double.doubleToLongBits(this.minY) != Double.doubleToLongBits(other.minY))
419 return false;
420 if (Double.doubleToLongBits(this.minZ) != Double.doubleToLongBits(other.minZ))
421 return false;
422 return true;
423 }
424
425 }