Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.geometry / org.gvsig.fmap.geometry.jts / src / main / java / org / gvsig / fmap / geom / jts / transform / Transform2D.java @ 42775

History | View | Annotate | Download (11.6 KB)

1
package org.gvsig.fmap.geom.jts.transform;
2

    
3
import static java.lang.Math.abs;
4
import org.gvsig.fmap.geom.Geometry;
5
import org.gvsig.fmap.geom.GeometryLocator;
6
import org.gvsig.fmap.geom.exception.CreateGeometryException;
7
import org.gvsig.fmap.geom.primitive.Point;
8
import org.gvsig.fmap.geom.transform.Transform;
9

    
10
public class Transform2D implements Transform {
11

    
12
    /**
13
     * The constant used for testing results.
14
     */
15
    public final static double ACCURACY = 1e-12;
16

    
17
    // coefficients for x coordinate.
18
    protected double m00, m01, m02;
19

    
20
    // coefficients for y coordinate.
21
    protected double m10, m11, m12;
22

    
23
    /**
24
     * Creates a new AffineTransform2D, initialized with Identity.
25
     */
26
    public Transform2D() {
27
        // init to identity matrix
28
        m00 = m11 = 1;
29
        m01 = m10 = 0;
30
        m02 = m12 = 0;
31
    }
32

    
33
    /**
34
     * Creates a new transform from a java AWT transform.
35
     * @param transform
36
     */
37
    public Transform2D(java.awt.geom.AffineTransform transform) {
38
        double[] coefs = new double[6];
39
        transform.getMatrix(coefs);
40
        m00 = coefs[0];
41
        m10 = coefs[1];
42
        m01 = coefs[2];
43
        m11 = coefs[3];
44
        m02 = coefs[4];
45
        m12 = coefs[5];
46
    }
47

    
48
    /**
49
     * Creates a new Affine Transform by directly specifying the coefficients,
50
     * in the order m00, m01, m02, m10, m11, m12 (different order of
51
     * java.awt.geom.AffineTransform).
52
     * @param coefs
53
     */
54
    public Transform2D(double[] coefs) {
55
        if (coefs.length == 4) {
56
            m00 = coefs[0];
57
            m01 = coefs[1];
58
            m10 = coefs[2];
59
            m11 = coefs[3];
60
        } else {
61
            m00 = coefs[0];
62
            m01 = coefs[1];
63
            m02 = coefs[2];
64
            m10 = coefs[3];
65
            m11 = coefs[4];
66
            m12 = coefs[5];
67
        }
68
    }
69

    
70
    public Transform2D(double xx, double yx, double tx, double xy,
71
            double yy, double ty) {
72
        m00 = xx;
73
        m01 = yx;
74
        m02 = tx;
75
        m10 = xy;
76
        m11 = yy;
77
        m12 = ty;
78
    }
79
    
80
    /**
81
     * Returns coefficients of the transform in a linear array of 6 double.
82
     * @return 
83
     */
84
    public double[] coefficients() {
85
        double[] tab = {m00, m01, m02, m10, m11, m12};
86
        return tab;
87
    }
88

    
89
    /**
90
     * Returns the 3x3 square matrix representing the transform.
91
     *
92
     * @return the 3x3 affine transform representing the matrix
93
     */
94
    public double[][] affineMatrix() {
95
        double[][] tab = new double[][]{
96
            new double[]{m00, m01, m02},
97
            new double[]{m10, m11, m12},
98
            new double[]{0, 0, 1}};
99
        return tab;
100
    }
101

    
102
    /**
103
     * Returns this transform as an instance of java AWT AffineTransform.
104
     * @return 
105
     */
106
    public java.awt.geom.AffineTransform asAwtTransform() {
107
        return new java.awt.geom.AffineTransform(
108
                this.m00, this.m10, this.m01, this.m11, this.m02, this.m12);
109
    }
110

    
111
    /**
112
     * Returns the affine transform created by applying first the affine
113
     * transform given by <code>other</code>, then this affine transform. This
114
     * is the equivalent method of the 'concatenate' method in
115
     * java.awt.geom.AffineTransform.
116
     *
117
     * @param other the transform to apply first
118
     * @return the composition this * that
119
     */
120
    @Override
121
    public Transform concatenate(Transform other) {
122
        if (!(other instanceof Transform2D)) {
123
            throw new IllegalArgumentException("Can't concatenate an AffineTransform2D with a non AffineTransform2D (other " + other.toString() + ")");
124
        }
125
        Transform2D that = (Transform2D) other;
126
        double n00 = this.m00 * that.m00 + this.m01 * that.m10;
127
        double n01 = this.m00 * that.m01 + this.m01 * that.m11;
128
        double n02 = this.m00 * that.m02 + this.m01 * that.m12 + this.m02;
129
        double n10 = this.m10 * that.m00 + this.m11 * that.m10;
130
        double n11 = this.m10 * that.m01 + this.m11 * that.m11;
131
        double n12 = this.m10 * that.m02 + this.m11 * that.m12 + this.m12;
132
        return new Transform2D(n00, n01, n02, n10, n11, n12);
133
    }
134

    
135
    /**
136
     * Returns the affine transform created by applying first this affine
137
     * transform, then the affine transform given by <code>that</code>. This the
138
     * equivalent method of the 'preConcatenate' method in
139
     * java.awt.geom.AffineTransform. <code><pre>
140
     * shape = shape.transform(T1.chain(T2).chain(T3));
141
     * </pre></code> is equivalent to the sequence: <code><pre>
142
     * shape = shape.transform(T1);
143
     * shape = shape.transform(T2);
144
     * shape = shape.transform(T3);
145
     * </pre></code>
146
     *
147
     * @param other the transform to apply in a second step
148
     * @return the composition that * this
149
     */
150
    public Transform chain(Transform other) {
151
        if (!(other instanceof Transform2D)) {
152
            throw new IllegalArgumentException("Can't concatenate an AffineTransform2D with a non AffineTransform2D (other " + other.toString() + ")");
153
        }
154
        Transform2D that = (Transform2D) other;
155
        return new Transform2D(
156
                that.m00 * this.m00 + that.m01 * this.m10,
157
                that.m00 * this.m01 + that.m01 * this.m11,
158
                that.m00 * this.m02 + that.m01 * this.m12 + that.m02,
159
                that.m10 * this.m00 + that.m11 * this.m10,
160
                that.m10 * this.m01 + that.m11 * this.m11,
161
                that.m10 * this.m02 + that.m11 * this.m12 + that.m12);
162
    }
163

    
164
    /**
165
     * Return the affine transform created by applying first this affine
166
     * transform, then the affine transform given by <code>that</code>. This the
167
     * equivalent method of the 'preConcatenate' method in
168
     * java.awt.geom.AffineTransform.
169
     *
170
     * @param other the transform to apply in a second step
171
     * @return the composition that * this
172
     */
173
    @Override
174
    public Transform preConcatenate(Transform other) {
175
        return this.chain(other);
176
    }
177

    
178
    /**
179
     * Tests if this affine transform is a similarity.
180
     *
181
     * @return
182
     */
183
    public boolean isSimilarity() {
184
        // computation shortcuts
185
        double a = this.m00;
186
        double b = this.m01;
187
        double c = this.m10;
188
        double d = this.m11;
189

    
190
        // determinant
191
        double k2 = abs(a * d - b * c);
192

    
193
        // test each condition
194
        if (abs(a * a + b * b - k2) > ACCURACY) {
195
            return false;
196
        }
197
        if (abs(c * c + d * d - k2) > ACCURACY) {
198
            return false;
199
        }
200
        if (abs(a * a + c * c - k2) > ACCURACY) {
201
            return false;
202
        }
203
        if (abs(b * b + d * d - k2) > ACCURACY) {
204
            return false;
205
        }
206

    
207
        // if each test passed, return true
208
        return true;
209
    }
210

    
211
    /**
212
     * Tests if this affine transform is a motion, i.e. is composed only of
213
     * rotations and translations.
214
     * @return 
215
     */
216
    public boolean isMotion() {
217
        // Transform must be 1) an isometry and 2) be direct
218
        return isIsometry() && isDirect();
219
    }
220

    
221
    /**
222
     * Tests if this affine transform is an isometry, i.e. is equivalent to a
223
     * compound of translations, rotations and reflections. Isometry keeps area
224
     * of shapes unchanged, but can change orientation (direct or indirect).
225
     *
226
     * @return true in case of isometry.
227
     */
228
    public boolean isIsometry() {
229
        // extract matrix coefficients
230
        double a = this.m00;
231
        double b = this.m01;
232
        double c = this.m10;
233
        double d = this.m11;
234

    
235
        // transform vectors should be normalized
236
        if (abs(a * a + b * b - 1) > ACCURACY) {
237
            return false;
238
        }
239
        if (abs(c * c + d * d - 1) > ACCURACY) {
240
            return false;
241
        }
242

    
243
        // determinant must be -1 or +1
244
        if (abs(a * b + c * d) > ACCURACY) {
245
            return false;
246
        }
247

    
248
        // if all tests passed, return true;
249
        return true;
250
    }
251

    
252
    /**
253
     * Tests if this affine transform is direct, i.e. the sign of the
254
     * determinant of the associated matrix is positive. Direct transforms
255
     * preserve the orientation of transformed shapes.
256
     * @return 
257
     */
258
    public boolean isDirect() {
259
        return this.m00 * this.m11 - this.m01 * this.m10 > 0;
260
    }
261

    
262
    /**
263
     * Tests is this affine transform is equal to the identity transform.
264
     *
265
     * @return true if this transform is the identity transform
266
     */
267
    @Override
268
    public boolean isIdentity() {
269
        if (abs(this.m00 - 1) > ACCURACY) {
270
            return false;
271
        }
272
        if (abs(this.m01) > ACCURACY) {
273
            return false;
274
        }
275
        if (abs(this.m02) > ACCURACY) {
276
            return false;
277
        }
278
        if (abs(this.m10) > ACCURACY) {
279
            return false;
280
        }
281
        if (abs(this.m11 - 1) > ACCURACY) {
282
            return false;
283
        }
284
        if (abs(this.m12) > ACCURACY) {
285
            return false;
286
        }
287
        return true;
288
    }
289

    
290
    /**
291
     * Returns the inverse transform. If the transform is not invertible, throws
292
     * a new NonInvertibleTransformException.
293
     *
294
     * @return
295
     */
296
    @Override
297
    public Transform inverse() {
298
        double det = m00 * m11 - m10 * m01;
299

    
300
        if (Math.abs(det) < ACCURACY) {
301
            throw new NonInvertibleTransformException(this);
302
        }
303

    
304
        return new Transform2D(
305
                m11 / det, -m01 / det, (m01 * m12 - m02 * m11) / det,
306
                -m10 / det, m00 / det, (m02 * m10 - m00 * m12) / det);
307
    }
308

    
309
    private Point createPoint2D(double x, double y) {
310
        try {
311
            return GeometryLocator.getGeometryManager().createPoint(x, y, Geometry.SUBTYPES.GEOM2D);
312
        } catch (CreateGeometryException ex) {
313
            throw new RuntimeException("Can't create Point2D", ex);
314
        }
315
    }
316

    
317
    /**
318
     * Computes the coordinates of the transformed point.
319
     *
320
     * @param p
321
     * @return
322
     */
323
    @Override
324
    public Point transform(Point p) {
325
        Point dst = createPoint2D(
326
                p.getX() * m00 + p.getY() * m01 + m02,
327
                p.getX() * m10 + p.getY() * m11 + m12);
328
        return dst;
329
    }
330

    
331
    @Override
332
    public Point[] transform(Point[] src, Point[] dst) {
333
        if (dst == null) {
334
            dst = new Point[src.length];
335
        }
336

    
337
        double x, y;
338
        for (int i = 0; i < src.length; i++) {
339
            x = src[i].getX();
340
            y = src[i].getY();
341
            dst[i] = createPoint2D(
342
                    x * m00 + y * m01 + m02,
343
                    x * m10 + y * m11 + m12);
344
        }
345
        return dst;
346
    }
347

    
348
    /**
349
     * Displays the coefficients of the transform, row by row.
350
     *
351
     * @return
352
     */
353
    @Override
354
    public String toString() {
355
        return new String("AffineTransform2D(" + m00 + "," + m01 + "," + m02
356
                + "," + m10 + "," + m11 + "," + m12 + ",");
357
    }
358

    
359
    @Override
360
    public boolean equals(Object obj) {
361
        if (this == obj) {
362
            return true;
363
        }
364

    
365
        if (!(obj instanceof Transform2D)) {
366
            return false;
367
        }
368

    
369
        Transform2D that = (Transform2D) obj;
370

    
371
        if (!EqualUtils.areEqual(this.m00, that.m00)) {
372
            return false;
373
        }
374
        if (!EqualUtils.areEqual(this.m01, that.m01)) {
375
            return false;
376
        }
377
        if (!EqualUtils.areEqual(this.m02, that.m02)) {
378
            return false;
379
        }
380
        if (!EqualUtils.areEqual(this.m00, that.m00)) {
381
            return false;
382
        }
383
        if (!EqualUtils.areEqual(this.m01, that.m01)) {
384
            return false;
385
        }
386
        if (!EqualUtils.areEqual(this.m02, that.m02)) {
387
            return false;
388
        }
389

    
390
        return true;
391
    }
392

    
393
    @Override
394
    public Transform2D clone() {
395
        return new Transform2D(m00, m01, m02, m10, m11, m12);
396
    }
397
}