1 |
/* Copyright (C) 2000, 2001, 2002 Free Software Foundation |
/* AffineTransform.java -- transform coordinates between two 2-D spaces |
2 |
|
Copyright (C) 2000, 2001, 2002 Free Software Foundation |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
35 |
obligated to do so. If you do not wish to do so, delete this |
obligated to do so. If you do not wish to do so, delete this |
36 |
exception statement from your version. */ |
exception statement from your version. */ |
37 |
|
|
38 |
|
|
39 |
package java.awt.geom; |
package java.awt.geom; |
40 |
import java.awt.*; |
|
41 |
|
import java.awt.Shape; |
42 |
|
import java.io.IOException; |
43 |
|
import java.io.ObjectInputStream; |
44 |
import java.io.Serializable; |
import java.io.Serializable; |
45 |
|
|
46 |
/** |
/** |
47 |
|
* This class represents an affine transformation between two coordinate |
48 |
|
* spaces in 2 dimensions. Such a transform preserves the "straightness" |
49 |
|
* and "parallelness" of lines. The transform is built from a sequence of |
50 |
|
* translations, scales, flips, rotations, and shears. |
51 |
|
* |
52 |
|
* <p>The transformation can be represented using matrix math on a 3x3 array. |
53 |
|
* Given (x,y), the transformation (x',y') can be found by: |
54 |
|
* <pre> |
55 |
|
* [ x'] [ m00 m01 m02 ] [ x ] [ m00*x + m01*y + m02 ] |
56 |
|
* [ y'] = [ m10 m11 m12 ] [ y ] = [ m10*x + m11*y + m12 ] |
57 |
|
* [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] |
58 |
|
* </pre> |
59 |
|
* The bottom row of the matrix is constant, so a transform can be uniquely |
60 |
|
* represented (as in toString) by "[[m00, m01, m02], [m10, m11, m12]]". |
61 |
|
* |
62 |
* @author Tom Tromey <tromey@cygnus.com> |
* @author Tom Tromey <tromey@cygnus.com> |
63 |
* @date April 16, 2000 |
* @author Eric Blake <ebb9@email.byu.edu> |
64 |
|
* @since 1.2 |
65 |
|
* @status partially updated to 1.4, still has some problems |
66 |
*/ |
*/ |
|
|
|
|
/* Status: mostly complete. Search for fixme to see problems. |
|
|
Also, TYPE_ returns are not handled correctly. */ |
|
|
|
|
67 |
public class AffineTransform implements Cloneable, Serializable |
public class AffineTransform implements Cloneable, Serializable |
68 |
{ |
{ |
69 |
|
/** |
70 |
|
* Compatible with JDK 1.2+. |
71 |
|
*/ |
72 |
|
private static final long serialVersionUID = 1330973210523860834L; |
73 |
|
|
74 |
|
/** |
75 |
|
* The transformation is the identity (x' = x, y' = y). All other transforms |
76 |
|
* have either a combination of the appropriate transform flag bits for |
77 |
|
* their type, or the type GENERAL_TRANSFORM. |
78 |
|
* |
79 |
|
* @see #TYPE_TRANSLATION |
80 |
|
* @see #TYPE_UNIFORM_SCALE |
81 |
|
* @see #TYPE_GENERAL_SCALE |
82 |
|
* @see #TYPE_FLIP |
83 |
|
* @see #TYPE_QUADRANT_ROTATION |
84 |
|
* @see #TYPE_GENERAL_ROTATION |
85 |
|
* @see #TYPE_GENERAL_TRANSFORM |
86 |
|
* @see #getType() |
87 |
|
*/ |
88 |
public static final int TYPE_IDENTITY = 0; |
public static final int TYPE_IDENTITY = 0; |
89 |
public static final int TYPE_FLIP = 64; |
|
90 |
public static final int TYPE_GENERAL_ROTATION = 16; |
/** |
91 |
|
* The transformation includes a translation - shifting in the x or y |
92 |
|
* direction without changing length or angles. |
93 |
|
* |
94 |
|
* @see #TYPE_IDENTITY |
95 |
|
* @see #TYPE_UNIFORM_SCALE |
96 |
|
* @see #TYPE_GENERAL_SCALE |
97 |
|
* @see #TYPE_FLIP |
98 |
|
* @see #TYPE_QUADRANT_ROTATION |
99 |
|
* @see #TYPE_GENERAL_ROTATION |
100 |
|
* @see #TYPE_GENERAL_TRANSFORM |
101 |
|
* @see #getType() |
102 |
|
*/ |
103 |
|
public static final int TYPE_TRANSLATION = 1; |
104 |
|
|
105 |
|
/** |
106 |
|
* The transformation includes a uniform scale - length is scaled in both |
107 |
|
* the x and y directions by the same amount, without affecting angles. |
108 |
|
* This is mutually exclusive with TYPE_GENERAL_SCALE. |
109 |
|
* |
110 |
|
* @see #TYPE_IDENTITY |
111 |
|
* @see #TYPE_TRANSLATION |
112 |
|
* @see #TYPE_GENERAL_SCALE |
113 |
|
* @see #TYPE_FLIP |
114 |
|
* @see #TYPE_QUADRANT_ROTATION |
115 |
|
* @see #TYPE_GENERAL_ROTATION |
116 |
|
* @see #TYPE_GENERAL_TRANSFORM |
117 |
|
* @see #TYPE_MASK_SCALE |
118 |
|
* @see #getType() |
119 |
|
*/ |
120 |
|
public static final int TYPE_UNIFORM_SCALE = 2; |
121 |
|
|
122 |
|
/** |
123 |
|
* The transformation includes a general scale - length is scaled in either |
124 |
|
* or both the x and y directions, but by different amounts; without |
125 |
|
* affecting angles. This is mutually exclusive with TYPE_UNIFORM_SCALE. |
126 |
|
* |
127 |
|
* @see #TYPE_IDENTITY |
128 |
|
* @see #TYPE_TRANSLATION |
129 |
|
* @see #TYPE_UNIFORM_SCALE |
130 |
|
* @see #TYPE_FLIP |
131 |
|
* @see #TYPE_QUADRANT_ROTATION |
132 |
|
* @see #TYPE_GENERAL_ROTATION |
133 |
|
* @see #TYPE_GENERAL_TRANSFORM |
134 |
|
* @see #TYPE_MASK_SCALE |
135 |
|
* @see #getType() |
136 |
|
*/ |
137 |
public static final int TYPE_GENERAL_SCALE = 4; |
public static final int TYPE_GENERAL_SCALE = 4; |
138 |
public static final int TYPE_GENERAL_TRANSFORM = 32; |
|
139 |
public static final int TYPE_MASK_ROTATION = 24; |
/** |
140 |
|
* This constant checks if either variety of scale transform is performed. |
141 |
|
* |
142 |
|
* @see #TYPE_UNIFORM_SCALE |
143 |
|
* @see #TYPE_GENERAL_SCALE |
144 |
|
*/ |
145 |
public static final int TYPE_MASK_SCALE = 6; |
public static final int TYPE_MASK_SCALE = 6; |
146 |
|
|
147 |
|
/** |
148 |
|
* The transformation includes a flip about an axis, swapping between |
149 |
|
* right-handed and left-handed coordinate systems. In a right-handed |
150 |
|
* system, the positive x-axis rotates counter-clockwise to the positive |
151 |
|
* y-axis; in a left-handed system it rotates clockwise. |
152 |
|
* |
153 |
|
* @see #TYPE_IDENTITY |
154 |
|
* @see #TYPE_TRANSLATION |
155 |
|
* @see #TYPE_UNIFORM_SCALE |
156 |
|
* @see #TYPE_GENERAL_SCALE |
157 |
|
* @see #TYPE_QUADRANT_ROTATION |
158 |
|
* @see #TYPE_GENERAL_ROTATION |
159 |
|
* @see #TYPE_GENERAL_TRANSFORM |
160 |
|
* @see #getType() |
161 |
|
*/ |
162 |
|
public static final int TYPE_FLIP = 64; |
163 |
|
|
164 |
|
/** |
165 |
|
* The transformation includes a rotation of a multiple of 90 degrees (PI/2 |
166 |
|
* radians). Angles are rotated, but length is preserved. This is mutually |
167 |
|
* exclusive with TYPE_GENERAL_ROTATION. |
168 |
|
* |
169 |
|
* @see #TYPE_IDENTITY |
170 |
|
* @see #TYPE_TRANSLATION |
171 |
|
* @see #TYPE_UNIFORM_SCALE |
172 |
|
* @see #TYPE_GENERAL_SCALE |
173 |
|
* @see #TYPE_FLIP |
174 |
|
* @see #TYPE_GENERAL_ROTATION |
175 |
|
* @see #TYPE_GENERAL_TRANSFORM |
176 |
|
* @see #TYPE_MASK_ROTATION |
177 |
|
* @see #getType() |
178 |
|
*/ |
179 |
public static final int TYPE_QUADRANT_ROTATION = 8; |
public static final int TYPE_QUADRANT_ROTATION = 8; |
|
public static final int TYPE_TRANSLATION = 1; |
|
|
public static final int TYPE_UNIFORM_SCALE = 2; |
|
180 |
|
|
181 |
public AffineTransform () |
/** |
182 |
{ |
* The transformation includes a rotation by an arbitrary angle. Angles are |
183 |
setToIdentity (); |
* rotated, but length is preserved. This is mutually exclusive with |
184 |
} |
* TYPE_QUADRANT_ROTATION. |
185 |
|
* |
186 |
|
* @see #TYPE_IDENTITY |
187 |
|
* @see #TYPE_TRANSLATION |
188 |
|
* @see #TYPE_UNIFORM_SCALE |
189 |
|
* @see #TYPE_GENERAL_SCALE |
190 |
|
* @see #TYPE_FLIP |
191 |
|
* @see #TYPE_QUADRANT_ROTATION |
192 |
|
* @see #TYPE_GENERAL_TRANSFORM |
193 |
|
* @see #TYPE_MASK_ROTATION |
194 |
|
* @see #getType() |
195 |
|
*/ |
196 |
|
public static final int TYPE_GENERAL_ROTATION = 16; |
197 |
|
|
198 |
public AffineTransform (AffineTransform tx) |
/** |
199 |
|
* This constant checks if either variety of rotation is performed. |
200 |
|
* |
201 |
|
* @see #TYPE_QUADRANT_ROTATION |
202 |
|
* @see #TYPE_GENERAL_ROTATION |
203 |
|
*/ |
204 |
|
public static final int TYPE_MASK_ROTATION = 24; |
205 |
|
|
206 |
|
/** |
207 |
|
* The transformation is an arbitrary conversion of coordinates which |
208 |
|
* could not be decomposed into the other TYPEs. |
209 |
|
* |
210 |
|
* @see #TYPE_IDENTITY |
211 |
|
* @see #TYPE_TRANSLATION |
212 |
|
* @see #TYPE_UNIFORM_SCALE |
213 |
|
* @see #TYPE_GENERAL_SCALE |
214 |
|
* @see #TYPE_FLIP |
215 |
|
* @see #TYPE_QUADRANT_ROTATION |
216 |
|
* @see #TYPE_GENERAL_ROTATION |
217 |
|
* @see #getType() |
218 |
|
*/ |
219 |
|
public static final int TYPE_GENERAL_TRANSFORM = 32; |
220 |
|
|
221 |
|
/** |
222 |
|
* The X coordinate scaling element of the transform matrix. |
223 |
|
* |
224 |
|
* @serial matrix[0,0] |
225 |
|
*/ |
226 |
|
private double m00; |
227 |
|
|
228 |
|
/** |
229 |
|
* The Y coordinate scaling element of the transform matrix. |
230 |
|
* |
231 |
|
* @serial matrix[1,0] |
232 |
|
*/ |
233 |
|
private double m10; |
234 |
|
|
235 |
|
/** |
236 |
|
* The X coordinate shearing element of the transform matrix. |
237 |
|
* |
238 |
|
* @serial matrix[0,1] |
239 |
|
*/ |
240 |
|
private double m01; |
241 |
|
|
242 |
|
/** |
243 |
|
* The Y coordinate shearing element of the transform matrix. |
244 |
|
* |
245 |
|
* @serial matrix[1,1] |
246 |
|
*/ |
247 |
|
private double m11; |
248 |
|
|
249 |
|
/** |
250 |
|
* The X coordinate translation element of the transform matrix. |
251 |
|
* |
252 |
|
* @serial matrix[0,2] |
253 |
|
*/ |
254 |
|
private double m02; |
255 |
|
|
256 |
|
/** |
257 |
|
* The Y coordinate translation element of the transform matrix. |
258 |
|
* |
259 |
|
* @serial matrix[1,2] |
260 |
|
*/ |
261 |
|
private double m12; |
262 |
|
|
263 |
|
/** The type of this transform. */ |
264 |
|
private transient int type; |
265 |
|
|
266 |
|
/** |
267 |
|
* Construct a new identity transform: |
268 |
|
* <pre> |
269 |
|
* [ 1 0 0 ] |
270 |
|
* [ 0 1 0 ] |
271 |
|
* [ 0 0 1 ] |
272 |
|
* </pre> |
273 |
|
*/ |
274 |
|
public AffineTransform() |
275 |
{ |
{ |
276 |
setTransform (tx); |
m00 = m11 = 1; |
277 |
} |
} |
278 |
|
|
279 |
public AffineTransform (float m00, float m10, |
/** |
280 |
float m01, float m11, |
* Create a new transform which copies the given one. |
281 |
float m02, float m12) |
* |
282 |
|
* @param tx the transform to copy |
283 |
|
* @throws NullPointerException if tx is null |
284 |
|
*/ |
285 |
|
public AffineTransform(AffineTransform tx) |
286 |
|
{ |
287 |
|
setTransform(tx); |
288 |
|
} |
289 |
|
|
290 |
|
/** |
291 |
|
* Construct a transform with the given matrix entries: |
292 |
|
* <pre> |
293 |
|
* [ m00 m01 m02 ] |
294 |
|
* [ m10 m11 m12 ] |
295 |
|
* [ 0 0 1 ] |
296 |
|
* </pre> |
297 |
|
* |
298 |
|
* @param m00 the x scaling component |
299 |
|
* @param m10 the y shearing component |
300 |
|
* @param m01 the x shearing component |
301 |
|
* @param m11 the y scaling component |
302 |
|
* @param m02 the x translation component |
303 |
|
* @param m12 the y translation component |
304 |
|
*/ |
305 |
|
public AffineTransform(float m00, float m10, |
306 |
|
float m01, float m11, |
307 |
|
float m02, float m12) |
308 |
{ |
{ |
309 |
this.m00 = m00; |
this.m00 = m00; |
310 |
this.m10 = m10; |
this.m10 = m10; |
312 |
this.m11 = m11; |
this.m11 = m11; |
313 |
this.m02 = m02; |
this.m02 = m02; |
314 |
this.m12 = m12; |
this.m12 = m12; |
315 |
this.type = TYPE_GENERAL_TRANSFORM; |
updateType(); |
316 |
} |
} |
317 |
|
|
318 |
public AffineTransform (float[] flatmatrix) |
/** |
319 |
{ |
* Construct a transform from a sequence of float entries. The array must |
320 |
m00 = flatmatrix[0]; |
* have at least 4 entries, which has a translation factor of 0; or 6 |
321 |
m10 = flatmatrix[1]; |
* entries, for specifying all parameters: |
322 |
m01 = flatmatrix[2]; |
* <pre> |
323 |
m11 = flatmatrix[3]; |
* [ f[0] f[2] (f[4]) ] |
324 |
if (flatmatrix.length >= 6) |
* [ f[1] f[3] (f[5]) ] |
325 |
|
* [ 0 0 1 ] |
326 |
|
* </pre> |
327 |
|
* |
328 |
|
* @param f the matrix to copy from, with at least 4 (6) entries |
329 |
|
* @throws NullPointerException if f is null |
330 |
|
* @throws ArrayIndexOutOfBoundsException if f is too small |
331 |
|
*/ |
332 |
|
public AffineTransform(float[] f) |
333 |
|
{ |
334 |
|
m00 = f[0]; |
335 |
|
m10 = f[1]; |
336 |
|
m01 = f[2]; |
337 |
|
m11 = f[3]; |
338 |
|
if (f.length >= 6) |
339 |
{ |
{ |
340 |
m02 = flatmatrix[4]; |
m02 = f[4]; |
341 |
m12 = flatmatrix[5]; |
m12 = f[5]; |
342 |
} |
} |
343 |
|
updateType(); |
344 |
} |
} |
345 |
|
|
346 |
public AffineTransform (double m00, double m10, double m01, |
/** |
347 |
double m11, double m02, double m12) |
* Construct a transform with the given matrix entries: |
348 |
|
* <pre> |
349 |
|
* [ m00 m01 m02 ] |
350 |
|
* [ m10 m11 m12 ] |
351 |
|
* [ 0 0 1 ] |
352 |
|
* </pre> |
353 |
|
* |
354 |
|
* @param m00 the x scaling component |
355 |
|
* @param m10 the y shearing component |
356 |
|
* @param m01 the x shearing component |
357 |
|
* @param m11 the y scaling component |
358 |
|
* @param m02 the x translation component |
359 |
|
* @param m12 the y translation component |
360 |
|
*/ |
361 |
|
public AffineTransform(double m00, double m10, double m01, |
362 |
|
double m11, double m02, double m12) |
363 |
{ |
{ |
364 |
this.m00 = m00; |
this.m00 = m00; |
365 |
this.m10 = m10; |
this.m10 = m10; |
367 |
this.m11 = m11; |
this.m11 = m11; |
368 |
this.m02 = m02; |
this.m02 = m02; |
369 |
this.m12 = m12; |
this.m12 = m12; |
370 |
this.type = TYPE_GENERAL_TRANSFORM; |
updateType(); |
371 |
} |
} |
372 |
|
|
373 |
public AffineTransform (double[] flatmatrix) |
/** |
374 |
{ |
* Construct a transform from a sequence of double entries. The array must |
375 |
m00 = flatmatrix[0]; |
* have at least 4 entries, which has a translation factor of 0; or 6 |
376 |
m10 = flatmatrix[1]; |
* entries, for specifying all parameters: |
377 |
m01 = flatmatrix[2]; |
* <pre> |
378 |
m11 = flatmatrix[3]; |
* [ d[0] d[2] (d[4]) ] |
379 |
if (flatmatrix.length >= 6) |
* [ d[1] d[3] (d[5]) ] |
380 |
|
* [ 0 0 1 ] |
381 |
|
* </pre> |
382 |
|
* |
383 |
|
* @param d the matrix to copy from, with at least 4 (6) entries |
384 |
|
* @throws NullPointerException if d is null |
385 |
|
* @throws ArrayIndexOutOfBoundsException if d is too small |
386 |
|
*/ |
387 |
|
public AffineTransform(double[] d) |
388 |
|
{ |
389 |
|
m00 = d[0]; |
390 |
|
m10 = d[1]; |
391 |
|
m01 = d[2]; |
392 |
|
m11 = d[3]; |
393 |
|
if (d.length >= 6) |
394 |
{ |
{ |
395 |
m02 = flatmatrix[4]; |
m02 = d[4]; |
396 |
m12 = flatmatrix[5]; |
m12 = d[5]; |
397 |
} |
} |
398 |
|
updateType(); |
399 |
} |
} |
400 |
|
|
401 |
public static AffineTransform getTranslateInstance (double tx, double ty) |
/** |
402 |
|
* Returns a translation transform: |
403 |
|
* <pre> |
404 |
|
* [ 1 0 tx ] |
405 |
|
* [ 0 1 ty ] |
406 |
|
* [ 0 0 1 ] |
407 |
|
* </pre> |
408 |
|
* |
409 |
|
* @param tx the x translation distance |
410 |
|
* @param ty the y translation distance |
411 |
|
* @return the translating transform |
412 |
|
*/ |
413 |
|
public static AffineTransform getTranslateInstance(double tx, double ty) |
414 |
{ |
{ |
415 |
AffineTransform t = new AffineTransform (); |
AffineTransform t = new AffineTransform(); |
416 |
t.setToTranslation (tx, ty); |
t.setToTranslation(tx, ty); |
417 |
return t; |
return t; |
418 |
} |
} |
419 |
|
|
420 |
public static AffineTransform getRotateInstance (double theta) |
/** |
421 |
|
* Returns a rotation transform. A positive angle (in radians) rotates |
422 |
|
* the positive x-axis to the positive y-axis: |
423 |
|
* <pre> |
424 |
|
* [ cos(theta) -sin(theta) 0 ] |
425 |
|
* [ sin(theta) cos(theta) 0 ] |
426 |
|
* [ 0 0 1 ] |
427 |
|
* </pre> |
428 |
|
* |
429 |
|
* @param theta the rotation angle |
430 |
|
* @return the rotating transform |
431 |
|
*/ |
432 |
|
public static AffineTransform getRotateInstance(double theta) |
433 |
{ |
{ |
434 |
AffineTransform t = new AffineTransform (); |
AffineTransform t = new AffineTransform(); |
435 |
t.setToRotation (theta); |
t.setToRotation(theta); |
436 |
return t; |
return t; |
437 |
} |
} |
438 |
|
|
439 |
public static AffineTransform getRotateInstance (double theta, |
/** |
440 |
double x, double y) |
* Returns a rotation transform about a point. A positive angle (in radians) |
441 |
{ |
* rotates the positive x-axis to the positive y-axis. This is the same |
442 |
AffineTransform t = new AffineTransform (); |
* as calling: |
443 |
t.rotate (theta, x, y); |
* <pre> |
444 |
|
* AffineTransform tx = new AffineTransform(); |
445 |
|
* tx.setToTranslation(x, y); |
446 |
|
* tx.rotate(theta); |
447 |
|
* tx.translate(-x, -y); |
448 |
|
* </pre> |
449 |
|
* |
450 |
|
* <p>The resulting matrix is: |
451 |
|
* <pre> |
452 |
|
* [ cos(theta) -sin(theta) x-x*cos+y*sin ] |
453 |
|
* [ sin(theta) cos(theta) y-x*sin-y*cos ] |
454 |
|
* [ 0 0 1 ] |
455 |
|
* </pre> |
456 |
|
* |
457 |
|
* @param theta the rotation angle |
458 |
|
* @param x the x coordinate of the pivot point |
459 |
|
* @param y the y coordinate of the pivot point |
460 |
|
* @return the rotating transform |
461 |
|
*/ |
462 |
|
public static AffineTransform getRotateInstance(double theta, |
463 |
|
double x, double y) |
464 |
|
{ |
465 |
|
AffineTransform t = new AffineTransform(); |
466 |
|
t.setToTranslation(x, y); |
467 |
|
t.rotate(theta); |
468 |
|
t.translate(-x, -y); |
469 |
return t; |
return t; |
470 |
} |
} |
471 |
|
|
472 |
public static AffineTransform getScaleInstance (double sx, double sy) |
/** |
473 |
|
* Returns a scaling transform: |
474 |
|
* <pre> |
475 |
|
* [ sx 0 0 ] |
476 |
|
* [ 0 sy 0 ] |
477 |
|
* [ 0 0 1 ] |
478 |
|
* </pre> |
479 |
|
* |
480 |
|
* @param sx the x scaling factor |
481 |
|
* @param sy the y scaling factor |
482 |
|
* @return the scaling transform |
483 |
|
*/ |
484 |
|
public static AffineTransform getScaleInstance(double sx, double sy) |
485 |
{ |
{ |
486 |
AffineTransform t = new AffineTransform (); |
AffineTransform t = new AffineTransform(); |
487 |
t.setToScale (sx, sy); |
t.setToScale(sx, sy); |
488 |
return t; |
return t; |
489 |
} |
} |
490 |
|
|
491 |
public static AffineTransform getShearInstance (double shx, double shy) |
/** |
492 |
|
* Returns a shearing transform (points are shifted in the x direction based |
493 |
|
* on a factor of their y coordinate, and in the y direction as a factor of |
494 |
|
* their x coordinate): |
495 |
|
* <pre> |
496 |
|
* [ 1 shx 0 ] |
497 |
|
* [ shy 1 0 ] |
498 |
|
* [ 0 0 1 ] |
499 |
|
* </pre> |
500 |
|
* |
501 |
|
* @param shx the x shearing factor |
502 |
|
* @param shy the y shearing factor |
503 |
|
* @return the shearing transform |
504 |
|
*/ |
505 |
|
public static AffineTransform getShearInstance(double shx, double shy) |
506 |
{ |
{ |
507 |
AffineTransform t = new AffineTransform (); |
AffineTransform t = new AffineTransform(); |
508 |
t.setToShear (shx, shy); |
t.setToShear(shx, shy); |
509 |
return t; |
return t; |
510 |
} |
} |
511 |
|
|
512 |
public int getType () |
/** |
513 |
|
* Returns the type of this transform. The result is always valid, although |
514 |
|
* it may not be the simplest interpretation (in other words, there are |
515 |
|
* sequences of transforms which reduce to something simpler, which this |
516 |
|
* does not always detect). The result is either TYPE_GENERAL_TRANSFORM, |
517 |
|
* or a bit-wise combination of TYPE_TRANSLATION, the mutually exclusive |
518 |
|
* TYPE_*_ROTATIONs, and the mutually exclusive TYPE_*_SCALEs. |
519 |
|
* |
520 |
|
* @see #TYPE_IDENTITY |
521 |
|
* @see #TYPE_TRANSLATION |
522 |
|
* @see #TYPE_UNIFORM_SCALE |
523 |
|
* @see #TYPE_GENERAL_SCALE |
524 |
|
* @see #TYPE_QUADRANT_ROTATION |
525 |
|
* @see #TYPE_GENERAL_ROTATION |
526 |
|
* @see #TYPE_GENERAL_TRANSFORM |
527 |
|
*/ |
528 |
|
public int getType() |
529 |
{ |
{ |
530 |
return type; |
return type; |
531 |
} |
} |
532 |
|
|
533 |
public double getDeterminant () |
/** |
534 |
|
* Return the determinant of this transform matrix. If the determinant is |
535 |
|
* non-zero, the transform is invertible; otherwise operations which require |
536 |
|
* an inverse throw a NoninvertibleTransformException. A result very near |
537 |
|
* zero, due to rounding errors, may indicate that inversion results do not |
538 |
|
* carry enough precision to be meaningful. |
539 |
|
* |
540 |
|
* <p>If this is a uniform scale transformation, the determinant also |
541 |
|
* represents the squared value of the scale. Otherwise, it carries little |
542 |
|
* additional meaning. The determinant is calculated as: |
543 |
|
* <pre> |
544 |
|
* | m00 m01 m02 | |
545 |
|
* | m10 m11 m12 | = m00 * m11 - m01 * m10 |
546 |
|
* | 0 0 1 | |
547 |
|
* </pre> |
548 |
|
* |
549 |
|
* @return the determinant |
550 |
|
* @see #createInverse() |
551 |
|
*/ |
552 |
|
public double getDeterminant() |
553 |
{ |
{ |
554 |
return m00 * m11 - m01 * m10; |
return m00 * m11 - m01 * m10; |
555 |
} |
} |
556 |
|
|
557 |
public void getMatrix (double[] flatmatrix) |
/** |
558 |
{ |
* Return the matrix of values used in this transform. If the matrix has |
559 |
flatmatrix[0] = m00; |
* fewer than 6 entries, only the scale and shear factors are returned; |
560 |
flatmatrix[1] = m10; |
* otherwise the translation factors are copied as well. The resulting |
561 |
flatmatrix[2] = m01; |
* values are: |
562 |
flatmatrix[3] = m11; |
* <pre> |
563 |
if (flatmatrix.length >= 6) |
* [ d[0] d[2] (d[4]) ] |
564 |
|
* [ d[1] d[3] (d[5]) ] |
565 |
|
* [ 0 0 1 ] |
566 |
|
* </pre> |
567 |
|
* |
568 |
|
* @param d the matrix to store the results into; with 4 (6) entries |
569 |
|
* @throws NullPointerException if d is null |
570 |
|
* @throws ArrayIndexOutOfBoundsException if d is too small |
571 |
|
*/ |
572 |
|
public void getMatrix(double[] d) |
573 |
|
{ |
574 |
|
d[0] = m00; |
575 |
|
d[1] = m10; |
576 |
|
d[2] = m01; |
577 |
|
d[3] = m11; |
578 |
|
if (d.length >= 6) |
579 |
{ |
{ |
580 |
flatmatrix[4] = m02; |
d[4] = m02; |
581 |
flatmatrix[5] = m12; |
d[5] = m12; |
582 |
} |
} |
583 |
} |
} |
584 |
|
|
585 |
public double getScaleX () |
/** |
586 |
|
* Returns the X coordinate scaling factor of the matrix. |
587 |
|
* |
588 |
|
* @return m00 |
589 |
|
* @see #getMatrix(double[]) |
590 |
|
*/ |
591 |
|
public double getScaleX() |
592 |
{ |
{ |
593 |
return m00; |
return m00; |
594 |
} |
} |
595 |
|
|
596 |
public double getScaleY () |
/** |
597 |
|
* Returns the Y coordinate scaling factor of the matrix. |
598 |
|
* |
599 |
|
* @return m11 |
600 |
|
* @see #getMatrix(double[]) |
601 |
|
*/ |
602 |
|
public double getScaleY() |
603 |
{ |
{ |
604 |
return m11; |
return m11; |
605 |
} |
} |
606 |
|
|
607 |
public double getShearX () |
/** |
608 |
|
* Returns the X coordinate shearing factor of the matrix. |
609 |
|
* |
610 |
|
* @return m01 |
611 |
|
* @see #getMatrix(double[]) |
612 |
|
*/ |
613 |
|
public double getShearX() |
614 |
{ |
{ |
615 |
return m01; |
return m01; |
616 |
} |
} |
617 |
|
|
618 |
public double getShearY () |
/** |
619 |
|
* Returns the Y coordinate shearing factor of the matrix. |
620 |
|
* |
621 |
|
* @return m10 |
622 |
|
* @see #getMatrix(double[]) |
623 |
|
*/ |
624 |
|
public double getShearY() |
625 |
{ |
{ |
626 |
return m10; |
return m10; |
627 |
} |
} |
628 |
|
|
629 |
public double getTranslateX () |
/** |
630 |
|
* Returns the X coordinate translation factor of the matrix. |
631 |
|
* |
632 |
|
* @return m02 |
633 |
|
* @see #getMatrix(double[]) |
634 |
|
*/ |
635 |
|
public double getTranslateX() |
636 |
{ |
{ |
637 |
return m02; |
return m02; |
638 |
} |
} |
639 |
|
|
640 |
public double getTranslateY () |
/** |
641 |
|
* Returns the Y coordinate translation factor of the matrix. |
642 |
|
* |
643 |
|
* @return m12 |
644 |
|
* @see #getMatrix(double[]) |
645 |
|
*/ |
646 |
|
public double getTranslateY() |
647 |
{ |
{ |
648 |
return m12; |
return m12; |
649 |
} |
} |
650 |
|
|
651 |
public void translate (double tx, double ty) |
/** |
652 |
|
* Concatenate a translation onto this transform. This is equivalent, but |
653 |
|
* more efficient than |
654 |
|
* <code>concatenate(AffineTransform.getTranslateInstance(tx, ty))</code>. |
655 |
|
* |
656 |
|
* @param tx the x translation distance |
657 |
|
* @param ty the y translation distance |
658 |
|
* @see #getTranslateInstance(double, double) |
659 |
|
* @see #concatenate(AffineTransform) |
660 |
|
*/ |
661 |
|
public void translate(double tx, double ty) |
662 |
{ |
{ |
663 |
m02 += tx * m00 + ty * m01; |
m02 += tx * m00 + ty * m01; |
664 |
m12 += tx * m10 + ty * m11; |
m12 += tx * m10 + ty * m11; |
665 |
|
updateType(); |
666 |
} |
} |
667 |
|
|
668 |
public void rotate (double theta) |
/** |
669 |
|
* Concatenate a rotation onto this transform. This is equivalent, but |
670 |
|
* more efficient than |
671 |
|
* <code>concatenate(AffineTransform.getRotateInstance(theta))</code>. |
672 |
|
* |
673 |
|
* @param theta the rotation angle |
674 |
|
* @see #getRotateInstance(double) |
675 |
|
* @see #concatenate(AffineTransform) |
676 |
|
*/ |
677 |
|
public void rotate(double theta) |
678 |
{ |
{ |
679 |
double c = Math.cos (theta); |
double c = Math.cos(theta); |
680 |
double s = Math.sin (theta); |
double s = Math.sin(theta); |
681 |
double n00 = m00 * c + m01 * s; |
double n00 = m00 * c + m01 * s; |
682 |
double n01 = m00 * -s + m01 * c; |
double n01 = m00 * -s + m01 * c; |
683 |
double n10 = m10 * c + m11 * s; |
double n10 = m10 * c + m11 * s; |
684 |
double n11 = m10 * -s + m11 * c; |
double n11 = m10 * -s + m11 * c; |
|
|
|
685 |
m00 = n00; |
m00 = n00; |
686 |
m01 = n01; |
m01 = n01; |
687 |
m10 = n10; |
m10 = n10; |
688 |
m11 = n11; |
m11 = n11; |
689 |
|
updateType(); |
690 |
} |
} |
691 |
|
|
692 |
public void rotate (double theta, double x, double y) |
/** |
693 |
{ |
* Concatenate a rotation about a point onto this transform. This is |
694 |
translate (x, y); |
* equivalent, but more efficient than |
695 |
rotate (theta); |
* <code>concatenate(AffineTransform.getRotateInstance(theta, x, y))</code>. |
696 |
translate (-x, -y); |
* |
697 |
} |
* @param theta the rotation angle |
698 |
|
* @param x the x coordinate of the pivot point |
699 |
public void scale (double sx, double sy) |
* @param y the y coordinate of the pivot point |
700 |
|
* @see #getRotateInstance(double, double, double) |
701 |
|
* @see #concatenate(AffineTransform) |
702 |
|
*/ |
703 |
|
public void rotate(double theta, double x, double y) |
704 |
|
{ |
705 |
|
translate(x, y); |
706 |
|
rotate(theta); |
707 |
|
translate(-x, -y); |
708 |
|
} |
709 |
|
|
710 |
|
/** |
711 |
|
* Concatenate a scale onto this transform. This is equivalent, but more |
712 |
|
* efficient than |
713 |
|
* <code>concatenate(AffineTransform.getScaleInstance(sx, sy))</code>. |
714 |
|
* |
715 |
|
* @param sx the x scaling factor |
716 |
|
* @param sy the y scaling factor |
717 |
|
* @see #getScaleInstance(double, double) |
718 |
|
* @see #concatenate(AffineTransform) |
719 |
|
*/ |
720 |
|
public void scale(double sx, double sy) |
721 |
{ |
{ |
722 |
m00 *= sx; |
m00 *= sx; |
723 |
m01 *= sy; |
m01 *= sy; |
724 |
m10 *= sx; |
m10 *= sx; |
725 |
m11 *= sy; |
m11 *= sy; |
726 |
|
updateType(); |
727 |
} |
} |
728 |
|
|
729 |
public void shear (double shx, double shy) |
/** |
730 |
|
* Concatenate a shearing onto this transform. This is equivalent, but more |
731 |
|
* efficient than |
732 |
|
* <code>concatenate(AffineTransform.getShearInstance(sx, sy))</code>. |
733 |
|
* |
734 |
|
* @param shx the x shearing factor |
735 |
|
* @param shy the y shearing factor |
736 |
|
* @see #getShearInstance(double, double) |
737 |
|
* @see #concatenate(AffineTransform) |
738 |
|
*/ |
739 |
|
public void shear(double shx, double shy) |
740 |
{ |
{ |
741 |
double n00 = m00 + shx * m01; |
double n00 = m00 + shx * m01; |
742 |
double n01 = shx * m00 + m01; |
double n01 = shx * m00 + m01; |
743 |
double n10 = m10 * shy + m11; |
double n10 = m10 * shy + m11; |
744 |
double n11 = shx * m10 + m11; |
double n11 = shx * m10 + m11; |
|
|
|
745 |
m00 = n00; |
m00 = n00; |
746 |
m01 = n01; |
m01 = n01; |
747 |
m10 = n10; |
m10 = n10; |
748 |
m11 = n11; |
m11 = n11; |
749 |
|
updateType(); |
750 |
} |
} |
751 |
|
|
752 |
public void setToIdentity () |
/** |
753 |
|
* Reset this transform to the identity (no transformation): |
754 |
|
* <pre> |
755 |
|
* [ 1 0 0 ] |
756 |
|
* [ 0 1 0 ] |
757 |
|
* [ 0 0 1 ] |
758 |
|
* </pre> |
759 |
|
*/ |
760 |
|
public void setToIdentity() |
761 |
{ |
{ |
762 |
m00 = m11 = 1; |
m00 = m11 = 1; |
763 |
m01 = m02 = m10 = m12 = 0; |
m01 = m02 = m10 = m12 = 0; |
764 |
type = TYPE_IDENTITY; |
type = TYPE_IDENTITY; |
765 |
} |
} |
766 |
|
|
767 |
public void setToTranslation (double tx, double ty) |
/** |
768 |
|
* Set this transform to a translation: |
769 |
|
* <pre> |
770 |
|
* [ 1 0 tx ] |
771 |
|
* [ 0 1 ty ] |
772 |
|
* [ 0 0 1 ] |
773 |
|
* </pre> |
774 |
|
* |
775 |
|
* @param tx the x translation distance |
776 |
|
* @param ty the y translation distance |
777 |
|
*/ |
778 |
|
public void setToTranslation(double tx, double ty) |
779 |
{ |
{ |
780 |
m00 = m11 = 1; |
m00 = m11 = 1; |
781 |
m01 = m10 = 0; |
m01 = m10 = 0; |
782 |
m02 = tx; |
m02 = tx; |
783 |
m12 = ty; |
m12 = ty; |
784 |
type = TYPE_TRANSLATION; |
type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; |
785 |
} |
} |
786 |
|
|
787 |
public void setToRotation (double theta) |
/** |
788 |
|
* Set this transform to a rotation. A positive angle (in radians) rotates |
789 |
|
* the positive x-axis to the positive y-axis: |
790 |
|
* <pre> |
791 |
|
* [ cos(theta) -sin(theta) 0 ] |
792 |
|
* [ sin(theta) cos(theta) 0 ] |
793 |
|
* [ 0 0 1 ] |
794 |
|
* </pre> |
795 |
|
* |
796 |
|
* @param theta the rotation angle |
797 |
|
*/ |
798 |
|
public void setToRotation(double theta) |
799 |
{ |
{ |
800 |
double c = Math.cos (theta); |
double c = Math.cos(theta); |
801 |
double s = Math.sin (theta); |
double s = Math.sin(theta); |
|
|
|
802 |
m00 = c; |
m00 = c; |
803 |
m01 = -s; |
m01 = -s; |
804 |
m02 = 0; |
m02 = 0; |
805 |
m10 = s; |
m10 = s; |
806 |
m11 = c; |
m11 = c; |
807 |
m12 = 0; |
m12 = 0; |
808 |
type = TYPE_GENERAL_ROTATION; |
type = (c == 1 ? TYPE_IDENTITY |
809 |
} |
: c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION |
810 |
|
: TYPE_GENERAL_ROTATION); |
811 |
public void setToRotation (double theta, double x, double y) |
} |
812 |
|
|
813 |
|
/** |
814 |
|
* Set this transform to a rotation about a point. A positive angle (in |
815 |
|
* radians) rotates the positive x-axis to the positive y-axis. This is the |
816 |
|
* same as calling: |
817 |
|
* <pre> |
818 |
|
* tx.setToTranslation(x, y); |
819 |
|
* tx.rotate(theta); |
820 |
|
* tx.translate(-x, -y); |
821 |
|
* </pre> |
822 |
|
* |
823 |
|
* <p>The resulting matrix is: |
824 |
|
* <pre> |
825 |
|
* [ cos(theta) -sin(theta) x-x*cos+y*sin ] |
826 |
|
* [ sin(theta) cos(theta) y-x*sin-y*cos ] |
827 |
|
* [ 0 0 1 ] |
828 |
|
* </pre> |
829 |
|
* |
830 |
|
* @param theta the rotation angle |
831 |
|
* @param x the x coordinate of the pivot point |
832 |
|
* @param y the y coordinate of the pivot point |
833 |
|
*/ |
834 |
|
public void setToRotation(double theta, double x, double y) |
835 |
{ |
{ |
836 |
double c = Math.cos (theta); |
double c = Math.cos(theta); |
837 |
double s = Math.sin (theta); |
double s = Math.sin(theta); |
|
|
|
838 |
m00 = c; |
m00 = c; |
839 |
m01 = -s; |
m01 = -s; |
840 |
m02 = x - x * c + y * s; |
m02 = x - x * c + y * s; |
841 |
m10 = s; |
m10 = s; |
842 |
m11 = c; |
m11 = c; |
843 |
m12 = y - x * s - y * c; |
m12 = y - x * s - y * c; |
844 |
type = TYPE_GENERAL_TRANSFORM; |
updateType(); |
845 |
} |
} |
846 |
|
|
847 |
public void setToScale (double sx, double sy) |
/** |
848 |
|
* Set this transform to a scale: |
849 |
|
* <pre> |
850 |
|
* [ sx 0 0 ] |
851 |
|
* [ 0 sy 0 ] |
852 |
|
* [ 0 0 1 ] |
853 |
|
* </pre> |
854 |
|
* |
855 |
|
* @param sx the x scaling factor |
856 |
|
* @param sy the y scaling factor |
857 |
|
*/ |
858 |
|
public void setToScale(double sx, double sy) |
859 |
{ |
{ |
860 |
m00 = sx; |
m00 = sx; |
861 |
m01 = m02 = m10 = m12 = 0; |
m01 = m02 = m10 = m12 = 0; |
862 |
m11 = sy; |
m11 = sy; |
863 |
type = (sx == sy) ? TYPE_UNIFORM_SCALE : TYPE_GENERAL_SCALE; |
type = (sx != sy ? TYPE_GENERAL_SCALE |
864 |
|
: sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE); |
865 |
} |
} |
866 |
|
|
867 |
public void setToShear (double shx, double shy) |
/** |
868 |
|
* Set this transform to a shear (points are shifted in the x direction based |
869 |
|
* on a factor of their y coordinate, and in the y direction as a factor of |
870 |
|
* their x coordinate): |
871 |
|
* <pre> |
872 |
|
* [ 1 shx 0 ] |
873 |
|
* [ shy 1 0 ] |
874 |
|
* [ 0 0 1 ] |
875 |
|
* </pre> |
876 |
|
* |
877 |
|
* @param shx the x shearing factor |
878 |
|
* @param shy the y shearing factor |
879 |
|
*/ |
880 |
|
public void setToShear(double shx, double shy) |
881 |
{ |
{ |
882 |
m00 = m11 = 1; |
m00 = m11 = 1; |
883 |
m01 = shx; |
m01 = shx; |
884 |
m10 = shy; |
m10 = shy; |
885 |
m02 = m12 = 0; |
m02 = m12 = 0; |
886 |
type = TYPE_GENERAL_TRANSFORM; |
updateType(); |
887 |
} |
} |
888 |
|
|
889 |
public void setTransform (AffineTransform tx) |
/** |
890 |
|
* Set this transform to a copy of the given one. |
891 |
|
* |
892 |
|
* @param tx the transform to copy |
893 |
|
* @throws NullPointerException if tx is null |
894 |
|
*/ |
895 |
|
public void setTransform(AffineTransform tx) |
896 |
{ |
{ |
897 |
m00 = tx.m00; |
m00 = tx.m00; |
898 |
m01 = tx.m01; |
m01 = tx.m01; |
903 |
type = tx.type; |
type = tx.type; |
904 |
} |
} |
905 |
|
|
906 |
public void setTransform (double m00, double m10, double m01, |
/** |
907 |
double m11, double m02, double m12) |
* Set this transform to the given values: |
908 |
|
* <pre> |
909 |
|
* [ m00 m01 m02 ] |
910 |
|
* [ m10 m11 m12 ] |
911 |
|
* [ 0 0 1 ] |
912 |
|
* </pre> |
913 |
|
* |
914 |
|
* @param m00 the x scaling component |
915 |
|
* @param m10 the y shearing component |
916 |
|
* @param m01 the x shearing component |
917 |
|
* @param m11 the y scaling component |
918 |
|
* @param m02 the x translation component |
919 |
|
* @param m12 the y translation component |
920 |
|
*/ |
921 |
|
public void setTransform(double m00, double m10, double m01, |
922 |
|
double m11, double m02, double m12) |
923 |
{ |
{ |
924 |
this.m00 = m00; |
this.m00 = m00; |
925 |
this.m10 = m10; |
this.m10 = m10; |
927 |
this.m11 = m11; |
this.m11 = m11; |
928 |
this.m02 = m02; |
this.m02 = m02; |
929 |
this.m12 = m12; |
this.m12 = m12; |
930 |
this.type = 0; // FIXME |
updateType(); |
931 |
} |
} |
932 |
|
|
933 |
public void concatenate (AffineTransform tx) |
/** |
934 |
|
* Set this transform to the result of performing the original version of |
935 |
|
* this followed by tx. This is commonly used when chaining transformations |
936 |
|
* from one space to another. In matrix form: |
937 |
|
* <pre> |
938 |
|
* [ this ] = [ this ] x [ tx ] |
939 |
|
* </pre> |
940 |
|
* |
941 |
|
* @param tx the transform to concatenate |
942 |
|
* @throws NullPointerException if tx is null |
943 |
|
* @see #preConcatenate(AffineTransform) |
944 |
|
*/ |
945 |
|
public void concatenate(AffineTransform tx) |
946 |
{ |
{ |
947 |
double n00 = m00 * tx.m00 + m01 * tx.m10; |
double n00 = m00 * tx.m00 + m01 * tx.m10; |
948 |
double n01 = m00 * tx.m01 + m01 * tx.m11; |
double n01 = m00 * tx.m01 + m01 * tx.m11; |
950 |
double n10 = m10 * tx.m00 + m11 * tx.m10; |
double n10 = m10 * tx.m00 + m11 * tx.m10; |
951 |
double n11 = m10 * tx.m01 + m11 * tx.m11; |
double n11 = m10 * tx.m01 + m11 * tx.m11; |
952 |
double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; |
double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; |
|
|
|
953 |
m00 = n00; |
m00 = n00; |
954 |
m01 = n01; |
m01 = n01; |
955 |
m02 = n02; |
m02 = n02; |
956 |
m10 = n10; |
m10 = n10; |
957 |
m11 = n11; |
m11 = n11; |
958 |
m12 = n12; |
m12 = n12; |
959 |
|
updateType(); |
960 |
} |
} |
961 |
|
|
962 |
public void preConcatenate (AffineTransform tx) |
/** |
963 |
|
* Set this transform to the result of performing tx followed by the |
964 |
|
* original version of this. This is less common than normal concatenation, |
965 |
|
* but can still be used to chain transformations from one space to another. |
966 |
|
* In matrix form: |
967 |
|
* <pre> |
968 |
|
* [ this ] = [ tx ] x [ this ] |
969 |
|
* </pre> |
970 |
|
* |
971 |
|
* @param tx the transform to concatenate |
972 |
|
* @throws NullPointerException if tx is null |
973 |
|
* @see #concatenate(AffineTransform) |
974 |
|
*/ |
975 |
|
public void preConcatenate(AffineTransform tx) |
976 |
{ |
{ |
977 |
double n00 = tx.m00 * m00 + tx.m01 * m10; |
double n00 = tx.m00 * m00 + tx.m01 * m10; |
978 |
double n01 = tx.m00 * m01 + tx.m01 * m11; |
double n01 = tx.m00 * m01 + tx.m01 * m11; |
980 |
double n10 = tx.m10 * m00 + tx.m11 * m10; |
double n10 = tx.m10 * m00 + tx.m11 * m10; |
981 |
double n11 = tx.m10 * m01 + tx.m11 * m11; |
double n11 = tx.m10 * m01 + tx.m11 * m11; |
982 |
double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; |
double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; |
|
|
|
983 |
m00 = n00; |
m00 = n00; |
984 |
m01 = n01; |
m01 = n01; |
985 |
m02 = n02; |
m02 = n02; |
986 |
m10 = n10; |
m10 = n10; |
987 |
m11 = n11; |
m11 = n11; |
988 |
m12 = n12; |
m12 = n12; |
989 |
|
updateType(); |
990 |
} |
} |
991 |
|
|
992 |
public AffineTransform createInverse () |
/** |
993 |
|
* Returns a transform, which if concatenated to this one, will result in |
994 |
|
* the identity transform. This is useful for undoing transformations, but |
995 |
|
* is only possible if the original transform has an inverse (ie. does not |
996 |
|
* map multiple points to the same line or point). A transform exists only |
997 |
|
* if getDeterminant() has a non-zero value. |
998 |
|
* |
999 |
|
* @return a new inverse transform |
1000 |
|
* @throws NoninvertibleTransformException if inversion is not possible |
1001 |
|
* @see #getDeterminant() |
1002 |
|
*/ |
1003 |
|
public AffineTransform createInverse() |
1004 |
throws NoninvertibleTransformException |
throws NoninvertibleTransformException |
1005 |
{ |
{ |
1006 |
double det = getDeterminant (); |
double det = getDeterminant(); |
1007 |
if (det == 0) |
if (det == 0) |
1008 |
throw new NoninvertibleTransformException ("can't invert transform"); |
throw new NoninvertibleTransformException("can't invert transform"); |
1009 |
|
return new AffineTransform(m11 / det, -m10 / det, m01 / det, -m00 / det, |
1010 |
double i00 = m11 / det; |
-m02, -m12); |
1011 |
double i01 = -m10 / det; |
} |
1012 |
double i02 = 0; |
|
1013 |
double i10 = m01 / det; |
/** |
1014 |
double i11 = -m00 / det; |
* Perform this transformation on the given source point, and store the |
1015 |
double i12 = 0; |
* result in the destination (creating it if necessary). It is safe for |
1016 |
|
* src and dst to be the same. |
1017 |
return new AffineTransform (i00, i01, i02, |
* |
1018 |
i10, i11, i12); |
* @param src the source point |
1019 |
} |
* @param dst the destination, or null |
1020 |
|
* @return the transformation of src, in dst if it was non-null |
1021 |
public Point2D transform (Point2D src, Point2D dst) |
* @throws NullPointerException if src is null |
1022 |
|
*/ |
1023 |
|
public Point2D transform(Point2D src, Point2D dst) |
1024 |
{ |
{ |
1025 |
if (dst == null) |
if (dst == null) |
1026 |
dst = new Point2D.Double (); |
dst = new Point2D.Double(); |
1027 |
|
double x = src.getX(); |
1028 |
// We compute and set separately to correctly overwrite if |
double y = src.getY(); |
|
// src==dst. |
|
|
double x = src.getX (); |
|
|
double y = src.getY (); |
|
1029 |
double nx = m00 * x + m01 * y + m02; |
double nx = m00 * x + m01 * y + m02; |
1030 |
double ny = m10 * x + m11 * y + m12; |
double ny = m10 * x + m11 * y + m12; |
1031 |
|
dst.setLocation(nx, ny); |
|
dst.setLocation (nx, ny); |
|
|
|
|
1032 |
return dst; |
return dst; |
1033 |
} |
} |
1034 |
|
|
1035 |
public void transform (Point2D[] src, int srcOff, |
/** |
1036 |
Point2D[] dst, int dstOff, |
* Perform this transformation on an array of points, storing the results |
1037 |
int num) |
* in another (possibly same) array. This will not create a destination |
1038 |
|
* array, but will create points for the null entries of the destination. |
1039 |
|
* The transformation is done sequentially. While having a single source |
1040 |
|
* and destination point be the same is safe, you should be aware that |
1041 |
|
* duplicate references to the same point in the source, and having the |
1042 |
|
* source overlap the destination, may result in your source points changing |
1043 |
|
* from a previous transform before it is their turn to be evaluated. |
1044 |
|
* |
1045 |
|
* @param src the array of source points |
1046 |
|
* @param srcOff the starting offset into src |
1047 |
|
* @param dst the array of destination points (may have null entries) |
1048 |
|
* @param dstOff the starting offset into dst |
1049 |
|
* @param num the number of points to transform |
1050 |
|
* @throws NullPointerException if src or dst is null, or src has null |
1051 |
|
* entries |
1052 |
|
* @throws ArrayIndexOutOfBoundsException if array bounds are exceeded |
1053 |
|
* @throws ArrayStoreException if new points are incompatible with dst |
1054 |
|
*/ |
1055 |
|
public void transform(Point2D[] src, int srcOff, |
1056 |
|
Point2D[] dst, int dstOff, int num) |
1057 |
|
{ |
1058 |
|
while (--num >= 0) |
1059 |
|
dst[dstOff] = transform(src[srcOff++], dst[dstOff++]); |
1060 |
|
} |
1061 |
|
|
1062 |
|
/** |
1063 |
|
* Perform this transformation on an array of points, in (x,y) pairs, |
1064 |
|
* storing the results in another (possibly same) array. This will not |
1065 |
|
* create a destination array. All sources are copied before the |
1066 |
|
* transformation, so that no result will overwrite a point that has not yet |
1067 |
|
* been evaluated. |
1068 |
|
* |
1069 |
|
* @param src the array of source points |
1070 |
|
* @param srcOff the starting offset into src |
1071 |
|
* @param dst the array of destination points |
1072 |
|
* @param dstOff the starting offset into dst |
1073 |
|
* @param num the number of points to transform |
1074 |
|
* @throws NullPointerException if src or dst is null |
1075 |
|
* @throws ArrayIndexOutOfBoundsException if array bounds are exceeded |
1076 |
|
*/ |
1077 |
|
public void transform(float[] srcPts, int srcOff, |
1078 |
|
float[] dstPts, int dstOff, int num) |
1079 |
{ |
{ |
1080 |
while (num-- > 0) |
if (srcPts == dstPts && dstOff > srcOff |
1081 |
|
&& num > 1 && srcOff + 2 * num > dstOff) |
1082 |
{ |
{ |
1083 |
dst[dstOff] = transform (src[srcOff], dst[dstOff]); |
float[] f = new float[2 * num]; |
1084 |
++srcOff; |
System.arraycopy(srcPts, srcOff, f, 0, 2 * num); |
1085 |
++dstOff; |
srcPts = f; |
1086 |
} |
} |
1087 |
} |
while (--num >= 0) |
|
|
|
|
public void transform (float[] srcPts, int srcOff, |
|
|
float[] dstPts, int dstOff, |
|
|
int num) |
|
|
{ |
|
|
while (num-- > 0) |
|
1088 |
{ |
{ |
1089 |
float x = srcPts[srcOff]; |
float x = srcPts[srcOff++]; |
1090 |
float y = srcPts[srcOff + 1]; |
float y = srcPts[srcOff++]; |
1091 |
srcOff += 2; |
dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); |
1092 |
float nx = (float) (m00 * x + m01 * y + m02); |
dstPts[dstOff++] = (float) (m10 * x + m10 * y + m12); |
|
float ny = (float) (m10 * x + m10 * y + m12); |
|
|
dstPts[dstOff] = nx; |
|
|
dstPts[dstOff + 1] = ny; |
|
|
dstOff += 2; |
|
1093 |
} |
} |
1094 |
} |
} |
1095 |
|
|
1096 |
public void transform (double[] srcPts, int srcOff, |
/** |
1097 |
double[] dstPts, int dstOff, |
* Perform this transformation on an array of points, in (x,y) pairs, |
1098 |
int num) |
* storing the results in another (possibly same) array. This will not |
1099 |
|
* create a destination array. All sources are copied before the |
1100 |
|
* transformation, so that no result will overwrite a point that has not yet |
1101 |
|
* been evaluated. |
1102 |
|
* |
1103 |
|
* @param src the array of source points |
1104 |
|
* @param srcOff the starting offset into src |
1105 |
|
* @param dst the array of destination points |
1106 |
|
* @param dstOff the starting offset into dst |
1107 |
|
* @param num the number of points to transform |
1108 |
|
* @throws NullPointerException if src or dst is null |
1109 |
|
* @throws ArrayIndexOutOfBoundsException if array bounds are exceeded |
1110 |
|
*/ |
1111 |
|
public void transform(double[] srcPts, int srcOff, |
1112 |
|
double[] dstPts, int dstOff, int num) |
1113 |
{ |
{ |
1114 |
while (num-- > 0) |
if (srcPts == dstPts && dstOff > srcOff |
1115 |
|
&& num > 1 && srcOff + 2 * num > dstOff) |
1116 |
|
{ |
1117 |
|
double[] d = new double[2 * num]; |
1118 |
|
System.arraycopy(srcPts, srcOff, d, 0, 2 * num); |
1119 |
|
srcPts = d; |
1120 |
|
} |
1121 |
|
while (--num >= 0) |
1122 |
{ |
{ |
1123 |
double x = srcPts[srcOff]; |
double x = srcPts[srcOff++]; |
1124 |
double y = srcPts[srcOff + 1]; |
double y = srcPts[srcOff++]; |
1125 |
srcOff += 2; |
dstPts[dstOff++] = m00 * x + m01 * y + m02; |
1126 |
double nx = m00 * x + m01 * y + m02; |
dstPts[dstOff++] = m10 * x + m10 * y + m12; |
|
double ny = m10 * x + m10 * y + m12; |
|
|
dstPts[dstOff] = nx; |
|
|
dstPts[dstOff + 1] = ny; |
|
|
dstOff += 2; |
|
1127 |
} |
} |
1128 |
} |
} |
1129 |
|
|
1130 |
public void transform (float[] srcPts, int srcOff, |
/** |
1131 |
double[] dstPts, int dstOff, |
* Perform this transformation on an array of points, in (x,y) pairs, |
1132 |
int num) |
* storing the results in another array. This will not create a destination |
1133 |
|
* array. |
1134 |
|
* |
1135 |
|
* @param src the array of source points |
1136 |
|
* @param srcOff the starting offset into src |
1137 |
|
* @param dst the array of destination points |
1138 |
|
* @param dstOff the starting offset into dst |
1139 |
|
* @param num the number of points to transform |
1140 |
|
* @throws NullPointerException if src or dst is null |
1141 |
|
* @throws ArrayIndexOutOfBoundsException if array bounds are exceeded |
1142 |
|
*/ |
1143 |
|
public void transform(float[] srcPts, int srcOff, |
1144 |
|
double[] dstPts, int dstOff, int num) |
1145 |
{ |
{ |
1146 |
while (num-- > 0) |
while (--num >= 0) |
1147 |
{ |
{ |
1148 |
float x = srcPts[srcOff]; |
float x = srcPts[srcOff++]; |
1149 |
float y = srcPts[srcOff + 1]; |
float y = srcPts[srcOff++]; |
1150 |
srcOff += 2; |
dstPts[dstOff++] = m00 * x + m01 * y + m02; |
1151 |
double nx = m00 * x + m01 * y + m02; |
dstPts[dstOff++] = m10 * x + m10 * y + m12; |
|
double ny = m10 * x + m10 * y + m12; |
|
|
dstPts[dstOff] = nx; |
|
|
dstPts[dstOff + 1] = ny; |
|
|
dstOff += 2; |
|
1152 |
} |
} |
1153 |
} |
} |
1154 |
|
|
1155 |
public void transform (double[] srcPts, int srcOff, |
/** |
1156 |
float[] dstPts, int dstOff, |
* Perform this transformation on an array of points, in (x,y) pairs, |
1157 |
int num) |
* storing the results in another array. This will not create a destination |
1158 |
|
* array. |
1159 |
|
* |
1160 |
|
* @param src the array of source points |
1161 |
|
* @param srcOff the starting offset into src |
1162 |
|
* @param dst the array of destination points |
1163 |
|
* @param dstOff the starting offset into dst |
1164 |
|
* @param num the number of points to transform |
1165 |
|
* @throws NullPointerException if src or dst is null |
1166 |
|
* @throws ArrayIndexOutOfBoundsException if array bounds are exceeded |
1167 |
|
*/ |
1168 |
|
public void transform(double[] srcPts, int srcOff, |
1169 |
|
float[] dstPts, int dstOff, int num) |
1170 |
{ |
{ |
1171 |
while (num-- > 0) |
while (--num >= 0) |
1172 |
{ |
{ |
1173 |
double x = srcPts[srcOff]; |
double x = srcPts[srcOff++]; |
1174 |
double y = srcPts[srcOff + 1]; |
double y = srcPts[srcOff++]; |
1175 |
srcOff += 2; |
dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); |
1176 |
float nx = (float) (m00 * x + m01 * y + m02); |
dstPts[dstOff++] = (float) (m10 * x + m10 * y + m12); |
|
float ny = (float) (m10 * x + m10 * y + m12); |
|
|
dstPts[dstOff] = nx; |
|
|
dstPts[dstOff + 1] = ny; |
|
|
dstOff += 2; |
|
1177 |
} |
} |
1178 |
} |
} |
1179 |
|
|
1180 |
public Point2D inverseTransform (Point2D src, Point2D dst) |
/** |
1181 |
|
* Perform the inverse of this transformation on the given source point, |
1182 |
|
* and store the result in the destination (creating it if necessary). It |
1183 |
|
* is safe for src and dst to be the same. |
1184 |
|
* |
1185 |
|
* @param src the source point |
1186 |
|
* @param dst the destination, or null |
1187 |
|
* @return the inverse transformation of src, in dst if it was non-null |
1188 |
|
* @throws NullPointerException if src is null |
1189 |
|
* @throws NoninvertibleTransformException if the inverse does not exist |
1190 |
|
* @see #getDeterminant() |
1191 |
|
*/ |
1192 |
|
public Point2D inverseTransform(Point2D src, Point2D dst) |
1193 |
throws NoninvertibleTransformException |
throws NoninvertibleTransformException |
1194 |
{ |
{ |
1195 |
double det = getDeterminant (); |
double det = getDeterminant(); |
1196 |
if (det == 0) |
if (det == 0) |
1197 |
throw new NoninvertibleTransformException ("couldn't invert transform"); |
throw new NoninvertibleTransformException("couldn't invert transform"); |
|
|
|
1198 |
if (dst == null) |
if (dst == null) |
1199 |
dst = new Point2D.Double (); |
dst = new Point2D.Double(); |
1200 |
double x = src.getX (); |
double x = src.getX(); |
1201 |
double y = src.getY (); |
double y = src.getY(); |
1202 |
double nx = (m11 * x + - m10 * y) / det; |
double nx = (m11 * x + -m10 * y) / det - m02; |
1203 |
double ny = (m01 * x + - m00 * y) / det; |
double ny = (m01 * x + -m00 * y) / det - m12; |
1204 |
dst.setLocation (nx, ny); |
dst.setLocation(nx, ny); |
1205 |
return dst; |
return dst; |
1206 |
} |
} |
1207 |
|
|
1208 |
public void inverseTransform (double[] srcPts, int srcOff, |
/** |
1209 |
double[] dstPts, int dstOff, |
* Perform the inverse of this transformation on an array of points, in |
1210 |
int num) |
* (x,y) pairs, storing the results in another (possibly same) array. This |
1211 |
|
* will not create a destination array. All sources are copied before the |
1212 |
|
* transformation, so that no result will overwrite a point that has not yet |
1213 |
|
* been evaluated. |
1214 |
|
* |
1215 |
|
* @param src the array of source points |
1216 |
|
* @param srcOff the starting offset into src |
1217 |
|
* @param dst the array of destination points |
1218 |
|
* @param dstOff the starting offset into dst |
1219 |
|
* @param num the number of points to transform |
1220 |
|
* @throws NullPointerException if src or dst is null |
1221 |
|
* @throws ArrayIndexOutOfBoundsException if array bounds are exceeded |
1222 |
|
* @throws NoninvertibleTransformException if the inverse does not exist |
1223 |
|
* @see #getDeterminant() |
1224 |
|
*/ |
1225 |
|
public void inverseTransform(double[] srcPts, int srcOff, |
1226 |
|
double[] dstPts, int dstOff, int num) |
1227 |
throws NoninvertibleTransformException |
throws NoninvertibleTransformException |
1228 |
{ |
{ |
1229 |
double det = getDeterminant (); |
double det = getDeterminant(); |
1230 |
if (det == 0) |
if (det == 0) |
1231 |
throw new NoninvertibleTransformException ("couldn't invert transform"); |
throw new NoninvertibleTransformException("couldn't invert transform"); |
1232 |
|
if (srcPts == dstPts && dstOff > srcOff |
1233 |
while (num-- > 0) |
&& num > 1 && srcOff + 2 * num > dstOff) |
1234 |
{ |
{ |
1235 |
double x = srcPts[srcOff]; |
double[] d = new double[2 * num]; |
1236 |
double y = srcPts[srcOff + 1]; |
System.arraycopy(srcPts, srcOff, d, 0, 2 * num); |
1237 |
double nx = (m11 * x + - m10 * y) / det; |
srcPts = d; |
1238 |
double ny = (m01 * x + - m00 * y) / det; |
} |
1239 |
dstPts[dstOff] = nx; |
while (--num >= 0) |
1240 |
dstPts[dstOff + 1] = ny; |
{ |
1241 |
dstOff += 2; |
double x = srcPts[srcOff++]; |
1242 |
srcOff += 2; |
double y = srcPts[srcOff++]; |
1243 |
|
dstPts[dstOff++] = (m11 * x + -m10 * y) / det - m02; |
1244 |
|
dstPts[dstOff++] = (m01 * x + -m00 * y) / det - m12; |
1245 |
} |
} |
1246 |
} |
} |
1247 |
|
|
1248 |
public Point2D deltaTransform (Point2D src, Point2D dst) |
/** |
1249 |
|
* Perform this transformation, less any translation, on the given source |
1250 |
|
* point, and store the result in the destination (creating it if |
1251 |
|
* necessary). It is safe for src and dst to be the same. The reduced |
1252 |
|
* transform is equivalent to: |
1253 |
|
* <pre> |
1254 |
|
* [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] |
1255 |
|
* [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] |
1256 |
|
* </pre> |
1257 |
|
* |
1258 |
|
* @param src the source point |
1259 |
|
* @param dst the destination, or null |
1260 |
|
* @return the delta transformation of src, in dst if it was non-null |
1261 |
|
* @throws NullPointerException if src is null |
1262 |
|
*/ |
1263 |
|
public Point2D deltaTransform(Point2D src, Point2D dst) |
1264 |
{ |
{ |
1265 |
if (dst == null) |
if (dst == null) |
1266 |
dst = new Point2D.Double (); |
dst = new Point2D.Double(); |
1267 |
double x = src.getX (); |
double x = src.getX(); |
1268 |
double y = src.getY (); |
double y = src.getY(); |
1269 |
double nx = m00 * x + m01 * y; |
double nx = m00 * x + m01 * y; |
1270 |
double ny = m10 * x + m11 * y; |
double ny = m10 * x + m11 * y; |
1271 |
dst.setLocation (nx, ny); |
dst.setLocation(nx, ny); |
1272 |
return dst; |
return dst; |
1273 |
} |
} |
1274 |
|
|
1275 |
public void deltaTransform (double[] srcPts, int srcOff, |
/** |
1276 |
double[] dstPts, int dstOff, |
* Perform this transformation, less any translation, on an array of points, |
1277 |
int num) |
* in (x,y) pairs, storing the results in another (possibly same) array. |
1278 |
|
* This will not create a destination array. All sources are copied before |
1279 |
|
* the transformation, so that no result will overwrite a point that has |
1280 |
|
* not yet been evaluated. The reduced transform is equivalent to: |
1281 |
|
* <pre> |
1282 |
|
* [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] |
1283 |
|
* [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] |
1284 |
|
* </pre> |
1285 |
|
* |
1286 |
|
* @param src the array of source points |
1287 |
|
* @param srcOff the starting offset into src |
1288 |
|
* @param dst the array of destination points |
1289 |
|
* @param dstOff the starting offset into dst |
1290 |
|
* @param num the number of points to transform |
1291 |
|
* @throws NullPointerException if src or dst is null |
1292 |
|
* @throws ArrayIndexOutOfBoundsException if array bounds are exceeded |
1293 |
|
*/ |
1294 |
|
public void deltaTransform(double[] srcPts, int srcOff, |
1295 |
|
double[] dstPts, int dstOff, |
1296 |
|
int num) |
1297 |
{ |
{ |
1298 |
while (num-- > 0) |
if (srcPts == dstPts && dstOff > srcOff |
1299 |
|
&& num > 1 && srcOff + 2 * num > dstOff) |
1300 |
{ |
{ |
1301 |
double x = srcPts[srcOff]; |
double[] d = new double[2 * num]; |
1302 |
double y = srcPts[srcOff + 1]; |
System.arraycopy(srcPts, srcOff, d, 0, 2 * num); |
1303 |
double nx = m00 * x + m01 * y; |
srcPts = d; |
1304 |
double ny = m10 * x + m11 * y; |
} |
1305 |
dstPts[dstOff] = nx; |
while (--num >= 0) |
1306 |
dstPts[dstOff + 1] = ny; |
{ |
1307 |
dstOff += 2; |
double x = srcPts[srcOff++]; |
1308 |
srcOff += 2; |
double y = srcPts[srcOff++]; |
1309 |
|
dstPts[dstOff++] = m00 * x + m01 * y; |
1310 |
|
dstPts[dstOff++] = m10 * x + m11 * y; |
1311 |
} |
} |
1312 |
} |
} |
1313 |
|
|
1314 |
public Shape createTransformedShape (Shape pSrc) |
/** |
1315 |
{ |
* Return a new Shape, based on the given one, where the path of the shape |
1316 |
// FIXME |
* has been transformed by this transform. Notice that this uses GeneralPath, |
1317 |
return null; |
* which only stores points in float precision. |
1318 |
} |
* |
1319 |
|
* @param src the shape source to transform |
1320 |
public String toString () |
* @return the shape, transformed by this |
1321 |
{ |
* @throws NullPointerException if src is null |
1322 |
// FIXME |
* @see GeneralPath#transform(AffineTransform) |
1323 |
return null; |
*/ |
1324 |
} |
public Shape createTransformedShape(Shape src) |
1325 |
|
{ |
1326 |
public boolean isIdentity () |
GeneralPath p = new GeneralPath(src); |
1327 |
|
p.transform(this); |
1328 |
|
return p; |
1329 |
|
} |
1330 |
|
|
1331 |
|
/** |
1332 |
|
* Returns a string representation of the transform, in the format: |
1333 |
|
* <code>"AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" |
1334 |
|
* + m10 + ", " + m11 + ", " + m12 + "]]"</code>. |
1335 |
|
* |
1336 |
|
* @return the string representation |
1337 |
|
*/ |
1338 |
|
public String toString() |
1339 |
|
{ |
1340 |
|
return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" |
1341 |
|
+ m10 + ", " + m11 + ", " + m12 + "]]"; |
1342 |
|
} |
1343 |
|
|
1344 |
|
/** |
1345 |
|
* Tests if this transformation is the identity: |
1346 |
|
* <pre> |
1347 |
|
* [ 1 0 0 ] |
1348 |
|
* [ 0 1 0 ] |
1349 |
|
* [ 0 0 1 ] |
1350 |
|
* </pre> |
1351 |
|
* |
1352 |
|
* @return true if this is the identity transform |
1353 |
|
*/ |
1354 |
|
public boolean isIdentity() |
1355 |
{ |
{ |
1356 |
|
// Rather than rely on type, check explicitly. |
1357 |
return (m00 == 1 && m01 == 0 && m02 == 0 |
return (m00 == 1 && m01 == 0 && m02 == 0 |
1358 |
&& m10 == 0 && m11 == 1 && m12 == 0); |
&& m10 == 0 && m11 == 1 && m12 == 0); |
|
} |
|
|
|
|
|
public Object clone () |
|
|
{ |
|
|
return new AffineTransform (this); |
|
1359 |
} |
} |
1360 |
|
|
1361 |
public int hashCode () |
/** |
1362 |
|
* Create a new transform of the same run-time type, with the same |
1363 |
|
* transforming properties as this one. |
1364 |
|
* |
1365 |
|
* @return the clone |
1366 |
|
*/ |
1367 |
|
public Object clone() |
1368 |
{ |
{ |
1369 |
// FIXME |
try |
1370 |
return 23; |
{ |
1371 |
|
return super.clone(); |
1372 |
|
} |
1373 |
|
catch (CloneNotSupportedException e) |
1374 |
|
{ |
1375 |
|
throw (Error) new InternalError().initCause(e); // Impossible |
1376 |
|
} |
1377 |
} |
} |
1378 |
|
|
1379 |
public boolean equals (Object obj) |
/** |
1380 |
|
* Return the hashcode for this transformation. The formula is not |
1381 |
|
* documented, but appears to be the same as: |
1382 |
|
* <pre> |
1383 |
|
* long l = Double.doubleToLongBits(getScaleX()); |
1384 |
|
* l = l * 31 + Double.doubleToLongBits(getShearY()); |
1385 |
|
* l = l * 31 + Double.doubleToLongBits(getShearX()); |
1386 |
|
* l = l * 31 + Double.doubleToLongBits(getScaleY()); |
1387 |
|
* l = l * 31 + Double.doubleToLongBits(getTranslateX()); |
1388 |
|
* l = l * 31 + Double.doubleToLongBits(getTranslateY()); |
1389 |
|
* return (int) ((l >> 32) ^ l); |
1390 |
|
* </pre> |
1391 |
|
* |
1392 |
|
* @return the hashcode |
1393 |
|
*/ |
1394 |
|
public int hashCode() |
1395 |
|
{ |
1396 |
|
long l = Double.doubleToLongBits(m00); |
1397 |
|
l = l * 31 + Double.doubleToLongBits(m10); |
1398 |
|
l = l * 31 + Double.doubleToLongBits(m01); |
1399 |
|
l = l * 31 + Double.doubleToLongBits(m11); |
1400 |
|
l = l * 31 + Double.doubleToLongBits(m02); |
1401 |
|
l = l * 31 + Double.doubleToLongBits(m12); |
1402 |
|
return (int) ((l >> 32) ^ l); |
1403 |
|
} |
1404 |
|
|
1405 |
|
/** |
1406 |
|
* Compares two transforms for equality. This returns true if they have the |
1407 |
|
* same matrix values. |
1408 |
|
* |
1409 |
|
* @param o the transform to compare |
1410 |
|
* @return true if it is equal |
1411 |
|
*/ |
1412 |
|
public boolean equals(Object obj) |
1413 |
{ |
{ |
1414 |
if (! (obj instanceof AffineTransform)) |
if (! (obj instanceof AffineTransform)) |
1415 |
return false; |
return false; |
1416 |
AffineTransform t = (AffineTransform) obj; |
AffineTransform t = (AffineTransform) obj; |
1417 |
return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02 |
return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02 |
1418 |
&& m10 == t.m10 && m11 == t.m11 && m12 == t.m12); |
&& m10 == t.m10 && m11 == t.m11 && m12 == t.m12); |
1419 |
} |
} |
1420 |
|
|
1421 |
private double m00, m01, m02; |
/** |
1422 |
private double m10, m11, m12; |
* Helper to decode the type from the matrix. This is not guaranteed |
1423 |
private int type; |
* to find the optimal type, but at least it will be valid. |
1424 |
} |
*/ |
1425 |
|
private void updateType() |
1426 |
|
{ |
1427 |
|
double det = getDeterminant(); |
1428 |
|
if (det == 0) |
1429 |
|
{ |
1430 |
|
type = TYPE_GENERAL_TRANSFORM; |
1431 |
|
return; |
1432 |
|
} |
1433 |
|
// Scale (includes rotation by PI) or translation. |
1434 |
|
if (m01 == 0 && m10 == 0) |
1435 |
|
{ |
1436 |
|
if (m00 == m11) |
1437 |
|
type = m00 == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE; |
1438 |
|
else |
1439 |
|
type = TYPE_GENERAL_SCALE; |
1440 |
|
if (m02 != 0 || m12 != 0) |
1441 |
|
type |= TYPE_TRANSLATION; |
1442 |
|
} |
1443 |
|
// Rotation. |
1444 |
|
else if (m00 == m11 && m01 == -m10) |
1445 |
|
{ |
1446 |
|
type = m00 == 0 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION; |
1447 |
|
if (det != 1) |
1448 |
|
type |= TYPE_UNIFORM_SCALE; |
1449 |
|
if (m02 != 0 || m12 != 0) |
1450 |
|
type |= TYPE_TRANSLATION; |
1451 |
|
} |
1452 |
|
else |
1453 |
|
type = TYPE_GENERAL_TRANSFORM; |
1454 |
|
} |
1455 |
|
|
1456 |
|
/** |
1457 |
|
* Reads a transform from an object stream. |
1458 |
|
* |
1459 |
|
* @param s the stream to read from |
1460 |
|
* @throws ClassNotFoundException if there is a problem deserializing |
1461 |
|
* @throws IOException if there is a problem deserializing |
1462 |
|
*/ |
1463 |
|
private void readObject(ObjectInputStream s) |
1464 |
|
throws ClassNotFoundException, IOException |
1465 |
|
{ |
1466 |
|
s.defaultReadObject(); |
1467 |
|
updateType(); |
1468 |
|
} |
1469 |
|
} // class AffineTransform |