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