Statistics
| Revision:

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
}