Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtransform.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4#include "qtransform.h"
5
6#include "qdatastream.h"
7#include "qdebug.h"
9#include "qregion.h"
10#include "qpainterpath.h"
11#include "qpainterpath_p.h"
12#include "qvariant.h"
13#include "qmath_p.h"
14#include <qnumeric.h>
15
16#include <private/qbezier_p.h>
17
19
20#ifndef QT_NO_DEBUG
21Q_NEVER_INLINE
22static void nanWarning(const char *func)
23{
24 qWarning("QTransform::%s with NaN called", func);
25}
26#endif // QT_NO_DEBUG
27
28#define Q_NEAR_CLIP (sizeof(qreal) == sizeof(double) ? 0.000001 : 0.0001)
29
30void QTransform::do_map(qreal x, qreal y, qreal &nx, qreal &ny) const
31{
32 const TransformationType t = inline_type();
33 switch (t) {
34 case QTransform::TxNone:
35 nx = x;
36 ny = y;
37 return;
38 case QTransform::TxTranslate:
39 nx = x + m_matrix[2][0];
40 ny = y + m_matrix[2][1];
41 return;
42 case QTransform::TxScale:
43 nx = m_matrix[0][0] * x + m_matrix[2][0];
44 ny = m_matrix[1][1] * y + m_matrix[2][1];
45 return;
46 case QTransform::TxRotate:
47 case QTransform::TxShear:
48 case QTransform::TxProject:
49 nx = m_matrix[0][0] * x + m_matrix[1][0] * y + m_matrix[2][0];
50 ny = m_matrix[0][1] * x + m_matrix[1][1] * y + m_matrix[2][1];
51 if (t == QTransform::TxProject) {
52 qreal w = (m_matrix[0][2] * x + m_matrix[1][2] * y + m_matrix[2][2]);
53 if (w < qreal(Q_NEAR_CLIP)) w = qreal(Q_NEAR_CLIP);
54 w = qreal(1.)/w;
55 nx *= w;
56 ny *= w;
57 }
58 return;
59 }
60 Q_UNREACHABLE_RETURN();
61}
62
63/*!
64 \class QTransform
65 \brief The QTransform class specifies 2D transformations of a coordinate system.
66 \since 4.3
67 \ingroup painting
68 \inmodule QtGui
69
70 A transformation specifies how to translate, scale, shear, rotate
71 or project the coordinate system, and is typically used when
72 rendering graphics.
73
74 A QTransform object can be built using the setMatrix(), scale(),
75 rotate(), translate() and shear() functions. Alternatively, it
76 can be built by applying \l {QTransform#Basic Matrix
77 Operations}{basic matrix operations}. The matrix can also be
78 defined when constructed, and it can be reset to the identity
79 matrix (the default) using the reset() function.
80
81 The QTransform class supports mapping of graphic primitives: A given
82 point, line, polygon, region, or painter path can be mapped to the
83 coordinate system defined by \e this matrix using the map()
84 function. In case of a rectangle, its coordinates can be
85 transformed using the mapRect() function. A rectangle can also be
86 transformed into a \e polygon (mapped to the coordinate system
87 defined by \e this matrix), using the mapToPolygon() function.
88
89 QTransform provides the isIdentity() function which returns \c true if
90 the matrix is the identity matrix, and the isInvertible() function
91 which returns \c true if the matrix is non-singular (i.e. AB = BA =
92 I). The inverted() function returns an inverted copy of \e this
93 matrix if it is invertible (otherwise it returns the identity
94 matrix), and adjoint() returns the matrix's classical adjoint.
95 In addition, QTransform provides the determinant() function which
96 returns the matrix's determinant.
97
98 Finally, the QTransform class supports matrix multiplication, addition
99 and subtraction, and objects of the class can be streamed as well
100 as compared.
101
102 \section1 Rendering Graphics
103
104 When rendering graphics, the matrix defines the transformations
105 but the actual transformation is performed by the drawing routines
106 in QPainter.
107
108 By default, QPainter operates on the associated device's own
109 coordinate system. The standard coordinate system of a
110 QPaintDevice has its origin located at the top-left position. The
111 \e x values increase to the right; \e y values increase
112 downward. For a complete description, see the \l {Coordinate
113 System} {coordinate system} documentation.
114
115 QPainter has functions to translate, scale, shear and rotate the
116 coordinate system without using a QTransform. For example:
117
118 \table 100%
119 \row
120 \li \inlineimage qtransform-simpletransformation.png
121 {Text rotated 45 degrees}
122 \li
123 \snippet transform/main.cpp 0
124 \endtable
125
126 Although these functions are very convenient, it can be more
127 efficient to build a QTransform and call QPainter::setTransform() if you
128 want to perform more than a single transform operation. For
129 example:
130
131 \table 100%
132 \row
133 \li \inlineimage qtransform-combinedtransformation.png
134 {Text scaled, rotated, and translated}
135 \li
136 \snippet transform/main.cpp 1
137 \endtable
138
139 \section1 Basic Matrix Operations
140
141 \image qtransform-representation.png {Illustration showing an object that
142 contains a 3x3 matrix where every row is a different element}
143
144 A QTransform object contains a 3 x 3 matrix. The \c m31 (\c dx) and
145 \c m32 (\c dy) elements specify horizontal and vertical translation.
146 The \c m11 and \c m22 elements specify horizontal and vertical scaling.
147 The \c m21 and \c m12 elements specify horizontal and vertical \e shearing.
148 And finally, the \c m13 and \c m23 elements specify horizontal and vertical
149 projection, with \c m33 as an additional projection factor.
150
151 QTransform transforms a point in the plane to another point using the
152 following formulas:
153
154 \snippet code/src_gui_painting_qtransform.cpp 0
155
156 The point \e (x, y) is the original point, and \e (x', y') is the
157 transformed point. \e (x', y') can be transformed back to \e (x,
158 y) by performing the same operation on the inverted() matrix.
159
160 The various matrix elements can be set when constructing the
161 matrix, or by using the setMatrix() function later on. They can also
162 be manipulated using the translate(), rotate(), scale() and
163 shear() convenience functions. The currently set values can be
164 retrieved using the m11(), m12(), m13(), m21(), m22(), m23(),
165 m31(), m32(), m33(), dx() and dy() functions.
166
167 Translation is the simplest transformation. Setting \c dx and \c
168 dy will move the coordinate system \c dx units along the X axis
169 and \c dy units along the Y axis. Scaling can be done by setting
170 \c m11 and \c m22. For example, setting \c m11 to 2 and \c m22 to
171 1.5 will double the height and increase the width by 50%. The
172 identity matrix has \c m11, \c m22, and \c m33 set to 1 (all others are set
173 to 0) mapping a point to itself. Shearing is controlled by \c m12
174 and \c m21. Setting these elements to values different from zero
175 will twist the coordinate system. Rotation is achieved by
176 setting both the shearing factors and the scaling factors. Perspective
177 transformation is achieved by setting both the projection factors and
178 the scaling factors.
179
180 \section2 Combining Transforms
181 Here's the combined transformations example using basic matrix
182 operations:
183
184 \table 100%
185 \row
186 \li \inlineimage qtransform-combinedtransformation2.png
187 {Text with combined matrix transformation}
188 \li
189 \snippet transform/main.cpp 2
190 \endtable
191
192 The combined transform first scales each operand, then rotates it, and
193 finally translates it, just as in the order in which the product of its
194 factors is written. This means the point to which the transforms are
195 applied is implicitly multiplied on the left with the transform
196 to its right.
197
198 The convenience functions translate(), scale(), rotate(), and shear()
199 work the other way around: each applies its operation to a point before
200 the transformation the matrix already represents. For example,
201 \c {QTransform().translate(10, 0).scale(2, 2)} scales a point first and
202 then translates it, the same as
203 \c {QTransform::fromScale(2, 2) * QTransform::fromTranslate(10, 0)}.
204
205 \section2 Relation to Matrix Notation
206 The matrix notation in QTransform is the transpose of a commonly-taught
207 convention which represents transforms and points as matrices and vectors.
208 That convention multiplies its matrix on the left and column vector to the
209 right. In other words, when several transforms are applied to a point, the
210 right-most matrix acts directly on the vector first. Then the next matrix
211 to the left acts on the result of the first operation - and so on. As a
212 result, that convention multiplies the matrices that make up a composite
213 transform in the reverse of the order in QTransform, as you can see in
214 \l {Combining Transforms}. Transposing the matrices, and combining them to
215 the right of a row vector that represents the point, lets the matrices of
216 transforms appear, in their product, in the order in which we think of the
217 transforms being applied to the point.
218
219 \sa QPainter, {Coordinate System}, {painting/affine}{Affine
220 Transformations Example}, {Transformations Example}
221*/
222
223/*!
224 \enum QTransform::TransformationType
225
226 \value TxNone
227 \value TxTranslate
228 \value TxScale
229 \value TxRotate
230 \value TxShear
231 \value TxProject
232*/
233
234/*!
235 \fn QTransform::QTransform(Qt::Initialization)
236 \internal
237*/
238
239/*!
240 \fn QTransform::QTransform()
241
242 Constructs an identity matrix.
243
244 All elements are set to zero except \c m11 and \c m22 (specifying
245 the scale) and \c m33 which are set to 1.
246
247 \sa reset()
248*/
249
250/*!
251 \fn QTransform::QTransform(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33)
252
253 Constructs a matrix with the elements, \a m11, \a m12, \a m13,
254 \a m21, \a m22, \a m23, \a m31, \a m32, \a m33.
255
256 \sa setMatrix()
257*/
258
259/*!
260 \fn QTransform::QTransform(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy)
261
262 Constructs a matrix with the elements, \a m11, \a m12, \a m21, \a m22, \a dx and \a dy.
263
264 \sa setMatrix()
265*/
266
267/*!
268 Returns the adjoint of this matrix.
269*/
270QTransform QTransform::adjoint() const
271{
272 qreal h11, h12, h13,
273 h21, h22, h23,
274 h31, h32, h33;
275 h11 = m_matrix[1][1] * m_matrix[2][2] - m_matrix[1][2] * m_matrix[2][1];
276 h21 = m_matrix[1][2] * m_matrix[2][0] - m_matrix[1][0] * m_matrix[2][2];
277 h31 = m_matrix[1][0] * m_matrix[2][1] - m_matrix[1][1] * m_matrix[2][0];
278 h12 = m_matrix[0][2] * m_matrix[2][1] - m_matrix[0][1] * m_matrix[2][2];
279 h22 = m_matrix[0][0] * m_matrix[2][2] - m_matrix[0][2] * m_matrix[2][0];
280 h32 = m_matrix[0][1] * m_matrix[2][0] - m_matrix[0][0] * m_matrix[2][1];
281 h13 = m_matrix[0][1] * m_matrix[1][2] - m_matrix[0][2] * m_matrix[1][1];
282 h23 = m_matrix[0][2] * m_matrix[1][0] - m_matrix[0][0] * m_matrix[1][2];
283 h33 = m_matrix[0][0] * m_matrix[1][1] - m_matrix[0][1] * m_matrix[1][0];
284
285 return QTransform(h11, h12, h13,
286 h21, h22, h23,
287 h31, h32, h33);
288}
289
290/*!
291 Returns the transpose of this matrix.
292*/
293QTransform QTransform::transposed() const
294{
295 QTransform t(m_matrix[0][0], m_matrix[1][0], m_matrix[2][0],
296 m_matrix[0][1], m_matrix[1][1], m_matrix[2][1],
297 m_matrix[0][2], m_matrix[1][2], m_matrix[2][2]);
298 return t;
299}
300
301/*!
302 Returns an inverted copy of this matrix.
303
304 If the matrix is singular (not invertible), the returned matrix is
305 the identity matrix. If \a invertible is valid (i.e. not 0), its
306 value is set to true if the matrix is invertible, otherwise it is
307 set to false.
308
309 \sa isInvertible()
310*/
311QTransform QTransform::inverted(bool *invertible) const
312{
313 QTransform invert;
314 bool inv = true;
315
316 switch(inline_type()) {
317 case TxNone:
318 break;
319 case TxTranslate:
320 invert.m_matrix[2][0] = -m_matrix[2][0];
321 invert.m_matrix[2][1] = -m_matrix[2][1];
322 break;
323 case TxScale:
324 inv = !qFuzzyIsNull(m_matrix[0][0]);
325 inv &= !qFuzzyIsNull(m_matrix[1][1]);
326 if (inv) {
327 invert.m_matrix[0][0] = 1. / m_matrix[0][0];
328 invert.m_matrix[1][1] = 1. / m_matrix[1][1];
329 invert.m_matrix[2][0] = -m_matrix[2][0] * invert.m_matrix[0][0];
330 invert.m_matrix[2][1] = -m_matrix[2][1] * invert.m_matrix[1][1];
331 }
332 break;
333 default:
334 // general case
335 qreal det = determinant();
336 inv = !qFuzzyIsNull(det);
337 if (inv)
338 invert = adjoint() / det;
339 break;
340 }
341
342 if (invertible)
343 *invertible = inv;
344
345 if (inv) {
346 // inverting doesn't change the type
347 invert.m_type = m_type;
348 invert.m_dirty = m_dirty;
349 }
350
351 return invert;
352}
353
354/*!
355 Moves the coordinate system \a dx along the x axis and \a dy along
356 the y axis, and returns a reference to the matrix.
357
358//! [transform-order-note]
359 This operation is applied to a point before the transformation the
360 matrix already represents. See \l {Combining Transforms}.
361//! [transform-order-note]
362
363 \sa setMatrix()
364*/
365QTransform &QTransform::translate(qreal dx, qreal dy)
366{
367 if (dx == 0 && dy == 0)
368 return *this;
369#ifndef QT_NO_DEBUG
370 if (qIsNaN(dx) || qIsNaN(dy)) {
371 nanWarning("translate");
372 return *this;
373 }
374#endif
375
376 switch(inline_type()) {
377 case TxNone:
378 m_matrix[2][0] = dx;
379 m_matrix[2][1] = dy;
380 break;
381 case TxTranslate:
382 m_matrix[2][0] += dx;
383 m_matrix[2][1] += dy;
384 break;
385 case TxScale:
386 m_matrix[2][0] += dx * m_matrix[0][0];
387 m_matrix[2][1] += dy * m_matrix[1][1];
388 break;
389 case TxProject:
390 m_matrix[2][2] += dx * m_matrix[0][2] + dy * m_matrix[1][2];
391 Q_FALLTHROUGH();
392 case TxShear:
393 case TxRotate:
394 m_matrix[2][0] += dx * m_matrix[0][0] + dy * m_matrix[1][0];
395 m_matrix[2][1] += dy * m_matrix[1][1] + dx * m_matrix[0][1];
396 break;
397 }
398 if (m_dirty < TxTranslate)
399 m_dirty = TxTranslate;
400 return *this;
401}
402
403/*!
404 Creates a matrix which corresponds to a translation of \a dx along
405 the x axis and \a dy along the y axis. This is the same as
406 QTransform().translate(dx, dy) but slightly faster.
407
408 \since 4.5
409*/
410QTransform QTransform::fromTranslate(qreal dx, qreal dy)
411{
412#ifndef QT_NO_DEBUG
413 if (qIsNaN(dx) || qIsNaN(dy)) {
414 nanWarning("fromTranslate");
415 return QTransform();
416}
417#endif
418 QTransform transform(1, 0, 0, 0, 1, 0, dx, dy, 1);
419 if (dx == 0 && dy == 0)
420 transform.m_type = TxNone;
421 else
422 transform.m_type = TxTranslate;
423 transform.m_dirty = TxNone;
424 return transform;
425}
426
427/*!
428 Scales the coordinate system by \a sx horizontally and \a sy
429 vertically, and returns a reference to the matrix.
430
431 \include qtransform.cpp transform-order-note
432
433 \sa setMatrix()
434*/
435QTransform & QTransform::scale(qreal sx, qreal sy)
436{
437 if (sx == 1 && sy == 1)
438 return *this;
439#ifndef QT_NO_DEBUG
440 if (qIsNaN(sx) || qIsNaN(sy)) {
441 nanWarning("scale");
442 return *this;
443 }
444#endif
445
446 switch(inline_type()) {
447 case TxNone:
448 case TxTranslate:
449 m_matrix[0][0] = sx;
450 m_matrix[1][1] = sy;
451 break;
452 case TxProject:
453 m_matrix[0][2] *= sx;
454 m_matrix[1][2] *= sy;
455 Q_FALLTHROUGH();
456 case TxRotate:
457 case TxShear:
458 m_matrix[0][1] *= sx;
459 m_matrix[1][0] *= sy;
460 Q_FALLTHROUGH();
461 case TxScale:
462 m_matrix[0][0] *= sx;
463 m_matrix[1][1] *= sy;
464 break;
465 }
466 if (m_dirty < TxScale)
467 m_dirty = TxScale;
468 return *this;
469}
470
471/*!
472 Creates a matrix which corresponds to a scaling of
473 \a sx horizontally and \a sy vertically.
474 This is the same as QTransform().scale(sx, sy) but slightly faster.
475
476 \since 4.5
477*/
478QTransform QTransform::fromScale(qreal sx, qreal sy)
479{
480#ifndef QT_NO_DEBUG
481 if (qIsNaN(sx) || qIsNaN(sy)) {
482 nanWarning("fromScale");
483 return QTransform();
484}
485#endif
486 QTransform transform(sx, 0, 0, 0, sy, 0, 0, 0, 1);
487 if (sx == 1. && sy == 1.)
488 transform.m_type = TxNone;
489 else
490 transform.m_type = TxScale;
491 transform.m_dirty = TxNone;
492 return transform;
493}
494
495/*!
496 Shears the coordinate system by \a sh horizontally and \a sv
497 vertically, and returns a reference to the matrix.
498
499 \include qtransform.cpp transform-order-note
500
501 \sa setMatrix()
502*/
503QTransform & QTransform::shear(qreal sh, qreal sv)
504{
505 if (sh == 0 && sv == 0)
506 return *this;
507#ifndef QT_NO_DEBUG
508 if (qIsNaN(sh) || qIsNaN(sv)) {
509 nanWarning("shear");
510 return *this;
511 }
512#endif
513
514 switch(inline_type()) {
515 case TxNone:
516 case TxTranslate:
517 m_matrix[0][1] = sv;
518 m_matrix[1][0] = sh;
519 break;
520 case TxScale:
521 m_matrix[0][1] = sv*m_matrix[1][1];
522 m_matrix[1][0] = sh*m_matrix[0][0];
523 break;
524 case TxProject: {
525 qreal tm13 = sv * m_matrix[1][2];
526 qreal tm23 = sh * m_matrix[0][2];
527 m_matrix[0][2] += tm13;
528 m_matrix[1][2] += tm23;
529 }
530 Q_FALLTHROUGH();
531 case TxRotate:
532 case TxShear: {
533 qreal tm11 = sv * m_matrix[1][0];
534 qreal tm22 = sh * m_matrix[0][1];
535 qreal tm12 = sv * m_matrix[1][1];
536 qreal tm21 = sh * m_matrix[0][0];
537 m_matrix[0][0] += tm11;
538 m_matrix[0][1] += tm12;
539 m_matrix[1][0] += tm21;
540 m_matrix[1][1] += tm22;
541 break;
542 }
543 }
544 if (m_dirty < TxShear)
545 m_dirty = TxShear;
546 return *this;
547}
548
549/*!
550 \since 6.5
551
552 Rotates the coordinate system counterclockwise by the given angle \a a
553 about the specified \a axis at distance \a distanceToPlane from the
554 screen and returns a reference to the matrix.
555
556//! [transform-rotate-note]
557 Note that if you apply a QTransform to a point defined in widget
558 coordinates, the direction of the rotation will be clockwise
559 because the y-axis points downwards.
560
561 The angle is specified in degrees.
562
563 This operation is applied to a point before the transformation the
564 matrix already represents. See \l {Combining Transforms}.
565//! [transform-rotate-note]
566
567 If \a distanceToPlane is zero, it will be ignored. This is suitable
568 for implementing orthographic projections where the z coordinate should
569 be dropped rather than projected.
570
571 \sa setMatrix()
572*/
573QTransform & QTransform::rotate(qreal a, Qt::Axis axis, qreal distanceToPlane)
574{
575 if (a == 0)
576 return *this;
577#ifndef QT_NO_DEBUG
578 if (qIsNaN(a) || qIsNaN(distanceToPlane)) {
579 nanWarning("rotate");
580 return *this;
581 }
582#endif
583
584 qreal sina = 0;
585 qreal cosa = 0;
586 if (a == 90. || a == -270.)
587 sina = 1.;
588 else if (a == 270. || a == -90.)
589 sina = -1.;
590 else if (a == 180.)
591 cosa = -1.;
592 else{
593 qreal b = qDegreesToRadians(a);
594 sina = qSin(b); // fast and convenient
595 cosa = qCos(b);
596 }
597
598 if (axis == Qt::ZAxis) {
599 switch(inline_type()) {
600 case TxNone:
601 case TxTranslate:
602 m_matrix[0][0] = cosa;
603 m_matrix[0][1] = sina;
604 m_matrix[1][0] = -sina;
605 m_matrix[1][1] = cosa;
606 break;
607 case TxScale: {
608 qreal tm11 = cosa * m_matrix[0][0];
609 qreal tm12 = sina * m_matrix[1][1];
610 qreal tm21 = -sina * m_matrix[0][0];
611 qreal tm22 = cosa * m_matrix[1][1];
612 m_matrix[0][0] = tm11;
613 m_matrix[0][1] = tm12;
614 m_matrix[1][0] = tm21;
615 m_matrix[1][1] = tm22;
616 break;
617 }
618 case TxProject: {
619 qreal tm13 = cosa * m_matrix[0][2] + sina * m_matrix[1][2];
620 qreal tm23 = -sina * m_matrix[0][2] + cosa * m_matrix[1][2];
621 m_matrix[0][2] = tm13;
622 m_matrix[1][2] = tm23;
623 Q_FALLTHROUGH();
624 }
625 case TxRotate:
626 case TxShear: {
627 qreal tm11 = cosa * m_matrix[0][0] + sina * m_matrix[1][0];
628 qreal tm12 = cosa * m_matrix[0][1] + sina * m_matrix[1][1];
629 qreal tm21 = -sina * m_matrix[0][0] + cosa * m_matrix[1][0];
630 qreal tm22 = -sina * m_matrix[0][1] + cosa * m_matrix[1][1];
631 m_matrix[0][0] = tm11;
632 m_matrix[0][1] = tm12;
633 m_matrix[1][0] = tm21;
634 m_matrix[1][1] = tm22;
635 break;
636 }
637 }
638 if (m_dirty < TxRotate)
639 m_dirty = TxRotate;
640 } else {
641 if (!qIsNull(distanceToPlane))
642 sina /= distanceToPlane;
643
644 QTransform result;
645 if (axis == Qt::YAxis) {
646 result.m_matrix[0][0] = cosa;
647 result.m_matrix[0][2] = -sina;
648 } else {
649 result.m_matrix[1][1] = cosa;
650 result.m_matrix[1][2] = -sina;
651 }
652 result.m_type = TxProject;
653 *this = result * *this;
654 }
655
656 return *this;
657}
658
659#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
660/*!
661 \overload
662
663 Rotates the coordinate system counterclockwise by the given angle \a a
664 about the specified \a axis at distance 1024.0 from the screen and
665 returns a reference to the matrix.
666
667 \include qtransform.cpp transform-rotate-note
668
669 \sa setMatrix
670*/
671QTransform &QTransform::rotate(qreal a, Qt::Axis axis)
672{
673 return rotate(a, axis, 1024.0);
674}
675#endif
676
677/*!
678 \since 6.5
679
680 Rotates the coordinate system counterclockwise by the given angle \a a
681 about the specified \a axis at distance \a distanceToPlane from the
682 screen and returns a reference to the matrix.
683
684//! [transform-rotate-radians-note]
685 Note that if you apply a QTransform to a point defined in widget
686 coordinates, the direction of the rotation will be clockwise
687 because the y-axis points downwards.
688
689 The angle is specified in radians.
690
691 This operation is applied to a point before the transformation the
692 matrix already represents. See \l {Combining Transforms}.
693//! [transform-rotate-radians-note]
694
695 If \a distanceToPlane is zero, it will be ignored. This is suitable
696 for implementing orthographic projections where the z coordinate should
697 be dropped rather than projected.
698
699 \sa setMatrix()
700*/
701QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis, qreal distanceToPlane)
702{
703#ifndef QT_NO_DEBUG
704 if (qIsNaN(a) || qIsNaN(distanceToPlane)) {
705 nanWarning("rotateRadians");
706 return *this;
707 }
708#endif
709 qreal sina = qSin(a);
710 qreal cosa = qCos(a);
711
712 if (axis == Qt::ZAxis) {
713 switch(inline_type()) {
714 case TxNone:
715 case TxTranslate:
716 m_matrix[0][0] = cosa;
717 m_matrix[0][1] = sina;
718 m_matrix[1][0] = -sina;
719 m_matrix[1][1] = cosa;
720 break;
721 case TxScale: {
722 qreal tm11 = cosa * m_matrix[0][0];
723 qreal tm12 = sina * m_matrix[1][1];
724 qreal tm21 = -sina * m_matrix[0][0];
725 qreal tm22 = cosa * m_matrix[1][1];
726 m_matrix[0][0] = tm11;
727 m_matrix[0][1] = tm12;
728 m_matrix[1][0] = tm21;
729 m_matrix[1][1] = tm22;
730 break;
731 }
732 case TxProject: {
733 qreal tm13 = cosa * m_matrix[0][2] + sina * m_matrix[1][2];
734 qreal tm23 = -sina * m_matrix[0][2] + cosa * m_matrix[1][2];
735 m_matrix[0][2] = tm13;
736 m_matrix[1][2] = tm23;
737 Q_FALLTHROUGH();
738 }
739 case TxRotate:
740 case TxShear: {
741 qreal tm11 = cosa * m_matrix[0][0] + sina * m_matrix[1][0];
742 qreal tm12 = cosa * m_matrix[0][1] + sina * m_matrix[1][1];
743 qreal tm21 = -sina * m_matrix[0][0] + cosa * m_matrix[1][0];
744 qreal tm22 = -sina * m_matrix[0][1] + cosa * m_matrix[1][1];
745 m_matrix[0][0] = tm11;
746 m_matrix[0][1] = tm12;
747 m_matrix[1][0] = tm21;
748 m_matrix[1][1] = tm22;
749 break;
750 }
751 }
752 if (m_dirty < TxRotate)
753 m_dirty = TxRotate;
754 } else {
755 if (!qIsNull(distanceToPlane))
756 sina /= distanceToPlane;
757
758 QTransform result;
759 if (axis == Qt::YAxis) {
760 result.m_matrix[0][0] = cosa;
761 result.m_matrix[0][2] = -sina;
762 } else {
763 result.m_matrix[1][1] = cosa;
764 result.m_matrix[1][2] = -sina;
765 }
766 result.m_type = TxProject;
767 *this = result * *this;
768 }
769 return *this;
770}
771
772#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
773/*!
774 \overload
775
776 Rotates the coordinate system counterclockwise by the given angle \a a
777 about the specified \a axis at distance 1024.0 from the screen and
778 returns a reference to the matrix.
779
780 \include qtransform.cpp transform-rotate-radians-note
781
782 \sa setMatrix()
783*/
784QTransform &QTransform::rotateRadians(qreal a, Qt::Axis axis)
785{
786 return rotateRadians(a, axis, 1024.0);
787}
788#endif
789
790/*!
791 \fn bool QTransform::operator==(const QTransform &matrix) const
792 Returns \c true if this matrix is equal to the given \a matrix,
793 otherwise returns \c false.
794*/
795bool QTransform::operator==(const QTransform &o) const
796{
797 return m_matrix[0][0] == o.m_matrix[0][0] &&
798 m_matrix[0][1] == o.m_matrix[0][1] &&
799 m_matrix[1][0] == o.m_matrix[1][0] &&
800 m_matrix[1][1] == o.m_matrix[1][1] &&
801 m_matrix[2][0] == o.m_matrix[2][0] &&
802 m_matrix[2][1] == o.m_matrix[2][1] &&
803 m_matrix[0][2] == o.m_matrix[0][2] &&
804 m_matrix[1][2] == o.m_matrix[1][2] &&
805 m_matrix[2][2] == o.m_matrix[2][2];
806}
807
808/*!
809 \since 5.6
810 \qhashold{QTransform}
811*/
812size_t qHash(const QTransform &key, size_t seed) noexcept
813{
814 QtPrivate::QHashCombineWithSeed hash(seed);
815 seed = hash(seed, key.m11());
816 seed = hash(seed, key.m12());
817 seed = hash(seed, key.m21());
818 seed = hash(seed, key.m22());
819 seed = hash(seed, key.dx());
820 seed = hash(seed, key.dy());
821 seed = hash(seed, key.m13());
822 seed = hash(seed, key.m23());
823 seed = hash(seed, key.m33());
824 return seed;
825}
826
827
828/*!
829 \fn bool QTransform::operator!=(const QTransform &matrix) const
830 Returns \c true if this matrix is not equal to the given \a matrix,
831 otherwise returns \c false.
832*/
833bool QTransform::operator!=(const QTransform &o) const
834{
835 return !operator==(o);
836}
837
838/*!
839 \fn QTransform & QTransform::operator*=(const QTransform &matrix)
840 \overload
841
842 Multiplies this matrix by the given \a matrix and returns a reference
843 to this matrix. The resulting matrix applies \a matrix to a point after
844 the transformation this matrix represented before the call.
845*/
846QTransform & QTransform::operator*=(const QTransform &o)
847{
848 const TransformationType otherType = o.inline_type();
849 if (otherType == TxNone)
850 return *this;
851
852 const TransformationType thisType = inline_type();
853 if (thisType == TxNone)
854 return operator=(o);
855
856 TransformationType t = qMax(thisType, otherType);
857 switch(t) {
858 case TxNone:
859 break;
860 case TxTranslate:
861 m_matrix[2][0] += o.m_matrix[2][0];
862 m_matrix[2][1] += o.m_matrix[2][1];
863 break;
864 case TxScale:
865 {
866 qreal m11 = m_matrix[0][0] * o.m_matrix[0][0];
867 qreal m22 = m_matrix[1][1] * o.m_matrix[1][1];
868
869 qreal m31 = m_matrix[2][0] * o.m_matrix[0][0] + o.m_matrix[2][0];
870 qreal m32 = m_matrix[2][1] * o.m_matrix[1][1] + o.m_matrix[2][1];
871
872 m_matrix[0][0] = m11;
873 m_matrix[1][1] = m22;
874 m_matrix[2][0] = m31; m_matrix[2][1] = m32;
875 break;
876 }
877 case TxRotate:
878 case TxShear:
879 {
880 qreal m11 = m_matrix[0][0] * o.m_matrix[0][0] + m_matrix[0][1] * o.m_matrix[1][0];
881 qreal m12 = m_matrix[0][0] * o.m_matrix[0][1] + m_matrix[0][1] * o.m_matrix[1][1];
882
883 qreal m21 = m_matrix[1][0] * o.m_matrix[0][0] + m_matrix[1][1] * o.m_matrix[1][0];
884 qreal m22 = m_matrix[1][0] * o.m_matrix[0][1] + m_matrix[1][1] * o.m_matrix[1][1];
885
886 qreal m31 = m_matrix[2][0] * o.m_matrix[0][0] + m_matrix[2][1] * o.m_matrix[1][0] + o.m_matrix[2][0];
887 qreal m32 = m_matrix[2][0] * o.m_matrix[0][1] + m_matrix[2][1] * o.m_matrix[1][1] + o.m_matrix[2][1];
888
889 m_matrix[0][0] = m11;
890 m_matrix[0][1] = m12;
891 m_matrix[1][0] = m21;
892 m_matrix[1][1] = m22;
893 m_matrix[2][0] = m31;
894 m_matrix[2][1] = m32;
895 break;
896 }
897 case TxProject:
898 {
899 qreal m11 = m_matrix[0][0] * o.m_matrix[0][0] + m_matrix[0][1] * o.m_matrix[1][0] + m_matrix[0][2] * o.m_matrix[2][0];
900 qreal m12 = m_matrix[0][0] * o.m_matrix[0][1] + m_matrix[0][1] * o.m_matrix[1][1] + m_matrix[0][2] * o.m_matrix[2][1];
901 qreal m13 = m_matrix[0][0] * o.m_matrix[0][2] + m_matrix[0][1] * o.m_matrix[1][2] + m_matrix[0][2] * o.m_matrix[2][2];
902
903 qreal m21 = m_matrix[1][0] * o.m_matrix[0][0] + m_matrix[1][1] * o.m_matrix[1][0] + m_matrix[1][2] * o.m_matrix[2][0];
904 qreal m22 = m_matrix[1][0] * o.m_matrix[0][1] + m_matrix[1][1] * o.m_matrix[1][1] + m_matrix[1][2] * o.m_matrix[2][1];
905 qreal m23 = m_matrix[1][0] * o.m_matrix[0][2] + m_matrix[1][1] * o.m_matrix[1][2] + m_matrix[1][2] * o.m_matrix[2][2];
906
907 qreal m31 = m_matrix[2][0] * o.m_matrix[0][0] + m_matrix[2][1] * o.m_matrix[1][0] + m_matrix[2][2] * o.m_matrix[2][0];
908 qreal m32 = m_matrix[2][0] * o.m_matrix[0][1] + m_matrix[2][1] * o.m_matrix[1][1] + m_matrix[2][2] * o.m_matrix[2][1];
909 qreal m33 = m_matrix[2][0] * o.m_matrix[0][2] + m_matrix[2][1] * o.m_matrix[1][2] + m_matrix[2][2] * o.m_matrix[2][2];
910
911 m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13;
912 m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23;
913 m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33;
914 }
915 }
916
917 m_dirty = t;
918 m_type = t;
919
920 return *this;
921}
922
923/*!
924 \fn QTransform QTransform::operator*(const QTransform &matrix) const
925 Returns the result of multiplying this matrix by the given \a
926 matrix. The result applies this matrix to a point first, followed by
927 \a matrix.
928
929 Note that matrix multiplication is not commutative, i.e. a*b !=
930 b*a.
931*/
932QTransform QTransform::operator*(const QTransform &m) const
933{
934 const TransformationType otherType = m.inline_type();
935 if (otherType == TxNone)
936 return *this;
937
938 const TransformationType thisType = inline_type();
939 if (thisType == TxNone)
940 return m;
941
942 QTransform t;
943 TransformationType type = qMax(thisType, otherType);
944 switch(type) {
945 case TxNone:
946 break;
947 case TxTranslate:
948 t.m_matrix[2][0] = m_matrix[2][0] + m.m_matrix[2][0];
949 t.m_matrix[2][1] = m_matrix[2][1] + m.m_matrix[2][1];
950 break;
951 case TxScale:
952 {
953 qreal m11 = m_matrix[0][0] * m.m_matrix[0][0];
954 qreal m22 = m_matrix[1][1] * m.m_matrix[1][1];
955
956 qreal m31 = m_matrix[2][0] * m.m_matrix[0][0] + m.m_matrix[2][0];
957 qreal m32 = m_matrix[2][1] * m.m_matrix[1][1] + m.m_matrix[2][1];
958
959 t.m_matrix[0][0] = m11;
960 t.m_matrix[1][1] = m22;
961 t.m_matrix[2][0] = m31;
962 t.m_matrix[2][1] = m32;
963 break;
964 }
965 case TxRotate:
966 case TxShear:
967 {
968 qreal m11 = m_matrix[0][0] * m.m_matrix[0][0] + m_matrix[0][1] * m.m_matrix[1][0];
969 qreal m12 = m_matrix[0][0] * m.m_matrix[0][1] + m_matrix[0][1] * m.m_matrix[1][1];
970
971 qreal m21 = m_matrix[1][0] * m.m_matrix[0][0] + m_matrix[1][1] * m.m_matrix[1][0];
972 qreal m22 = m_matrix[1][0] * m.m_matrix[0][1] + m_matrix[1][1] * m.m_matrix[1][1];
973
974 qreal m31 = m_matrix[2][0] * m.m_matrix[0][0] + m_matrix[2][1] * m.m_matrix[1][0] + m.m_matrix[2][0];
975 qreal m32 = m_matrix[2][0] * m.m_matrix[0][1] + m_matrix[2][1] * m.m_matrix[1][1] + m.m_matrix[2][1];
976
977 t.m_matrix[0][0] = m11; t.m_matrix[0][1] = m12;
978 t.m_matrix[1][0] = m21; t.m_matrix[1][1] = m22;
979 t.m_matrix[2][0] = m31; t.m_matrix[2][1] = m32;
980 break;
981 }
982 case TxProject:
983 {
984 qreal m11 = m_matrix[0][0] * m.m_matrix[0][0] + m_matrix[0][1] * m.m_matrix[1][0] + m_matrix[0][2] * m.m_matrix[2][0];
985 qreal m12 = m_matrix[0][0] * m.m_matrix[0][1] + m_matrix[0][1] * m.m_matrix[1][1] + m_matrix[0][2] * m.m_matrix[2][1];
986 qreal m13 = m_matrix[0][0] * m.m_matrix[0][2] + m_matrix[0][1] * m.m_matrix[1][2] + m_matrix[0][2] * m.m_matrix[2][2];
987
988 qreal m21 = m_matrix[1][0] * m.m_matrix[0][0] + m_matrix[1][1] * m.m_matrix[1][0] + m_matrix[1][2] * m.m_matrix[2][0];
989 qreal m22 = m_matrix[1][0] * m.m_matrix[0][1] + m_matrix[1][1] * m.m_matrix[1][1] + m_matrix[1][2] * m.m_matrix[2][1];
990 qreal m23 = m_matrix[1][0] * m.m_matrix[0][2] + m_matrix[1][1] * m.m_matrix[1][2] + m_matrix[1][2] * m.m_matrix[2][2];
991
992 qreal m31 = m_matrix[2][0] * m.m_matrix[0][0] + m_matrix[2][1] * m.m_matrix[1][0] + m_matrix[2][2] * m.m_matrix[2][0];
993 qreal m32 = m_matrix[2][0] * m.m_matrix[0][1] + m_matrix[2][1] * m.m_matrix[1][1] + m_matrix[2][2] * m.m_matrix[2][1];
994 qreal m33 = m_matrix[2][0] * m.m_matrix[0][2] + m_matrix[2][1] * m.m_matrix[1][2] + m_matrix[2][2] * m.m_matrix[2][2];
995
996 t.m_matrix[0][0] = m11; t.m_matrix[0][1] = m12; t.m_matrix[0][2] = m13;
997 t.m_matrix[1][0] = m21; t.m_matrix[1][1] = m22; t.m_matrix[1][2] = m23;
998 t.m_matrix[2][0] = m31; t.m_matrix[2][1] = m32; t.m_matrix[2][2] = m33;
999 }
1000 }
1001
1002 t.m_dirty = type;
1003 t.m_type = type;
1004
1005 return t;
1006}
1007
1008/*!
1009 \fn QTransform & QTransform::operator*=(qreal scalar)
1010 \overload
1011
1012 Returns the result of performing an element-wise multiplication of this
1013 matrix with the given \a scalar.
1014*/
1015
1016/*!
1017 \fn QTransform & QTransform::operator/=(qreal scalar)
1018 \overload
1019
1020 Returns the result of performing an element-wise division of this
1021 matrix by the given \a scalar.
1022*/
1023
1024/*!
1025 \fn QTransform & QTransform::operator+=(qreal scalar)
1026 \overload
1027
1028 Returns the matrix obtained by adding the given \a scalar to each
1029 element of this matrix.
1030*/
1031
1032/*!
1033 \fn QTransform & QTransform::operator-=(qreal scalar)
1034 \overload
1035
1036 Returns the matrix obtained by subtracting the given \a scalar from each
1037 element of this matrix.
1038*/
1039
1040/*!
1041 \fn QTransform &QTransform::operator=(const QTransform &matrix) noexcept
1042
1043 Assigns the given \a matrix's values to this matrix.
1044*/
1045
1046/*!
1047 Resets the matrix to an identity matrix, i.e. all elements are set
1048 to zero, except \c m11 and \c m22 (specifying the scale) and \c m33
1049 which are set to 1.
1050
1051 \sa QTransform(), isIdentity(), {QTransform#Basic Matrix
1052 Operations}{Basic Matrix Operations}
1053*/
1054void QTransform::reset()
1055{
1056 *this = QTransform();
1057}
1058
1059#ifndef QT_NO_DATASTREAM
1060/*!
1061 \fn QDataStream &operator<<(QDataStream &stream, const QTransform &matrix)
1062 \since 4.3
1063 \relates QTransform
1064
1065 Writes the given \a matrix to the given \a stream and returns a
1066 reference to the stream.
1067
1068 \sa {Serializing Qt Data Types}
1069*/
1070QDataStream & operator<<(QDataStream &s, const QTransform &m)
1071{
1072 s << double(m.m11())
1073 << double(m.m12())
1074 << double(m.m13())
1075 << double(m.m21())
1076 << double(m.m22())
1077 << double(m.m23())
1078 << double(m.m31())
1079 << double(m.m32())
1080 << double(m.m33());
1081 return s;
1082}
1083
1084/*!
1085 \fn QDataStream &operator>>(QDataStream &stream, QTransform &matrix)
1086 \since 4.3
1087 \relates QTransform
1088
1089 Reads the given \a matrix from the given \a stream and returns a
1090 reference to the stream.
1091
1092 \sa {Serializing Qt Data Types}
1093*/
1094QDataStream & operator>>(QDataStream &s, QTransform &t)
1095{
1096 double m11, m12, m13,
1097 m21, m22, m23,
1098 m31, m32, m33;
1099
1100 s >> m11;
1101 s >> m12;
1102 s >> m13;
1103 s >> m21;
1104 s >> m22;
1105 s >> m23;
1106 s >> m31;
1107 s >> m32;
1108 s >> m33;
1109 t.setMatrix(m11, m12, m13,
1110 m21, m22, m23,
1111 m31, m32, m33);
1112 return s;
1113}
1114
1115#endif // QT_NO_DATASTREAM
1116
1117#ifndef QT_NO_DEBUG_STREAM
1118QDebug operator<<(QDebug dbg, const QTransform &m)
1119{
1120 static const char typeStr[][12] =
1121 {
1122 "TxNone",
1123 "TxTranslate",
1124 "TxScale",
1125 "",
1126 "TxRotate",
1127 "", "", "",
1128 "TxShear",
1129 "", "", "", "", "", "", "",
1130 "TxProject"
1131 };
1132
1133 QDebugStateSaver saver(dbg);
1134 dbg.nospace() << "QTransform(type=" << typeStr[m.type()] << ','
1135 << " 11=" << m.m11()
1136 << " 12=" << m.m12()
1137 << " 13=" << m.m13()
1138 << " 21=" << m.m21()
1139 << " 22=" << m.m22()
1140 << " 23=" << m.m23()
1141 << " 31=" << m.m31()
1142 << " 32=" << m.m32()
1143 << " 33=" << m.m33()
1144 << ')';
1145
1146 return dbg;
1147}
1148#endif
1149
1150/*!
1151 \fn QPoint operator*(const QPoint &point, const QTransform &matrix)
1152 \relates QTransform
1153
1154 This is the same as \a{matrix}.map(\a{point}).
1155
1156 \sa QTransform::map()
1157*/
1158QPoint QTransform::map(const QPoint &p) const
1159{
1160 qreal fx = p.x();
1161 qreal fy = p.y();
1162
1163 qreal x = 0, y = 0;
1164
1165 do_map(fx, fy, x, y);
1166
1167 return QPoint(qRound(x), qRound(y));
1168}
1169
1170
1171/*!
1172 \fn QPointF operator*(const QPointF &point, const QTransform &matrix)
1173 \relates QTransform
1174
1175 Same as \a{matrix}.map(\a{point}).
1176
1177 \sa QTransform::map()
1178*/
1179
1180/*!
1181 \overload
1182
1183 Creates and returns a QPointF object that is a copy of the given point,
1184 \a p, mapped into the coordinate system defined by this matrix.
1185*/
1186QPointF QTransform::map(const QPointF &p) const
1187{
1188 qreal fx = p.x();
1189 qreal fy = p.y();
1190
1191 qreal x = 0, y = 0;
1192
1193 do_map(fx, fy, x, y);
1194
1195 return QPointF(x, y);
1196}
1197
1198/*!
1199 \fn QPoint QTransform::map(const QPoint &point) const
1200 \overload
1201
1202 Creates and returns a QPoint object that is a copy of the given \a
1203 point, mapped into the coordinate system defined by this
1204 matrix. Note that the transformed coordinates are rounded to the
1205 nearest integer.
1206*/
1207
1208/*!
1209 \fn QLineF operator*(const QLineF &line, const QTransform &matrix)
1210 \relates QTransform
1211
1212 This is the same as \a{matrix}.map(\a{line}).
1213
1214 \sa QTransform::map()
1215*/
1216
1217/*!
1218 \fn QLine operator*(const QLine &line, const QTransform &matrix)
1219 \relates QTransform
1220
1221 This is the same as \a{matrix}.map(\a{line}).
1222
1223 \sa QTransform::map()
1224*/
1225
1226/*!
1227 \overload
1228
1229 Creates and returns a QLineF object that is a copy of the given line,
1230 \a l, mapped into the coordinate system defined by this matrix.
1231*/
1232QLine QTransform::map(const QLine &l) const
1233{
1234 qreal fx1 = l.x1();
1235 qreal fy1 = l.y1();
1236 qreal fx2 = l.x2();
1237 qreal fy2 = l.y2();
1238
1239 qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1240
1241 do_map(fx1, fy1, x1, y1);
1242 do_map(fx2, fy2, x2, y2);
1243
1244 return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2));
1245}
1246
1247/*!
1248 \overload
1249
1250 \fn QLineF QTransform::map(const QLineF &line) const
1251
1252 Creates and returns a QLine object that is a copy of the given \a
1253 line, mapped into the coordinate system defined by this matrix.
1254 Note that the transformed coordinates are rounded to the nearest
1255 integer.
1256*/
1257
1258QLineF QTransform::map(const QLineF &l) const
1259{
1260 qreal fx1 = l.x1();
1261 qreal fy1 = l.y1();
1262 qreal fx2 = l.x2();
1263 qreal fy2 = l.y2();
1264
1265 qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1266
1267 do_map(fx1, fy1, x1, y1);
1268 do_map(fx2, fy2, x2, y2);
1269
1270 return QLineF(x1, y1, x2, y2);
1271}
1272
1273/*!
1274 \fn QPolygonF operator *(const QPolygonF &polygon, const QTransform &matrix)
1275 \since 4.3
1276 \relates QTransform
1277
1278 This is the same as \a{matrix}.map(\a{polygon}).
1279
1280 \sa QTransform::map()
1281*/
1282
1283/*!
1284 \fn QPolygon operator*(const QPolygon &polygon, const QTransform &matrix)
1285 \relates QTransform
1286
1287 This is the same as \a{matrix}.map(\a{polygon}).
1288
1289 \sa QTransform::map()
1290*/
1291
1292/*!
1293 \fn QPolygonF QTransform::map(const QPolygonF &polygon) const
1294 \overload
1295
1296 Creates and returns a QPolygonF object that is a copy of the given
1297 \a polygon, mapped into the coordinate system defined by this
1298 matrix.
1299*/
1300QPolygonF QTransform::map(const QPolygonF &a) const
1301{
1302 TransformationType t = inline_type();
1303 if (t <= TxTranslate)
1304 return a.translated(m_matrix[2][0], m_matrix[2][1]);
1305
1306 int size = a.size();
1307 int i;
1308 QPolygonF p(size);
1309 const QPointF *da = a.constData();
1310 QPointF *dp = p.data();
1311
1312 for(i = 0; i < size; ++i) {
1313 do_map(da[i].x(), da[i].y(), dp[i].rx(), dp[i].ry());
1314 }
1315 return p;
1316}
1317
1318/*!
1319 \fn QPolygon QTransform::map(const QPolygon &polygon) const
1320 \overload
1321
1322 Creates and returns a QPolygon object that is a copy of the given
1323 \a polygon, mapped into the coordinate system defined by this
1324 matrix. Note that the transformed coordinates are rounded to the
1325 nearest integer.
1326*/
1327QPolygon QTransform::map(const QPolygon &a) const
1328{
1329 TransformationType t = inline_type();
1330 if (t <= TxTranslate)
1331 return a.translated(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1332
1333 int size = a.size();
1334 int i;
1335 QPolygon p(size);
1336 const QPoint *da = a.constData();
1337 QPoint *dp = p.data();
1338
1339 for(i = 0; i < size; ++i) {
1340 qreal nx = 0, ny = 0;
1341 do_map(da[i].x(), da[i].y(), nx, ny);
1342 dp[i].rx() = qRound(nx);
1343 dp[i].ry() = qRound(ny);
1344 }
1345 return p;
1346}
1347
1348/*!
1349 \fn QRegion operator*(const QRegion &region, const QTransform &matrix)
1350 \relates QTransform
1351
1352 This is the same as \a{matrix}.map(\a{region}).
1353
1354 \sa QTransform::map()
1355*/
1356
1357Q_GUI_EXPORT extern QPainterPath qt_regionToPath(const QRegion &region);
1358
1359/*!
1360 \fn QRegion QTransform::map(const QRegion &region) const
1361 \overload
1362
1363 Creates and returns a QRegion object that is a copy of the given
1364 \a region, mapped into the coordinate system defined by this matrix.
1365
1366 Calling this method can be rather expensive if rotations or
1367 shearing are used.
1368*/
1369QRegion QTransform::map(const QRegion &r) const
1370{
1371 TransformationType t = inline_type();
1372 if (t == TxNone)
1373 return r;
1374
1375 if (t == TxTranslate) {
1376 QRegion copy(r);
1377 copy.translate(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1378 return copy;
1379 }
1380
1381 if (t == TxScale) {
1382 QRegion res;
1383 if (m11() < 0 || m22() < 0) {
1384 for (const QRect &rect : r)
1385 res += qt_mapFillRect(QRectF(rect), *this);
1386 } else {
1387 QVarLengthArray<QRect, 32> rects;
1388 rects.reserve(r.rectCount());
1389 for (const QRect &rect : r) {
1390 QRect nr = qt_mapFillRect(QRectF(rect), *this);
1391 if (!nr.isEmpty())
1392 rects.append(nr);
1393 }
1394 res.setRects(rects.constData(), rects.size());
1395 }
1396 return res;
1397 }
1398
1399 QPainterPath p = map(qt_regionToPath(r));
1400 return p.toFillPolygon().toPolygon();
1401}
1402
1404{
1408
1410 QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_) : x(x_), y(y_), w(w_) {}
1411
1412 const QPointF toPoint() const {
1413 qreal iw = 1. / w;
1414 return QPointF(x * iw, y * iw);
1415 }
1416};
1417
1418static inline QHomogeneousCoordinate mapHomogeneous(const QTransform &transform, const QPointF &p)
1419{
1421 c.x = transform.m11() * p.x() + transform.m21() * p.y() + transform.m31();
1422 c.y = transform.m12() * p.x() + transform.m22() * p.y() + transform.m32();
1423 c.w = transform.m13() * p.x() + transform.m23() * p.y() + transform.m33();
1424 return c;
1425}
1426
1427static inline bool lineTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b,
1428 bool needsMoveTo, bool needsLineTo = true)
1429{
1430 QHomogeneousCoordinate ha = mapHomogeneous(transform, a);
1431 QHomogeneousCoordinate hb = mapHomogeneous(transform, b);
1432
1433 if (ha.w < Q_NEAR_CLIP && hb.w < Q_NEAR_CLIP)
1434 return false;
1435
1436 if (hb.w < Q_NEAR_CLIP) {
1437 const qreal t = (Q_NEAR_CLIP - hb.w) / (ha.w - hb.w);
1438
1439 hb.x += (ha.x - hb.x) * t;
1440 hb.y += (ha.y - hb.y) * t;
1441 hb.w = qreal(Q_NEAR_CLIP);
1442 } else if (ha.w < Q_NEAR_CLIP) {
1443 const qreal t = (Q_NEAR_CLIP - ha.w) / (hb.w - ha.w);
1444
1445 ha.x += (hb.x - ha.x) * t;
1446 ha.y += (hb.y - ha.y) * t;
1447 ha.w = qreal(Q_NEAR_CLIP);
1448
1449 const QPointF p = ha.toPoint();
1450 if (needsMoveTo) {
1451 path.moveTo(p);
1452 needsMoveTo = false;
1453 } else {
1454 path.lineTo(p);
1455 }
1456 }
1457
1458 if (needsMoveTo)
1459 path.moveTo(ha.toPoint());
1460
1461 if (needsLineTo)
1462 path.lineTo(hb.toPoint());
1463
1464 return true;
1465}
1466Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
1467
1468static inline bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo)
1469{
1470 // Convert projective xformed curves to line
1471 // segments so they can be transformed more accurately
1472
1473 qreal scale;
1474 qt_scaleForTransform(transform, &scale);
1475
1476 qreal curveThreshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale);
1477
1478 QPolygonF segment = QBezier::fromPoints(a, b, c, d).toPolygon(curveThreshold);
1479
1480 for (int i = 0; i < segment.size() - 1; ++i)
1481 if (lineTo_clipped(path, transform, segment.at(i), segment.at(i+1), needsMoveTo))
1482 needsMoveTo = false;
1483
1484 return !needsMoveTo;
1485}
1486
1487static QPainterPath mapProjective(const QTransform &transform, const QPainterPath &path)
1488{
1489 QPainterPath result;
1490
1491 QPointF last;
1492 QPointF lastMoveTo;
1493 bool needsMoveTo = true;
1494 for (int i = 0; i < path.elementCount(); ++i) {
1495 switch (path.elementAt(i).type) {
1496 case QPainterPath::MoveToElement:
1497 if (i > 0 && lastMoveTo != last)
1498 lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo);
1499
1500 lastMoveTo = path.elementAt(i);
1501 last = path.elementAt(i);
1502 needsMoveTo = true;
1503 break;
1504 case QPainterPath::LineToElement:
1505 if (lineTo_clipped(result, transform, last, path.elementAt(i), needsMoveTo))
1506 needsMoveTo = false;
1507 last = path.elementAt(i);
1508 break;
1509 case QPainterPath::CurveToElement:
1510 if (cubicTo_clipped(result, transform, last, path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), needsMoveTo))
1511 needsMoveTo = false;
1512 i += 2;
1513 last = path.elementAt(i);
1514 break;
1515 default:
1516 Q_ASSERT(false);
1517 }
1518 }
1519
1520 if (path.elementCount() > 0 && lastMoveTo != last)
1521 lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo, false);
1522
1523 result.setFillRule(path.fillRule());
1524 return result;
1525}
1526
1527/*!
1528 \fn QPainterPath operator *(const QPainterPath &path, const QTransform &matrix)
1529 \since 4.3
1530 \relates QTransform
1531
1532 This is the same as \a{matrix}.map(\a{path}).
1533
1534 \sa QTransform::map()
1535*/
1536
1537/*!
1538 \overload
1539
1540 Creates and returns a QPainterPath object that is a copy of the
1541 given \a path, mapped into the coordinate system defined by this
1542 matrix.
1543*/
1544QPainterPath QTransform::map(const QPainterPath &path) const
1545{
1546 TransformationType t = inline_type();
1547 if (t == TxNone || path.elementCount() == 0)
1548 return path;
1549
1550 if (t >= TxProject)
1551 return mapProjective(*this, path);
1552
1553 QPainterPath copy = path;
1554
1555 if (t == TxTranslate) {
1556 copy.translate(m_matrix[2][0], m_matrix[2][1]);
1557 } else {
1558 copy.setDirty(true);
1559 // Full xform
1560 for (int i=0; i<path.elementCount(); ++i) {
1561 QPainterPath::Element &e = copy.d_ptr->elements[i];
1562 do_map(e.x, e.y, e.x, e.y);
1563 }
1564 }
1565
1566 return copy;
1567}
1568
1569/*!
1570 \fn QPolygon QTransform::mapToPolygon(const QRect &rectangle) const
1571
1572 Creates and returns a QPolygon representation of the given \a
1573 rectangle, mapped into the coordinate system defined by this
1574 matrix.
1575
1576 The rectangle's coordinates are transformed using the following
1577 formulas:
1578
1579 \snippet code/src_gui_painting_qtransform.cpp 1
1580
1581 Polygons and rectangles behave slightly differently when
1582 transformed (due to integer rounding), so
1583 \c{matrix.map(QPolygon(rectangle))} is not always the same as
1584 \c{matrix.mapToPolygon(rectangle)}.
1585
1586 \sa mapRect(), {QTransform#Basic Matrix Operations}{Basic Matrix
1587 Operations}
1588*/
1589QPolygon QTransform::mapToPolygon(const QRect &rect) const
1590{
1591 TransformationType t = inline_type();
1592
1593 QPolygon a(4);
1594 qreal x[4] = { 0, 0, 0, 0 }, y[4] = { 0, 0, 0, 0 };
1595 if (t <= TxScale) {
1596 x[0] = m_matrix[0][0]*rect.x() + m_matrix[2][0];
1597 y[0] = m_matrix[1][1]*rect.y() + m_matrix[2][1];
1598 qreal w = m_matrix[0][0]*rect.width();
1599 qreal h = m_matrix[1][1]*rect.height();
1600 if (w < 0) {
1601 w = -w;
1602 x[0] -= w;
1603 }
1604 if (h < 0) {
1605 h = -h;
1606 y[0] -= h;
1607 }
1608 x[1] = x[0]+w;
1609 x[2] = x[1];
1610 x[3] = x[0];
1611 y[1] = y[0];
1612 y[2] = y[0]+h;
1613 y[3] = y[2];
1614 } else {
1615 auto right = rect.x() + rect.width();
1616 auto bottom = rect.y() + rect.height();
1617 do_map(rect.x(), rect.y(), x[0], y[0]);
1618 do_map(right, rect.y(), x[1], y[1]);
1619 do_map(right, bottom, x[2], y[2]);
1620 do_map(rect.x(), bottom, x[3], y[3]);
1621 }
1622
1623 // all coordinates are correctly, transform to a pointarray
1624 // (rounding to the next integer)
1625 a.setPoints(4, qRound(x[0]), qRound(y[0]),
1626 qRound(x[1]), qRound(y[1]),
1627 qRound(x[2]), qRound(y[2]),
1628 qRound(x[3]), qRound(y[3]));
1629 return a;
1630}
1631
1632/*!
1633 Creates a transformation matrix, \a trans, that maps a unit square
1634 to a four-sided polygon, \a quad. Returns \c true if the transformation
1635 is constructed or false if such a transformation does not exist.
1636
1637 \sa quadToSquare(), quadToQuad()
1638*/
1639bool QTransform::squareToQuad(const QPolygonF &quad, QTransform &trans)
1640{
1641 if (quad.size() != (quad.isClosed() ? 5 : 4))
1642 return false;
1643
1644 qreal dx0 = quad[0].x();
1645 qreal dx1 = quad[1].x();
1646 qreal dx2 = quad[2].x();
1647 qreal dx3 = quad[3].x();
1648
1649 qreal dy0 = quad[0].y();
1650 qreal dy1 = quad[1].y();
1651 qreal dy2 = quad[2].y();
1652 qreal dy3 = quad[3].y();
1653
1654 double ax = dx0 - dx1 + dx2 - dx3;
1655 double ay = dy0 - dy1 + dy2 - dy3;
1656
1657 if (!ax && !ay) { //afine transform
1658 trans.setMatrix(dx1 - dx0, dy1 - dy0, 0,
1659 dx2 - dx1, dy2 - dy1, 0,
1660 dx0, dy0, 1);
1661 } else {
1662 double ax1 = dx1 - dx2;
1663 double ax2 = dx3 - dx2;
1664 double ay1 = dy1 - dy2;
1665 double ay2 = dy3 - dy2;
1666
1667 /*determinants */
1668 double gtop = ax * ay2 - ax2 * ay;
1669 double htop = ax1 * ay - ax * ay1;
1670 double bottom = ax1 * ay2 - ax2 * ay1;
1671
1672 double a, b, c, d, e, f, g, h; /*i is always 1*/
1673
1674 if (!bottom)
1675 return false;
1676
1677 g = gtop/bottom;
1678 h = htop/bottom;
1679
1680 a = dx1 - dx0 + g * dx1;
1681 b = dx3 - dx0 + h * dx3;
1682 c = dx0;
1683 d = dy1 - dy0 + g * dy1;
1684 e = dy3 - dy0 + h * dy3;
1685 f = dy0;
1686
1687 trans.setMatrix(a, d, g,
1688 b, e, h,
1689 c, f, 1.0);
1690 }
1691
1692 return true;
1693}
1694
1695/*!
1696 \fn bool QTransform::quadToSquare(const QPolygonF &quad, QTransform &trans)
1697
1698 Creates a transformation matrix, \a trans, that maps a four-sided polygon,
1699 \a quad, to a unit square. Returns \c true if the transformation is constructed
1700 or false if such a transformation does not exist.
1701
1702 \sa squareToQuad(), quadToQuad()
1703*/
1704bool QTransform::quadToSquare(const QPolygonF &quad, QTransform &trans)
1705{
1706 if (!squareToQuad(quad, trans))
1707 return false;
1708
1709 bool invertible = false;
1710 trans = trans.inverted(&invertible);
1711
1712 return invertible;
1713}
1714
1715/*!
1716 Creates a transformation matrix, \a trans, that maps a four-sided
1717 polygon, \a one, to another four-sided polygon, \a two.
1718 Returns \c true if the transformation is possible; otherwise returns
1719 false.
1720
1721 This is a convenience method combining quadToSquare() and
1722 squareToQuad() methods. It allows the input quad to be
1723 transformed into any other quad.
1724
1725 \sa squareToQuad(), quadToSquare()
1726*/
1727bool QTransform::quadToQuad(const QPolygonF &one,
1728 const QPolygonF &two,
1729 QTransform &trans)
1730{
1731 QTransform stq;
1732 if (!quadToSquare(one, trans))
1733 return false;
1734 if (!squareToQuad(two, stq))
1735 return false;
1736 trans *= stq;
1737 //qDebug()<<"Final = "<<trans;
1738 return true;
1739}
1740
1741/*!
1742 Sets the matrix elements to the specified values, \a m11,
1743 \a m12, \a m13 \a m21, \a m22, \a m23 \a m31, \a m32 and
1744 \a m33. Note that this function replaces the previous values.
1745 QTransform provides the translate(), rotate(), scale() and shear()
1746 convenience functions to manipulate the various matrix elements
1747 based on the currently defined coordinate system.
1748
1749 \sa QTransform()
1750*/
1751
1752void QTransform::setMatrix(qreal m11, qreal m12, qreal m13,
1753 qreal m21, qreal m22, qreal m23,
1754 qreal m31, qreal m32, qreal m33)
1755{
1756 m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13;
1757 m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23;
1758 m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33;
1759 m_type = TxNone;
1760 m_dirty = TxProject;
1761}
1762
1763QRect QTransform::mapRect(const QRect &rect) const
1764{
1765 TransformationType t = inline_type();
1766 if (t <= TxTranslate)
1767 return rect.translated(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1768
1769 if (t <= TxScale) {
1770 int x = qRound(m_matrix[0][0] * rect.x() + m_matrix[2][0]);
1771 int y = qRound(m_matrix[1][1] * rect.y() + m_matrix[2][1]);
1772 int w = qRound(m_matrix[0][0] * rect.width());
1773 int h = qRound(m_matrix[1][1] * rect.height());
1774 if (w < 0) {
1775 w = -w;
1776 x -= w;
1777 }
1778 if (h < 0) {
1779 h = -h;
1780 y -= h;
1781 }
1782 return QRect(x, y, w, h);
1783 } else {
1784 qreal x = 0, y = 0;
1785 do_map(rect.left(), rect.top(), x, y);
1786 qreal xmin = x;
1787 qreal ymin = y;
1788 qreal xmax = x;
1789 qreal ymax = y;
1790 do_map(rect.right() + 1, rect.top(), x, y);
1791 xmin = qMin(xmin, x);
1792 ymin = qMin(ymin, y);
1793 xmax = qMax(xmax, x);
1794 ymax = qMax(ymax, y);
1795 do_map(rect.right() + 1, rect.bottom() + 1, x, y);
1796 xmin = qMin(xmin, x);
1797 ymin = qMin(ymin, y);
1798 xmax = qMax(xmax, x);
1799 ymax = qMax(ymax, y);
1800 do_map(rect.left(), rect.bottom() + 1, x, y);
1801 xmin = qMin(xmin, x);
1802 ymin = qMin(ymin, y);
1803 xmax = qMax(xmax, x);
1804 ymax = qMax(ymax, y);
1805 return QRectF(xmin, ymin, xmax-xmin, ymax-ymin).toRect();
1806 }
1807}
1808
1809/*!
1810 \fn QRectF QTransform::mapRect(const QRectF &rectangle) const
1811
1812 Creates and returns a QRectF object that is a copy of the given \a
1813 rectangle, mapped into the coordinate system defined by this
1814 matrix.
1815
1816 The rectangle's coordinates are transformed using the following
1817 formulas:
1818
1819 \snippet code/src_gui_painting_qtransform.cpp 2
1820
1821 If rotation or shearing has been specified, this function returns
1822 the \e bounding rectangle. To retrieve the exact region the given
1823 \a rectangle maps to, use the mapToPolygon() function instead.
1824
1825 \sa mapToPolygon(), {QTransform#Basic Matrix Operations}{Basic Matrix
1826 Operations}
1827*/
1828QRectF QTransform::mapRect(const QRectF &rect) const
1829{
1830 TransformationType t = inline_type();
1831 if (t <= TxTranslate)
1832 return rect.translated(m_matrix[2][0], m_matrix[2][1]);
1833
1834 if (t <= TxScale) {
1835 qreal x = m_matrix[0][0] * rect.x() + m_matrix[2][0];
1836 qreal y = m_matrix[1][1] * rect.y() + m_matrix[2][1];
1837 qreal w = m_matrix[0][0] * rect.width();
1838 qreal h = m_matrix[1][1] * rect.height();
1839 if (w < 0) {
1840 w = -w;
1841 x -= w;
1842 }
1843 if (h < 0) {
1844 h = -h;
1845 y -= h;
1846 }
1847 return QRectF(x, y, w, h);
1848 } else {
1849 qreal x = 0, y = 0;
1850 do_map(rect.x(), rect.y(), x, y);
1851 qreal xmin = x;
1852 qreal ymin = y;
1853 qreal xmax = x;
1854 qreal ymax = y;
1855 do_map(rect.x() + rect.width(), rect.y(), x, y);
1856 xmin = qMin(xmin, x);
1857 ymin = qMin(ymin, y);
1858 xmax = qMax(xmax, x);
1859 ymax = qMax(ymax, y);
1860 do_map(rect.x() + rect.width(), rect.y() + rect.height(), x, y);
1861 xmin = qMin(xmin, x);
1862 ymin = qMin(ymin, y);
1863 xmax = qMax(xmax, x);
1864 ymax = qMax(ymax, y);
1865 do_map(rect.x(), rect.y() + rect.height(), x, y);
1866 xmin = qMin(xmin, x);
1867 ymin = qMin(ymin, y);
1868 xmax = qMax(xmax, x);
1869 ymax = qMax(ymax, y);
1870 return QRectF(xmin, ymin, xmax-xmin, ymax - ymin);
1871 }
1872}
1873
1874/*!
1875 \fn QRect QTransform::mapRect(const QRect &rectangle) const
1876 \overload
1877
1878 Creates and returns a QRect object that is a copy of the given \a
1879 rectangle, mapped into the coordinate system defined by this
1880 matrix. Note that the transformed coordinates are rounded to the
1881 nearest integer.
1882*/
1883
1884/*!
1885 Maps the given coordinates \a x and \a y into the coordinate
1886 system defined by this matrix. The resulting values are put in *\a
1887 tx and *\a ty, respectively.
1888
1889 The coordinates are transformed using the following formulas:
1890
1891 \snippet code/src_gui_painting_qtransform.cpp 3
1892
1893 The point (x, y) is the original point, and (x', y') is the
1894 transformed point.
1895
1896 \sa {QTransform#Basic Matrix Operations}{Basic Matrix Operations}
1897*/
1898void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const
1899{
1900 do_map(x, y, *tx, *ty);
1901}
1902
1903/*!
1904 \overload
1905
1906 Maps the given coordinates \a x and \a y into the coordinate
1907 system defined by this matrix. The resulting values are put in *\a
1908 tx and *\a ty, respectively. Note that the transformed coordinates
1909 are rounded to the nearest integer.
1910*/
1911void QTransform::map(int x, int y, int *tx, int *ty) const
1912{
1913 qreal fx = 0, fy = 0;
1914 do_map(x, y, fx, fy);
1915 *tx = qRound(fx);
1916 *ty = qRound(fy);
1917}
1918
1919/*!
1920 Returns the transformation type of this matrix.
1921
1922 The transformation type is the highest enumeration value
1923 capturing all of the matrix's transformations. For example,
1924 if the matrix both scales and shears, the type would be \c TxShear,
1925 because \c TxShear has a higher enumeration value than \c TxScale.
1926
1927 Knowing the transformation type of a matrix is useful for optimization:
1928 you can often handle specific types more optimally than handling
1929 the generic case.
1930 */
1931QTransform::TransformationType QTransform::type() const
1932{
1933 if (m_dirty == TxNone || m_dirty < m_type)
1934 return static_cast<TransformationType>(m_type);
1935
1936 switch (static_cast<TransformationType>(m_dirty)) {
1937 case TxProject:
1938 if (!qFuzzyIsNull(m_matrix[0][2]) || !qFuzzyIsNull(m_matrix[1][2]) || !qFuzzyIsNull(m_matrix[2][2] - 1)) {
1939 m_type = TxProject;
1940 break;
1941 }
1942 Q_FALLTHROUGH();
1943 case TxShear:
1944 case TxRotate:
1945 if (!qFuzzyIsNull(m_matrix[0][1]) || !qFuzzyIsNull(m_matrix[1][0])) {
1946 const qreal dot = m_matrix[0][0] * m_matrix[1][0] + m_matrix[0][1] * m_matrix[1][1];
1947 if (qFuzzyIsNull(dot))
1948 m_type = TxRotate;
1949 else
1950 m_type = TxShear;
1951 break;
1952 }
1953 Q_FALLTHROUGH();
1954 case TxScale:
1955 if (!qFuzzyIsNull(m_matrix[0][0] - 1) || !qFuzzyIsNull(m_matrix[1][1] - 1)) {
1956 m_type = TxScale;
1957 break;
1958 }
1959 Q_FALLTHROUGH();
1960 case TxTranslate:
1961 if (!qFuzzyIsNull(m_matrix[2][0]) || !qFuzzyIsNull(m_matrix[2][1])) {
1962 m_type = TxTranslate;
1963 break;
1964 }
1965 Q_FALLTHROUGH();
1966 case TxNone:
1967 m_type = TxNone;
1968 break;
1969 }
1970
1971 m_dirty = TxNone;
1972 return static_cast<TransformationType>(m_type);
1973}
1974
1975/*!
1976
1977 Returns the transform as a QVariant.
1978*/
1979QTransform::operator QVariant() const
1980{
1981 return QVariant::fromValue(*this);
1982}
1983
1984
1985/*!
1986 \fn bool QTransform::isInvertible() const
1987
1988 Returns \c true if the matrix is invertible, otherwise returns \c false.
1989
1990 \sa inverted()
1991*/
1992
1993/*!
1994 \fn qreal QTransform::m11() const
1995
1996 Returns the horizontal scaling factor.
1997
1998 \sa scale(), {QTransform#Basic Matrix Operations}{Basic Matrix
1999 Operations}
2000*/
2001
2002/*!
2003 \fn qreal QTransform::m12() const
2004
2005 Returns the vertical shearing factor.
2006
2007 \sa shear(), {QTransform#Basic Matrix Operations}{Basic Matrix
2008 Operations}
2009*/
2010
2011/*!
2012 \fn qreal QTransform::m21() const
2013
2014 Returns the horizontal shearing factor.
2015
2016 \sa shear(), {QTransform#Basic Matrix Operations}{Basic Matrix
2017 Operations}
2018*/
2019
2020/*!
2021 \fn qreal QTransform::m22() const
2022
2023 Returns the vertical scaling factor.
2024
2025 \sa scale(), {QTransform#Basic Matrix Operations}{Basic Matrix
2026 Operations}
2027*/
2028
2029/*!
2030 \fn qreal QTransform::dx() const
2031
2032 Returns the horizontal translation factor.
2033
2034 \sa m31(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
2035 Operations}
2036*/
2037
2038/*!
2039 \fn qreal QTransform::dy() const
2040
2041 Returns the vertical translation factor.
2042
2043 \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
2044 Operations}
2045*/
2046
2047
2048/*!
2049 \fn qreal QTransform::m13() const
2050
2051 Returns the horizontal projection factor.
2052
2053 \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
2054 Operations}
2055*/
2056
2057
2058/*!
2059 \fn qreal QTransform::m23() const
2060
2061 Returns the vertical projection factor.
2062
2063 \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
2064 Operations}
2065*/
2066
2067/*!
2068 \fn qreal QTransform::m31() const
2069
2070 Returns the horizontal translation factor.
2071
2072 \sa dx(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
2073 Operations}
2074*/
2075
2076/*!
2077 \fn qreal QTransform::m32() const
2078
2079 Returns the vertical translation factor.
2080
2081 \sa dy(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
2082 Operations}
2083*/
2084
2085/*!
2086 \fn qreal QTransform::m33() const
2087
2088 Returns the division factor.
2089
2090 \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
2091 Operations}
2092*/
2093
2094/*!
2095 \fn qreal QTransform::determinant() const
2096
2097 Returns the matrix's determinant.
2098*/
2099
2100/*!
2101 \fn bool QTransform::isIdentity() const
2102
2103 Returns \c true if the matrix is the identity matrix, otherwise
2104 returns \c false.
2105
2106 \sa reset()
2107*/
2108
2109/*!
2110 \fn bool QTransform::isAffine() const
2111
2112 Returns \c true if the matrix represent an affine transformation,
2113 otherwise returns \c false.
2114*/
2115
2116/*!
2117 \fn bool QTransform::isScaling() const
2118
2119 Returns \c true if the matrix represents a scaling
2120 transformation, otherwise returns \c false.
2121
2122 \sa reset()
2123*/
2124
2125/*!
2126 \fn bool QTransform::isRotating() const
2127
2128 Returns \c true if the matrix represents some kind of a
2129 rotating transformation, otherwise returns \c false.
2130
2131 \note A rotation transformation of 180 degrees and/or 360 degrees is treated as a scaling transformation.
2132
2133 \sa reset()
2134*/
2135
2136/*!
2137 \fn bool QTransform::isTranslating() const
2138
2139 Returns \c true if the matrix represents a translating
2140 transformation, otherwise returns \c false.
2141
2142 \sa reset()
2143*/
2144
2145/*!
2146 \fn bool qFuzzyCompare(const QTransform& t1, const QTransform& t2)
2147
2148 \relates QTransform
2149 \since 4.6
2150
2151 Returns \c true if \a t1 and \a t2 are equal, allowing for a small
2152 fuzziness factor for floating-point comparisons; false otherwise.
2153*/
2154
2155
2156// returns true if the transform is uniformly scaling
2157// (same scale in x and y direction)
2158// scale is set to the max of x and y scaling factors
2159Q_GUI_EXPORT
2160bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
2161{
2162 const QTransform::TransformationType type = transform.type();
2163 if (type <= QTransform::TxTranslate) {
2164 if (scale)
2165 *scale = 1;
2166 return true;
2167 } else if (type == QTransform::TxScale) {
2168 const qreal xScale = qAbs(transform.m11());
2169 const qreal yScale = qAbs(transform.m22());
2170 if (scale)
2171 *scale = qMax(xScale, yScale);
2172 return qFuzzyCompare(xScale, yScale);
2173 }
2174
2175 // rotate then scale: compare columns
2176 const qreal xScale1 = transform.m11() * transform.m11()
2177 + transform.m21() * transform.m21();
2178 const qreal yScale1 = transform.m12() * transform.m12()
2179 + transform.m22() * transform.m22();
2180
2181 // scale then rotate: compare rows
2182 const qreal xScale2 = transform.m11() * transform.m11()
2183 + transform.m12() * transform.m12();
2184 const qreal yScale2 = transform.m21() * transform.m21()
2185 + transform.m22() * transform.m22();
2186
2187 // decide the order of rotate and scale operations
2188 if (qAbs(xScale1 - yScale1) > qAbs(xScale2 - yScale2)) {
2189 if (scale)
2190 *scale = qSqrt(qMax(xScale1, yScale1));
2191
2192 return type == QTransform::TxRotate && qFuzzyCompare(xScale1, yScale1);
2193 } else {
2194 if (scale)
2195 *scale = qSqrt(qMax(xScale2, yScale2));
2196
2197 return type == QTransform::TxRotate && qFuzzyCompare(xScale2, yScale2);
2198 }
2199}
2200
2201QDataStream & operator>>(QDataStream &s, QTransform::Affine &m)
2202{
2203 if (s.version() == 1) {
2204 float m11, m12, m21, m22, dx, dy;
2205 s >> m11; s >> m12; s >> m21; s >> m22; s >> dx; s >> dy;
2206
2207 m.m_matrix[0][0] = m11;
2208 m.m_matrix[0][1] = m12;
2209 m.m_matrix[1][0] = m21;
2210 m.m_matrix[1][1] = m22;
2211 m.m_matrix[2][0] = dx;
2212 m.m_matrix[2][1] = dy;
2213 } else {
2214 s >> m.m_matrix[0][0];
2215 s >> m.m_matrix[0][1];
2216 s >> m.m_matrix[1][0];
2217 s >> m.m_matrix[1][1];
2218 s >> m.m_matrix[2][0];
2219 s >> m.m_matrix[2][1];
2220 }
2221 m.m_matrix[0][2] = 0;
2222 m.m_matrix[1][2] = 0;
2223 m.m_matrix[2][2] = 1;
2224 return s;
2225}
2226
2227QDataStream &operator<<(QDataStream &s, const QTransform::Affine &m)
2228{
2229 if (s.version() == 1) {
2230 s << (float)m.m_matrix[0][0]
2231 << (float)m.m_matrix[0][1]
2232 << (float)m.m_matrix[1][0]
2233 << (float)m.m_matrix[1][1]
2234 << (float)m.m_matrix[2][0]
2235 << (float)m.m_matrix[2][1];
2236 } else {
2237 s << m.m_matrix[0][0]
2238 << m.m_matrix[0][1]
2239 << m.m_matrix[1][0]
2240 << m.m_matrix[1][1]
2241 << m.m_matrix[2][0]
2242 << m.m_matrix[2][1];
2243 }
2244 return s;
2245}
2246
2247QT_END_NAMESPACE
\inmodule QtGui
\inmodule QtCore\reentrant
Definition qpoint.h:232
Combined button and popup list for selecting options.
QDataStream & operator>>(QDataStream &s, QKeyCombination &combination)
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
#define Q_NEAR_CLIP
static QHomogeneousCoordinate mapHomogeneous(const QTransform &transform, const QPointF &p)
static QPainterPath mapProjective(const QTransform &transform, const QPainterPath &path)
static bool lineTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, bool needsMoveTo, bool needsLineTo=true)
size_t qHash(const QTransform &key, size_t seed) noexcept
static bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo)
QDataStream & operator>>(QDataStream &s, QTransform &t)
QDebug operator<<(QDebug dbg, const QTransform &m)
const QPointF toPoint() const
QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_)