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