|
1 /* |
|
2 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 |
|
24 /** |
|
25 * @test |
|
26 * @bug 4980035 |
|
27 * @summary Unit test for new methods: |
|
28 * |
|
29 * AffineTransform.getRotateInstance(double x, double y); |
|
30 * AffineTransform.setToRotation(double x, double y); |
|
31 * AffineTransform.rotate(double x, double y); |
|
32 * |
|
33 * AffineTransform.getQuadrantRotateInstance(int numquads); |
|
34 * AffineTransform.setToQuadrantRotation(int numquads); |
|
35 * AffineTransform.quadrantRotate(int numquads); |
|
36 * |
|
37 * @author flar |
|
38 * @run main TestRotateMethods |
|
39 */ |
|
40 |
|
41 import java.awt.geom.AffineTransform; |
|
42 import java.awt.geom.Point2D; |
|
43 |
|
44 public class TestRotateMethods { |
|
45 /* The maximum errors allowed, measured in double precision "ulps" |
|
46 * Note that for most fields, the tests are extremely accurate - to |
|
47 * within 3 ulps of the smaller value in the comparison |
|
48 * For the translation components, the tests are still very accurate, |
|
49 * but the absolute number of ulps can be noticeably higher when we |
|
50 * use one of the rotate methods that takes an anchor point. |
|
51 * Since a double precision value has 56 bits of precision, even |
|
52 * 1024 ulps is extremely small as a ratio of the value. |
|
53 */ |
|
54 public static final double MAX_ULPS = 3.0; |
|
55 public static final double MAX_ANCHOR_TX_ULPS = 1024.0; |
|
56 public static double MAX_TX_ULPS = MAX_ULPS; |
|
57 |
|
58 // Vectors for quadrant rotations |
|
59 public static final double quadxvec[] = { 1.0, 0.0, -1.0, 0.0 }; |
|
60 public static final double quadyvec[] = { 0.0, 1.0, 0.0, -1.0 }; |
|
61 |
|
62 // Run tests once for each type of method: |
|
63 // tx = AffineTransform.get<Rotate>Instance() |
|
64 // tx.set<Rotate>() |
|
65 // tx.<rotate>() |
|
66 public static enum Mode { GET, SET, MOD }; |
|
67 |
|
68 // Used to accumulate and report largest differences encountered by tests |
|
69 public static double maxulps = 0.0; |
|
70 public static double maxtxulps = 0.0; |
|
71 |
|
72 // Sample anchor points for testing. |
|
73 public static Point2D zeropt = new Point2D.Double(0, 0); |
|
74 public static Point2D testtxpts[] = { |
|
75 new Point2D.Double( 5, 5), |
|
76 new Point2D.Double( 20, -10), |
|
77 new Point2D.Double(-Math.PI, Math.E), |
|
78 }; |
|
79 |
|
80 public static void main(String argv[]) { |
|
81 test(Mode.GET); |
|
82 test(Mode.SET); |
|
83 test(Mode.MOD); |
|
84 |
|
85 System.out.println("Max scale and shear difference: "+maxulps+" ulps"); |
|
86 System.out.println("Max translate difference: "+maxtxulps+" ulps"); |
|
87 } |
|
88 |
|
89 public static void test(Mode mode) { |
|
90 MAX_TX_ULPS = MAX_ULPS; // Stricter tx testing with no anchor point |
|
91 test(mode, 0.5, null); |
|
92 test(mode, 1.0, null); |
|
93 test(mode, 3.0, null); |
|
94 |
|
95 // Anchor points make the tx values less reliable |
|
96 MAX_TX_ULPS = MAX_ANCHOR_TX_ULPS; |
|
97 for (int i = 0; i < testtxpts.length; i++) { |
|
98 test(mode, 1.0, testtxpts[i]); |
|
99 } |
|
100 MAX_TX_ULPS = MAX_ULPS; // Restore to default |
|
101 } |
|
102 |
|
103 public static void verify(AffineTransform at1, AffineTransform at2, |
|
104 Mode mode, double vectorscale, Point2D txpt, |
|
105 String message, double num, String units) |
|
106 { |
|
107 if (!compare(at1, at2)) { |
|
108 System.out.println("mode == "+mode); |
|
109 System.out.println("vectorscale == "+vectorscale); |
|
110 System.out.println("txpt == "+txpt); |
|
111 System.out.println(at1+", type = "+at1.getType()); |
|
112 System.out.println(at2+", type = "+at2.getType()); |
|
113 System.out.println("ScaleX values differ by "+ |
|
114 ulps(at1.getScaleX(), at2.getScaleX())+" ulps"); |
|
115 System.out.println("ScaleY values differ by "+ |
|
116 ulps(at1.getScaleY(), at2.getScaleY())+" ulps"); |
|
117 System.out.println("ShearX values differ by "+ |
|
118 ulps(at1.getShearX(), at2.getShearX())+" ulps"); |
|
119 System.out.println("ShearY values differ by "+ |
|
120 ulps(at1.getShearY(), at2.getShearY())+" ulps"); |
|
121 System.out.println("TranslateX values differ by "+ |
|
122 ulps(at1.getTranslateX(), |
|
123 at2.getTranslateX())+" ulps"); |
|
124 System.out.println("TranslateY values differ by "+ |
|
125 ulps(at1.getTranslateY(), |
|
126 at2.getTranslateY())+" ulps"); |
|
127 throw new RuntimeException(message + num + units); |
|
128 } |
|
129 } |
|
130 |
|
131 public static void test(Mode mode, double vectorscale, Point2D txpt) { |
|
132 AffineTransform at1, at2, at3; |
|
133 |
|
134 for (int deg = -720; deg <= 720; deg++) { |
|
135 if ((deg % 90) == 0) continue; |
|
136 double radians = Math.toRadians(deg); |
|
137 double vecy = Math.sin(radians) * vectorscale; |
|
138 double vecx = Math.cos(radians) * vectorscale; |
|
139 |
|
140 at1 = makeAT(mode, txpt, radians); |
|
141 at2 = makeAT(mode, txpt, vecx, vecy); |
|
142 verify(at1, at2, mode, vectorscale, txpt, |
|
143 "vector and radians do not match for ", deg, " degrees"); |
|
144 |
|
145 if (txpt == null) { |
|
146 // Make sure output was same as a with a 0,0 anchor point |
|
147 if (vectorscale == 1.0) { |
|
148 // Only need to test radians method for one scale factor |
|
149 at3 = makeAT(mode, zeropt, radians); |
|
150 verify(at1, at3, mode, vectorscale, zeropt, |
|
151 "radians not invariant with 0,0 translate at ", |
|
152 deg, " degrees"); |
|
153 } |
|
154 // But test vector methods with all scale factors |
|
155 at3 = makeAT(mode, zeropt, vecx, vecy); |
|
156 verify(at2, at3, mode, vectorscale, zeropt, |
|
157 "vector not invariant with 0,0 translate at ", |
|
158 deg, " degrees"); |
|
159 } |
|
160 } |
|
161 |
|
162 for (int quad = -8; quad <= 8; quad++) { |
|
163 double degrees = quad * 90.0; |
|
164 double radians = Math.toRadians(degrees); |
|
165 double vecx = quadxvec[quad & 3] * vectorscale; |
|
166 double vecy = quadyvec[quad & 3] * vectorscale; |
|
167 |
|
168 at1 = makeAT(mode, txpt, radians); |
|
169 at2 = makeAT(mode, txpt, vecx, vecy); |
|
170 verify(at1, at2, mode, vectorscale, txpt, |
|
171 "quadrant vector and radians do not match for ", |
|
172 degrees, " degrees"); |
|
173 at2 = makeQuadAT(mode, txpt, quad); |
|
174 verify(at1, at2, mode, vectorscale, txpt, |
|
175 "quadrant and radians do not match for ", |
|
176 quad, " quadrants"); |
|
177 if (txpt == null) { |
|
178 at3 = makeQuadAT(mode, zeropt, quad); |
|
179 verify(at2, at3, mode, vectorscale, zeropt, |
|
180 "quadrant not invariant with 0,0 translate at ", |
|
181 quad, " quadrants"); |
|
182 } |
|
183 } |
|
184 } |
|
185 |
|
186 public static AffineTransform makeRandomAT() { |
|
187 AffineTransform at = new AffineTransform(); |
|
188 at.scale(Math.random() * -10.0, Math.random() * 100.0); |
|
189 at.rotate(Math.random() * Math.PI); |
|
190 at.shear(Math.random(), Math.random()); |
|
191 at.translate(Math.random() * 300.0, Math.random() * -20.0); |
|
192 return at; |
|
193 } |
|
194 |
|
195 public static AffineTransform makeAT(Mode mode, Point2D txpt, |
|
196 double radians) |
|
197 { |
|
198 AffineTransform at; |
|
199 double tx = (txpt == null) ? 0.0 : txpt.getX(); |
|
200 double ty = (txpt == null) ? 0.0 : txpt.getY(); |
|
201 switch (mode) { |
|
202 case GET: |
|
203 if (txpt != null) { |
|
204 at = AffineTransform.getRotateInstance(radians, tx, ty); |
|
205 } else { |
|
206 at = AffineTransform.getRotateInstance(radians); |
|
207 } |
|
208 break; |
|
209 case SET: |
|
210 at = makeRandomAT(); |
|
211 if (txpt != null) { |
|
212 at.setToRotation(radians, tx, ty); |
|
213 } else { |
|
214 at.setToRotation(radians); |
|
215 } |
|
216 break; |
|
217 case MOD: |
|
218 at = makeRandomAT(); |
|
219 at.setToIdentity(); |
|
220 if (txpt != null) { |
|
221 at.rotate(radians, tx, ty); |
|
222 } else { |
|
223 at.rotate(radians); |
|
224 } |
|
225 break; |
|
226 default: |
|
227 throw new InternalError("unrecognized mode: "+mode); |
|
228 } |
|
229 |
|
230 return at; |
|
231 } |
|
232 |
|
233 public static AffineTransform makeAT(Mode mode, Point2D txpt, |
|
234 double vx, double vy) |
|
235 { |
|
236 AffineTransform at; |
|
237 double tx = (txpt == null) ? 0.0 : txpt.getX(); |
|
238 double ty = (txpt == null) ? 0.0 : txpt.getY(); |
|
239 switch (mode) { |
|
240 case GET: |
|
241 if (txpt != null) { |
|
242 at = AffineTransform.getRotateInstance(vx, vy, tx, ty); |
|
243 } else { |
|
244 at = AffineTransform.getRotateInstance(vx, vy); |
|
245 } |
|
246 break; |
|
247 case SET: |
|
248 at = makeRandomAT(); |
|
249 if (txpt != null) { |
|
250 at.setToRotation(vx, vy, tx, ty); |
|
251 } else { |
|
252 at.setToRotation(vx, vy); |
|
253 } |
|
254 break; |
|
255 case MOD: |
|
256 at = makeRandomAT(); |
|
257 at.setToIdentity(); |
|
258 if (txpt != null) { |
|
259 at.rotate(vx, vy, tx, ty); |
|
260 } else { |
|
261 at.rotate(vx, vy); |
|
262 } |
|
263 break; |
|
264 default: |
|
265 throw new InternalError("unrecognized mode: "+mode); |
|
266 } |
|
267 |
|
268 return at; |
|
269 } |
|
270 |
|
271 public static AffineTransform makeQuadAT(Mode mode, Point2D txpt, |
|
272 int quads) |
|
273 { |
|
274 AffineTransform at; |
|
275 double tx = (txpt == null) ? 0.0 : txpt.getX(); |
|
276 double ty = (txpt == null) ? 0.0 : txpt.getY(); |
|
277 switch (mode) { |
|
278 case GET: |
|
279 if (txpt != null) { |
|
280 at = AffineTransform.getQuadrantRotateInstance(quads, tx, ty); |
|
281 } else { |
|
282 at = AffineTransform.getQuadrantRotateInstance(quads); |
|
283 } |
|
284 break; |
|
285 case SET: |
|
286 at = makeRandomAT(); |
|
287 if (txpt != null) { |
|
288 at.setToQuadrantRotation(quads, tx, ty); |
|
289 } else { |
|
290 at.setToQuadrantRotation(quads); |
|
291 } |
|
292 break; |
|
293 case MOD: |
|
294 at = makeRandomAT(); |
|
295 at.setToIdentity(); |
|
296 if (txpt != null) { |
|
297 at.quadrantRotate(quads, tx, ty); |
|
298 } else { |
|
299 at.quadrantRotate(quads); |
|
300 } |
|
301 break; |
|
302 default: |
|
303 throw new InternalError("unrecognized mode: "+mode); |
|
304 } |
|
305 |
|
306 return at; |
|
307 } |
|
308 |
|
309 public static boolean compare(AffineTransform at1, AffineTransform at2) { |
|
310 maxulps = Math.max(maxulps, ulps(at1.getScaleX(), at2.getScaleX())); |
|
311 maxulps = Math.max(maxulps, ulps(at1.getScaleY(), at2.getScaleY())); |
|
312 maxulps = Math.max(maxulps, ulps(at1.getShearX(), at2.getShearX())); |
|
313 maxulps = Math.max(maxulps, ulps(at1.getShearY(), at2.getShearY())); |
|
314 maxtxulps = Math.max(maxtxulps, |
|
315 ulps(at1.getTranslateX(), at2.getTranslateX())); |
|
316 maxtxulps = Math.max(maxtxulps, |
|
317 ulps(at1.getTranslateY(), at2.getTranslateY())); |
|
318 return (getModifiedType(at1) == getModifiedType(at2) && |
|
319 (compare(at1.getScaleX(), at2.getScaleX(), MAX_ULPS)) && |
|
320 (compare(at1.getScaleY(), at2.getScaleY(), MAX_ULPS)) && |
|
321 (compare(at1.getShearX(), at2.getShearX(), MAX_ULPS)) && |
|
322 (compare(at1.getShearY(), at2.getShearY(), MAX_ULPS)) && |
|
323 (compare(at1.getTranslateX(), |
|
324 at2.getTranslateX(), MAX_TX_ULPS)) && |
|
325 (compare(at1.getTranslateY(), |
|
326 at2.getTranslateY(), MAX_TX_ULPS))); |
|
327 } |
|
328 |
|
329 public static int getModifiedType(AffineTransform at) { |
|
330 int type = at.getType(); |
|
331 // Some of the vector methods can introduce a tiny uniform scale |
|
332 // at some angles... |
|
333 if ((type & AffineTransform.TYPE_UNIFORM_SCALE) != 0) { |
|
334 maxulps = Math.max(maxulps, ulps(at.getDeterminant(), 1.0)); |
|
335 if (ulps(at.getDeterminant(), 1.0) <= MAX_ULPS) { |
|
336 // Really tiny - we will ignore it |
|
337 type &= (~AffineTransform.TYPE_UNIFORM_SCALE); |
|
338 } |
|
339 } |
|
340 return type; |
|
341 } |
|
342 |
|
343 public static boolean compare(double val1, double val2, double maxulps) { |
|
344 return (ulps(val1, val2) <= maxulps); |
|
345 } |
|
346 |
|
347 public static double ulps(double val1, double val2) { |
|
348 double diff = Math.abs(val1 - val2); |
|
349 double ulpmax = Math.min(Math.ulp(val1), Math.ulp(val2)); |
|
350 return (diff / ulpmax); |
|
351 } |
|
352 } |