Statistics
| Revision:

root / trunk / extensions / extGraph_predes / src / com / iver / cit / gvsig / topology / triangulation / Pnt.java @ 8150

History | View | Annotate | Download (17.4 KB)

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