1. /*
  2. * @(#)AffineTransform.java 1.62 00/02/02
  3. *
  4. * Copyright 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.awt.geom;
  11. import java.awt.Shape;
  12. /**
  13. * The <code>AffineTransform</code> class represents a 2D affine transform
  14. * that performs a linear mapping from 2D coordinates to other 2D
  15. * coordinates that preserves the "straightness" and
  16. * "parallelness" of lines. Affine transformations can be constructed
  17. * using sequences of translations, scales, flips, rotations, and shears.
  18. * <p>
  19. * Such a coordinate transformation can be represented by a 3 row by
  20. * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix
  21. * transforms source coordinates <code>(x, y)</code> into
  22. * destination coordinates <code>(x', y')</code> by considering
  23. * them to be a column vector and multiplying the coordinate vector
  24. * by the matrix according to the following process:
  25. * <pre>
  26. * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
  27. * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]
  28. * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
  29. * </pre>
  30. *
  31. * @version 1.62, 02/02/00
  32. * @author Jim Graham
  33. */
  34. public class AffineTransform implements Cloneable, java.io.Serializable {
  35. /**
  36. * This constant indicates that the transform defined by this object
  37. * is an identity transform.
  38. * An identity transform is one in which the output coordinates are
  39. * always the same as the input coordinates.
  40. * If this transform is anything other than the identity transform,
  41. * the type will either be the constant GENERAL_TRANSFORM or a
  42. * combination of the appropriate flag bits for the various coordinate
  43. * conversions that this transform performs.
  44. * @see #TYPE_TRANSLATION
  45. * @see #TYPE_UNIFORM_SCALE
  46. * @see #TYPE_GENERAL_SCALE
  47. * @see #TYPE_FLIP
  48. * @see #TYPE_QUADRANT_ROTATION
  49. * @see #TYPE_GENERAL_ROTATION
  50. * @see #TYPE_GENERAL_TRANSFORM
  51. * @see #getType
  52. */
  53. public static final int TYPE_IDENTITY = 0;
  54. /**
  55. * This flag bit indicates that the transform defined by this object
  56. * performs a translation in addition to the conversions indicated
  57. * by other flag bits.
  58. * A translation moves the coordinates by a constant amount in x
  59. * and y without changing the length or angle of vectors.
  60. * @see #TYPE_IDENTITY
  61. * @see #TYPE_UNIFORM_SCALE
  62. * @see #TYPE_GENERAL_SCALE
  63. * @see #TYPE_FLIP
  64. * @see #TYPE_QUADRANT_ROTATION
  65. * @see #TYPE_GENERAL_ROTATION
  66. * @see #TYPE_GENERAL_TRANSFORM
  67. * @see #getType
  68. */
  69. public static final int TYPE_TRANSLATION = 1;
  70. /**
  71. * This flag bit indicates that the transform defined by this object
  72. * performs a uniform scale in addition to the conversions indicated
  73. * by other flag bits.
  74. * A uniform scale multiplies the length of vectors by the same amount
  75. * in both the x and y directions without changing the angle between
  76. * vectors.
  77. * This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag.
  78. * @see #TYPE_IDENTITY
  79. * @see #TYPE_TRANSLATION
  80. * @see #TYPE_GENERAL_SCALE
  81. * @see #TYPE_FLIP
  82. * @see #TYPE_QUADRANT_ROTATION
  83. * @see #TYPE_GENERAL_ROTATION
  84. * @see #TYPE_GENERAL_TRANSFORM
  85. * @see #getType
  86. */
  87. public static final int TYPE_UNIFORM_SCALE = 2;
  88. /**
  89. * This flag bit indicates that the transform defined by this object
  90. * performs a general scale in addition to the conversions indicated
  91. * by other flag bits.
  92. * A general scale multiplies the length of vectors by different
  93. * amounts in the x and y directions without changing the angle
  94. * between perpendicular vectors.
  95. * This flag bit is mutually exclusive with the TYPE_UNIFORM_SCALE flag.
  96. * @see #TYPE_IDENTITY
  97. * @see #TYPE_TRANSLATION
  98. * @see #TYPE_UNIFORM_SCALE
  99. * @see #TYPE_FLIP
  100. * @see #TYPE_QUADRANT_ROTATION
  101. * @see #TYPE_GENERAL_ROTATION
  102. * @see #TYPE_GENERAL_TRANSFORM
  103. * @see #getType
  104. */
  105. public static final int TYPE_GENERAL_SCALE = 4;
  106. /**
  107. * This constant is a bit mask for any of the scale flag bits.
  108. * @see #TYPE_UNIFORM_SCALE
  109. * @see #TYPE_GENERAL_SCALE
  110. */
  111. public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE |
  112. TYPE_GENERAL_SCALE);
  113. /**
  114. * This flag bit indicates that the transform defined by this object
  115. * performs a mirror image flip about some axis which changes the
  116. * normally right handed coordinate system into a left handed
  117. * system in addition to the conversions indicated by other flag bits.
  118. * A right handed coordinate system is one where the positive X
  119. * axis rotates counterclockwise to overlay the positive Y axis
  120. * similar to the direction that the fingers on your right hand
  121. * curl when you stare end on at your thumb.
  122. * A left handed coordinate system is one where the positive X
  123. * axis rotates clockwise to overlay the positive Y axis similar
  124. * to the direction that the fingers on your left hand curl.
  125. * There is no mathematical way to determine the angle of the
  126. * original flipping or mirroring transformation since all angles
  127. * of flip are identical given an appropriate adjusting rotation.
  128. * @see #TYPE_IDENTITY
  129. * @see #TYPE_TRANSLATION
  130. * @see #TYPE_UNIFORM_SCALE
  131. * @see #TYPE_GENERAL_SCALE
  132. * @see #TYPE_QUADRANT_ROTATION
  133. * @see #TYPE_GENERAL_ROTATION
  134. * @see #TYPE_GENERAL_TRANSFORM
  135. * @see #getType
  136. */
  137. public static final int TYPE_FLIP = 64;
  138. /* NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public
  139. * circulation and the flag bits could no longer be conveniently
  140. * renumbered without introducing binary incompatibility in outside
  141. * code.
  142. */
  143. /**
  144. * This flag bit indicates that the transform defined by this object
  145. * performs a quadrant rotation by some multiple of 90 degrees in
  146. * addition to the conversions indicated by other flag bits.
  147. * A rotation changes the angles of vectors by the same amount
  148. * regardless of the original direction of the vector and without
  149. * changing the length of the vector.
  150. * This flag bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag.
  151. * @see #TYPE_IDENTITY
  152. * @see #TYPE_TRANSLATION
  153. * @see #TYPE_UNIFORM_SCALE
  154. * @see #TYPE_GENERAL_SCALE
  155. * @see #TYPE_FLIP
  156. * @see #TYPE_GENERAL_ROTATION
  157. * @see #TYPE_GENERAL_TRANSFORM
  158. * @see #getType
  159. */
  160. public static final int TYPE_QUADRANT_ROTATION = 8;
  161. /**
  162. * This flag bit indicates that the transform defined by this object
  163. * performs a rotation by an arbitrary angle in addition to the
  164. * conversions indicated by other flag bits.
  165. * A rotation changes the angles of vectors by the same amount
  166. * regardless of the original direction of the vector and without
  167. * changing the length of the vector.
  168. * This flag bit is mutually exclusive with the
  169. * TYPE_QUADRANT_ROTATION flag.
  170. * @see #TYPE_IDENTITY
  171. * @see #TYPE_TRANSLATION
  172. * @see #TYPE_UNIFORM_SCALE
  173. * @see #TYPE_GENERAL_SCALE
  174. * @see #TYPE_FLIP
  175. * @see #TYPE_QUADRANT_ROTATION
  176. * @see #TYPE_GENERAL_TRANSFORM
  177. * @see #getType
  178. */
  179. public static final int TYPE_GENERAL_ROTATION = 16;
  180. /**
  181. * This constant is a bit mask for any of the rotation flag bits.
  182. * @see #TYPE_QUADRANT_ROTATION
  183. * @see #TYPE_GENERAL_ROTATION
  184. */
  185. public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION |
  186. TYPE_GENERAL_ROTATION);
  187. /**
  188. * This constant indicates that the transform defined by this object
  189. * performs an arbitrary conversion of the input coordinates.
  190. * If this transform can be classified by any of the above constants,
  191. * the type will either be the constant TYPE_IDENTITY or a
  192. * combination of the appropriate flag bits for the various coordinate
  193. * conversions that this transform performs.
  194. * @see #TYPE_IDENTITY
  195. * @see #TYPE_TRANSLATION
  196. * @see #TYPE_UNIFORM_SCALE
  197. * @see #TYPE_GENERAL_SCALE
  198. * @see #TYPE_FLIP
  199. * @see #TYPE_QUADRANT_ROTATION
  200. * @see #TYPE_GENERAL_ROTATION
  201. * @see #getType
  202. */
  203. public static final int TYPE_GENERAL_TRANSFORM = 32;
  204. /**
  205. * This constant is used for the internal state variable to indicate
  206. * that no calculations need to be performed and that the source
  207. * coordinates only need to be copied to their destinations to
  208. * complete the transformation equation of this transform.
  209. * @see #APPLY_TRANSLATE
  210. * @see #APPLY_SCALE
  211. * @see #APPLY_SHEAR
  212. * @see #state
  213. */
  214. static final int APPLY_IDENTITY = 0;
  215. /**
  216. * This constant is used for the internal state variable to indicate
  217. * that the translation components of the matrix (m02 and m12) need
  218. * to be added to complete the transformation equation of this transform.
  219. * @see #APPLY_IDENTITY
  220. * @see #APPLY_SCALE
  221. * @see #APPLY_SHEAR
  222. * @see #state
  223. */
  224. static final int APPLY_TRANSLATE = 1;
  225. /**
  226. * This constant is used for the internal state variable to indicate
  227. * that the scaling components of the matrix (m00 and m11) need
  228. * to be factored in to complete the transformation equation of
  229. * this transform. If the APPLY_SHEAR bit is also set then it
  230. * indicates that the scaling components are not both 0.0. If the
  231. * APPLY_SHEAR bit is not also set then it indicates that the
  232. * scaling components are not both 1.0. If neither the APPLY_SHEAR
  233. * nor the APPLY_SCALE bits are set then the scaling components
  234. * are both 1.0, which means that the x and y components contribute
  235. * to the transformed coordinate, but they are not multiplied by
  236. * any scaling factor.
  237. * @see #APPLY_IDENTITY
  238. * @see #APPLY_TRANSLATE
  239. * @see #APPLY_SHEAR
  240. * @see #state
  241. */
  242. static final int APPLY_SCALE = 2;
  243. /**
  244. * This constant is used for the internal state variable to indicate
  245. * that the shearing components of the matrix (m01 and m10) need
  246. * to be factored in to complete the transformation equation of this
  247. * transform. The presence of this bit in the state variable changes
  248. * the interpretation of the APPLY_SCALE bit as indicated in its
  249. * documentation.
  250. * @see #APPLY_IDENTITY
  251. * @see #APPLY_TRANSLATE
  252. * @see #APPLY_SCALE
  253. * @see #state
  254. */
  255. static final int APPLY_SHEAR = 4;
  256. /*
  257. * For methods which combine together the state of two separate
  258. * transforms and dispatch based upon the combination, these constants
  259. * specify how far to shift one of the states so that the two states
  260. * are mutually non-interfering and provide constants for testing the
  261. * bits of the shifted (HI) state. The methods in this class use
  262. * the convention that the state of "this" transform is unshifted and
  263. * the state of the "other" or "argument" transform is shifted (HI).
  264. */
  265. private static final int HI_SHIFT = 3;
  266. private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT;
  267. private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT;
  268. private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT;
  269. private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT;
  270. /**
  271. * The X coordinate scaling element of the 3x3
  272. * affine transformation matrix.
  273. *
  274. * @serial
  275. */
  276. double m00;
  277. /**
  278. * The Y coordinate shearing element of the 3x3
  279. * affine transformation matrix.
  280. *
  281. * @serial
  282. */
  283. double m10;
  284. /**
  285. * The X coordinate shearing element of the 3x3
  286. * affine transformation matrix.
  287. *
  288. * @serial
  289. */
  290. double m01;
  291. /**
  292. * The Y coordinate scaling element of the 3x3
  293. * affine transformation matrix.
  294. *
  295. * @serial
  296. */
  297. double m11;
  298. /**
  299. * The X coordinate of the translation element of the
  300. * 3x3 affine transformation matrix.
  301. *
  302. * @serial
  303. */
  304. double m02;
  305. /**
  306. * The Y coordinate of the translation element of the
  307. * 3x3 affine transformation matrix.
  308. *
  309. * @serial
  310. */
  311. double m12;
  312. /**
  313. * This field keeps track of which components of the matrix need to
  314. * be applied when performing a transformation.
  315. * @see #APPLY_IDENTITY
  316. * @see #APPLY_TRANSLATE
  317. * @see #APPLY_SCALE
  318. * @see #APPLY_SHEAR
  319. */
  320. transient int state;
  321. private AffineTransform(double m00, double m10,
  322. double m01, double m11,
  323. double m02, double m12,
  324. int state) {
  325. this.m00 = m00;
  326. this.m10 = m10;
  327. this.m01 = m01;
  328. this.m11 = m11;
  329. this.m02 = m02;
  330. this.m12 = m12;
  331. this.state = state;
  332. }
  333. /**
  334. * Constructs a new <code>AffineTransform</code> representing the
  335. * Identity transformation.
  336. */
  337. public AffineTransform() {
  338. m00 = m11 = 1.0;
  339. // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */
  340. // state = APPLY_IDENTITY; /* Not needed. */
  341. }
  342. /**
  343. * Constructs a new <code>AffineTransform</code> that is a copy of
  344. * the specified <code>AffineTransform</code> object.
  345. * @param Tx the <code>AffineTransform</code> object to copy
  346. */
  347. public AffineTransform(AffineTransform Tx) {
  348. this.m00 = Tx.m00;
  349. this.m10 = Tx.m10;
  350. this.m01 = Tx.m01;
  351. this.m11 = Tx.m11;
  352. this.m02 = Tx.m02;
  353. this.m12 = Tx.m12;
  354. this.state = Tx.state;
  355. }
  356. /**
  357. * Constructs a new <code>AffineTransform</code> from 6 floating point
  358. * values representing the 6 specifiable entries of the 3x3
  359. * transformation matrix.
  360. * @param m00, m01, m02, m10, m11, m12 the
  361. * 6 floating point values that compose the 3x3 transformation matrix
  362. */
  363. public AffineTransform(float m00, float m10,
  364. float m01, float m11,
  365. float m02, float m12) {
  366. this.m00 = m00;
  367. this.m10 = m10;
  368. this.m01 = m01;
  369. this.m11 = m11;
  370. this.m02 = m02;
  371. this.m12 = m12;
  372. updateState();
  373. }
  374. /**
  375. * Constructs a new <code>AffineTransform</code> from an array of
  376. * floating point values representing either the 4 non-translation
  377. * enries or the 6 specifiable entries of the 3x3 transformation
  378. * matrix. The values are retrieved from the array as
  379. * { m00 m10 m01 m11 [m02 m12]}.
  380. * @param flatmatrix the float array containing the values to be set
  381. * in the new <code>AffineTransform</code> object. The length of the
  382. * array is assumed to be at least 4. If the length of the array is
  383. * less than 6, only the first 4 values are taken. If the length of
  384. * the array is greater than 6, the first 6 values are taken.
  385. */
  386. public AffineTransform(float[] flatmatrix) {
  387. m00 = flatmatrix[0];
  388. m10 = flatmatrix[1];
  389. m01 = flatmatrix[2];
  390. m11 = flatmatrix[3];
  391. if (flatmatrix.length > 5) {
  392. m02 = flatmatrix[4];
  393. m12 = flatmatrix[5];
  394. }
  395. updateState();
  396. }
  397. /**
  398. * Constructs a new <code>AffineTransform</code> from 6 double
  399. * precision values representing the 6 specifiable entries of the 3x3
  400. * transformation matrix.
  401. * @param m00, m01, m02, m10, m11, m12 the
  402. * 6 floating point values that compose the 3x3 transformation matrix
  403. */
  404. public AffineTransform(double m00, double m10,
  405. double m01, double m11,
  406. double m02, double m12) {
  407. this.m00 = m00;
  408. this.m10 = m10;
  409. this.m01 = m01;
  410. this.m11 = m11;
  411. this.m02 = m02;
  412. this.m12 = m12;
  413. updateState();
  414. }
  415. /**
  416. * Constructs a new <code>AffineTransform</code> from an array of
  417. * double precision values representing either the 4 non-translation
  418. * entries or the 6 specifiable entries of the 3x3 transformation
  419. * matrix. The values are retrieved from the array as
  420. * { m00 m10 m01 m11 [m02 m12]}.
  421. * @param flatmatrix the double array containing the values to be set
  422. * in the new <code>AffineTransform</code> object. The length of the
  423. * array is assumed to be at least 4. If the length of the array is
  424. * less than 6, only the first 4 values are taken. If the length of
  425. * the array is greater than 6, the first 6 values are taken.
  426. */
  427. public AffineTransform(double[] flatmatrix) {
  428. m00 = flatmatrix[0];
  429. m10 = flatmatrix[1];
  430. m01 = flatmatrix[2];
  431. m11 = flatmatrix[3];
  432. if (flatmatrix.length > 5) {
  433. m02 = flatmatrix[4];
  434. m12 = flatmatrix[5];
  435. }
  436. updateState();
  437. }
  438. /**
  439. * Returns a transform representing a translation transformation.
  440. * The matrix representing the returned transform is:
  441. * <pre>
  442. * [ 1 0 tx ]
  443. * [ 0 1 ty ]
  444. * [ 0 0 1 ]
  445. * </pre>
  446. * @param tx the distance by which coordinates are translated in the
  447. * X axis direction
  448. * @param ty the distance by which coordinates are translated in the
  449. * Y axis direction
  450. * @return an <code>AffineTransform</code> object that represents a
  451. * translation transformation, created with the specified vector.
  452. */
  453. public static AffineTransform getTranslateInstance(double tx, double ty) {
  454. AffineTransform Tx = new AffineTransform();
  455. Tx.setToTranslation(tx, ty);
  456. return Tx;
  457. }
  458. /**
  459. * Returns a transform representing a rotation transformation.
  460. * The matrix representing the returned transform is:
  461. * <pre>
  462. * [ cos(theta) -sin(theta) 0 ]
  463. * [ sin(theta) cos(theta) 0 ]
  464. * [ 0 0 1 ]
  465. * </pre>
  466. * Rotating with a positive angle theta rotates points on the positive
  467. * x axis toward the positive y axis.
  468. * @param theta the angle of rotation in radians
  469. * @return an <code>AffineTransform</code> object that is a rotation
  470. * transformation, created with the specified angle of rotation.
  471. */
  472. public static AffineTransform getRotateInstance(double theta) {
  473. AffineTransform Tx = new AffineTransform();
  474. Tx.setToRotation(theta);
  475. return Tx;
  476. }
  477. /**
  478. * Returns a transform that rotates coordinates around an anchor point.
  479. * This operation is equivalent to translating the coordinates so
  480. * that the anchor point is at the origin (S1), then rotating them
  481. * about the new origin (S2), and finally translating so that the
  482. * intermediate origin is restored to the coordinates of the original
  483. * anchor point (S3).
  484. * <p>
  485. * This operation is equivalent to the following sequence of calls:
  486. * <pre>
  487. * AffineTransform Tx = new AffineTransform();
  488. * Tx.setToTranslation(x, y); // S3: final translation
  489. * Tx.rotate(theta); // S2: rotate around anchor
  490. * Tx.translate(-x, -y); // S1: translate anchor to origin
  491. * </pre>
  492. * The matrix representing the returned transform is:
  493. * <pre>
  494. * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
  495. * [ sin(theta) cos(theta) y-x*sin-y*cos ]
  496. * [ 0 0 1 ]
  497. * </pre>
  498. * Rotating with a positive angle theta rotates points on the positive
  499. * x axis toward the positive y axis.
  500. * @param theta the angle of rotation in radians
  501. * @param x, y the coordinates of the anchor point of the
  502. * rotation
  503. * @return an <code>AffineTransform</code> object that rotates
  504. * coordinates around the specified point by the specified angle of
  505. * rotation.
  506. */
  507. public static AffineTransform getRotateInstance(double theta,
  508. double x, double y) {
  509. AffineTransform Tx = new AffineTransform();
  510. Tx.setToRotation(theta, x, y);
  511. return Tx;
  512. }
  513. /**
  514. * Returns a transform representing a scaling transformation.
  515. * The matrix representing the returned transform is:
  516. * <pre>
  517. * [ sx 0 0 ]
  518. * [ 0 sy 0 ]
  519. * [ 0 0 1 ]
  520. * </pre>
  521. * @param sx the factor by which coordinates are scaled along the
  522. * X axis direction
  523. * @param sy the factor by which coordinates are scaled along the
  524. * Y axis direction
  525. * @return an <code>AffineTransform</code> object that scales
  526. * coordinates by the specified factors.
  527. */
  528. public static AffineTransform getScaleInstance(double sx, double sy) {
  529. AffineTransform Tx = new AffineTransform();
  530. Tx.setToScale(sx, sy);
  531. return Tx;
  532. }
  533. /**
  534. * Returns a transform representing a shearing transformation.
  535. * The matrix representing the returned transform is:
  536. * <pre>
  537. * [ 1 shx 0 ]
  538. * [ shy 1 0 ]
  539. * [ 0 0 1 ]
  540. * </pre>
  541. * @param shx the multiplier by which coordinates are shifted in the
  542. * direction of the positive X axis as a factor of their Y coordinate
  543. * @param shy the multiplier by which coordinates are shifted in the
  544. * direction of the positive Y axis as a factor of their X coordinate
  545. * @return an <code>AffineTransform</code> object that shears
  546. * coordinates by the specified multipliers.
  547. */
  548. public static AffineTransform getShearInstance(double shx, double shy) {
  549. AffineTransform Tx = new AffineTransform();
  550. Tx.setToShear(shx, shy);
  551. return Tx;
  552. }
  553. /**
  554. * Retrieves the flag bits describing the conversion properties of
  555. * this transform.
  556. * The return value is either one of the constants TYPE_IDENTITY
  557. * or TYPE_GENERAL_TRANSFORM, or a combination of the
  558. * appriopriate flag bits.
  559. * A valid combination of flag bits is an exclusive OR operation
  560. * that can combine
  561. * the TYPE_TRANSLATION flag bit
  562. * in addition to either of the
  563. * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits
  564. * as well as either of the
  565. * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits.
  566. * @return the OR combination of any of the indicated flags that
  567. * apply to this transform
  568. * @see #TYPE_IDENTITY
  569. * @see #TYPE_TRANSLATION
  570. * @see #TYPE_UNIFORM_SCALE
  571. * @see #TYPE_GENERAL_SCALE
  572. * @see #TYPE_QUADRANT_ROTATION
  573. * @see #TYPE_GENERAL_ROTATION
  574. * @see #TYPE_GENERAL_TRANSFORM
  575. */
  576. public int getType() {
  577. int ret = TYPE_IDENTITY;
  578. boolean sgn0, sgn1;
  579. double M0, M1, M2, M3;
  580. switch (state) {
  581. default:
  582. stateError();
  583. /* NOTREACHED */
  584. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  585. ret = TYPE_TRANSLATION;
  586. /* NOBREAK */
  587. case (APPLY_SHEAR | APPLY_SCALE):
  588. if ((M0 = m00) * (M2 = m01) + (M3 = m10) * (M1 = m11) != 0) {
  589. // Transformed unit vectors are not perpendicular...
  590. return TYPE_GENERAL_TRANSFORM;
  591. }
  592. sgn0 = (M0 >= 0.0);
  593. sgn1 = (M1 >= 0.0);
  594. if (sgn0 == sgn1) {
  595. // sgn(M0) == sgn(M1) therefore sgn(M2) == -sgn(M3)
  596. // This is the "unflipped" (right-handed) state
  597. if (M0 != M1 || M2 != -M3) {
  598. ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE);
  599. } else if (M0 * M1 - M2 * M3 != 1.0) {
  600. ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE);
  601. } else {
  602. ret |= TYPE_GENERAL_ROTATION;
  603. }
  604. } else {
  605. // sgn(M0) == -sgn(M1) therefore sgn(M2) == sgn(M3)
  606. // This is the "flipped" (left-handed) state
  607. if (M0 != -M1 || M2 != M3) {
  608. ret |= (TYPE_GENERAL_ROTATION |
  609. TYPE_FLIP |
  610. TYPE_GENERAL_SCALE);
  611. } else if (M0 * M1 - M2 * M3 != 1.0) {
  612. ret |= (TYPE_GENERAL_ROTATION |
  613. TYPE_FLIP |
  614. TYPE_UNIFORM_SCALE);
  615. } else {
  616. ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP);
  617. }
  618. }
  619. break;
  620. case (APPLY_SHEAR | APPLY_TRANSLATE):
  621. ret = TYPE_TRANSLATION;
  622. /* NOBREAK */
  623. case (APPLY_SHEAR):
  624. sgn0 = ((M0 = m01) >= 0.0);
  625. sgn1 = ((M1 = m10) >= 0.0);
  626. if (sgn0 != sgn1) {
  627. // Different signs - simple 90 degree rotation
  628. if (M0 == -M1) {
  629. ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
  630. } else {
  631. ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
  632. }
  633. } else {
  634. // Same signs - 90 degree rotation plus an axis flip too
  635. if (M0 == M1) {
  636. ret |= (TYPE_QUADRANT_ROTATION |
  637. TYPE_FLIP |
  638. TYPE_UNIFORM_SCALE);
  639. } else {
  640. ret |= (TYPE_QUADRANT_ROTATION |
  641. TYPE_FLIP |
  642. TYPE_GENERAL_SCALE);
  643. }
  644. }
  645. break;
  646. case (APPLY_SCALE | APPLY_TRANSLATE):
  647. ret = TYPE_TRANSLATION;
  648. /* NOBREAK */
  649. case (APPLY_SCALE):
  650. sgn0 = ((M0 = m00) >= 0.0);
  651. sgn1 = ((M1 = m11) >= 0.0);
  652. if (sgn0 == sgn1) {
  653. if (sgn0) {
  654. // Both scaling factors non-negative - simple scale
  655. if (M0 == M1) {
  656. ret |= TYPE_UNIFORM_SCALE;
  657. } else {
  658. ret |= TYPE_GENERAL_SCALE;
  659. }
  660. } else {
  661. // Both scaling factors negative - 180 degree rotation
  662. if (M0 == M1) {
  663. ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
  664. } else {
  665. ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
  666. }
  667. }
  668. } else {
  669. // Scaling factor signs different - flip about some axis
  670. if (M0 == -M1) {
  671. if (M0 == 1.0 || M0 == -1.0) {
  672. ret |= TYPE_FLIP;
  673. } else {
  674. ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE);
  675. }
  676. } else {
  677. ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE);
  678. }
  679. }
  680. break;
  681. case (APPLY_TRANSLATE):
  682. ret = TYPE_TRANSLATION;
  683. break;
  684. case (APPLY_IDENTITY):
  685. break;
  686. }
  687. return ret;
  688. }
  689. /**
  690. * Returns the determinant of the matrix representation of the transform.
  691. * The determinant is useful both to determine if the transform can
  692. * be inverted and to get a single value representing the
  693. * combined X and Y scaling of the transform.
  694. * <p>
  695. * If the determinant is non-zero, then this transform is
  696. * invertible and the various methods that depend on the inverse
  697. * transform do not need to throw a
  698. * {@link NoninvertibleTransformException}.
  699. * If the determinant is zero then this transform can not be
  700. * inverted since the transform maps all input coordinates onto
  701. * a line or a point.
  702. * If the determinant is near enough to zero then inverse transform
  703. * operations might not carry enough precision to produce meaningful
  704. * results.
  705. * <p>
  706. * If this transform represents a uniform scale, as indicated by
  707. * the <code>getType</code> method then the determinant also
  708. * represents the square of the uniform scale factor by which all of
  709. * the points are expanded from or contracted towards the origin.
  710. * If this transform represents a non-uniform scale or more general
  711. * transform then the determinant is not likely to represent a
  712. * value useful for any purpose other than determining if inverse
  713. * transforms are possible.
  714. * <p>
  715. * Mathematically, the determinant is calculated using the formula:
  716. * <pre>
  717. * | m00 m01 m02 |
  718. * | m10 m11 m12 | = m00 * m11 - m01 * m10
  719. * | 0 0 1 |
  720. * </pre>
  721. *
  722. * @return the determinant of the matrix used to transform the
  723. * coordinates.
  724. * @see #getType
  725. * @see #createInverse
  726. * @see #inverseTransform
  727. * @see #TYPE_UNIFORM_SCALE
  728. */
  729. public double getDeterminant() {
  730. switch (state) {
  731. default:
  732. stateError();
  733. /* NOTREACHED */
  734. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  735. case (APPLY_SHEAR | APPLY_SCALE):
  736. return m00 * m11 - m01 * m10;
  737. case (APPLY_SHEAR | APPLY_TRANSLATE):
  738. case (APPLY_SHEAR):
  739. return -(m01 * m10);
  740. case (APPLY_SCALE | APPLY_TRANSLATE):
  741. case (APPLY_SCALE):
  742. return m00 * m11;
  743. case (APPLY_TRANSLATE):
  744. case (APPLY_IDENTITY):
  745. return 1.0;
  746. }
  747. }
  748. /**
  749. * Manually recalculates the state of the transform when the matrix
  750. * changes too much to predict the effects on the state.
  751. */
  752. void updateState() {
  753. if (m01 == 0.0 && m10 == 0.0) {
  754. if (m00 == 1.0 && m11 == 1.0) {
  755. if (m02 == 0.0 && m12 == 0.0) {
  756. state = APPLY_IDENTITY;
  757. } else {
  758. state = APPLY_TRANSLATE;
  759. }
  760. } else {
  761. if (m02 == 0.0 && m12 == 0.0) {
  762. state = APPLY_SCALE;
  763. } else {
  764. state = (APPLY_SCALE | APPLY_TRANSLATE);
  765. }
  766. }
  767. } else {
  768. if (m00 == 0.0 && m11 == 0.0) {
  769. if (m02 == 0.0 && m12 == 0.0) {
  770. state = APPLY_SHEAR;
  771. } else {
  772. state = (APPLY_SHEAR | APPLY_TRANSLATE);
  773. }
  774. } else {
  775. if (m02 == 0.0 && m12 == 0.0) {
  776. state = (APPLY_SHEAR | APPLY_SCALE);
  777. } else {
  778. state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);
  779. }
  780. }
  781. }
  782. }
  783. /*
  784. * Convenience method used internally to throw exceptions when
  785. * a case was forgotten in a switch statement.
  786. */
  787. private void stateError() {
  788. throw new InternalError("missing case in transform state switch");
  789. }
  790. /**
  791. * Retrieves the 6 specifiable values in the 3x3 affine transformation
  792. * matrix and places them into an array of double precisions values.
  793. * The values are stored in the array as
  794. * { m00 m10 m01 m11 m02 m12 }.
  795. * An array of 4 doubles can also be specified, in which case only the
  796. * first four elements representing the non-transform
  797. * parts of the array are retrieved and the values are stored into
  798. * the array as { m00 m10 m01 m11 }
  799. * @param flatmatrix the double array used to store the returned
  800. * values.
  801. * @see #getScaleX
  802. * @see #getScaleY
  803. * @see #getShearX
  804. * @see #getShearY
  805. * @see #getTranslateX
  806. * @see #getTranslateY
  807. */
  808. public void getMatrix(double[] flatmatrix) {
  809. flatmatrix[0] = m00;
  810. flatmatrix[1] = m10;
  811. flatmatrix[2] = m01;
  812. flatmatrix[3] = m11;
  813. if (flatmatrix.length > 5) {
  814. flatmatrix[4] = m02;
  815. flatmatrix[5] = m12;
  816. }
  817. }
  818. /**
  819. * Returns the X coordinate scaling element (m00) of the 3x3
  820. * affine transformation matrix.
  821. * @return a double value that is the X coordinate of the scaling
  822. * element of the affine transformation matrix.
  823. * @see #getMatrix
  824. */
  825. public double getScaleX() {
  826. return m00;
  827. }
  828. /**
  829. * Returns the Y coordinate scaling element (m11) of the 3x3
  830. * affine transformation matrix.
  831. * @return a double value that is the Y coordinate of the scaling
  832. * element of the affine transformation matrix.
  833. * @see #getMatrix
  834. */
  835. public double getScaleY() {
  836. return m11;
  837. }
  838. /**
  839. * Returns the X coordinate shearing element (m01) of the 3x3
  840. * affine transformation matrix.
  841. * @return a double value that is the X coordinate of the shearing
  842. * element of the affine transformation matrix.
  843. * @see #getMatrix
  844. */
  845. public double getShearX() {
  846. return m01;
  847. }
  848. /**
  849. * Returns the Y coordinate shearing element (m10) of the 3x3
  850. * affine transformation matrix.
  851. * @return a double value that is the Y coordinate of the shearing
  852. * element of the affine transformation matrix.
  853. * @see #getMatrix
  854. */
  855. public double getShearY() {
  856. return m10;
  857. }
  858. /**
  859. * Returns the X coordinate of the translation element (m02) of the
  860. * 3x3 affine transformation matrix.
  861. * @return a double value that is the X coordinate of the translation
  862. * element of the affine transformation matrix.
  863. * @see #getMatrix
  864. */
  865. public double getTranslateX() {
  866. return m02;
  867. }
  868. /**
  869. * Returns the Y coordinate of the translation element (m12) of the
  870. * 3x3 affine transformation matrix.
  871. * @return a double value that is the Y coordinate of the translation
  872. * element of the affine transformation matrix.
  873. * @see #getMatrix
  874. */
  875. public double getTranslateY() {
  876. return m12;
  877. }
  878. /**
  879. * Concatenates this transform with a translation transformation.
  880. * This is equivalent to calling concatenate(T), where T is an
  881. * <code>AffineTransform</code> represented by the following matrix:
  882. * <pre>
  883. * [ 1 0 tx ]
  884. * [ 0 1 ty ]
  885. * [ 0 0 1 ]
  886. * </pre>
  887. * @param tx the distance by which coordinates are translated in the
  888. * X axis direction
  889. * @param ty the distance by which coordinates are translated in the
  890. * Y axis direction
  891. */
  892. public void translate(double tx, double ty) {
  893. switch (state) {
  894. default:
  895. stateError();
  896. /* NOTREACHED */
  897. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  898. m02 = tx * m00 + ty * m01 + m02;
  899. m12 = tx * m10 + ty * m11 + m12;
  900. return;
  901. case (APPLY_SHEAR | APPLY_SCALE):
  902. m02 = tx * m00 + ty * m01;
  903. m12 = tx * m10 + ty * m11;
  904. state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
  905. return;
  906. case (APPLY_SHEAR | APPLY_TRANSLATE):
  907. m02 = ty * m01 + m02;
  908. m12 = tx * m10 + m12;
  909. return;
  910. case (APPLY_SHEAR):
  911. m02 = ty * m01;
  912. m12 = tx * m10;
  913. state = APPLY_SHEAR | APPLY_TRANSLATE;
  914. return;
  915. case (APPLY_SCALE | APPLY_TRANSLATE):
  916. m02 = tx * m00 + m02;
  917. m12 = ty * m11 + m12;
  918. return;
  919. case (APPLY_SCALE):
  920. m02 = tx * m00;
  921. m12 = ty * m11;
  922. state = APPLY_SCALE | APPLY_TRANSLATE;
  923. return;
  924. case (APPLY_TRANSLATE):
  925. m02 = tx + m02;
  926. m12 = ty + m12;
  927. return;
  928. case (APPLY_IDENTITY):
  929. m02 = tx;
  930. m12 = ty;
  931. state = APPLY_TRANSLATE;
  932. return;
  933. }
  934. }
  935. /**
  936. * Concatenates this transform with a rotation transformation.
  937. * This is equivalent to calling concatenate(R), where R is an
  938. * <code>AffineTransform</code> represented by the following matrix:
  939. * <pre>
  940. * [ cos(theta) -sin(theta) 0 ]
  941. * [ sin(theta) cos(theta) 0 ]
  942. * [ 0 0 1 ]
  943. * </pre>
  944. * Rotating with a positive angle theta rotates points on the positive
  945. * x axis toward the positive y axis.
  946. * @param theta the angle of rotation in radians
  947. */
  948. public void rotate(double theta) {
  949. double sin = Math.sin(theta);
  950. double cos = Math.cos(theta);
  951. if (Math.abs(sin) < 1E-15) {
  952. if (cos < 0.0) {
  953. m00 = -m00;
  954. m11 = -m11;
  955. int state = this.state;
  956. if ((state & (APPLY_SHEAR)) != 0) {
  957. // If there was a shear, then this rotation has no
  958. // effect on the state.
  959. m01 = -m01;
  960. m10 = -m10;
  961. } else {
  962. // No shear means the SCALE state may toggle when
  963. // m00 and m11 are negated.
  964. if (m00 == 1.0 && m11 == 1.0) {
  965. this.state = state & ~APPLY_SCALE;
  966. } else {
  967. this.state = state | APPLY_SCALE;
  968. }
  969. }
  970. }
  971. return;
  972. }
  973. if (Math.abs(cos) < 1E-15) {
  974. if (sin < 0.0) {
  975. double M0 = m00;
  976. m00 = -m01;
  977. m01 = M0;
  978. M0 = m10;
  979. m10 = -m11;
  980. m11 = M0;
  981. } else {
  982. double M0 = m00;
  983. m00 = m01;
  984. m01 = -M0;
  985. M0 = m10;
  986. m10 = m11;
  987. m11 = -M0;
  988. }
  989. state = rot90conversion[state];
  990. return;
  991. }
  992. double M0, M1;
  993. M0 = m00;
  994. M1 = m01;
  995. m00 = cos * M0 + sin * M1;
  996. m01 = -sin * M0 + cos * M1;
  997. M0 = m10;
  998. M1 = m11;
  999. m10 = cos * M0 + sin * M1;
  1000. m11 = -sin * M0 + cos * M1;
  1001. updateState();
  1002. }
  1003. private static int rot90conversion[] = {
  1004. APPLY_SHEAR, APPLY_SHEAR | APPLY_TRANSLATE,
  1005. APPLY_SHEAR, APPLY_SHEAR | APPLY_TRANSLATE,
  1006. APPLY_SCALE, APPLY_SCALE | APPLY_TRANSLATE,
  1007. APPLY_SHEAR | APPLY_SCALE,
  1008. APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE,
  1009. };
  1010. /**
  1011. * Concatenates this transform with a transform that rotates
  1012. * coordinates around an anchor point.
  1013. * This operation is equivalent to translating the coordinates so
  1014. * that the anchor point is at the origin (S1), then rotating them
  1015. * about the new origin (S2), and finally translating so that the
  1016. * intermediate origin is restored to the coordinates of the original
  1017. * anchor point (S3).
  1018. * <p>
  1019. * This operation is equivalent to the following sequence of calls:
  1020. * <pre>
  1021. * translate(x, y); // S3: final translation
  1022. * rotate(theta); // S2: rotate around anchor
  1023. * translate(-x, -y); // S1: translate anchor to origin
  1024. * </pre>
  1025. * Rotating with a positive angle theta rotates points on the positive
  1026. * x axis toward the positive y axis.
  1027. * @param theta the angle of rotation in radians
  1028. * @param x, y the coordinates of the anchor point of the
  1029. * rotation
  1030. */
  1031. public void rotate(double theta, double x, double y) {
  1032. // REMIND: Simple for now - optimize later
  1033. translate(x, y);
  1034. rotate(theta);
  1035. translate(-x, -y);
  1036. }
  1037. /**
  1038. * Concatenates this transform with a scaling transformation.
  1039. * This is equivalent to calling concatenate(S), where S is an
  1040. * <code>AffineTransform</code> represented by the following matrix:
  1041. * <pre>
  1042. * [ sx 0 0 ]
  1043. * [ 0 sy 0 ]
  1044. * [ 0 0 1 ]
  1045. * </pre>
  1046. * @param sx the factor by which coordinates are scaled along the
  1047. * X axis direction
  1048. * @param sy the factor by which coordinates are scaled along the
  1049. * Y axis direction
  1050. */
  1051. public void scale(double sx, double sy) {
  1052. int state = this.state;
  1053. switch (state) {
  1054. default:
  1055. stateError();
  1056. /* NOTREACHED */
  1057. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1058. case (APPLY_SHEAR | APPLY_SCALE):
  1059. m00 *= sx;
  1060. m11 *= sy;
  1061. /* NOBREAK */
  1062. case (APPLY_SHEAR | APPLY_TRANSLATE):
  1063. case (APPLY_SHEAR):
  1064. m01 *= sy;
  1065. m10 *= sx;
  1066. return;
  1067. case (APPLY_SCALE | APPLY_TRANSLATE):
  1068. case (APPLY_SCALE):
  1069. m00 *= sx;
  1070. m11 *= sy;
  1071. return;
  1072. case (APPLY_TRANSLATE):
  1073. case (APPLY_IDENTITY):
  1074. m00 = sx;
  1075. m11 = sy;
  1076. this.state = state | APPLY_SCALE;
  1077. return;
  1078. }
  1079. }
  1080. /**
  1081. * Concatenates this transform with a shearing transformation.
  1082. * This is equivalent to calling concatenate(SH), where SH is an
  1083. * <code>AffineTransform</code> represented by the following matrix:
  1084. * <pre>
  1085. * [ 1 shx 0 ]
  1086. * [ shy 1 0 ]
  1087. * [ 0 0 1 ]
  1088. * </pre>
  1089. * @param shx the multiplier by which coordinates are shifted in the
  1090. * direction of the positive X axis as a factor of their Y coordinate
  1091. * @param shy the multiplier by which coordinates are shifted in the
  1092. * direction of the positive Y axis as a factor of their X coordinate
  1093. */
  1094. public void shear(double shx, double shy) {
  1095. int state = this.state;
  1096. switch (state) {
  1097. default:
  1098. stateError();
  1099. /* NOTREACHED */
  1100. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1101. case (APPLY_SHEAR | APPLY_SCALE):
  1102. double M0, M1;
  1103. M0 = m00;
  1104. M1 = m01;
  1105. m00 = M0 + M1 * shy;
  1106. m01 = M0 * shx + M1;
  1107. M0 = m10;
  1108. M1 = m11;
  1109. m10 = M0 + M1 * shy;
  1110. m11 = M0 * shx + M1;
  1111. return;
  1112. case (APPLY_SHEAR | APPLY_TRANSLATE):
  1113. case (APPLY_SHEAR):
  1114. m00 = m01 * shy;
  1115. m11 = m10 * shx;
  1116. this.state = state | APPLY_SCALE;
  1117. return;
  1118. case (APPLY_SCALE | APPLY_TRANSLATE):
  1119. case (APPLY_SCALE):
  1120. m01 = m00 * shx;
  1121. m10 = m11 * shy;
  1122. this.state = state | APPLY_SHEAR;
  1123. return;
  1124. case (APPLY_TRANSLATE):
  1125. case (APPLY_IDENTITY):
  1126. m01 = shx;
  1127. m10 = shy;
  1128. this.state = state | APPLY_SCALE | APPLY_SHEAR;
  1129. return;
  1130. }
  1131. }
  1132. /**
  1133. * Resets this transform to the Identity transform.
  1134. */
  1135. public void setToIdentity() {
  1136. m00 = m11 = 1.0;
  1137. m10 = m01 = m02 = m12 = 0.0;
  1138. state = APPLY_IDENTITY;
  1139. }
  1140. /**
  1141. * Sets this transform to a translation transformation.
  1142. * The matrix representing this transform becomes:
  1143. * <pre>
  1144. * [ 1 0 tx ]
  1145. * [ 0 1 ty ]
  1146. * [ 0 0 1 ]
  1147. * </pre>
  1148. * @param tx the distance by which coordinates are translated in the
  1149. * X axis direction
  1150. * @param ty the distance by which coordinates are translated in the
  1151. * Y axis direction
  1152. */
  1153. public void setToTranslation(double tx, double ty) {
  1154. m00 = 1.0;
  1155. m10 = 0.0;
  1156. m01 = 0.0;
  1157. m11 = 1.0;
  1158. m02 = tx;
  1159. m12 = ty;
  1160. state = APPLY_TRANSLATE;
  1161. }
  1162. /**
  1163. * Sets this transform to a rotation transformation.
  1164. * The matrix representing this transform becomes:
  1165. * <pre>
  1166. * [ cos(theta) -sin(theta) 0 ]
  1167. * [ sin(theta) cos(theta) 0 ]
  1168. * [ 0 0 1 ]
  1169. * </pre>
  1170. * Rotating with a positive angle theta rotates points on the positive
  1171. * x axis toward the positive y axis.
  1172. * @param theta the angle of rotation in radians
  1173. */
  1174. public void setToRotation(double theta) {
  1175. m02 = 0.0;
  1176. m12 = 0.0;
  1177. double sin = Math.sin(theta);
  1178. double cos = Math.cos(theta);
  1179. if (Math.abs(sin) < 1E-15) {
  1180. m01 = m10 = 0.0;
  1181. if (cos < 0) {
  1182. m00 = m11 = -1.0;
  1183. state = APPLY_SCALE;
  1184. } else {
  1185. m00 = m11 = 1.0;
  1186. state = APPLY_IDENTITY;
  1187. }
  1188. return;
  1189. }
  1190. if (Math.abs(cos) < 1E-15) {
  1191. m00 = m11 = 0.0;
  1192. if (sin < 0.0) {
  1193. m01 = 1.0;
  1194. m10 = -1.0;
  1195. } else {
  1196. m01 = -1.0;
  1197. m10 = 1.0;
  1198. }
  1199. state = APPLY_SHEAR;
  1200. return;
  1201. }
  1202. m00 = cos;
  1203. m01 = -sin;
  1204. m10 = sin;
  1205. m11 = cos;
  1206. state = APPLY_SHEAR | APPLY_SCALE;
  1207. return;
  1208. }
  1209. /**
  1210. * Sets this transform to a translated rotation transformation.
  1211. * This operation is equivalent to translating the coordinates so
  1212. * that the anchor point is at the origin (S1), then rotating them
  1213. * about the new origin (S2), and finally translating so that the
  1214. * intermediate origin is restored to the coordinates of the original
  1215. * anchor point (S3).
  1216. * <p>
  1217. * This operation is equivalent to the following sequence of calls:
  1218. * <pre>
  1219. * setToTranslation(x, y); // S3: final translation
  1220. * rotate(theta); // S2: rotate around anchor
  1221. * translate(-x, -y); // S1: translate anchor to origin
  1222. * </pre>
  1223. * The matrix representing this transform becomes:
  1224. * <pre>
  1225. * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
  1226. * [ sin(theta) cos(theta) y-x*sin-y*cos ]
  1227. * [ 0 0 1 ]
  1228. * </pre>
  1229. * Rotating with a positive angle theta rotates points on the positive
  1230. * x axis toward the positive y axis.
  1231. * @param theta the angle of rotation in radians
  1232. * @param x, y the coordinates of the anchor point of the
  1233. * rotation
  1234. */
  1235. public void setToRotation(double theta, double x, double y) {
  1236. setToRotation(theta);
  1237. double sin = m10;
  1238. double oneMinusCos = 1.0 - m00;
  1239. m02 = x * oneMinusCos + y * sin;
  1240. m12 = y * oneMinusCos - x * sin;
  1241. state |= APPLY_TRANSLATE;
  1242. return;
  1243. }
  1244. /**
  1245. * Sets this transform to a scaling transformation.
  1246. * The matrix representing this transform becomes:
  1247. * <pre>
  1248. * [ sx 0 0 ]
  1249. * [ 0 sy 0 ]
  1250. * [ 0 0 1 ]
  1251. * </pre>
  1252. * @param sx the factor by which coordinates are scaled along the
  1253. * X axis direction
  1254. * @param sy the factor by which coordinates are scaled along the
  1255. * Y axis direction
  1256. */
  1257. public void setToScale(double sx, double sy) {
  1258. m00 = sx;
  1259. m10 = 0.0;
  1260. m01 = 0.0;
  1261. m11 = sy;
  1262. m02 = 0.0;
  1263. m12 = 0.0;
  1264. state = APPLY_SCALE;
  1265. }
  1266. /**
  1267. * Sets this transform to a shearing transformation.
  1268. * The matrix representing this transform becomes:
  1269. * <pre>
  1270. * [ 1 shx 0 ]
  1271. * [ shy 1 0 ]
  1272. * [ 0 0 1 ]
  1273. * </pre>
  1274. * @param shx the multiplier by which coordinates are shifted in the
  1275. * direction of the positive X axis as a factor of their Y coordinate
  1276. * @param shy the multiplier by which coordinates are shifted in the
  1277. * direction of the positive Y axis as a factor of their X coordinate
  1278. */
  1279. public void setToShear(double shx, double shy) {
  1280. m00 = 1.0;
  1281. m01 = shx;
  1282. m10 = shy;
  1283. m11 = 1.0;
  1284. m02 = 0.0;
  1285. m12 = 0.0;
  1286. state = APPLY_SHEAR | APPLY_SCALE;
  1287. }
  1288. /**
  1289. * Sets this transform to a copy of the transform in the specified
  1290. * <code>AffineTransform</code> object.
  1291. * @param Tx the <code>AffineTransform</code> object from which to
  1292. * copy the transform
  1293. */
  1294. public void setTransform(AffineTransform Tx) {
  1295. this.m00 = Tx.m00;
  1296. this.m10 = Tx.m10;
  1297. this.m01 = Tx.m01;
  1298. this.m11 = Tx.m11;
  1299. this.m02 = Tx.m02;
  1300. this.m12 = Tx.m12;
  1301. this.state = Tx.state;
  1302. }
  1303. /**
  1304. * Sets this transform to the matrix specified by the 6
  1305. * double precision values.
  1306. * @param m00, m01, m02, m10, m11, m12 the
  1307. * 6 floating point values that compose the 3x3 transformation matrix
  1308. */
  1309. public void setTransform(double m00, double m10,
  1310. double m01, double m11,
  1311. double m02, double m12) {
  1312. this.m00 = m00;
  1313. this.m10 = m10;
  1314. this.m01 = m01;
  1315. this.m11 = m11;
  1316. this.m02 = m02;
  1317. this.m12 = m12;
  1318. updateState();
  1319. }
  1320. /**
  1321. * Concatenates an <code>AffineTransform</code> <code>Tx</code> to
  1322. * this <code>AffineTransform</code> Cx in the most commonly useful
  1323. * way to provide a new user space
  1324. * that is mapped to the former user space by <code>Tx</code>.
  1325. * Cx is updated to perform the combined transformation.
  1326. * Transforming a point p by the updated transform Cx' is
  1327. * equivalent to first transforming p by <code>Tx</code> and then
  1328. * transforming the result by the original transform Cx like this:
  1329. * Cx'(p) = Cx(Tx(p))
  1330. * In matrix notation, if this transform Cx is
  1331. * represented by the matrix [this] and <code>Tx</code> is represented
  1332. * by the matrix [Tx] then this method does the following:
  1333. * <pre>
  1334. * [this] = [this] x [Tx]
  1335. * </pre>
  1336. * @param Tx the <code>AffineTransform</code> object to be
  1337. * concatenated with this <code>AffineTransform</code> object.
  1338. * @see #preConcatenate
  1339. */
  1340. public void concatenate(AffineTransform Tx) {
  1341. double M0, M1;
  1342. double T00, T01, T10, T11;
  1343. double T02, T12;
  1344. int mystate = state;
  1345. int txstate = Tx.state;
  1346. switch ((txstate << HI_SHIFT) | mystate) {
  1347. /* ---------- Tx == IDENTITY cases ---------- */
  1348. case (HI_IDENTITY | APPLY_IDENTITY):
  1349. case (HI_IDENTITY | APPLY_TRANSLATE):
  1350. case (HI_IDENTITY | APPLY_SCALE):
  1351. case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
  1352. case (HI_IDENTITY | APPLY_SHEAR):
  1353. case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
  1354. case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
  1355. case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1356. return;
  1357. /* ---------- this == IDENTITY cases ---------- */
  1358. case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
  1359. m01 = Tx.m01;
  1360. m10 = Tx.m10;
  1361. /* NOBREAK */
  1362. case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
  1363. m00 = Tx.m00;
  1364. m11 = Tx.m11;
  1365. /* NOBREAK */
  1366. case (HI_TRANSLATE | APPLY_IDENTITY):
  1367. m02 = Tx.m02;
  1368. m12 = Tx.m12;
  1369. state = txstate;
  1370. return;
  1371. case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY):
  1372. m01 = Tx.m01;
  1373. m10 = Tx.m10;
  1374. /* NOBREAK */
  1375. case (HI_SCALE | APPLY_IDENTITY):
  1376. m00 = Tx.m00;
  1377. m11 = Tx.m11;
  1378. state = txstate;
  1379. return;
  1380. case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY):
  1381. m02 = Tx.m02;
  1382. m12 = Tx.m12;
  1383. /* NOBREAK */
  1384. case (HI_SHEAR | APPLY_IDENTITY):
  1385. m01 = Tx.m01;
  1386. m10 = Tx.m10;
  1387. m00 = m11 = 0.0;
  1388. state = txstate;
  1389. return;
  1390. /* ---------- Tx == TRANSLATE cases ---------- */
  1391. case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1392. T02 = Tx.m02;
  1393. T12 = Tx.m12;
  1394. m02 = T02 * m00 + T12 * m01 + m02;
  1395. m12 = T02 * m10 + T12 * m11 + m12;
  1396. return;
  1397. case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
  1398. T02 = Tx.m02;
  1399. T12 = Tx.m12;
  1400. m02 = T02 * m00 + T12 * m01;
  1401. m12 = T02 * m10 + T12 * m11;
  1402. state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
  1403. return;
  1404. case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
  1405. m02 = Tx.m12 * m01 + m02;
  1406. m12 = Tx.m02 * m10 + m12;
  1407. return;
  1408. case (HI_TRANSLATE | APPLY_SHEAR):
  1409. m02 = Tx.m12 * m01;
  1410. m12 = Tx.m02 * m10;
  1411. state = APPLY_SHEAR | APPLY_TRANSLATE;
  1412. return;
  1413. case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
  1414. m02 = Tx.m02 * m00 + m02;
  1415. m12 = Tx.m12 * m11 + m12;
  1416. return;
  1417. case (HI_TRANSLATE | APPLY_SCALE):
  1418. m02 = Tx.m02 * m00;
  1419. m12 = Tx.m12 * m11;
  1420. state = APPLY_SCALE | APPLY_TRANSLATE;
  1421. return;
  1422. case (HI_TRANSLATE | APPLY_TRANSLATE):
  1423. m02 = Tx.m02 + m02;
  1424. m12 = Tx.m12 + m12;
  1425. return;
  1426. /* ---------- Tx == SCALE cases ---------- */
  1427. case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1428. case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
  1429. T00 = Tx.m00; T11 = Tx.m11;
  1430. m00 *= T00;
  1431. m11 *= T11;
  1432. m01 *= T11;
  1433. m10 *= T00;
  1434. return;
  1435. case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
  1436. case (HI_SCALE | APPLY_SHEAR):
  1437. m01 *= Tx.m11;
  1438. m10 *= Tx.m00;
  1439. return;
  1440. case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
  1441. case (HI_SCALE | APPLY_SCALE):
  1442. m00 *= Tx.m00;
  1443. m11 *= Tx.m11;
  1444. return;
  1445. case (HI_SCALE | APPLY_TRANSLATE):
  1446. m00 = Tx.m00;
  1447. m11 = Tx.m11;
  1448. state = APPLY_SCALE | APPLY_TRANSLATE;
  1449. return;
  1450. /* ---------- Tx == SHEAR cases ---------- */
  1451. case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1452. case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
  1453. T01 = Tx.m01; T10 = Tx.m10;
  1454. M0 = m00;
  1455. m00 = m01 * T10;
  1456. m01 = M0 * T01;
  1457. M0 = m10;
  1458. m10 = m11 * T10;
  1459. m11 = M0 * T01;
  1460. return;
  1461. case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
  1462. case (HI_SHEAR | APPLY_SHEAR):
  1463. m00 = m01 * Tx.m10;
  1464. m01 = 0.0;
  1465. m11 = m10 * Tx.m01;
  1466. m10 = 0.0;
  1467. state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
  1468. return;
  1469. case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1470. case (HI_SHEAR | APPLY_SCALE):
  1471. m01 = m00 * Tx.m01;
  1472. m00 = 0.0;
  1473. m10 = m11 * Tx.m10;
  1474. m11 = 0.0;
  1475. state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
  1476. return;
  1477. case (HI_SHEAR | APPLY_TRANSLATE):
  1478. m00 = 0.0;
  1479. m01 = Tx.m01;
  1480. m10 = Tx.m10;
  1481. m11 = 0.0;
  1482. state = APPLY_TRANSLATE | APPLY_SHEAR;
  1483. return;
  1484. }
  1485. // If Tx has more than one attribute, it is not worth optimizing
  1486. // all of those cases...
  1487. T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;
  1488. T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;
  1489. switch (mystate) {
  1490. default:
  1491. stateError();
  1492. /* NOTREACHED */
  1493. case (APPLY_SHEAR | APPLY_SCALE):
  1494. state = mystate | txstate;
  1495. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1496. M0 = m00;
  1497. M1 = m01;
  1498. m00 = T00 * M0 + T10 * M1;
  1499. m01 = T01 * M0 + T11 * M1;
  1500. m02 += T02 * M0 + T12 * M1;
  1501. M0 = m10;
  1502. M1 = m11;
  1503. m10 = T00 * M0 + T10 * M1;
  1504. m11 = T01 * M0 + T11 * M1;
  1505. m12 += T02 * M0 + T12 * M1;
  1506. return;
  1507. case (APPLY_SHEAR | APPLY_TRANSLATE):
  1508. case (APPLY_SHEAR):
  1509. M0 = m01;
  1510. m00 = T10 * M0;
  1511. m01 = T11 * M0;
  1512. m02 += T12 * M0;
  1513. M0 = m10;
  1514. m10 = T00 * M0;
  1515. m11 = T01 * M0;
  1516. m12 += T02 * M0;
  1517. break;
  1518. case (APPLY_SCALE | APPLY_TRANSLATE):
  1519. case (APPLY_SCALE):
  1520. M0 = m00;
  1521. m00 = T00 * M0;
  1522. m01 = T01 * M0;
  1523. m02 += T02 * M0;
  1524. M0 = m11;
  1525. m10 = T10 * M0;
  1526. m11 = T11 * M0;
  1527. m12 += T12 * M0;
  1528. break;
  1529. case (APPLY_TRANSLATE):
  1530. m00 = T00;
  1531. m01 = T01;
  1532. m02 += T02;
  1533. m10 = T10;
  1534. m11 = T11;
  1535. m12 += T12;
  1536. state = txstate | APPLY_TRANSLATE;
  1537. return;
  1538. }
  1539. updateState();
  1540. }
  1541. /**
  1542. * Concatenates an <code>AffineTransform</code> <code>Tx</code> to
  1543. * this <code>AffineTransform</code> Cx
  1544. * in a less commonly used way such that <code>Tx</code> modifies the
  1545. * coordinate transformation relative to the absolute pixel
  1546. * space rather than relative to the existing user space.
  1547. * Cx is updated to perform the combined transformation.
  1548. * Transforming a point p by the updated transform Cx' is
  1549. * equivalent to first transforming p by the original transform
  1550. * Cx and then transforming the result by
  1551. * <code>Tx</code> like this:
  1552. * Cx'(p) = Tx(Cx(p))
  1553. * In matrix notation, if this transform Cx
  1554. * is represented by the matrix [this] and <code>Tx</code> is
  1555. * represented by the matrix [Tx] then this method does the
  1556. * following:
  1557. * <pre>
  1558. * [this] = [Tx] x [this]
  1559. * </pre>
  1560. * @param Tx the <code>AffineTransform</code> object to be
  1561. * concatenated with this <code>AffineTransform</code> object.
  1562. * @see #concatenate
  1563. */
  1564. public void preConcatenate(AffineTransform Tx) {
  1565. double M0, M1;
  1566. double T00, T01, T10, T11;
  1567. double T02, T12;
  1568. int mystate = state;
  1569. int txstate = Tx.state;
  1570. switch ((txstate << HI_SHIFT) | mystate) {
  1571. case (HI_IDENTITY | APPLY_IDENTITY):
  1572. case (HI_IDENTITY | APPLY_TRANSLATE):
  1573. case (HI_IDENTITY | APPLY_SCALE):
  1574. case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
  1575. case (HI_IDENTITY | APPLY_SHEAR):
  1576. case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
  1577. case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
  1578. case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1579. // Tx is IDENTITY...
  1580. return;
  1581. case (HI_TRANSLATE | APPLY_IDENTITY):
  1582. case (HI_TRANSLATE | APPLY_SCALE):
  1583. case (HI_TRANSLATE | APPLY_SHEAR):
  1584. case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
  1585. // Tx is TRANSLATE, this has no TRANSLATE
  1586. m02 = Tx.m02;
  1587. m12 = Tx.m12;
  1588. state = mystate | APPLY_TRANSLATE;
  1589. return;
  1590. case (HI_TRANSLATE | APPLY_TRANSLATE):
  1591. case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
  1592. case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
  1593. case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1594. // Tx is TRANSLATE, this has one too
  1595. m02 = m02 + Tx.m02;
  1596. m12 = m12 + Tx.m12;
  1597. return;
  1598. case (HI_SCALE | APPLY_TRANSLATE):
  1599. case (HI_SCALE | APPLY_IDENTITY):
  1600. // Only these two existing states need a new state
  1601. state = mystate | APPLY_SCALE;
  1602. /* NOBREAK */
  1603. case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1604. case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
  1605. case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
  1606. case (HI_SCALE | APPLY_SHEAR):
  1607. case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
  1608. case (HI_SCALE | APPLY_SCALE):
  1609. // Tx is SCALE, this is anything
  1610. T00 = Tx.m00;
  1611. T11 = Tx.m11;
  1612. if ((mystate & APPLY_SHEAR) != 0) {
  1613. m01 = m01 * T00;
  1614. m10 = m10 * T11;
  1615. if ((mystate & APPLY_SCALE) != 0) {
  1616. m00 = m00 * T00;
  1617. m11 = m11 * T11;
  1618. }
  1619. } else {
  1620. m00 = m00 * T00;
  1621. m11 = m11 * T11;
  1622. }
  1623. if ((mystate & APPLY_TRANSLATE) != 0) {
  1624. m02 = m02 * T00;
  1625. m12 = m12 * T11;
  1626. }
  1627. return;
  1628. case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
  1629. case (HI_SHEAR | APPLY_SHEAR):
  1630. mystate = mystate | APPLY_SCALE;
  1631. /* NOBREAK */
  1632. case (HI_SHEAR | APPLY_TRANSLATE):
  1633. case (HI_SHEAR | APPLY_IDENTITY):
  1634. case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1635. case (HI_SHEAR | APPLY_SCALE):
  1636. state = mystate ^ APPLY_SHEAR;
  1637. /* NOBREAK */
  1638. case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1639. case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
  1640. // Tx is SHEAR, this is anything
  1641. T01 = Tx.m01;
  1642. T10 = Tx.m10;
  1643. M0 = m00;
  1644. m00 = m10 * T01;
  1645. m10 = M0 * T10;
  1646. M0 = m01;
  1647. m01 = m11 * T01;
  1648. m11 = M0 * T10;
  1649. M0 = m02;
  1650. m02 = m12 * T01;
  1651. m12 = M0 * T10;
  1652. return;
  1653. }
  1654. // If Tx has more than one attribute, it is not worth optimizing
  1655. // all of those cases...
  1656. T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;
  1657. T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;
  1658. switch (mystate) {
  1659. default:
  1660. stateError();
  1661. /* NOTREACHED */
  1662. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1663. M0 = m02;
  1664. M1 = m12;
  1665. T02 += M0 * T00 + M1 * T01;
  1666. T12 += M0 * T10 + M1 * T11;
  1667. /* NOBREAK */
  1668. case (APPLY_SHEAR | APPLY_SCALE):
  1669. m02 = T02;
  1670. m12 = T12;
  1671. M0 = m00;
  1672. M1 = m10;
  1673. m00 = M0 * T00 + M1 * T01;
  1674. m10 = M0 * T10 + M1 * T11;
  1675. M0 = m01;
  1676. M1 = m11;
  1677. m01 = M0 * T00 + M1 * T01;
  1678. m11 = M0 * T10 + M1 * T11;
  1679. break;
  1680. case (APPLY_SHEAR | APPLY_TRANSLATE):
  1681. M0 = m02;
  1682. M1 = m12;
  1683. T02 += M0 * T00 + M1 * T01;
  1684. T12 += M0 * T10 + M1 * T11;
  1685. /* NOBREAK */
  1686. case (APPLY_SHEAR):
  1687. m02 = T02;
  1688. m12 = T12;
  1689. M0 = m10;
  1690. m00 = M0 * T01;
  1691. m10 = M0 * T11;
  1692. M0 = m01;
  1693. m01 = M0 * T00;
  1694. m11 = M0 * T10;
  1695. break;
  1696. case (APPLY_SCALE | APPLY_TRANSLATE):
  1697. M0 = m02;
  1698. M1 = m12;
  1699. T02 += M0 * T00 + M1 * T01;
  1700. T12 += M0 * T10 + M1 * T11;
  1701. /* NOBREAK */
  1702. case (APPLY_SCALE):
  1703. m02 = T02;
  1704. m12 = T12;
  1705. M0 = m00;
  1706. m00 = M0 * T00;
  1707. m10 = M0 * T10;
  1708. M0 = m11;
  1709. m01 = M0 * T01;
  1710. m11 = M0 * T11;
  1711. break;
  1712. case (APPLY_TRANSLATE):
  1713. M0 = m02;
  1714. M1 = m12;
  1715. T02 += M0 * T00 + M1 * T01;
  1716. T12 += M0 * T10 + M1 * T11;
  1717. /* NOBREAK */
  1718. case (APPLY_IDENTITY):
  1719. m02 = T02;
  1720. m12 = T12;
  1721. m00 = T00;
  1722. m10 = T10;
  1723. m01 = T01;
  1724. m11 = T11;
  1725. state = mystate | txstate;
  1726. return;
  1727. }
  1728. updateState();
  1729. }
  1730. /**
  1731. * Returns an <code>AffineTransform</code> object representing the
  1732. * inverse transformation.
  1733. * The inverse transform Tx' of this transform Tx
  1734. * maps coordinates transformed by Tx back
  1735. * to their original coordinates.
  1736. * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
  1737. * <p>
  1738. * If this transform maps all coordinates onto a point or a line
  1739. * then it will not have an inverse, since coordinates that do
  1740. * not lie on the destination point or line will not have an inverse
  1741. * mapping.
  1742. * The <code>getDeterminant</code> method can be used to determine if this
  1743. * transform has no inverse, in which case an exception will be
  1744. * thrown if the <code>createInverse</code> method is called.
  1745. * @return a new <code>AffineTransform</code> object representing the
  1746. * inverse transformation.
  1747. * @see #getDeterminant
  1748. * @exception NoninvertibleTransformException
  1749. * if the matrix cannot be inverted.
  1750. */
  1751. public AffineTransform createInverse()
  1752. throws NoninvertibleTransformException
  1753. {
  1754. double det;
  1755. switch (state) {
  1756. default:
  1757. stateError();
  1758. /* NOTREACHED */
  1759. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1760. det = m00 * m11 - m01 * m10;
  1761. if (Math.abs(det) <= Double.MIN_VALUE) {
  1762. throw new NoninvertibleTransformException("Determinant is "+
  1763. det);
  1764. }
  1765. return new AffineTransform( m11 / det, -m10 / det,
  1766. -m01 / det, m00 / det,
  1767. (m01 * m12 - m11 * m02) / det,
  1768. (m10 * m02 - m00 * m12) / det,
  1769. (APPLY_SHEAR |
  1770. APPLY_SCALE |
  1771. APPLY_TRANSLATE));
  1772. case (APPLY_SHEAR | APPLY_SCALE):
  1773. det = m00 * m11 - m01 * m10;
  1774. if (Math.abs(det) <= Double.MIN_VALUE) {
  1775. throw new NoninvertibleTransformException("Determinant is "+
  1776. det);
  1777. }
  1778. return new AffineTransform( m11 / det, -m10 / det,
  1779. -m01 / det, m00 / det,
  1780. 0.0, 0.0,
  1781. (APPLY_SHEAR | APPLY_SCALE));
  1782. case (APPLY_SHEAR | APPLY_TRANSLATE):
  1783. if (m01 == 0.0 || m10 == 0.0) {
  1784. throw new NoninvertibleTransformException("Determinant is 0");
  1785. }
  1786. return new AffineTransform( 0.0, 1.0 / m01,
  1787. 1.0 / m10, 0.0,
  1788. -m12 / m10, -m02 / m01,
  1789. (APPLY_SHEAR | APPLY_TRANSLATE));
  1790. case (APPLY_SHEAR):
  1791. if (m01 == 0.0 || m10 == 0.0) {
  1792. throw new NoninvertibleTransformException("Determinant is 0");
  1793. }
  1794. return new AffineTransform(0.0, 1.0 / m01,
  1795. 1.0 / m10, 0.0,
  1796. 0.0, 0.0,
  1797. (APPLY_SHEAR));
  1798. case (APPLY_SCALE | APPLY_TRANSLATE):
  1799. if (m00 == 0.0 || m11 == 0.0) {
  1800. throw new NoninvertibleTransformException("Determinant is 0");
  1801. }
  1802. return new AffineTransform( 1.0 / m00, 0.0,
  1803. 0.0, 1.0 / m11,
  1804. -m02 / m00, -m12 / m11,
  1805. (APPLY_SCALE | APPLY_TRANSLATE));
  1806. case (APPLY_SCALE):
  1807. if (m00 == 0.0 || m11 == 0.0) {
  1808. throw new NoninvertibleTransformException("Determinant is 0");
  1809. }
  1810. return new AffineTransform(1.0 / m00, 0.0,
  1811. 0.0, 1.0 / m11,
  1812. 0.0, 0.0,
  1813. (APPLY_SCALE));
  1814. case (APPLY_TRANSLATE):
  1815. return new AffineTransform( 1.0, 0.0,
  1816. 0.0, 1.0,
  1817. -m02, -m12,
  1818. (APPLY_TRANSLATE));
  1819. case (APPLY_IDENTITY):
  1820. return new AffineTransform();
  1821. }
  1822. /* NOTREACHED */
  1823. }
  1824. /**
  1825. * Transforms the specified <code>ptSrc</code> and stores the result
  1826. * in <code>ptDst</code>.
  1827. * If <code>ptDst</code> is <code>null</code>, a new {@link Point2D}
  1828. * object is allocated and then the result of the transformation is
  1829. * stored in this object.
  1830. * In either case, <code>ptDst</code>, which contains the
  1831. * transformed point, is returned for convenience.
  1832. * If <code>ptSrc</code> and <code>ptDst</code> are the same
  1833. * object, the input point is correctly overwritten with
  1834. * the transformed point.
  1835. * @param ptSrc the specified <code>Point2D</code> to be transformed
  1836. * @param ptDst the specified <code>Point2D</code> that stores the
  1837. * result of transforming <code>ptSrc</code>
  1838. * @return the <code>ptDst</code> after transforming
  1839. * <code>ptSrc</code> and stroring the result in <code>ptDst</code>.
  1840. */
  1841. public Point2D transform(Point2D ptSrc, Point2D ptDst) {
  1842. if (ptDst == null) {
  1843. if (ptSrc instanceof Point2D.Double) {
  1844. ptDst = new Point2D.Double();
  1845. } else {
  1846. ptDst = new Point2D.Float();
  1847. }
  1848. }
  1849. // Copy source coords into local variables in case src == dst
  1850. double x = ptSrc.getX();
  1851. double y = ptSrc.getY();
  1852. switch (state) {
  1853. default:
  1854. stateError();
  1855. /* NOTREACHED */
  1856. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1857. ptDst.setLocation(x * m00 + y * m01 + m02,
  1858. x * m10 + y * m11 + m12);
  1859. return ptDst;
  1860. case (APPLY_SHEAR | APPLY_SCALE):
  1861. ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
  1862. return ptDst;
  1863. case (APPLY_SHEAR | APPLY_TRANSLATE):
  1864. ptDst.setLocation(y * m01 + m02, x * m10 + m12);
  1865. return ptDst;
  1866. case (APPLY_SHEAR):
  1867. ptDst.setLocation(y * m01, x * m10);
  1868. return ptDst;
  1869. case (APPLY_SCALE | APPLY_TRANSLATE):
  1870. ptDst.setLocation(x * m00 + m02, y * m11 + m12);
  1871. return ptDst;
  1872. case (APPLY_SCALE):
  1873. ptDst.setLocation(x * m00, y * m11);
  1874. return ptDst;
  1875. case (APPLY_TRANSLATE):
  1876. ptDst.setLocation(x + m02, y + m12);
  1877. return ptDst;
  1878. case (APPLY_IDENTITY):
  1879. ptDst.setLocation(x, y);
  1880. return ptDst;
  1881. }
  1882. /* NOTREACHED */
  1883. }
  1884. /**
  1885. * Transforms an array of point objects by this transform.
  1886. * If any element of the <code>ptDst</code> array is
  1887. * <code>null</code>, a new <code>Point2D</code> object is allocated
  1888. * and stored into that element before storing the results of the
  1889. * transformation.
  1890. * <p>
  1891. * Note that this method does not take any precautions to
  1892. * avoid problems caused by storing results into <code>Point2D</code>
  1893. * objects that will be used as the source for calculations
  1894. * further down the source array.
  1895. * This method does guarantee that if a specified <code>Point2D</code>
  1896. * object is both the source and destination for the same single point
  1897. * transform operation then the results will not be stored until
  1898. * the calculations are complete to avoid storing the results on
  1899. * top of the operands.
  1900. * If, however, the destination <code>Point2D</code> object for one
  1901. * operation is the same object as the source <code>Point2D</code>
  1902. * object for another operation further down the source array then
  1903. * the original coordinates in that point are overwritten before
  1904. * they can be converted.
  1905. * @param ptSrc the array containing the source point objects
  1906. * @param ptDst the array into which the transform point objects are
  1907. * returned
  1908. * @param srcOff the offset to the first point object to be
  1909. * transformed in the source array
  1910. * @param dstOff the offset to the location of the first
  1911. * transformed point object that is stored in the destination array
  1912. * @param numPts the number of point objects to be transformed
  1913. */
  1914. public void transform(Point2D[] ptSrc, int srcOff,
  1915. Point2D[] ptDst, int dstOff,
  1916. int numPts) {
  1917. int state = this.state;
  1918. while (--numPts >= 0) {
  1919. // Copy source coords into local variables in case src == dst
  1920. Point2D src = ptSrc[srcOff++];
  1921. double x = src.getX();
  1922. double y = src.getY();
  1923. Point2D dst = ptDst[dstOff++];
  1924. if (dst == null) {
  1925. if (src instanceof Point2D.Double) {
  1926. dst = new Point2D.Double();
  1927. } else {
  1928. dst = new Point2D.Float();
  1929. }
  1930. ptDst[dstOff - 1] = dst;
  1931. }
  1932. switch (state) {
  1933. default:
  1934. stateError();
  1935. /* NOTREACHED */
  1936. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  1937. dst.setLocation(x * m00 + y * m01 + m02,
  1938. x * m10 + y * m11 + m12);
  1939. break;
  1940. case (APPLY_SHEAR | APPLY_SCALE):
  1941. dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
  1942. break;
  1943. case (APPLY_SHEAR | APPLY_TRANSLATE):
  1944. dst.setLocation(y * m01 + m02, x * m10 + m12);
  1945. break;
  1946. case (APPLY_SHEAR):
  1947. dst.setLocation(y * m01, x * m10);
  1948. break;
  1949. case (APPLY_SCALE | APPLY_TRANSLATE):
  1950. dst.setLocation(x * m00 + m02, y * m11 + m12);
  1951. break;
  1952. case (APPLY_SCALE):
  1953. dst.setLocation(x * m00, y * m11);
  1954. break;
  1955. case (APPLY_TRANSLATE):
  1956. dst.setLocation(x + m02, y + m12);
  1957. break;
  1958. case (APPLY_IDENTITY):
  1959. dst.setLocation(x, y);
  1960. break;
  1961. }
  1962. }
  1963. /* NOTREACHED */
  1964. }
  1965. /**
  1966. * Transforms an array of floating point coordinates by this transform.
  1967. * The two coordinate array sections can be exactly the same or
  1968. * can be overlapping sections of the same array without affecting the
  1969. * validity of the results.
  1970. * This method ensures that no source coordinates are overwritten by a
  1971. * previous operation before they can be transformed.
  1972. * The coordinates are stored in the arrays starting at the specified
  1973. * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
  1974. * @param ptSrc the array containing the source point coordinates.
  1975. * Each point is stored as a pair of x, y coordinates.
  1976. * @param ptDst the array into which the transformed point coordinates
  1977. * are returned. Each point is stored as a pair of x, y
  1978. * coordinates.
  1979. * @param srcOff the offset to the first point to be transformed
  1980. * in the source array
  1981. * @param dstOff the offset to the location of the first
  1982. * transformed point that is stored in the destination array
  1983. * @param numPts the number of points to be transformed
  1984. */
  1985. public void transform(float[] srcPts, int srcOff,
  1986. float[] dstPts, int dstOff,
  1987. int numPts) {
  1988. double M00, M01, M02, M10, M11, M12; // For caching
  1989. if (dstPts == srcPts &&
  1990. dstOff > srcOff && dstOff < srcOff + numPts * 2)
  1991. {
  1992. // If the arrays overlap partially with the destination higher
  1993. // than the source and we transform the coordinates normally
  1994. // we would overwrite some of the later source coordinates
  1995. // with results of previous transformations.
  1996. // To get around this we use arraycopy to copy the points
  1997. // to their final destination with correct overwrite
  1998. // handling and then transform them in place in the new
  1999. // safer location.
  2000. System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
  2001. // srcPts = dstPts; // They are known to be equal.
  2002. srcOff = dstOff;
  2003. }
  2004. switch (state) {
  2005. default:
  2006. stateError();
  2007. /* NOTREACHED */
  2008. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  2009. M00 = m00; M01 = m01; M02 = m02;
  2010. M10 = m10; M11 = m11; M12 = m12;
  2011. while (--numPts >= 0) {
  2012. double x = srcPts[srcOff++];
  2013. double y = srcPts[srcOff++];
  2014. dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
  2015. dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
  2016. }
  2017. return;
  2018. case (APPLY_SHEAR | APPLY_SCALE):
  2019. M00 = m00; M01 = m01;
  2020. M10 = m10; M11 = m11;
  2021. while (--numPts >= 0) {
  2022. double x = srcPts[srcOff++];
  2023. double y = srcPts[srcOff++];
  2024. dstPts[dstOff++] = (float) (M00 * x + M01 * y);
  2025. dstPts[dstOff++] = (float) (M10 * x + M11 * y);
  2026. }
  2027. return;
  2028. case (APPLY_SHEAR | APPLY_TRANSLATE):
  2029. M01 = m01; M02 = m02;
  2030. M10 = m10; M12 = m12;
  2031. while (--numPts >= 0) {
  2032. double x = srcPts[srcOff++];
  2033. dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
  2034. dstPts[dstOff++] = (float) (M10 * x + M12);
  2035. }
  2036. return;
  2037. case (APPLY_SHEAR):
  2038. M01 = m01; M10 = m10;
  2039. while (--numPts >= 0) {
  2040. double x = srcPts[srcOff++];
  2041. dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
  2042. dstPts[dstOff++] = (float) (M10 * x);
  2043. }
  2044. return;
  2045. case (APPLY_SCALE | APPLY_TRANSLATE):
  2046. M00 = m00; M02 = m02;
  2047. M11 = m11; M12 = m12;
  2048. while (--numPts >= 0) {
  2049. dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
  2050. dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
  2051. }
  2052. return;
  2053. case (APPLY_SCALE):
  2054. M00 = m00; M11 = m11;
  2055. while (--numPts >= 0) {
  2056. dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
  2057. dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
  2058. }
  2059. return;
  2060. case (APPLY_TRANSLATE):
  2061. M02 = m02; M12 = m12;
  2062. while (--numPts >= 0) {
  2063. dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
  2064. dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
  2065. }
  2066. return;
  2067. case (APPLY_IDENTITY):
  2068. if (srcPts != dstPts || srcOff != dstOff) {
  2069. System.arraycopy(srcPts, srcOff, dstPts, dstOff,
  2070. numPts * 2);
  2071. }
  2072. return;
  2073. }
  2074. /* NOTREACHED */
  2075. }
  2076. /**
  2077. * Transforms an array of double precision coordinates by this transform.
  2078. * The two coordinate array sections can be exactly the same or
  2079. * can be overlapping sections of the same array without affecting the
  2080. * validity of the results.
  2081. * This method ensures that no source coordinates are
  2082. * overwritten by a previous operation before they can be transformed.
  2083. * The coordinates are stored in the arrays starting at the indicated
  2084. * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
  2085. * @param srcPts the array containing the source point coordinates.
  2086. * Each point is stored as a pair of x, y coordinates.
  2087. * @param dstPts the array into which the transformed point
  2088. * coordinates are returned. Each point is stored as a pair of
  2089. * x, y coordinates.
  2090. * @param srcOff the offset to the first point to be transformed
  2091. * in the source array
  2092. * @param dstOff the offset to the location of the first
  2093. * transformed point that is stored in the destination array
  2094. * @param numPts the number of point objects to be transformed
  2095. */
  2096. public void transform(double[] srcPts, int srcOff,
  2097. double[] dstPts, int dstOff,
  2098. int numPts) {
  2099. double M00, M01, M02, M10, M11, M12; // For caching
  2100. if (dstPts == srcPts &&
  2101. dstOff > srcOff && dstOff < srcOff + numPts * 2)
  2102. {
  2103. // If the arrays overlap partially with the destination higher
  2104. // than the source and we transform the coordinates normally
  2105. // we would overwrite some of the later source coordinates
  2106. // with results of previous transformations.
  2107. // To get around this we use arraycopy to copy the points
  2108. // to their final destination with correct overwrite
  2109. // handling and then transform them in place in the new
  2110. // safer location.
  2111. System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
  2112. // srcPts = dstPts; // They are known to be equal.
  2113. srcOff = dstOff;
  2114. }
  2115. switch (state) {
  2116. default:
  2117. stateError();
  2118. /* NOTREACHED */
  2119. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  2120. M00 = m00; M01 = m01; M02 = m02;
  2121. M10 = m10; M11 = m11; M12 = m12;
  2122. while (--numPts >= 0) {
  2123. double x = srcPts[srcOff++];
  2124. double y = srcPts[srcOff++];
  2125. dstPts[dstOff++] = M00 * x + M01 * y + M02;
  2126. dstPts[dstOff++] = M10 * x + M11 * y + M12;
  2127. }
  2128. return;
  2129. case (APPLY_SHEAR | APPLY_SCALE):
  2130. M00 = m00; M01 = m01;
  2131. M10 = m10; M11 = m11;
  2132. while (--numPts >= 0) {
  2133. double x = srcPts[srcOff++];
  2134. double y = srcPts[srcOff++];
  2135. dstPts[dstOff++] = M00 * x + M01 * y;
  2136. dstPts[dstOff++] = M10 * x + M11 * y;
  2137. }
  2138. return;
  2139. case (APPLY_SHEAR | APPLY_TRANSLATE):
  2140. M01 = m01; M02 = m02;
  2141. M10 = m10; M12 = m12;
  2142. while (--numPts >= 0) {
  2143. double x = srcPts[srcOff++];
  2144. dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
  2145. dstPts[dstOff++] = M10 * x + M12;
  2146. }
  2147. return;
  2148. case (APPLY_SHEAR):
  2149. M01 = m01; M10 = m10;
  2150. while (--numPts >= 0) {
  2151. double x = srcPts[srcOff++];
  2152. dstPts[dstOff++] = M01 * srcPts[srcOff++];
  2153. dstPts[dstOff++] = M10 * x;
  2154. }
  2155. return;
  2156. case (APPLY_SCALE | APPLY_TRANSLATE):
  2157. M00 = m00; M02 = m02;
  2158. M11 = m11; M12 = m12;
  2159. while (--numPts >= 0) {
  2160. dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
  2161. dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
  2162. }
  2163. return;
  2164. case (APPLY_SCALE):
  2165. M00 = m00; M11 = m11;
  2166. while (--numPts >= 0) {
  2167. dstPts[dstOff++] = M00 * srcPts[srcOff++];
  2168. dstPts[dstOff++] = M11 * srcPts[srcOff++];
  2169. }
  2170. return;
  2171. case (APPLY_TRANSLATE):
  2172. M02 = m02; M12 = m12;
  2173. while (--numPts >= 0) {
  2174. dstPts[dstOff++] = srcPts[srcOff++] + M02;
  2175. dstPts[dstOff++] = srcPts[srcOff++] + M12;
  2176. }
  2177. return;
  2178. case (APPLY_IDENTITY):
  2179. if (srcPts != dstPts || srcOff != dstOff) {
  2180. System.arraycopy(srcPts, srcOff, dstPts, dstOff,
  2181. numPts * 2);
  2182. }
  2183. return;
  2184. }
  2185. /* NOTREACHED */
  2186. }
  2187. /**
  2188. * Transforms an array of floating point coordinates by this transform
  2189. * and stores the results into an array of doubles.
  2190. * The coordinates are stored in the arrays starting at the specified
  2191. * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
  2192. * @param ptSrc the array containing the source point coordinates.
  2193. * Each point is stored as a pair of x, y coordinates.
  2194. * @param ptDst the array into which the transformed point coordinates
  2195. * are returned. Each point is stored as a pair of x, y
  2196. * coordinates.
  2197. * @param srcOff the offset to the first point to be transformed
  2198. * in the source array
  2199. * @param dstOff the offset to the location of the first
  2200. * transformed point that is stored in the destination array
  2201. * @param numPts the number of points to be transformed
  2202. */
  2203. public void transform(float[] srcPts, int srcOff,
  2204. double[] dstPts, int dstOff,
  2205. int numPts) {
  2206. double M00, M01, M02, M10, M11, M12; // For caching
  2207. switch (state) {
  2208. default:
  2209. stateError();
  2210. /* NOTREACHED */
  2211. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  2212. M00 = m00; M01 = m01; M02 = m02;
  2213. M10 = m10; M11 = m11; M12 = m12;
  2214. while (--numPts >= 0) {
  2215. double x = srcPts[srcOff++];
  2216. double y = srcPts[srcOff++];
  2217. dstPts[dstOff++] = M00 * x + M01 * y + M02;
  2218. dstPts[dstOff++] = M10 * x + M11 * y + M12;
  2219. }
  2220. return;
  2221. case (APPLY_SHEAR | APPLY_SCALE):
  2222. M00 = m00; M01 = m01;
  2223. M10 = m10; M11 = m11;
  2224. while (--numPts >= 0) {
  2225. double x = srcPts[srcOff++];
  2226. double y = srcPts[srcOff++];
  2227. dstPts[dstOff++] = M00 * x + M01 * y;
  2228. dstPts[dstOff++] = M10 * x + M11 * y;
  2229. }
  2230. return;
  2231. case (APPLY_SHEAR | APPLY_TRANSLATE):
  2232. M01 = m01; M02 = m02;
  2233. M10 = m10; M12 = m12;
  2234. while (--numPts >= 0) {
  2235. double x = srcPts[srcOff++];
  2236. dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
  2237. dstPts[dstOff++] = M10 * x + M12;
  2238. }
  2239. return;
  2240. case (APPLY_SHEAR):
  2241. M01 = m01; M10 = m10;
  2242. while (--numPts >= 0) {
  2243. double x = srcPts[srcOff++];
  2244. dstPts[dstOff++] = M01 * srcPts[srcOff++];
  2245. dstPts[dstOff++] = M10 * x;
  2246. }
  2247. return;
  2248. case (APPLY_SCALE | APPLY_TRANSLATE):
  2249. M00 = m00; M02 = m02;
  2250. M11 = m11; M12 = m12;
  2251. while (--numPts >= 0) {
  2252. dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
  2253. dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
  2254. }
  2255. return;
  2256. case (APPLY_SCALE):
  2257. M00 = m00; M11 = m11;
  2258. while (--numPts >= 0) {
  2259. dstPts[dstOff++] = M00 * srcPts[srcOff++];
  2260. dstPts[dstOff++] = M11 * srcPts[srcOff++];
  2261. }
  2262. return;
  2263. case (APPLY_TRANSLATE):
  2264. M02 = m02; M12 = m12;
  2265. while (--numPts >= 0) {
  2266. dstPts[dstOff++] = srcPts[srcOff++] + M02;
  2267. dstPts[dstOff++] = srcPts[srcOff++] + M12;
  2268. }
  2269. return;
  2270. case (APPLY_IDENTITY):
  2271. while (--numPts >= 0) {
  2272. dstPts[dstOff++] = srcPts[srcOff++];
  2273. dstPts[dstOff++] = srcPts[srcOff++];
  2274. }
  2275. return;
  2276. }
  2277. /* NOTREACHED */
  2278. }
  2279. /**
  2280. * Transforms an array of double precision coordinates by this transform
  2281. * and stores the results into an array of floats.
  2282. * The coordinates are stored in the arrays starting at the specified
  2283. * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
  2284. * @param srcPts the array containing the source point coordinates.
  2285. * Each point is stored as a pair of x, y coordinates.
  2286. * @param dstPts the array into which the transformed point
  2287. * coordinates are returned. Each point is stored as a pair of
  2288. * x, y coordinates.
  2289. * @param srcOff the offset to the first point to be transformed
  2290. * in the source array
  2291. * @param dstOff the offset to the location of the first
  2292. * transformed point that is stored in the destination array
  2293. * @param numPts the number of point objects to be transformed
  2294. */
  2295. public void transform(double[] srcPts, int srcOff,
  2296. float[] dstPts, int dstOff,
  2297. int numPts) {
  2298. double M00, M01, M02, M10, M11, M12; // For caching
  2299. switch (state) {
  2300. default:
  2301. stateError();
  2302. /* NOTREACHED */
  2303. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  2304. M00 = m00; M01 = m01; M02 = m02;
  2305. M10 = m10; M11 = m11; M12 = m12;
  2306. while (--numPts >= 0) {
  2307. double x = srcPts[srcOff++];
  2308. double y = srcPts[srcOff++];
  2309. dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
  2310. dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
  2311. }
  2312. return;
  2313. case (APPLY_SHEAR | APPLY_SCALE):
  2314. M00 = m00; M01 = m01;
  2315. M10 = m10; M11 = m11;
  2316. while (--numPts >= 0) {
  2317. double x = srcPts[srcOff++];
  2318. double y = srcPts[srcOff++];
  2319. dstPts[dstOff++] = (float) (M00 * x + M01 * y);
  2320. dstPts[dstOff++] = (float) (M10 * x + M11 * y);
  2321. }
  2322. return;
  2323. case (APPLY_SHEAR | APPLY_TRANSLATE):
  2324. M01 = m01; M02 = m02;
  2325. M10 = m10; M12 = m12;
  2326. while (--numPts >= 0) {
  2327. double x = srcPts[srcOff++];
  2328. dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
  2329. dstPts[dstOff++] = (float) (M10 * x + M12);
  2330. }
  2331. return;
  2332. case (APPLY_SHEAR):
  2333. M01 = m01; M10 = m10;
  2334. while (--numPts >= 0) {
  2335. double x = srcPts[srcOff++];
  2336. dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
  2337. dstPts[dstOff++] = (float) (M10 * x);
  2338. }
  2339. return;
  2340. case (APPLY_SCALE | APPLY_TRANSLATE):
  2341. M00 = m00; M02 = m02;
  2342. M11 = m11; M12 = m12;
  2343. while (--numPts >= 0) {
  2344. dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
  2345. dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
  2346. }
  2347. return;
  2348. case (APPLY_SCALE):
  2349. M00 = m00; M11 = m11;
  2350. while (--numPts >= 0) {
  2351. dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
  2352. dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
  2353. }
  2354. return;
  2355. case (APPLY_TRANSLATE):
  2356. M02 = m02; M12 = m12;
  2357. while (--numPts >= 0) {
  2358. dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
  2359. dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
  2360. }
  2361. return;
  2362. case (APPLY_IDENTITY):
  2363. while (--numPts >= 0) {
  2364. dstPts[dstOff++] = (float) (srcPts[srcOff++]);
  2365. dstPts[dstOff++] = (float) (srcPts[srcOff++]);
  2366. }
  2367. return;
  2368. }
  2369. /* NOTREACHED */
  2370. }
  2371. /**
  2372. * Inverse transforms the specified <code>ptSrc</code> and stores the
  2373. * result in <code>ptDst</code>.
  2374. * If <code>ptDst</code> is <code>null</code>, a new
  2375. * <code>Point2D</code> object is allocated and then the result of the
  2376. * transform is stored in this object.
  2377. * In either case, <code>ptDst</code>, which contains the transformed
  2378. * point, is returned for convenience.
  2379. * If <code>ptSrc</code> and <code>ptDst</code> are the same
  2380. * object, the input point is correctly overwritten with the
  2381. * transformed point.
  2382. * @param ptSrc the point to be inverse transformed
  2383. * @param ptDst the resulting transformed point
  2384. * @return <code>ptDst</code>, which contains the result of the
  2385. * inverse transform.
  2386. * @exception NoninvertibleTransformException if the matrix cannot be
  2387. * inverted.
  2388. */
  2389. public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst)
  2390. throws NoninvertibleTransformException
  2391. {
  2392. if (ptDst == null) {
  2393. if (ptSrc instanceof Point2D.Double) {
  2394. ptDst = new Point2D.Double();
  2395. } else {
  2396. ptDst = new Point2D.Float();
  2397. }
  2398. }
  2399. // Copy source coords into local variables in case src == dst
  2400. double x = ptSrc.getX();
  2401. double y = ptSrc.getY();
  2402. switch (state) {
  2403. default:
  2404. stateError();
  2405. /* NOTREACHED */
  2406. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  2407. x -= m02;
  2408. y -= m12;
  2409. /* NOBREAK */
  2410. case (APPLY_SHEAR | APPLY_SCALE):
  2411. double det = m00 * m11 - m01 * m10;
  2412. if (Math.abs(det) <= Double.MIN_VALUE) {
  2413. throw new NoninvertibleTransformException("Determinant is "+
  2414. det);
  2415. }
  2416. ptDst.setLocation((x * m11 - y * m01) / det,
  2417. (y * m00 - x * m10) / det);
  2418. return ptDst;
  2419. case (APPLY_SHEAR | APPLY_TRANSLATE):
  2420. x -= m02;
  2421. y -= m12;
  2422. /* NOBREAK */
  2423. case (APPLY_SHEAR):
  2424. if (m01 == 0.0 || m10 == 0.0) {
  2425. throw new NoninvertibleTransformException("Determinant is 0");
  2426. }
  2427. ptDst.setLocation(y / m10, x / m01);
  2428. return ptDst;
  2429. case (APPLY_SCALE | APPLY_TRANSLATE):
  2430. x -= m02;
  2431. y -= m12;
  2432. /* NOBREAK */
  2433. case (APPLY_SCALE):
  2434. if (m00 == 0.0 || m11 == 0.0) {
  2435. throw new NoninvertibleTransformException("Determinant is 0");
  2436. }
  2437. ptDst.setLocation(x / m00, y / m11);
  2438. return ptDst;
  2439. case (APPLY_TRANSLATE):
  2440. ptDst.setLocation(x - m02, y - m12);
  2441. return ptDst;
  2442. case (APPLY_IDENTITY):
  2443. ptDst.setLocation(x, y);
  2444. return ptDst;
  2445. }
  2446. /* NOTREACHED */
  2447. }
  2448. /**
  2449. * Inverse transforms an array of double precision coordinates by
  2450. * this transform.
  2451. * The two coordinate array sections can be exactly the same or
  2452. * can be overlapping sections of the same array without affecting the
  2453. * validity of the results.
  2454. * This method ensures that no source coordinates are
  2455. * overwritten by a previous operation before they can be transformed.
  2456. * The coordinates are stored in the arrays starting at the specified
  2457. * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
  2458. * @param srcPts the array containing the source point coordinates.
  2459. * Each point is stored as a pair of x, y coordinates.
  2460. * @param dstPts the array into which the transformed point
  2461. * coordinates are returned. Each point is stored as a pair of
  2462. * x, y coordinates.
  2463. * @param srcOff the offset to the first point to be transformed
  2464. * in the source array
  2465. * @param dstOff the offset to the location of the first
  2466. * transformed point that is stored in the destination array
  2467. * @param numPts the number of point objects to be transformed
  2468. * @exception NoninvertibleTransformException if the matrix cannot be
  2469. * inverted.
  2470. */
  2471. public void inverseTransform(double[] srcPts, int srcOff,
  2472. double[] dstPts, int dstOff,
  2473. int numPts)
  2474. throws NoninvertibleTransformException
  2475. {
  2476. double M00, M01, M02, M10, M11, M12; // For caching
  2477. double det;
  2478. if (dstPts == srcPts &&
  2479. dstOff > srcOff && dstOff < srcOff + numPts * 2)
  2480. {
  2481. // If the arrays overlap partially with the destination higher
  2482. // than the source and we transform the coordinates normally
  2483. // we would overwrite some of the later source coordinates
  2484. // with results of previous transformations.
  2485. // To get around this we use arraycopy to copy the points
  2486. // to their final destination with correct overwrite
  2487. // handling and then transform them in place in the new
  2488. // safer location.
  2489. System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
  2490. // srcPts = dstPts; // They are known to be equal.
  2491. srcOff = dstOff;
  2492. }
  2493. switch (state) {
  2494. default:
  2495. stateError();
  2496. /* NOTREACHED */
  2497. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  2498. M00 = m00; M01 = m01; M02 = m02;
  2499. M10 = m10; M11 = m11; M12 = m12;
  2500. det = M00 * M11 - M01 * M10;
  2501. if (Math.abs(det) <= Double.MIN_VALUE) {
  2502. throw new NoninvertibleTransformException("Determinant is "+
  2503. det);
  2504. }
  2505. while (--numPts >= 0) {
  2506. double x = srcPts[srcOff++] - M02;
  2507. double y = srcPts[srcOff++] - M12;
  2508. dstPts[dstOff++] = (x * M11 - y * M01) / det;
  2509. dstPts[dstOff++] = (y * M00 - x * M10) / det;
  2510. }
  2511. return;
  2512. case (APPLY_SHEAR | APPLY_SCALE):
  2513. M00 = m00; M01 = m01;
  2514. M10 = m10; M11 = m11;
  2515. det = M00 * M11 - M01 * M10;
  2516. if (Math.abs(det) <= Double.MIN_VALUE) {
  2517. throw new NoninvertibleTransformException("Determinant is "+
  2518. det);
  2519. }
  2520. while (--numPts >= 0) {
  2521. double x = srcPts[srcOff++];
  2522. double y = srcPts[srcOff++];
  2523. dstPts[dstOff++] = (x * M11 - y * M01) / det;
  2524. dstPts[dstOff++] = (y * M00 - x * M10) / det;
  2525. }
  2526. return;
  2527. case (APPLY_SHEAR | APPLY_TRANSLATE):
  2528. M01 = m01; M02 = m02;
  2529. M10 = m10; M12 = m12;
  2530. if (M01 == 0.0 || M10 == 0.0) {
  2531. throw new NoninvertibleTransformException("Determinant is 0");
  2532. }
  2533. while (--numPts >= 0) {
  2534. double x = srcPts[srcOff++] - M02;
  2535. dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M01;
  2536. dstPts[dstOff++] = x / M10;
  2537. }
  2538. return;
  2539. case (APPLY_SHEAR):
  2540. M01 = m01; M10 = m10;
  2541. if (M01 == 0.0 || M10 == 0.0) {
  2542. throw new NoninvertibleTransformException("Determinant is 0");
  2543. }
  2544. while (--numPts >= 0) {
  2545. double x = srcPts[srcOff++];
  2546. dstPts[dstOff++] = srcPts[srcOff++] / M01;
  2547. dstPts[dstOff++] = x / M10;
  2548. }
  2549. return;
  2550. case (APPLY_SCALE | APPLY_TRANSLATE):
  2551. M00 = m00; M02 = m02;
  2552. M11 = m11; M12 = m12;
  2553. if (M00 == 0.0 || M11 == 0.0) {
  2554. throw new NoninvertibleTransformException("Determinant is 0");
  2555. }
  2556. while (--numPts >= 0) {
  2557. dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00;
  2558. dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11;
  2559. }
  2560. return;
  2561. case (APPLY_SCALE):
  2562. M00 = m00; M11 = m11;
  2563. if (M00 == 0.0 || M11 == 0.0) {
  2564. throw new NoninvertibleTransformException("Determinant is 0");
  2565. }
  2566. while (--numPts >= 0) {
  2567. dstPts[dstOff++] = srcPts[srcOff++] / M00;
  2568. dstPts[dstOff++] = srcPts[srcOff++] / M11;
  2569. }
  2570. return;
  2571. case (APPLY_TRANSLATE):
  2572. M02 = m02; M12 = m12;
  2573. while (--numPts >= 0) {
  2574. dstPts[dstOff++] = srcPts[srcOff++] - M02;
  2575. dstPts[dstOff++] = srcPts[srcOff++] - M12;
  2576. }
  2577. return;
  2578. case (APPLY_IDENTITY):
  2579. if (srcPts != dstPts || srcOff != dstOff) {
  2580. System.arraycopy(srcPts, srcOff, dstPts, dstOff,
  2581. numPts * 2);
  2582. }
  2583. return;
  2584. }
  2585. /* NOTREACHED */
  2586. }
  2587. /**
  2588. * Transforms the relative distance vector specified by
  2589. * <code>ptSrc</code> and stores the result in <code>ptDst</code>.
  2590. * A relative distance vector is transformed without applying the
  2591. * translation components of the affine transformation matrix
  2592. * using the following equations:
  2593. * <pre>
  2594. * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ]
  2595. * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ]
  2596. * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ]
  2597. * </pre>
  2598. * If <code>ptDst</code> is <code>null</code>, a new
  2599. * <code>Point2D</code> object is allocated and then the result of the
  2600. * transform is stored in this object.
  2601. * In either case, <code>ptDst</code>, which contains the
  2602. * transformed point, is returned for convenience.
  2603. * If <code>ptSrc</code> and <code>ptDst</code> are the same object,
  2604. * the input point is correctly overwritten with the transformed
  2605. * point.
  2606. * @param ptSrc the distance vector to be delta transformed
  2607. * @param ptDst the resulting transformed distance vector
  2608. * @return <code>ptDst</code>, which contains the result of the
  2609. * transformation.
  2610. */
  2611. public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) {
  2612. if (ptDst == null) {
  2613. if (ptSrc instanceof Point2D.Double) {
  2614. ptDst = new Point2D.Double();
  2615. } else {
  2616. ptDst = new Point2D.Float();
  2617. }
  2618. }
  2619. // Copy source coords into local variables in case src == dst
  2620. double x = ptSrc.getX();
  2621. double y = ptSrc.getY();
  2622. switch (state) {
  2623. default:
  2624. stateError();
  2625. /* NOTREACHED */
  2626. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  2627. case (APPLY_SHEAR | APPLY_SCALE):
  2628. ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
  2629. return ptDst;
  2630. case (APPLY_SHEAR | APPLY_TRANSLATE):
  2631. case (APPLY_SHEAR):
  2632. ptDst.setLocation(y * m01, x * m10);
  2633. return ptDst;
  2634. case (APPLY_SCALE | APPLY_TRANSLATE):
  2635. case (APPLY_SCALE):
  2636. ptDst.setLocation(x * m00, y * m11);
  2637. return ptDst;
  2638. case (APPLY_TRANSLATE):
  2639. case (APPLY_IDENTITY):
  2640. ptDst.setLocation(x, y);
  2641. return ptDst;
  2642. }
  2643. /* NOTREACHED */
  2644. }
  2645. /**
  2646. * Transforms an array of relative distance vectors by this
  2647. * transform.
  2648. * A relative distance vector is transformed without applying the
  2649. * translation components of the affine transformation matrix
  2650. * using the following equations:
  2651. * <pre>
  2652. * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ]
  2653. * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ]
  2654. * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ]
  2655. * </pre>
  2656. * The two coordinate array sections can be exactly the same or
  2657. * can be overlapping sections of the same array without affecting the
  2658. * validity of the results.
  2659. * This method ensures that no source coordinates are
  2660. * overwritten by a previous operation before they can be transformed.
  2661. * The coordinates are stored in the arrays starting at the indicated
  2662. * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
  2663. * @param srcPts the array containing the source distance vectors.
  2664. * Each vector is stored as a pair of relative x, y coordinates.
  2665. * @param dstPts the array into which the transformed distance vectors
  2666. * are returned. Each vector is stored as a pair of relative
  2667. * x, y coordinates.
  2668. * @param srcOff the offset to the first vector to be transformed
  2669. * in the source array
  2670. * @param dstOff the offset to the location of the first
  2671. * transformed vector that is stored in the destination array
  2672. * @param numPts the number of vector coordinate pairs to be
  2673. * transformed
  2674. */
  2675. public void deltaTransform(double[] srcPts, int srcOff,
  2676. double[] dstPts, int dstOff,
  2677. int numPts) {
  2678. double M00, M01, M10, M11; // For caching
  2679. if (dstPts == srcPts &&
  2680. dstOff > srcOff && dstOff < srcOff + numPts * 2)
  2681. {
  2682. // If the arrays overlap partially with the destination higher
  2683. // than the source and we transform the coordinates normally
  2684. // we would overwrite some of the later source coordinates
  2685. // with results of previous transformations.
  2686. // To get around this we use arraycopy to copy the points
  2687. // to their final destination with correct overwrite
  2688. // handling and then transform them in place in the new
  2689. // safer location.
  2690. System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
  2691. // srcPts = dstPts; // They are known to be equal.
  2692. srcOff = dstOff;
  2693. }
  2694. switch (state) {
  2695. default:
  2696. stateError();
  2697. /* NOTREACHED */
  2698. case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
  2699. case (APPLY_SHEAR | APPLY_SCALE):
  2700. M00 = m00; M01 = m01;
  2701. M10 = m10; M11 = m11;
  2702. while (--numPts >= 0) {
  2703. double x = srcPts[srcOff++];
  2704. double y = srcPts[srcOff++];
  2705. dstPts[dstOff++] = x * M00 + y * M01;
  2706. dstPts[dstOff++] = x * M10 + y * M11;
  2707. }
  2708. return;
  2709. case (APPLY_SHEAR | APPLY_TRANSLATE):
  2710. case (APPLY_SHEAR):
  2711. M01 = m01; M10 = m10;
  2712. while (--numPts >= 0) {
  2713. double x = srcPts[srcOff++];
  2714. dstPts[dstOff++] = srcPts[srcOff++] * M01;
  2715. dstPts[dstOff++] = x * M10;
  2716. }
  2717. return;
  2718. case (APPLY_SCALE | APPLY_TRANSLATE):
  2719. case (APPLY_SCALE):
  2720. M00 = m00; M11 = m11;
  2721. while (--numPts >= 0) {
  2722. dstPts[dstOff++] = srcPts[srcOff++] * M00;
  2723. dstPts[dstOff++] = srcPts[srcOff++] * M11;
  2724. }
  2725. return;
  2726. case (APPLY_TRANSLATE):
  2727. case (APPLY_IDENTITY):
  2728. if (srcPts != dstPts || srcOff != dstOff) {
  2729. System.arraycopy(srcPts, srcOff, dstPts, dstOff,
  2730. numPts * 2);
  2731. }
  2732. return;
  2733. }
  2734. /* NOTREACHED */
  2735. }
  2736. /**
  2737. * Returns a new {@link Shape} object defined by the geometry of the
  2738. * specified <code>Shape</code> after it has been transformed by
  2739. * this transform.
  2740. * @param pSrc the specified <code>Shape</code> object to be
  2741. * transformed by this transform.
  2742. * @return a new <code>Shape</code> object that defines the geometry
  2743. * of the transformed <code>Shape</code>.
  2744. */
  2745. public Shape createTransformedShape(Shape pSrc) {
  2746. if (pSrc == null) {
  2747. return null;
  2748. }
  2749. if (pSrc instanceof GeneralPath) {
  2750. return ((GeneralPath)pSrc).createTransformedShape(this);
  2751. } else {
  2752. PathIterator pi = pSrc.getPathIterator(this);
  2753. GeneralPath gp = new GeneralPath(pi.getWindingRule());
  2754. gp.append(pi, false);
  2755. return gp;
  2756. }
  2757. /* NOTREACHED */
  2758. }
  2759. // Round values to sane precision for printing
  2760. // Note that Math.sin(Math.PI) has an error of about 10^-16
  2761. private static double _matround(double matval) {
  2762. return Math.rint(matval * 1E15) / 1E15;
  2763. }
  2764. /**
  2765. * Returns a <code>String</code> that represents the value of this
  2766. * {@link Object}.
  2767. * @return a <code>String</code> representing the value of this
  2768. * <code>Object</code>.
  2769. */
  2770. public String toString() {
  2771. return ("AffineTransform[["
  2772. + _matround(m00) + ", "
  2773. + _matround(m01) + ", "
  2774. + _matround(m02) + "], ["
  2775. + _matround(m10) + ", "
  2776. + _matround(m11) + ", "
  2777. + _matround(m12) + "]]");
  2778. }
  2779. /**
  2780. * Returns <code>true</code> if this <code>AffineTransform</code> is
  2781. * an identity transform.
  2782. * @return <code>true</code> if this <code>AffineTransform</code> is
  2783. * an identity transform; <code>false</code> otherwise.
  2784. */
  2785. public boolean isIdentity() {
  2786. return (state == APPLY_IDENTITY);
  2787. }
  2788. /**
  2789. * Returns a copy of this <code>AffineTransform</code> object.
  2790. * @return an <code>Object</code> that is a copy of this
  2791. * <code>AffineTransform</code> object.
  2792. */
  2793. public Object clone() {
  2794. try {
  2795. return super.clone();
  2796. } catch (CloneNotSupportedException e) {
  2797. // this shouldn't happen, since we are Cloneable
  2798. throw new InternalError();
  2799. }
  2800. }
  2801. /**
  2802. * Returns the hashcode for this transform.
  2803. * @return a hash code for this transform.
  2804. */
  2805. public int hashCode() {
  2806. long bits = Double.doubleToLongBits(m00);
  2807. bits = bits * 31 + Double.doubleToLongBits(m01);
  2808. bits = bits * 31 + Double.doubleToLongBits(m02);
  2809. bits = bits * 31 + Double.doubleToLongBits(m10);
  2810. bits = bits * 31 + Double.doubleToLongBits(m11);
  2811. bits = bits * 31 + Double.doubleToLongBits(m12);
  2812. return (((int) bits) ^ ((int) (bits >> 32)));
  2813. }
  2814. /**
  2815. * Returns <code>true</code> if this <code>AffineTransform</code>
  2816. * represents the same affine coordinate transform as the specified
  2817. * argument.
  2818. * @param obj the <code>Object</code> to test for equality with this
  2819. * <code>AffineTransform</code>
  2820. * @return <code>true</code> if <code>obj</code> equals this
  2821. * <code>AffineTransform</code> object; <code>false</code> otherwise.
  2822. */
  2823. public boolean equals(Object obj) {
  2824. if (!(obj instanceof AffineTransform)) {
  2825. return false;
  2826. }
  2827. AffineTransform a = (AffineTransform)obj;
  2828. return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) &&
  2829. (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12));
  2830. }
  2831. /* Serialization support. A readObject method is neccessary because
  2832. * the state field is part of the implementation of this particular
  2833. * AffineTransform and not part of the public specification. The
  2834. * state variable's value needs to be recalculated on the fly by the
  2835. * readObject method as it is in the 6-argument matrix constructor.
  2836. */
  2837. private void writeObject(java.io.ObjectOutputStream s)
  2838. throws java.lang.ClassNotFoundException, java.io.IOException
  2839. {
  2840. s.defaultWriteObject();
  2841. }
  2842. private void readObject(java.io.ObjectInputStream s)
  2843. throws java.lang.ClassNotFoundException, java.io.IOException
  2844. {
  2845. s.defaultReadObject();
  2846. updateState();
  2847. }
  2848. }