add RTree classes
[anneal.git] / src / com / infomatiq / jsi / Rectangle.java
1 //   Rectangle.java
2 //   Java Spatial Index Library
3 //   Copyright (C) 2002 Infomatiq Limited
4 //  
5 //  This library is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU Lesser General Public
7 //  License as published by the Free Software Foundation; either
8 //  version 2.1 of the License, or (at your option) any later version.
9 //  
10 //  This library is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 //  Lesser General Public License for more details.
14 //  
15 //  You should have received a copy of the GNU Lesser General Public
16 //  License along with this library; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 package com.infomatiq.jsi;
20
21 import java.util.Arrays;
22
23 /**
24  * Currently hardcoded to 2 dimensions, but could be extended.
25  * 
26  * @author  aled.morris@infomatiq.co.uk
27  * @version 1.0b2
28  */
29 public class Rectangle {
30   /**
31    * Number of dimensions in a rectangle. In theory this
32    * could be exended to three or more dimensions.
33    */
34   public final static int DIMENSIONS = 3;
35   
36   /**
37    * array containing the minimum value for each dimension; ie { min(x), min(y) }
38    */
39   public float[] max;
40   
41   /**
42    * array containing the maximum value for each dimension; ie { max(x), max(y) }
43    */
44   public float[] min;
45
46   /**
47    * Constructor.
48    * 
49    * @param x1 coordinate of any corner of the rectangle
50    * @param y1 (see x1)
51    * @param x2 coordinate of the opposite corner
52    * @param y2 (see x2)
53    */
54   public Rectangle(float x1, float y1, float z1, float x2, float y2, float z2) {
55     min = new float[DIMENSIONS];
56     max = new float[DIMENSIONS]; 
57     set(x1, y1, z1, x2, y2, z2);
58   }
59
60   /**
61    * Constructor.
62    * 
63    * @param min array containing the minimum value for each dimension; ie { min(x), min(y) }
64    * @param max array containing the maximum value for each dimension; ie { max(x), max(y) }
65    */  
66   public Rectangle(float[] min, float[] max) {
67     if (min.length != DIMENSIONS || max.length != DIMENSIONS) {
68       throw new RuntimeException("Error in Rectangle constructor: " +
69                 "min and max arrays must be of length " + DIMENSIONS); 
70     }
71    
72     this.min = new float[DIMENSIONS];
73     this.max = new float[DIMENSIONS];
74     
75     set(min, max);
76   }
77   
78  /**
79    * Sets the size of the rectangle.
80    * 
81    * @param x1 coordinate of any corner of the rectangle
82    * @param y1 (see x1)
83    * @param x2 coordinate of the opposite corner
84    * @param y2 (see x2)
85    */
86   public void set(float x1, float y1, float z1, float x2, float y2, float z2) {
87     min[0] = Math.min(x1, x2);
88     min[1] = Math.min(y1, y2);
89     min[2] = Math.min(z1, z2);
90     max[0] = Math.max(x1, x2);        
91     max[1] = Math.max(y1, y2); 
92     max[2] = Math.max(z1, z2);
93   }
94   
95   /**
96    * Sets the size of the rectangle.
97    * 
98    * @param min array containing the minimum value for each dimension; ie { min(x), min(y) }
99    * @param max array containing the maximum value for each dimension; ie { max(x), max(y) }
100    */
101   public void set(float[] min, float[] max) {
102     System.arraycopy(min, 0, this.min, 0, DIMENSIONS);
103     System.arraycopy(max, 0, this.max, 0, DIMENSIONS);
104   }
105   
106   /**
107    * Make a copy of this rectangle
108    * 
109    * @return copy of this rectangle
110    */
111   public Rectangle copy() {
112     return new Rectangle(min, max); 
113   }
114   
115   /**
116    * Determine whether an edge of this rectangle overlies the equivalent 
117    * edge of the passed rectangle
118    */
119   public boolean edgeOverlaps(Rectangle r) {
120     for (int i = 0; i < DIMENSIONS; i++) {
121       if (min[i] == r.min[i] || max[i] == r.max[i]) {
122         return true; 
123       } 
124     }  
125     return false;
126   }
127   
128   /**
129    * Determine whether this rectangle intersects the passed rectangle
130    * 
131    * @param r The rectangle that might intersect this rectangle
132    * 
133    * @return true if the rectangles intersect, false if they do not intersect
134    */
135   public boolean intersects(Rectangle r) {
136     // Every dimension must intersect. If any dimension
137     // does not intersect, return false immediately.
138     for (int i = 0; i < DIMENSIONS; i++) {
139       if (max[i] < r.min[i] || min[i] > r.max[i]) {
140         return false;
141       }
142     }
143     return true;
144   }
145  
146   /**
147    * Determine whether this rectangle contains the passed rectangle
148    * 
149    * @param r The rectangle that might be contained by this rectangle
150    * 
151    * @return true if this rectangle contains the passed rectangle, false if
152    *         it does not
153    */
154   public boolean contains(Rectangle r) {
155     for (int i = 0; i < DIMENSIONS; i++) {
156       if (max[i] < r.max[i] || min[i] > r.min[i]) {
157         return false;
158       }
159     }
160     return true;     
161   }
162  
163   /**
164    * Determine whether this rectangle is contained by the passed rectangle
165    * 
166    * @param r The rectangle that might contain this rectangle
167    * 
168    * @return true if the passed rectangle contains this rectangle, false if
169    *         it does not
170    */
171   public boolean containedBy(Rectangle r) {
172     for (int i = 0; i < DIMENSIONS; i++) {
173       if (max[i] > r.max[i] || min[i] < r.min[i]) {
174         return false;
175       }
176     }
177     return true;  
178   }
179   
180   /**
181    * Return the distance between this rectangle and the passed point.
182    * If the rectangle contains the point, the distance is zero.
183    * 
184    * @param p Point to find the distance to
185    * 
186    * @return distance beween this rectangle and the passed point.
187    */
188   public float distance(Point p) {
189     float distanceSquared = 0;
190     for (int i = 0; i < DIMENSIONS; i++) {
191       float greatestMin = Math.max(min[i], p.coordinates[i]);
192       float leastMax    = Math.min(max[i], p.coordinates[i]);
193       if (greatestMin > leastMax) {
194         distanceSquared += ((greatestMin - leastMax) * (greatestMin - leastMax)); 
195       }
196     }
197     return (float) Math.sqrt(distanceSquared);
198   }
199   
200   /**
201    * Return the distance between this rectangle and the passed rectangle.
202    * If the rectangles overlap, the distance is zero.
203    * 
204    * @param r Rectangle to find the distance to
205    * 
206    * @return distance between this rectangle and the passed rectangle
207    */
208
209   public float distance(Rectangle r) {
210     float distanceSquared = 0;
211     for (int i = 0; i < DIMENSIONS; i++) {
212       float greatestMin = Math.max(min[i], r.min[i]);
213       float leastMax    = Math.min(max[i], r.max[i]);
214       if (greatestMin > leastMax) {
215         distanceSquared += ((greatestMin - leastMax) * (greatestMin - leastMax)); 
216       }
217     }
218     return (float) Math.sqrt(distanceSquared);
219   }
220    
221   /**
222    * Return the squared distance from this rectangle to the passed point
223    */
224   private float distanceSquared(int dimension, float point) {
225     float distanceSquared = 0;
226     float tempDistance = point - max[dimension];
227     for (int i = 0; i < 2; i++) {
228       if (tempDistance > 0) {
229         distanceSquared = (tempDistance * tempDistance);
230         break;
231       } 
232       tempDistance = min[dimension] - point;
233     }
234     return distanceSquared;
235   }
236   
237   /**
238    * Return the furthst possible distance between this rectangle and
239    * the passed rectangle. 
240    * 
241    * Find the distance between this rectangle and each corner of the
242    * passed rectangle, and use the maximum.
243    *
244    */
245   public float furthestDistance(Rectangle r) {
246      float distanceSquared = 0;
247      
248      for (int i = 0; i < DIMENSIONS; i++) {
249        distanceSquared += Math.max(distanceSquared(i, r.min[i]), distanceSquared(i, r.max[i]));
250      }
251      
252      return (float) Math.sqrt(distanceSquared);
253   }
254   
255   /**
256    * Calculate the area by which this rectangle would be enlarged if
257    * added to the passed rectangle. Neither rectangle is altered.
258    * 
259    * @param r Rectangle to union with this rectangle, in order to 
260    *          compute the difference in area of the union and the
261    *          original rectangle
262    */
263   public float enlargement(Rectangle r) {
264     float enlargedArea = (Math.max(max[0], r.max[0]) - Math.min(min[0], r.min[0])) *
265                          (Math.max(max[1], r.max[1]) - Math.min(min[1], r.min[1]));
266                          
267     return enlargedArea - area();
268   }
269   
270   /**
271    * Compute the area of this rectangle.
272    * 
273    * @return The area of this rectangle
274    */
275   public float area() {
276     return (max[0] - min[0]) * (max[1] - min[1]);
277   }
278   
279   /**
280    * Computes the union of this rectangle and the passed rectangle, storing
281    * the result in this rectangle.
282    * 
283    * @param r Rectangle to add to this rectangle
284    */
285   public void add(Rectangle r) {
286     for (int i = 0; i < DIMENSIONS; i++) {
287       if (r.min[i] < min[i]) {
288         min[i] = r.min[i];
289       }
290       if (r.max[i] > max[i]) {
291         max[i] = r.max[i];
292       }
293     }
294   }
295   
296   /**
297    * Find the the union of this rectangle and the passed rectangle.
298    * Neither rectangle is altered
299    * 
300    * @param r The rectangle to union with this rectangle
301    */
302   public Rectangle union(Rectangle r) {
303     Rectangle union = this.copy();
304     union.add(r);
305     return union; 
306   }
307   
308   /**
309    * Determine whether this rectangle is equal to a given object.
310    * Equality is determined by the bounds of the rectangle.
311    * 
312    * @param The object to compare with this rectangle
313    */
314   public boolean equals(Object o) {
315     boolean equals = false;
316     if (o instanceof Rectangle) {
317       Rectangle r = (Rectangle) o;
318       if (Arrays.equals(r.min, min) && Arrays.equals(r.max, max)) {
319         equals = true;
320       }
321     } 
322     return equals;       
323   }
324
325   /** 
326    * Determine whether this rectangle is the same as another object
327    * 
328    * Note that two rectangles can be equal but not the same object, 
329    * if they both have the same bounds.
330    * 
331    * @param o The object to compare with this rectangle.
332    */  
333   public boolean sameObject(Object o) {
334     return super.equals(o); 
335   }
336   
337   /**
338    * Return a string representation of this rectangle, in the form: 
339    * (1.2, 3.4), (5.6, 7.8)
340    * 
341    * @return String String representation of this rectangle.
342    */
343   public String toString() {
344     StringBuffer sb = new StringBuffer();
345     
346     // min coordinates
347     sb.append('(');
348     for (int i = 0; i < DIMENSIONS; i++) {
349       if (i > 0) {
350         sb.append(", ");
351       }
352       sb.append(min[i]);
353     } 
354     sb.append("), (");
355     
356     // max coordinates
357     for (int i = 0; i < DIMENSIONS; i++) {
358       if (i > 0) {
359         sb.append(", ");
360       }
361       sb.append(max[i]);
362     } 
363     sb.append(')');
364     return sb.toString();
365   }
366 }