1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | |
30 | #include <QtTest/QtTest> |
31 | #include <qpainterpath.h> |
32 | #include <qpolygon.h> |
33 | #include <qtransform.h> |
34 | #include <qdebug.h> |
35 | |
36 | class tst_QTransform : public QObject |
37 | { |
38 | Q_OBJECT |
39 | |
40 | private slots: |
41 | void mapRect_data(); |
42 | void mapToPolygon_data(); |
43 | void mapRect(); |
44 | void assignments(); |
45 | void mapToPolygon(); |
46 | void qhash(); |
47 | void translate(); |
48 | void scale(); |
49 | void matrix(); |
50 | void testOffset(); |
51 | void types(); |
52 | void types2_data(); |
53 | void types2(); |
54 | void scalarOps(); |
55 | void transform(); |
56 | void mapEmptyPath(); |
57 | void boundingRect(); |
58 | void controlPointRect(); |
59 | void inverted_data(); |
60 | void inverted(); |
61 | void projectivePathMapping(); |
62 | void mapInt(); |
63 | void mapPathWithPoint(); |
64 | |
65 | private: |
66 | void mapping_data(); |
67 | }; |
68 | |
69 | Q_DECLARE_METATYPE(QTransform) |
70 | Q_DECLARE_METATYPE(QTransform::TransformationType) |
71 | |
72 | void tst_QTransform::mapRect_data() |
73 | { |
74 | mapping_data(); |
75 | |
76 | // rotations that are not multiples of 90 degrees. mapRect returns the bounding rect here. |
77 | qreal deg = -45; |
78 | QTest::newRow( dataTag: "rot 45 a" ) |
79 | << QTransform().rotate(a: deg) |
80 | << QRect( 0, 0, 10, 10 ) |
81 | << QPolygon( QRect( 0, -7, 14, 14 ) ); |
82 | QTest::newRow( dataTag: "rot 45 b" ) |
83 | << QTransform().rotate(a: deg) |
84 | << QRect( 10, 20, 30, 40 ) |
85 | << QPolygon( QRect( 21, -14, 50, 49 ) ); |
86 | QTest::newRow( dataTag: "rot 45 c" ) |
87 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
88 | << QRect( 0, 0, 10, 10 ) |
89 | << QPolygon( QRect( 0, -71, 141, 142 ) ); |
90 | QTest::newRow( dataTag: "rot 45 d" ) |
91 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
92 | << QRect( 10, 20, 30, 40 ) |
93 | << QPolygon( QRect( 212, -141, 495, 495 ) ); |
94 | |
95 | deg = 45; |
96 | QTest::newRow( dataTag: "rot -45 a" ) |
97 | << QTransform().rotate(a: deg) |
98 | << QRect( 0, 0, 10, 10 ) |
99 | << QPolygon( QRect( -7, 0, 14, 14 ) ); |
100 | QTest::newRow( dataTag: "rot -45 b" ) |
101 | << QTransform().rotate(a: deg) |
102 | << QRect( 10, 20, 30, 40 ) |
103 | << QPolygon( QRect( -35, 21, 49, 50 ) ); |
104 | QTest::newRow( dataTag: "rot -45 c" ) |
105 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
106 | << QRect( 0, 0, 10, 10 ) |
107 | << QPolygon( QRect( -71, 0, 142, 141 ) ); |
108 | QTest::newRow( dataTag: "rot -45 d" ) |
109 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
110 | << QRect( 10, 20, 30, 40 ) |
111 | << QPolygon( QRect( -354, 212, 495, 495 ) ); |
112 | } |
113 | |
114 | void tst_QTransform::mapToPolygon_data() |
115 | { |
116 | mapping_data(); |
117 | } |
118 | |
119 | void tst_QTransform::mapping_data() |
120 | { |
121 | //create the testtable instance and define the elements |
122 | QTest::addColumn<QTransform>(name: "matrix" ); |
123 | QTest::addColumn<QRect>(name: "src" ); |
124 | QTest::addColumn<QPolygon>(name: "res" ); |
125 | |
126 | //next we fill it with data |
127 | |
128 | // identity |
129 | QTest::newRow( dataTag: "identity" ) |
130 | << QTransform() |
131 | << QRect( 10, 20, 30, 40 ) |
132 | << QPolygon( QRect( 10, 20, 30, 40 ) ); |
133 | // scaling |
134 | QTest::newRow( dataTag: "scale 0" ) |
135 | << QTransform().scale(sx: 2, sy: 2) |
136 | << QRect( 10, 20, 30, 40 ) |
137 | << QPolygon( QRect( 20, 40, 60, 80 ) ); |
138 | QTest::newRow( dataTag: "scale 1" ) |
139 | << QTransform().scale(sx: 10, sy: 10) |
140 | << QRect( 10, 20, 30, 40 ) |
141 | << QPolygon( QRect( 100, 200, 300, 400 ) ); |
142 | // mirroring |
143 | QTest::newRow( dataTag: "mirror 0" ) |
144 | << QTransform().scale(sx: -1, sy: 1) |
145 | << QRect( 10, 20, 30, 40 ) |
146 | << QPolygon( QRect( -40, 20, 30, 40 ) ); |
147 | QTest::newRow( dataTag: "mirror 1" ) |
148 | << QTransform().scale(sx: 1, sy: -1) |
149 | << QRect( 10, 20, 30, 40 ) |
150 | << QPolygon( QRect( 10, -60, 30, 40 ) ); |
151 | QTest::newRow( dataTag: "mirror 2" ) |
152 | << QTransform().scale(sx: -1, sy: -1) |
153 | << QRect( 10, 20, 30, 40 ) |
154 | << QPolygon( QRect( -40, -60, 30, 40 ) ); |
155 | QTest::newRow( dataTag: "mirror 3" ) |
156 | << QTransform().scale(sx: -2, sy: -2) |
157 | << QRect( 10, 20, 30, 40 ) |
158 | << QPolygon( QRect( -80, -120, 60, 80 ) ); |
159 | QTest::newRow( dataTag: "mirror 4" ) |
160 | << QTransform().scale(sx: -10, sy: -10) |
161 | << QRect( 10, 20, 30, 40 ) |
162 | << QPolygon( QRect( -400, -600, 300, 400 ) ); |
163 | QTest::newRow( dataTag: "mirror 5" ) |
164 | << QTransform().scale(sx: -1, sy: 1) |
165 | << QRect( 0, 0, 30, 40 ) |
166 | << QPolygon( QRect( -30, 0, 30, 40 ) ); |
167 | QTest::newRow( dataTag: "mirror 6" ) |
168 | << QTransform().scale(sx: 1, sy: -1) |
169 | << QRect( 0, 0, 30, 40 ) |
170 | << QPolygon( QRect( 0, -40, 30, 40 ) ); |
171 | QTest::newRow( dataTag: "mirror 7" ) |
172 | << QTransform().scale(sx: -1, sy: -1) |
173 | << QRect( 0, 0, 30, 40 ) |
174 | << QPolygon( QRect( -30, -40, 30, 40 ) ); |
175 | QTest::newRow( dataTag: "mirror 8" ) |
176 | << QTransform().scale(sx: -2, sy: -2) |
177 | << QRect( 0, 0, 30, 40 ) |
178 | << QPolygon( QRect( -60, -80, 60, 80 ) ); |
179 | QTest::newRow( dataTag: "mirror 9" ) |
180 | << QTransform().scale(sx: -10, sy: -10) << QRect( 0, 0, 30, 40 ) |
181 | << QPolygon( QRect( -300, -400, 300, 400 ) ); |
182 | |
183 | // rotations |
184 | float deg = 0.; |
185 | QTest::newRow( dataTag: "rot 0 a" ) |
186 | << QTransform().rotate(a: deg) |
187 | << QRect( 0, 0, 30, 40 ) |
188 | << QPolygon ( QRect( 0, 0, 30, 40 ) ); |
189 | deg = 0.00001f; |
190 | QTest::newRow( dataTag: "rot 0 b" ) |
191 | << QTransform().rotate(a: deg) |
192 | << QRect( 0, 0, 30, 40 ) |
193 | << QPolygon ( QRect( 0, 0, 30, 40 ) ); |
194 | deg = 0.; |
195 | QTest::newRow( dataTag: "rot 0 c" ) |
196 | << QTransform().rotate(a: deg) |
197 | << QRect( 10, 20, 30, 40 ) |
198 | << QPolygon ( QRect( 10, 20, 30, 40 ) ); |
199 | deg = 0.00001f; |
200 | QTest::newRow( dataTag: "rot 0 d" ) |
201 | << QTransform().rotate(a: deg) |
202 | << QRect( 10, 20, 30, 40 ) |
203 | << QPolygon ( QRect( 10, 20, 30, 40 ) ); |
204 | |
205 | // rotations |
206 | deg = -90.f; |
207 | QTest::newRow( dataTag: "rotscale 90 a" ) |
208 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
209 | << QRect( 0, 0, 30, 40 ) |
210 | << QPolygon( QRect( 0, -300, 400, 300 ) ); |
211 | deg = -90.00001f; |
212 | QTest::newRow( dataTag: "rotscale 90 b" ) |
213 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
214 | << QRect( 0, 0, 30, 40 ) |
215 | << QPolygon( QRect( 0, -300, 400, 300 ) ); |
216 | deg = -90.f; |
217 | QTest::newRow( dataTag: "rotscale 90 c" ) |
218 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
219 | << QRect( 10, 20, 30, 40 ) |
220 | << QPolygon( QRect( 200, -400, 400, 300 ) ); |
221 | deg = -90.00001f; |
222 | QTest::newRow( dataTag: "rotscale 90 d" ) |
223 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
224 | << QRect( 10, 20, 30, 40 ) |
225 | << QPolygon( QRect( 200, -400, 400, 300 ) ); |
226 | |
227 | deg = 180.f; |
228 | QTest::newRow( dataTag: "rotscale 180 a" ) |
229 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
230 | << QRect( 0, 0, 30, 40 ) |
231 | << QPolygon( QRect( -300, -400, 300, 400 ) ); |
232 | deg = 180.000001f; |
233 | QTest::newRow( dataTag: "rotscale 180 b" ) |
234 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
235 | << QRect( 0, 0, 30, 40 ) |
236 | << QPolygon( QRect( -300, -400, 300, 400 ) ); |
237 | deg = 180.f; |
238 | QTest::newRow( dataTag: "rotscale 180 c" ) |
239 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
240 | << QRect( 10, 20, 30, 40 ) |
241 | << QPolygon( QRect( -400, -600, 300, 400 ) ); |
242 | deg = 180.000001f; |
243 | QTest::newRow( dataTag: "rotscale 180 d" ) |
244 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
245 | << QRect( 10, 20, 30, 40 ) |
246 | << QPolygon( QRect( -400, -600, 300, 400 ) ); |
247 | |
248 | deg = -270.f; |
249 | QTest::newRow( dataTag: "rotscale 270 a" ) |
250 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
251 | << QRect( 0, 0, 30, 40 ) |
252 | << QPolygon( QRect( -400, 0, 400, 300 ) ); |
253 | deg = -270.0000001f; |
254 | QTest::newRow( dataTag: "rotscale 270 b" ) |
255 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
256 | << QRect( 0, 0, 30, 40 ) |
257 | << QPolygon( QRect( -400, 0, 400, 300 ) ); |
258 | deg = -270.f; |
259 | QTest::newRow( dataTag: "rotscale 270 c" ) |
260 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
261 | << QRect( 10, 20, 30, 40 ) |
262 | << QPolygon( QRect( -600, 100, 400, 300 ) ); |
263 | deg = -270.000001f; |
264 | QTest::newRow( dataTag: "rotscale 270 d" ) |
265 | << QTransform().rotate(a: deg).scale(sx: 10, sy: 10) |
266 | << QRect( 10, 20, 30, 40 ) |
267 | << QPolygon( QRect( -600, 100, 400, 300 ) ); |
268 | } |
269 | |
270 | void tst_QTransform::mapRect() |
271 | { |
272 | QFETCH( QTransform, matrix ); |
273 | QFETCH( QRect, src ); |
274 | QFETCH( QPolygon, res ); |
275 | QRect mapped = matrix.mapRect(src); |
276 | QCOMPARE( mapped, res.boundingRect().adjusted(0, 0, -1, -1) ); |
277 | |
278 | QRectF r = matrix.mapRect(QRectF(src)); |
279 | QRect ir(r.topLeft().toPoint(), r.bottomRight().toPoint() - QPoint(1, 1)); |
280 | QCOMPARE( mapped, ir ); |
281 | } |
282 | |
283 | void tst_QTransform::assignments() |
284 | { |
285 | QTransform m; |
286 | m.scale(sx: 2, sy: 3); |
287 | m.rotate(a: 45); |
288 | m.shear(sh: 4, sv: 5); |
289 | |
290 | QTransform c1(m); |
291 | |
292 | QCOMPARE(m.m11(), c1.m11()); |
293 | QCOMPARE(m.m12(), c1.m12()); |
294 | QCOMPARE(m.m21(), c1.m21()); |
295 | QCOMPARE(m.m22(), c1.m22()); |
296 | QCOMPARE(m.dx(), c1.dx()); |
297 | QCOMPARE(m.dy(), c1.dy()); |
298 | |
299 | QTransform c2 = m; |
300 | QCOMPARE(m.m11(), c2.m11()); |
301 | QCOMPARE(m.m12(), c2.m12()); |
302 | QCOMPARE(m.m21(), c2.m21()); |
303 | QCOMPARE(m.m22(), c2.m22()); |
304 | QCOMPARE(m.dx(), c2.dx()); |
305 | QCOMPARE(m.dy(), c2.dy()); |
306 | } |
307 | |
308 | |
309 | void tst_QTransform::mapToPolygon() |
310 | { |
311 | QFETCH( QTransform, matrix ); |
312 | QFETCH( QRect, src ); |
313 | QFETCH( QPolygon, res ); |
314 | |
315 | QPolygon poly = matrix.mapToPolygon(r: src); |
316 | |
317 | // don't care about starting point |
318 | bool equal = false; |
319 | for (int i = 0; i < poly.size(); ++i) { |
320 | QPolygon rot; |
321 | for (int j = i; j < poly.size(); ++j) |
322 | rot << poly[j]; |
323 | for (int j = 0; j < i; ++j) |
324 | rot << poly[j]; |
325 | if (rot == res) |
326 | equal = true; |
327 | } |
328 | |
329 | QVERIFY(equal); |
330 | } |
331 | |
332 | void tst_QTransform::qhash() |
333 | { |
334 | QTransform t1; |
335 | t1.shear(sh: 3.0, sv: 2.0); |
336 | t1.rotate(a: 44); |
337 | |
338 | QTransform t2 = t1; |
339 | |
340 | // not really much to test here, so just the bare minimum: |
341 | QCOMPARE(qHash(t1), qHash(t2)); |
342 | } |
343 | |
344 | |
345 | void tst_QTransform::translate() |
346 | { |
347 | QTransform m( 1, 2, 3, 4, 5, 6 ); |
348 | QTransform res2( m ); |
349 | QTransform res( 1, 2, 3, 4, 75, 106 ); |
350 | m.translate( dx: 10, dy: 20 ); |
351 | QVERIFY( m == res ); |
352 | m.translate( dx: -10, dy: -20 ); |
353 | QVERIFY( m == res2 ); |
354 | QVERIFY( QTransform::fromTranslate( 0, 0 ).type() == QTransform::TxNone ); |
355 | QVERIFY( QTransform::fromTranslate( 10, 0 ).type() == QTransform::TxTranslate ); |
356 | QVERIFY( QTransform::fromTranslate( -1, 5 ) == QTransform().translate( -1, 5 )); |
357 | QVERIFY( QTransform::fromTranslate( 0, 0 ) == QTransform()); |
358 | } |
359 | |
360 | void tst_QTransform::scale() |
361 | { |
362 | QTransform m( 1, 2, 3, 4, 5, 6 ); |
363 | QTransform res2( m ); |
364 | QTransform res( 10, 20, 60, 80, 5, 6 ); |
365 | m.scale( sx: 10, sy: 20 ); |
366 | QVERIFY( m == res ); |
367 | m.scale( sx: 1./10., sy: 1./20. ); |
368 | QVERIFY( m == res2 ); |
369 | QVERIFY( QTransform::fromScale( 1, 1 ).type() == QTransform::TxNone ); |
370 | QVERIFY( QTransform::fromScale( 2, 4 ).type() == QTransform::TxScale ); |
371 | QVERIFY( QTransform::fromScale( 2, 4 ) == QTransform().scale( 2, 4 )); |
372 | QVERIFY( QTransform::fromScale( 1, 1 ) == QTransform()); |
373 | } |
374 | |
375 | #if QT_DEPRECATED_SINCE(5, 15) |
376 | QT_WARNING_PUSH |
377 | QT_WARNING_DISABLE_DEPRECATED |
378 | void tst_QTransform::matrix() |
379 | { |
380 | QMatrix mat1; |
381 | mat1.scale(sx: 0.3, sy: 0.7); |
382 | mat1.translate(dx: 53.3, dy: 94.4); |
383 | mat1.rotate(a: 45); |
384 | |
385 | QMatrix mat2; |
386 | mat2.rotate(a: 33); |
387 | mat2.scale(sx: 0.6, sy: 0.6); |
388 | mat2.translate(dx: 13.333, dy: 7.777); |
389 | |
390 | QTransform tran1(mat1); |
391 | QTransform tran2(mat2); |
392 | QTransform dummy; |
393 | dummy.setMatrix(m11: mat1.m11(), m12: mat1.m12(), m13: 0, |
394 | m21: mat1.m21(), m22: mat1.m22(), m23: 0, |
395 | m31: mat1.dx(), m32: mat1.dy(), m33: 1); |
396 | |
397 | QCOMPARE(tran1, dummy); |
398 | QCOMPARE(tran1.inverted(), dummy.inverted()); |
399 | QCOMPARE(tran1.inverted(), QTransform(mat1.inverted())); |
400 | QCOMPARE(tran2.inverted(), QTransform(mat2.inverted())); |
401 | |
402 | QMatrix mat3 = mat1 * mat2; |
403 | QTransform tran3 = tran1 * tran2; |
404 | QCOMPARE(QTransform(mat3), tran3); |
405 | |
406 | /* QMatrix::operator==() doesn't use qFuzzyCompare(), which |
407 | * on win32-g++ results in a failure. So we work around it by |
408 | * calling QTranform::operator==(), which performs a fuzzy compare. */ |
409 | QCOMPARE(QTransform(mat3), QTransform(tran3.toAffine())); |
410 | |
411 | QTransform tranInv = tran1.inverted(); |
412 | QMatrix matInv = mat1.inverted(); |
413 | |
414 | QRect rect(43, 70, 200, 200); |
415 | QPoint pt(43, 66); |
416 | QCOMPARE(tranInv.mapRect(rect), matInv.mapRect(rect)); |
417 | QCOMPARE(tranInv.map(pt), matInv.map(pt)); |
418 | |
419 | QPainterPath path; |
420 | path.moveTo(x: 55, y: 60); |
421 | path.lineTo(x: 110, y: 110); |
422 | path.quadTo(ctrlPtx: 220, ctrlPty: 50, endPtx: 10, endPty: 20); |
423 | path.closeSubpath(); |
424 | QCOMPARE(tranInv.map(path), matInv.map(path)); |
425 | } |
426 | |
427 | void tst_QTransform::testOffset() |
428 | { |
429 | QTransform trans; |
430 | const QMatrix &aff = trans.toAffine(); |
431 | QCOMPARE((void*)(&aff), (void*)(&trans)); |
432 | } |
433 | QT_WARNING_POP |
434 | #endif |
435 | |
436 | void tst_QTransform::types() |
437 | { |
438 | QTransform m1; |
439 | QCOMPARE(m1.type(), QTransform::TxNone); |
440 | |
441 | m1.translate(dx: 1.0f, dy: 0.0f); |
442 | QCOMPARE(m1.type(), QTransform::TxTranslate); |
443 | QCOMPARE(m1.inverted().type(), QTransform::TxTranslate); |
444 | |
445 | m1.scale(sx: 1.0f, sy: 2.0f); |
446 | QCOMPARE(m1.type(), QTransform::TxScale); |
447 | QCOMPARE(m1.inverted().type(), QTransform::TxScale); |
448 | |
449 | m1.rotate(a: 45.0f); |
450 | // Rotation after non-uniform scaling -> shearing. Uniform scale + rotate tested below. |
451 | QCOMPARE(m1.type(), QTransform::TxShear); |
452 | QCOMPARE(m1.inverted().type(), QTransform::TxShear); |
453 | |
454 | m1.shear(sh: 0.5f, sv: 0.25f); |
455 | QCOMPARE(m1.type(), QTransform::TxShear); |
456 | QCOMPARE(m1.inverted().type(), QTransform::TxShear); |
457 | |
458 | m1.rotate(a: 45.0f, axis: Qt::XAxis); |
459 | QCOMPARE(m1.type(), QTransform::TxProject); |
460 | m1.shear(sh: 0.5f, sv: 0.25f); |
461 | QCOMPARE(m1.type(), QTransform::TxProject); |
462 | m1.rotate(a: 45.0f); |
463 | QCOMPARE(m1.type(), QTransform::TxProject); |
464 | m1.scale(sx: 1.0f, sy: 2.0f); |
465 | QCOMPARE(m1.type(), QTransform::TxProject); |
466 | m1.translate(dx: 1.0f, dy: 0.0f); |
467 | QCOMPARE(m1.type(), QTransform::TxProject); |
468 | |
469 | QTransform m2(1.0f, 0.0f, 0.0f, |
470 | 0.0f, 1.0f, 0.0f, |
471 | -1.0f, -1.0f, 1.0f); |
472 | |
473 | QCOMPARE(m2.type(), QTransform::TxTranslate); |
474 | QCOMPARE((m1 * m2).type(), QTransform::TxProject); |
475 | |
476 | m1 *= QTransform(); |
477 | QCOMPARE(m1.type(), QTransform::TxProject); |
478 | |
479 | m1 *= QTransform(1.0f, 0.0f, 0.0f, |
480 | 0.0f, 1.0f, 0.0f, |
481 | 1.0f, 0.0f, 1.0f); |
482 | QCOMPARE(m1.type(), QTransform::TxProject); |
483 | |
484 | m2.reset(); |
485 | QCOMPARE(m2.type(), QTransform::TxNone); |
486 | |
487 | m2.setMatrix(m11: 1.0f, m12: 0.0f, m13: 0.0f, |
488 | m21: 0.0f, m22: 1.0f, m23: 0.0f, |
489 | m31: 0.0f, m32: 0.0f, m33: 1.0f); |
490 | QCOMPARE(m2.type(), QTransform::TxNone); |
491 | |
492 | m2 *= QTransform(); |
493 | QCOMPARE(m2.type(), QTransform::TxNone); |
494 | |
495 | m2.setMatrix(m11: 2.0f, m12: 0.0f, m13: 0.0f, |
496 | m21: 0.0f, m22: 1.0f, m23: 0.0f, |
497 | m31: 0.0f, m32: 0.0f, m33: 1.0f); |
498 | QCOMPARE(m2.type(), QTransform::TxScale); |
499 | m2 *= QTransform(); |
500 | QCOMPARE(m2.type(), QTransform::TxScale); |
501 | |
502 | m2.setMatrix(m11: 0.0f, m12: 1.0f, m13: 0.0f, |
503 | m21: 1.0f, m22: 0.0f, m23: 0.0f, |
504 | m31: 0.0f, m32: 1.0f, m33: 1.0f); |
505 | QCOMPARE(m2.type(), QTransform::TxRotate); |
506 | m2 *= QTransform(); |
507 | QCOMPARE(m2.type(), QTransform::TxRotate); |
508 | |
509 | m2.setMatrix(m11: 1.0f, m12: 0.0f, m13: 0.5f, |
510 | m21: 0.0f, m22: 1.0f, m23: 0.0f, |
511 | m31: 0.0f, m32: 0.0f, m33: 1.0f); |
512 | QCOMPARE(m2.type(), QTransform::TxProject); |
513 | m2 *= QTransform(); |
514 | QCOMPARE(m2.type(), QTransform::TxProject); |
515 | |
516 | m2.setMatrix(m11: 1.0f, m12: 1.0f, m13: 0.0f, |
517 | m21: 1.0f, m22: 0.0f, m23: 0.0f, |
518 | m31: 0.0f, m32: 1.0f, m33: 1.0f); |
519 | QCOMPARE(m2.type(), QTransform::TxShear); |
520 | |
521 | m2 *= m2.inverted(); |
522 | QCOMPARE(m2.type(), QTransform::TxNone); |
523 | |
524 | m2.translate(dx: 5.0f, dy: 5.0f); |
525 | m2.rotate(a: 45.0f); |
526 | m2.rotate(a: -45.0f); |
527 | QCOMPARE(m2.type(), QTransform::TxTranslate); |
528 | |
529 | m2.scale(sx: 2.0f, sy: 3.0f); |
530 | m2.shear(sh: 1.0f, sv: 0.0f); |
531 | m2.shear(sh: -1.0f, sv: 0.0f); |
532 | QCOMPARE(m2.type(), QTransform::TxScale); |
533 | |
534 | m2 *= QTransform(1.0f, 1.0f, 0.0f, |
535 | 0.0f, 1.0f, 0.0f, |
536 | 0.0f, 0.0f, 1.0f); |
537 | QCOMPARE(m2.type(), QTransform::TxShear); |
538 | |
539 | m2 *= QTransform(1.0f, 0.0f, 0.0f, |
540 | 0.0f, 1.0f, 0.0f, |
541 | 1.0f, 0.0f, 1.0f); |
542 | QCOMPARE(m2.type(), QTransform::TxShear); |
543 | |
544 | QTransform m3(1.8f, 0.0f, 0.0f, |
545 | 0.0f, 1.8f, 0.0f, |
546 | 0.0f, 0.0f, 1.0f); |
547 | |
548 | QCOMPARE(m3.type(), QTransform::TxScale); |
549 | m3.translate(dx: 5.0f, dy: 5.0f); |
550 | QCOMPARE(m3.type(), QTransform::TxScale); |
551 | QCOMPARE(m3.inverted().type(), QTransform::TxScale); |
552 | |
553 | m3.setMatrix(m11: 1.0f, m12: 0.0f, m13: 0.0f, |
554 | m21: 0.0f, m22: 1.0f, m23: 0.0f, |
555 | m31: 0.0f, m32: 0.0f, m33: 2.0f); |
556 | QCOMPARE(m3.type(), QTransform::TxProject); |
557 | |
558 | m3.setMatrix(m11: 0.0f, m12: 2.0f, m13: 0.0f, |
559 | m21: 1.0f, m22: 0.0f, m23: 0.0f, |
560 | m31: 0.0f, m32: 0.0f, m33: 2.0f); |
561 | QCOMPARE(m3.type(), QTransform::TxProject); |
562 | |
563 | QTransform m4; |
564 | m4.scale(sx: 5, sy: 5); |
565 | m4.translate(dx: 4, dy: 2); |
566 | m4.rotate(a: 45); |
567 | |
568 | QCOMPARE(m4.type(), QTransform::TxRotate); |
569 | |
570 | QTransform m5; |
571 | m5.scale(sx: 5, sy: 5); |
572 | m5 = m5.adjoint() / m5.determinant(); |
573 | QCOMPARE(m5.type(), QTransform::TxScale); |
574 | } |
575 | |
576 | void tst_QTransform::types2_data() |
577 | { |
578 | QTest::addColumn<QTransform>(name: "t1" ); |
579 | QTest::addColumn<QTransform::TransformationType>(name: "type" ); |
580 | |
581 | QTest::newRow( dataTag: "identity" ) << QTransform() << QTransform::TxNone; |
582 | QTest::newRow( dataTag: "translate" ) << QTransform().translate(dx: 10, dy: -0.1) << QTransform::TxTranslate; |
583 | QTest::newRow( dataTag: "scale" ) << QTransform().scale(sx: 10, sy: -0.1) << QTransform::TxScale; |
584 | QTest::newRow( dataTag: "rotate" ) << QTransform().rotate(a: 10) << QTransform::TxRotate; |
585 | QTest::newRow( dataTag: "shear" ) << QTransform().shear(sh: 10, sv: -0.1) << QTransform::TxShear; |
586 | QTest::newRow( dataTag: "project" ) << QTransform().rotate(a: 10, axis: Qt::XAxis) << QTransform::TxProject; |
587 | QTest::newRow( dataTag: "combined" ) << QTransform().translate(dx: 10, dy: -0.1).scale(sx: 10, sy: -0.1).rotate(a: 10, axis: Qt::YAxis) << QTransform::TxProject; |
588 | } |
589 | |
590 | void tst_QTransform::types2() |
591 | { |
592 | #define CHECKTXTYPE(func) { QTransform t2(func); \ |
593 | QTransform t3(t2.m11(), t2.m12(), t2.m13(), t2.m21(), t2.m22(), t2.m23(), t2.m31(), t2.m32(), t2.m33()); \ |
594 | QVERIFY2(t3.type() == t2.type(), #func); \ |
595 | } |
596 | |
597 | QFETCH( QTransform, t1 ); |
598 | QFETCH( QTransform::TransformationType, type ); |
599 | |
600 | Q_ASSERT(t1.type() == type); |
601 | |
602 | CHECKTXTYPE(t1.adjoint()); |
603 | CHECKTXTYPE(t1.inverted()); |
604 | CHECKTXTYPE(t1.transposed()); |
605 | |
606 | #undef CHECKTXTYPE |
607 | } |
608 | |
609 | void tst_QTransform::scalarOps() |
610 | { |
611 | QTransform t; |
612 | QCOMPARE(t.m11(), 1.); |
613 | QCOMPARE(t.m33(), 1.); |
614 | QCOMPARE(t.m21(), 0.); |
615 | |
616 | t = QTransform() + 3; |
617 | QCOMPARE(t.m11(), 4.); |
618 | QCOMPARE(t.m33(), 4.); |
619 | QCOMPARE(t.m21(), 3.); |
620 | |
621 | t = t - 3; |
622 | QCOMPARE(t.m11(), 1.); |
623 | QCOMPARE(t.m33(), 1.); |
624 | QCOMPARE(t.m21(), 0.); |
625 | QCOMPARE(t.isIdentity(), true); |
626 | |
627 | t += 3; |
628 | t = t * 2; |
629 | QCOMPARE(t.m11(), 8.); |
630 | QCOMPARE(t.m33(), 8.); |
631 | QCOMPARE(t.m21(), 6.); |
632 | } |
633 | |
634 | void tst_QTransform::transform() |
635 | { |
636 | QTransform t; |
637 | t.rotate(a: 30, axis: Qt::YAxis); |
638 | t.translate(dx: 15, dy: 10); |
639 | t.scale(sx: 2, sy: 2); |
640 | t.rotate(a: 30); |
641 | t.shear(sh: 0.5, sv: 0.5); |
642 | |
643 | QTransform a, b, c, d, e; |
644 | a.rotate(a: 30, axis: Qt::YAxis); |
645 | b.translate(dx: 15, dy: 10); |
646 | c.scale(sx: 2, sy: 2); |
647 | d.rotate(a: 30); |
648 | e.shear(sh: 0.5, sv: 0.5); |
649 | |
650 | QVERIFY(qFuzzyCompare(t, e * d * c * b * a)); |
651 | } |
652 | |
653 | void tst_QTransform::mapEmptyPath() |
654 | { |
655 | QPainterPath path; |
656 | path.moveTo(x: 10, y: 10); |
657 | path.lineTo(x: 10, y: 10); |
658 | QCOMPARE(QTransform().map(path), path); |
659 | } |
660 | |
661 | void tst_QTransform::boundingRect() |
662 | { |
663 | QPainterPath path; |
664 | path.moveTo(x: 10, y: 10); |
665 | path.lineTo(x: 10, y: 10); |
666 | QCOMPARE(path.boundingRect(), QRectF(10, 10, 0, 0)); |
667 | } |
668 | |
669 | void tst_QTransform::controlPointRect() |
670 | { |
671 | QPainterPath path; |
672 | path.moveTo(x: 10, y: 10); |
673 | path.lineTo(x: 10, y: 10); |
674 | QCOMPARE(path.controlPointRect(), QRectF(10, 10, 0, 0)); |
675 | } |
676 | |
677 | void tst_QTransform::inverted_data() |
678 | { |
679 | QTest::addColumn<QTransform>(name: "matrix" ); |
680 | |
681 | QTest::newRow(dataTag: "identity" ) |
682 | << QTransform(); |
683 | |
684 | QTest::newRow(dataTag: "TxTranslate" ) |
685 | << QTransform().translate(dx: 200, dy: 10); |
686 | |
687 | QTest::newRow(dataTag: "TxScale" ) |
688 | << QTransform().scale(sx: 5, sy: 2); |
689 | |
690 | QTest::newRow(dataTag: "TxTranslate TxScale" ) |
691 | << QTransform().translate(dx: 100, dy: -10).scale(sx: 40, sy: 2); |
692 | |
693 | QTest::newRow(dataTag: "TxScale TxTranslate" ) |
694 | << QTransform().scale(sx: 40, sy: 2).translate(dx: 100, dy: -10); |
695 | |
696 | QTest::newRow(dataTag: "TxRotate" ) |
697 | << QTransform().rotate(a: 40, axis: Qt::ZAxis); |
698 | |
699 | QTest::newRow(dataTag: "TxRotate TxScale" ) |
700 | << QTransform().rotate(a: 60, axis: Qt::ZAxis).scale(sx: 2, sy: 0.25); |
701 | |
702 | QTest::newRow(dataTag: "TxScale TxRotate" ) |
703 | << QTransform().scale(sx: 2, sy: 0.25).rotate(a: 30, axis: Qt::ZAxis); |
704 | |
705 | QTest::newRow(dataTag: "TxRotate TxScale TxTranslate" ) |
706 | << QTransform().rotate(a: 60, axis: Qt::ZAxis).scale(sx: 2, sy: 0.25).translate(dx: 200, dy: -3000); |
707 | |
708 | QTest::newRow(dataTag: "TxRotate TxTranslate TxScale" ) |
709 | << QTransform().rotate(a: 60, axis: Qt::ZAxis).translate(dx: 200, dy: -3000).scale(sx: 19, sy: 77); |
710 | |
711 | QTest::newRow(dataTag: "TxShear" ) |
712 | << QTransform().shear(sh: 10, sv: 10); |
713 | |
714 | QTest::newRow(dataTag: "TxShear TxRotate" ) |
715 | << QTransform().shear(sh: 10, sv: 10).rotate(a: 45, axis: Qt::ZAxis); |
716 | |
717 | QTest::newRow(dataTag: "TxShear TxRotate TxScale" ) |
718 | << QTransform().shear(sh: 10, sv: 10).rotate(a: 45, axis: Qt::ZAxis).scale(sx: 19, sy: 81); |
719 | |
720 | QTest::newRow(dataTag: "TxTranslate TxShear TxRotate TxScale" ) |
721 | << QTransform().translate(dx: 150, dy: -1).shear(sh: 10, sv: 10).rotate(a: 45, axis: Qt::ZAxis).scale(sx: 19, sy: 81); |
722 | |
723 | const qreal s = 500000; |
724 | |
725 | QTransform big; |
726 | big.scale(sx: s, sy: s); |
727 | |
728 | QTest::newRow(dataTag: "big" ) << big; |
729 | |
730 | QTransform smallTransform; |
731 | smallTransform.scale(sx: 1/s, sy: 1/s); |
732 | |
733 | QTest::newRow(dataTag: "small" ) << smallTransform; |
734 | } |
735 | |
736 | void tst_QTransform::inverted() |
737 | { |
738 | if (sizeof(qreal) != sizeof(double)) |
739 | QSKIP("precision error if qreal is not double" ); |
740 | |
741 | QFETCH(QTransform, matrix); |
742 | |
743 | const QTransform inverted = matrix.inverted(); |
744 | |
745 | QCOMPARE(matrix.isIdentity(), inverted.isIdentity()); |
746 | QCOMPARE(matrix.type(), inverted.type()); |
747 | |
748 | QVERIFY((matrix * inverted).isIdentity()); |
749 | QVERIFY((inverted * matrix).isIdentity()); |
750 | } |
751 | |
752 | void tst_QTransform::projectivePathMapping() |
753 | { |
754 | QPainterPath path; |
755 | path.addRect(x: -50, y: -50, w: 100, h: 100); |
756 | |
757 | const QRectF view(0, 0, 1024, 1024); |
758 | |
759 | QVERIFY(view.intersects(path.boundingRect())); |
760 | |
761 | for (int i = 0; i < 85; i += 5) { |
762 | QTransform transform; |
763 | transform.translate(dx: 512, dy: 512); |
764 | transform.rotate(a: i, axis: Qt::YAxis); |
765 | |
766 | const QPainterPath mapped = transform.map(p: path); |
767 | |
768 | QVERIFY(view.intersects(mapped.boundingRect())); |
769 | QVERIFY(transform.inverted().mapRect(view).intersects(path.boundingRect())); |
770 | } |
771 | } |
772 | |
773 | void tst_QTransform::mapInt() |
774 | { |
775 | int x = 0; |
776 | int y = 0; |
777 | |
778 | QTransform::fromTranslate(dx: 10, dy: 10).map(x, y, tx: &x, ty: &y); |
779 | |
780 | QCOMPARE(x, 10); |
781 | QCOMPARE(y, 10); |
782 | } |
783 | |
784 | void tst_QTransform::mapPathWithPoint() |
785 | { |
786 | QPainterPath p(QPointF(10, 10)); |
787 | p = QTransform::fromTranslate(dx: 10, dy: 10).map(p); |
788 | QCOMPARE(p.currentPosition(), QPointF(20, 20)); |
789 | } |
790 | |
791 | QTEST_APPLESS_MAIN(tst_QTransform) |
792 | |
793 | |
794 | #include "tst_qtransform.moc" |
795 | |