root / trunk / libraries / libTopology / src / org / gvsig / jts / voronoi / chew / Pnt.java @ 21246
History | View | Annotate | Download (17.3 KB)
1 |
package org.gvsig.jts.voronoi.chew; |
---|---|
2 |
/*
|
3 |
* Copyright (c) 2005 by L. Paul Chew.
|
4 |
*
|
5 |
* Permission is hereby granted, without written agreement and without
|
6 |
* license or royalty fees, to use, copy, modify, and distribute this
|
7 |
* software and its documentation for any purpose, subject to the following
|
8 |
* conditions:
|
9 |
*
|
10 |
* The above copyright notice and this permission notice shall be included
|
11 |
* in all copies or substantial portions of the Software.
|
12 |
*
|
13 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
14 |
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16 |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
18 |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
19 |
* DEALINGS IN THE SOFTWARE.
|
20 |
*/
|
21 |
|
22 |
/**
|
23 |
* Points in Euclidean space, implemented as double[].
|
24 |
*
|
25 |
* Includes simple geometric operations.
|
26 |
* Uses matrices; a matrix is represented as an array of Pnts.
|
27 |
* Uses simplices; a simplex is represented as an array of Pnts.
|
28 |
*
|
29 |
* @author Paul Chew
|
30 |
*
|
31 |
* Created July 2005. Derived from an earlier, messier version.
|
32 |
*/
|
33 |
public class Pnt { |
34 |
|
35 |
private double[] coordinates; // The point's coordinates |
36 |
|
37 |
/**
|
38 |
* Constructor.
|
39 |
* @param coords the coordinates
|
40 |
*/
|
41 |
public Pnt (double[] coords) { |
42 |
// Copying is done here to ensure that Pnt's coords cannot be altered.
|
43 |
coordinates = new double[coords.length]; |
44 |
System.arraycopy(coords, 0, coordinates, 0, coords.length); |
45 |
} |
46 |
|
47 |
/**
|
48 |
* Constructor.
|
49 |
* @param coordA
|
50 |
* @param coordB
|
51 |
*/
|
52 |
public Pnt (double coordA, double coordB) { |
53 |
this(new double[] {coordA, coordB}); |
54 |
} |
55 |
|
56 |
/**
|
57 |
* Constructor.
|
58 |
* @param coordA
|
59 |
* @param coordB
|
60 |
* @param coordC
|
61 |
*/
|
62 |
public Pnt (double coordA, double coordB, double coordC) { |
63 |
this(new double[] {coordA, coordB, coordC}); |
64 |
} |
65 |
|
66 |
/**
|
67 |
* Create a String for this Pnt.
|
68 |
* @return a String representation of this Pnt.
|
69 |
*/
|
70 |
public String toString () { |
71 |
if (coordinates.length == 0) return "()"; |
72 |
String result = "Pnt(" + coordinates[0]; |
73 |
for (int i = 1; i < coordinates.length; i++) |
74 |
result = result + "," + coordinates[i];
|
75 |
result = result + ")";
|
76 |
return result;
|
77 |
} |
78 |
|
79 |
/**
|
80 |
* Equality.
|
81 |
* @param other the other Object to compare to
|
82 |
* @return true iff the Pnts have the same coordinates
|
83 |
*/
|
84 |
public boolean equals (Object other) { |
85 |
if (!(other instanceof Pnt)) return false; |
86 |
Pnt p = (Pnt) other; |
87 |
if (this.coordinates.length != p.coordinates.length) return false; |
88 |
for (int i = 0; i < this.coordinates.length; i++) |
89 |
if (this.coordinates[i] != p.coordinates[i]) return false; |
90 |
return true; |
91 |
} |
92 |
|
93 |
/**
|
94 |
* HashCode.
|
95 |
* @return the hashCode for this Pnt
|
96 |
*/
|
97 |
public int hashCode () { |
98 |
int hash = 0; |
99 |
for (int i = 0; i < this.coordinates.length; i++) { |
100 |
long bits = Double.doubleToLongBits(this.coordinates[i]); |
101 |
hash = (31*hash) ^ (int)(bits ^ (bits >> 32)); |
102 |
} |
103 |
return hash;
|
104 |
} |
105 |
|
106 |
/* Pnts as vectors */
|
107 |
|
108 |
/**
|
109 |
* @return the specified coordinate of this Pnt
|
110 |
* @throws ArrayIndexOutOfBoundsException for bad coordinate
|
111 |
*/
|
112 |
public double coord (int i) { |
113 |
return this.coordinates[i]; |
114 |
} |
115 |
|
116 |
/**
|
117 |
* @return this Pnt's dimension.
|
118 |
*/
|
119 |
public int dimension () { |
120 |
return coordinates.length;
|
121 |
} |
122 |
|
123 |
/**
|
124 |
* Check that dimensions match.
|
125 |
* @param p the Pnt to check (against this Pnt)
|
126 |
* @return the dimension of the Pnts
|
127 |
* @throws IllegalArgumentException if dimension fail to match
|
128 |
*/
|
129 |
public int dimCheck (Pnt p) { |
130 |
int len = this.coordinates.length; |
131 |
if (len != p.coordinates.length)
|
132 |
throw new IllegalArgumentException("Dimension mismatch"); |
133 |
return len;
|
134 |
} |
135 |
|
136 |
/**
|
137 |
* Create a new Pnt by adding additional coordinates to this Pnt.
|
138 |
* @param coords the new coordinates (added on the right end)
|
139 |
* @return a new Pnt with the additional coordinates
|
140 |
*/
|
141 |
public Pnt extend (double[] coords) { |
142 |
double[] result = new double[coordinates.length + coords.length]; |
143 |
System.arraycopy(coordinates, 0, result, 0, coordinates.length); |
144 |
System.arraycopy(coords, 0, result, coordinates.length, coords.length); |
145 |
return new Pnt(result); |
146 |
} |
147 |
|
148 |
/**
|
149 |
* Dot product.
|
150 |
* @param p the other Pnt
|
151 |
* @return dot product of this Pnt and p
|
152 |
*/
|
153 |
public double dot (Pnt p) { |
154 |
int len = dimCheck(p);
|
155 |
double sum = 0; |
156 |
for (int i = 0; i < len; i++) |
157 |
sum += this.coordinates[i] * p.coordinates[i];
|
158 |
return sum;
|
159 |
} |
160 |
|
161 |
/**
|
162 |
* Magnitude (as a vector).
|
163 |
* @return the Euclidean length of this vector
|
164 |
*/
|
165 |
public double magnitude () { |
166 |
return Math.sqrt(this.dot(this)); |
167 |
} |
168 |
|
169 |
/**
|
170 |
* Subtract.
|
171 |
* @param p the other Pnt
|
172 |
* @return a new Pnt = this - p
|
173 |
*/
|
174 |
public Pnt subtract (Pnt p) {
|
175 |
int len = dimCheck(p);
|
176 |
double[] coords = new double[len]; |
177 |
for (int i = 0; i < len; i++) |
178 |
coords[i] = this.coordinates[i] - p.coordinates[i];
|
179 |
return new Pnt(coords); |
180 |
} |
181 |
|
182 |
/**
|
183 |
* Add.
|
184 |
* @param p the other Pnt
|
185 |
* @return a new Pnt = this + p
|
186 |
*/
|
187 |
public Pnt add (Pnt p) {
|
188 |
int len = dimCheck(p);
|
189 |
double[] coords = new double[len]; |
190 |
for (int i = 0; i < len; i++) |
191 |
coords[i] = this.coordinates[i] + p.coordinates[i];
|
192 |
return new Pnt(coords); |
193 |
} |
194 |
|
195 |
/**
|
196 |
* Angle (in radians) between two Pnts (treated as vectors).
|
197 |
* @param p the other Pnt
|
198 |
* @return the angle (in radians) between the two Pnts
|
199 |
*/
|
200 |
public double angle (Pnt p) { |
201 |
return Math.acos(this.dot(p) / (this.magnitude() * p.magnitude())); |
202 |
} |
203 |
|
204 |
/**
|
205 |
* Perpendicular bisector of two Pnts.
|
206 |
* Works in any dimension. The coefficients are returned as a Pnt of one
|
207 |
* higher dimension (e.g., (A,B,C,D) for an equation of the form
|
208 |
* Ax + By + Cz + D = 0).
|
209 |
* @param point the other point
|
210 |
* @return the coefficients of the perpendicular bisector
|
211 |
*/
|
212 |
public Pnt bisector (Pnt point) {
|
213 |
int dim = dimCheck(point);
|
214 |
Pnt diff = this.subtract(point);
|
215 |
Pnt sum = this.add(point);
|
216 |
double dot = diff.dot(sum);
|
217 |
return diff.extend(new double[] {-dot / 2}); |
218 |
} |
219 |
|
220 |
/* Pnts as matrices */
|
221 |
|
222 |
/**
|
223 |
* Create a String for a matrix.
|
224 |
* @param matrix the matrix (an array of Pnts)
|
225 |
* @return a String represenation of the matrix
|
226 |
*/
|
227 |
public static String toString (Pnt[] matrix) { |
228 |
StringBuffer buf = new StringBuffer("{"); |
229 |
for (int i = 0; i < matrix.length; i++) buf.append(" " + matrix[i]); |
230 |
buf.append(" }");
|
231 |
return buf.toString();
|
232 |
} |
233 |
|
234 |
/**
|
235 |
* Compute the determinant of a matrix (array of Pnts).
|
236 |
* This is not an efficient implementation, but should be adequate
|
237 |
* for low dimension.
|
238 |
* @param matrix the matrix as an array of Pnts
|
239 |
* @return the determinnant of the input matrix
|
240 |
* @throws IllegalArgumentException if dimensions are wrong
|
241 |
*/
|
242 |
public static double determinant (Pnt[] matrix) { |
243 |
if (matrix.length != matrix[0].dimension()) |
244 |
throw new IllegalArgumentException("Matrix is not square"); |
245 |
boolean[] columns = new boolean[matrix.length]; |
246 |
for (int i = 0; i < matrix.length; i++) columns[i] = true; |
247 |
try {return determinant(matrix, 0, columns);} |
248 |
catch (ArrayIndexOutOfBoundsException e) { |
249 |
throw new IllegalArgumentException("Matrix is wrong shape"); |
250 |
} |
251 |
} |
252 |
|
253 |
/**
|
254 |
* Compute the determinant of a submatrix specified by starting row
|
255 |
* and by "active" columns.
|
256 |
* @param matrix the matrix as an array of Pnts
|
257 |
* @param row the starting row
|
258 |
* @param columns a boolean array indicating the "active" columns
|
259 |
* @return the determinant of the specified submatrix
|
260 |
* @throws ArrayIndexOutOfBoundsException if dimensions are wrong
|
261 |
*/
|
262 |
private static double determinant(Pnt[] matrix, int row, boolean[] columns) { |
263 |
if (row == matrix.length) return 1; |
264 |
double sum = 0; |
265 |
int sign = 1; |
266 |
for (int col = 0; col < columns.length; col++) { |
267 |
if (!columns[col]) continue; |
268 |
columns[col] = false;
|
269 |
sum += sign * matrix[row].coordinates[col] * |
270 |
determinant(matrix, row+1, columns);
|
271 |
columns[col] = true;
|
272 |
sign = -sign; |
273 |
} |
274 |
return sum;
|
275 |
} |
276 |
|
277 |
/**
|
278 |
* Compute generalized cross-product of the rows of a matrix.
|
279 |
* The result is a Pnt perpendicular (as a vector) to each row of
|
280 |
* the matrix. This is not an efficient implementation, but should
|
281 |
* be adequate for low dimension.
|
282 |
* @param matrix the matrix of Pnts (one less row than the Pnt dimension)
|
283 |
* @return a Pnt perpendicular to each row Pnt
|
284 |
* @throws IllegalArgumentException if matrix is wrong shape
|
285 |
*/
|
286 |
public static Pnt cross (Pnt[] matrix) { |
287 |
int len = matrix.length + 1; |
288 |
if (len != matrix[0].dimension()) |
289 |
throw new IllegalArgumentException("Dimension mismatch"); |
290 |
boolean[] columns = new boolean[len]; |
291 |
for (int i = 0; i < len; i++) columns[i] = true; |
292 |
double[] result = new double[len]; |
293 |
int sign = 1; |
294 |
try {
|
295 |
for (int i = 0; i < len; i++) { |
296 |
columns[i] = false;
|
297 |
result[i] = sign * determinant(matrix, 0, columns);
|
298 |
columns[i] = true;
|
299 |
sign = -sign; |
300 |
} |
301 |
} catch (ArrayIndexOutOfBoundsException e) { |
302 |
throw new IllegalArgumentException("Matrix is wrong shape"); |
303 |
} |
304 |
return new Pnt(result); |
305 |
} |
306 |
|
307 |
/* Pnts as simplices */
|
308 |
|
309 |
/**
|
310 |
* Determine the signed content (i.e., area or volume, etc.) of a simplex.
|
311 |
* @param simplex the simplex (as an array of Pnts)
|
312 |
* @return the signed content of the simplex
|
313 |
*/
|
314 |
public static double content (Pnt[] simplex) { |
315 |
Pnt[] matrix = new Pnt[simplex.length]; |
316 |
for (int i = 0; i < matrix.length; i++) |
317 |
matrix[i] = simplex[i].extend(new double[] {1}); |
318 |
int fact = 1; |
319 |
for (int i = 1; i < matrix.length; i++) fact = fact*i; |
320 |
return determinant(matrix) / fact;
|
321 |
} |
322 |
|
323 |
/**
|
324 |
* Relation between this Pnt and a simplex (represented as an array of Pnts).
|
325 |
* Result is an array of signs, one for each vertex of the simplex, indicating
|
326 |
* the relation between the vertex, the vertex's opposite facet, and this
|
327 |
* Pnt. <pre>
|
328 |
* -1 means Pnt is on same side of facet
|
329 |
* 0 means Pnt is on the facet
|
330 |
* +1 means Pnt is on opposite side of facet</pre>
|
331 |
* @param simplex an array of Pnts representing a simplex
|
332 |
* @return an array of signs showing relation between this Pnt and the simplex
|
333 |
* @throws IllegalArgumentExcpetion if the simplex is degenerate
|
334 |
*/
|
335 |
public int[] relation (Pnt[] simplex) { |
336 |
/* In 2D, we compute the cross of this matrix:
|
337 |
* 1 1 1 1
|
338 |
* p0 a0 b0 c0
|
339 |
* p1 a1 b1 c1
|
340 |
* where (a, b, c) is the simplex and p is this Pnt. The result
|
341 |
* is a vector in which the first coordinate is the signed area
|
342 |
* (all signed areas are off by the same constant factor) of
|
343 |
* the simplex and the remaining coordinates are the *negated*
|
344 |
* signed areas for the simplices in which p is substituted for
|
345 |
* each of the vertices. Analogous results occur in higher dimensions.
|
346 |
*/
|
347 |
int dim = simplex.length - 1; |
348 |
if (this.dimension() != dim) |
349 |
throw new IllegalArgumentException("Dimension mismatch"); |
350 |
|
351 |
/* Create and load the matrix */
|
352 |
Pnt[] matrix = new Pnt[dim+1]; |
353 |
/* First row */
|
354 |
double[] coords = new double[dim+2]; |
355 |
for (int j = 0; j < coords.length; j++) coords[j] = 1; |
356 |
matrix[0] = new Pnt(coords); |
357 |
/* Other rows */
|
358 |
for (int i = 0; i < dim; i++) { |
359 |
coords[0] = this.coordinates[i]; |
360 |
for (int j = 0; j < simplex.length; j++) |
361 |
coords[j+1] = simplex[j].coordinates[i];
|
362 |
matrix[i+1] = new Pnt(coords); |
363 |
} |
364 |
|
365 |
/* Compute and analyze the vector of areas/volumes/contents */
|
366 |
Pnt vector = cross(matrix); |
367 |
double content = vector.coordinates[0]; |
368 |
int[] result = new int[dim+1]; |
369 |
for (int i = 0; i < result.length; i++) { |
370 |
double value = vector.coordinates[i+1]; |
371 |
if (Math.abs(value) <= 1.0e-6 * Math.abs(content)) result[i] = 0; |
372 |
else if (value < 0) result[i] = -1; |
373 |
else result[i] = 1; |
374 |
} |
375 |
if (content < 0) { |
376 |
for (int i = 0; i < result.length; i++) result[i] = -result[i]; |
377 |
} |
378 |
if (content == 0) { |
379 |
for (int i = 0; i < result.length; i++) result[i] = Math.abs(result[i]); |
380 |
} |
381 |
return result;
|
382 |
} |
383 |
|
384 |
/**
|
385 |
* Test if this Pnt is outside of simplex.
|
386 |
* @param simplex the simplex (an array of Pnts)
|
387 |
* @return the simplex Pnt that "witnesses" outsideness (or null if not outside)
|
388 |
*/
|
389 |
public Pnt isOutside (Pnt[] simplex) { |
390 |
int[] result = this.relation(simplex); |
391 |
for (int i = 0; i < result.length; i++) { |
392 |
if (result[i] > 0) return simplex[i]; |
393 |
} |
394 |
return null; |
395 |
} |
396 |
|
397 |
/**
|
398 |
* Test if this Pnt is on a simplex.
|
399 |
* @param simplex the simplex (an array of Pnts)
|
400 |
* @return the simplex Pnt that "witnesses" on-ness (or null if not on)
|
401 |
*/
|
402 |
public Pnt isOn (Pnt[] simplex) { |
403 |
int[] result = this.relation(simplex); |
404 |
Pnt witness = null;
|
405 |
for (int i = 0; i < result.length; i++) { |
406 |
if (result[i] == 0) witness = simplex[i]; |
407 |
else if (result[i] > 0) return null; |
408 |
} |
409 |
return witness;
|
410 |
} |
411 |
|
412 |
/**
|
413 |
* Test if this Pnt is inside a simplex.
|
414 |
* @param simplex the simplex (an arary of Pnts)
|
415 |
* @return true iff this Pnt is inside simplex.
|
416 |
*/
|
417 |
public boolean isInside (Pnt[] simplex) { |
418 |
int[] result = this.relation(simplex); |
419 |
for (int i = 0; i < result.length; i++) if (result[i] >= 0) return false; |
420 |
return true; |
421 |
} |
422 |
|
423 |
/**
|
424 |
* Test relation between this Pnt and circumcircle of a simplex.
|
425 |
* @param simplex the simplex (as an array of Pnts)
|
426 |
* @return -1, 0, or +1 for inside, on, or outside of circumcircle
|
427 |
*/
|
428 |
public int vsCircumcircle (Pnt[] simplex) { |
429 |
Pnt[] matrix = new Pnt[simplex.length + 1]; |
430 |
for (int i = 0; i < simplex.length; i++) |
431 |
matrix[i] = simplex[i].extend(new double[] {1, simplex[i].dot(simplex[i])}); |
432 |
matrix[simplex.length] = this.extend(new double[] {1, this.dot(this)}); |
433 |
double d = determinant(matrix);
|
434 |
int result = (d < 0)? -1 : ((d > 0)? +1 : 0); |
435 |
if (content(simplex) < 0) result = - result; |
436 |
return result;
|
437 |
} |
438 |
|
439 |
/**
|
440 |
* Circumcenter of a simplex.
|
441 |
* @param simplex the simplex (as an array of Pnts)
|
442 |
* @return the circumcenter (a Pnt) of simplex
|
443 |
*/
|
444 |
public static Pnt circumcenter (Pnt[] simplex) { |
445 |
int dim = simplex[0].dimension(); |
446 |
if (simplex.length - 1 != dim) |
447 |
throw new IllegalArgumentException("Dimension mismatch"); |
448 |
Pnt[] matrix = new Pnt[dim]; |
449 |
for (int i = 0; i < dim; i++) |
450 |
matrix[i] = simplex[i].bisector(simplex[i+1]);
|
451 |
Pnt hCenter = cross(matrix); // Center in homogeneous coordinates
|
452 |
double last = hCenter.coordinates[dim];
|
453 |
double[] result = new double[dim]; |
454 |
for (int i = 0; i < dim; i++) result[i] = hCenter.coordinates[i] / last; |
455 |
return new Pnt(result); |
456 |
} |
457 |
|
458 |
/**
|
459 |
* Main program (used for testing).
|
460 |
*/
|
461 |
public static void main (String[] args) { |
462 |
Pnt p = new Pnt(1, 2, 3); |
463 |
System.out.println("Pnt created: " + p); |
464 |
Pnt[] matrix1 = {new Pnt(1,2), new Pnt(3,4)}; |
465 |
Pnt[] matrix2 = {new Pnt(7,0,5), new Pnt(2,4,6), new Pnt(3,8,1)}; |
466 |
System.out.print("Results should be -2 and -288: "); |
467 |
System.out.println(determinant(matrix1) + " " + determinant(matrix2)); |
468 |
Pnt p1 = new Pnt(1,1); Pnt p2 = new Pnt(-1,1); |
469 |
System.out.println("Angle between " + p1 + " and " + p2 + ": " + p1.angle(p2)); |
470 |
System.out.println(p1 + " subtract " + p2 + ": " + p1.subtract(p2)); |
471 |
Pnt v0 = new Pnt(0,0), v1 = new Pnt(1,1), v2 = new Pnt(2,2); |
472 |
Pnt[] vs = {v0, new Pnt(0,1), new Pnt(1,0)}; |
473 |
Pnt vp = new Pnt(.1, .1); |
474 |
System.out.println(vp + " isInside " + toString(vs) + ": " + vp.isInside(vs)); |
475 |
System.out.println(v1 + " isInside " + toString(vs) + ": " + v1.isInside(vs)); |
476 |
System.out.println(vp + " vsCircumcircle " + toString(vs) + ": " + |
477 |
vp.vsCircumcircle(vs)); |
478 |
System.out.println(v1 + " vsCircumcircle " + toString(vs) + ": " + |
479 |
v1.vsCircumcircle(vs)); |
480 |
System.out.println(v2 + " vsCircumcircle " + toString(vs) + ": " + |
481 |
v2.vsCircumcircle(vs)); |
482 |
System.out.println("Circumcenter of " + toString(vs) + " is " + circumcenter(vs)); |
483 |
} |
484 |
} |