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 QtGui module of the Qt Toolkit. |

7 | ** |

8 | ** $QT_BEGIN_LICENSE:LGPL$ |

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 Lesser General Public License Usage |

18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |

19 | ** General Public License version 3 as published by the Free Software |

20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |

21 | ** packaging of this file. Please review the following information to |

22 | ** ensure the GNU Lesser General Public License version 3 requirements |

23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |

24 | ** |

25 | ** GNU General Public License Usage |

26 | ** Alternatively, this file may be used under the terms of the GNU |

27 | ** General Public License version 2.0 or (at your option) the GNU General |

28 | ** Public license version 3 or any later version approved by the KDE Free |

29 | ** Qt Foundation. The licenses are as published by the Free Software |

30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |

31 | ** included in the packaging of this file. Please review the following |

32 | ** information to ensure the GNU General Public License requirements will |

33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |

34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |

35 | ** |

36 | ** $QT_END_LICENSE$ |

37 | ** |

38 | ****************************************************************************/ |

39 | #include "qtransform.h" |

40 | |

41 | #include "qdatastream.h" |

42 | #include "qdebug.h" |

43 | #include "qhashfunctions.h" |

44 | #include "qmatrix.h" |

45 | #include "qregion.h" |

46 | #include "qpainterpath.h" |

47 | #include "qpainterpath_p.h" |

48 | #include "qvariant.h" |

49 | #include <qmath.h> |

50 | #include <qnumeric.h> |

51 | |

52 | #include <private/qbezier_p.h> |

53 | |

54 | QT_BEGIN_NAMESPACE |

55 | |

56 | #ifndef QT_NO_DEBUG |

57 | Q_NEVER_INLINE |

58 | static void nanWarning(const char *func) |

59 | { |

60 | qWarning("QTransform::%s with NaN called", func); |

61 | } |

62 | #endif // QT_NO_DEBUG |

63 | |

64 | #define Q_NEAR_CLIP (sizeof(qreal) == sizeof(double) ? 0.000001 : 0.0001) |

65 | |

66 | #ifdef MAP |

67 | # undef MAP |

68 | #endif |

69 | #define MAP(x, y, nx, ny) \ |

70 | do { \ |

71 | qreal FX_ = x; \ |

72 | qreal FY_ = y; \ |

73 | switch(t) { \ |

74 | case TxNone: \ |

75 | nx = FX_; \ |

76 | ny = FY_; \ |

77 | break; \ |

78 | case TxTranslate: \ |

79 | nx = FX_ + affine._dx; \ |

80 | ny = FY_ + affine._dy; \ |

81 | break; \ |

82 | case TxScale: \ |

83 | nx = affine._m11 * FX_ + affine._dx; \ |

84 | ny = affine._m22 * FY_ + affine._dy; \ |

85 | break; \ |

86 | case TxRotate: \ |

87 | case TxShear: \ |

88 | case TxProject: \ |

89 | nx = affine._m11 * FX_ + affine._m21 * FY_ + affine._dx; \ |

90 | ny = affine._m12 * FX_ + affine._m22 * FY_ + affine._dy; \ |

91 | if (t == TxProject) { \ |

92 | qreal w = (m_13 * FX_ + m_23 * FY_ + m_33); \ |

93 | if (w < qreal(Q_NEAR_CLIP)) w = qreal(Q_NEAR_CLIP); \ |

94 | w = 1./w; \ |

95 | nx *= w; \ |

96 | ny *= w; \ |

97 | } \ |

98 | } \ |

99 | } while (0) |

100 | |

101 | /*! |

102 | \class QTransform |

103 | \brief The QTransform class specifies 2D transformations of a coordinate system. |

104 | \since 4.3 |

105 | \ingroup painting |

106 | \inmodule QtGui |

107 | |

108 | A transformation specifies how to translate, scale, shear, rotate |

109 | or project the coordinate system, and is typically used when |

110 | rendering graphics. |

111 | |

112 | QTransform differs from QMatrix in that it is a true 3x3 matrix, |

113 | allowing perspective transformations. QTransform's toAffine() |

114 | method allows casting QTransform to QMatrix. If a perspective |

115 | transformation has been specified on the matrix, then the |

116 | conversion will cause loss of data. |

117 | |

118 | QTransform is the recommended transformation class in Qt. |

119 | |

120 | A QTransform object can be built using the setMatrix(), scale(), |

121 | rotate(), translate() and shear() functions. Alternatively, it |

122 | can be built by applying \l {QTransform#Basic Matrix |

123 | Operations}{basic matrix operations}. The matrix can also be |

124 | defined when constructed, and it can be reset to the identity |

125 | matrix (the default) using the reset() function. |

126 | |

127 | The QTransform class supports mapping of graphic primitives: A given |

128 | point, line, polygon, region, or painter path can be mapped to the |

129 | coordinate system defined by \e this matrix using the map() |

130 | function. In case of a rectangle, its coordinates can be |

131 | transformed using the mapRect() function. A rectangle can also be |

132 | transformed into a \e polygon (mapped to the coordinate system |

133 | defined by \e this matrix), using the mapToPolygon() function. |

134 | |

135 | QTransform provides the isIdentity() function which returns \c true if |

136 | the matrix is the identity matrix, and the isInvertible() function |

137 | which returns \c true if the matrix is non-singular (i.e. AB = BA = |

138 | I). The inverted() function returns an inverted copy of \e this |

139 | matrix if it is invertible (otherwise it returns the identity |

140 | matrix), and adjoint() returns the matrix's classical adjoint. |

141 | In addition, QTransform provides the determinant() function which |

142 | returns the matrix's determinant. |

143 | |

144 | Finally, the QTransform class supports matrix multiplication, addition |

145 | and subtraction, and objects of the class can be streamed as well |

146 | as compared. |

147 | |

148 | \tableofcontents |

149 | |

150 | \section1 Rendering Graphics |

151 | |

152 | When rendering graphics, the matrix defines the transformations |

153 | but the actual transformation is performed by the drawing routines |

154 | in QPainter. |

155 | |

156 | By default, QPainter operates on the associated device's own |

157 | coordinate system. The standard coordinate system of a |

158 | QPaintDevice has its origin located at the top-left position. The |

159 | \e x values increase to the right; \e y values increase |

160 | downward. For a complete description, see the \l {Coordinate |

161 | System} {coordinate system} documentation. |

162 | |

163 | QPainter has functions to translate, scale, shear and rotate the |

164 | coordinate system without using a QTransform. For example: |

165 | |

166 | \table 100% |

167 | \row |

168 | \li \inlineimage qtransform-simpletransformation.png |

169 | \li |

170 | \snippet transform/main.cpp 0 |

171 | \endtable |

172 | |

173 | Although these functions are very convenient, it can be more |

174 | efficient to build a QTransform and call QPainter::setTransform() if you |

175 | want to perform more than a single transform operation. For |

176 | example: |

177 | |

178 | \table 100% |

179 | \row |

180 | \li \inlineimage qtransform-combinedtransformation.png |

181 | \li |

182 | \snippet transform/main.cpp 1 |

183 | \endtable |

184 | |

185 | \section1 Basic Matrix Operations |

186 | |

187 | \image qtransform-representation.png |

188 | |

189 | A QTransform object contains a 3 x 3 matrix. The \c m31 (\c dx) and |

190 | \c m32 (\c dy) elements specify horizontal and vertical translation. |

191 | The \c m11 and \c m22 elements specify horizontal and vertical scaling. |

192 | The \c m21 and \c m12 elements specify horizontal and vertical \e shearing. |

193 | And finally, the \c m13 and \c m23 elements specify horizontal and vertical |

194 | projection, with \c m33 as an additional projection factor. |

195 | |

196 | QTransform transforms a point in the plane to another point using the |

197 | following formulas: |

198 | |

199 | \snippet code/src_gui_painting_qtransform.cpp 0 |

200 | |

201 | The point \e (x, y) is the original point, and \e (x', y') is the |

202 | transformed point. \e (x', y') can be transformed back to \e (x, |

203 | y) by performing the same operation on the inverted() matrix. |

204 | |

205 | The various matrix elements can be set when constructing the |

206 | matrix, or by using the setMatrix() function later on. They can also |

207 | be manipulated using the translate(), rotate(), scale() and |

208 | shear() convenience functions. The currently set values can be |

209 | retrieved using the m11(), m12(), m13(), m21(), m22(), m23(), |

210 | m31(), m32(), m33(), dx() and dy() functions. |

211 | |

212 | Translation is the simplest transformation. Setting \c dx and \c |

213 | dy will move the coordinate system \c dx units along the X axis |

214 | and \c dy units along the Y axis. Scaling can be done by setting |

215 | \c m11 and \c m22. For example, setting \c m11 to 2 and \c m22 to |

216 | 1.5 will double the height and increase the width by 50%. The |

217 | identity matrix has \c m11, \c m22, and \c m33 set to 1 (all others are set |

218 | to 0) mapping a point to itself. Shearing is controlled by \c m12 |

219 | and \c m21. Setting these elements to values different from zero |

220 | will twist the coordinate system. Rotation is achieved by |

221 | setting both the shearing factors and the scaling factors. Perspective |

222 | transformation is achieved by setting both the projection factors and |

223 | the scaling factors. |

224 | |

225 | Here's the combined transformations example using basic matrix |

226 | operations: |

227 | |

228 | \table 100% |

229 | \row |

230 | \li \inlineimage qtransform-combinedtransformation2.png |

231 | \li |

232 | \snippet transform/main.cpp 2 |

233 | \endtable |

234 | |

235 | \sa QPainter, {Coordinate System}, {painting/affine}{Affine |

236 | Transformations Example}, {Transformations Example} |

237 | */ |

238 | |

239 | /*! |

240 | \enum QTransform::TransformationType |

241 | |

242 | \value TxNone |

243 | \value TxTranslate |

244 | \value TxScale |

245 | \value TxRotate |

246 | \value TxShear |

247 | \value TxProject |

248 | */ |

249 | |

250 | /*! |

251 | \fn QTransform::QTransform(Qt::Initialization) |

252 | \internal |

253 | */ |

254 | |

255 | /*! |

256 | Constructs an identity matrix. |

257 | |

258 | All elements are set to zero except \c m11 and \c m22 (specifying |

259 | the scale) and \c m33 which are set to 1. |

260 | |

261 | \sa reset() |

262 | */ |

263 | QTransform::QTransform() |

264 | : affine(true) |

265 | , m_13(0), m_23(0), m_33(1) |

266 | , m_type(TxNone) |

267 | , m_dirty(TxNone) |

268 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |

269 | , d(nullptr) |

270 | #endif |

271 | { |

272 | } |

273 | |

274 | /*! |

275 | \fn QTransform::QTransform(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33) |

276 | |

277 | Constructs a matrix with the elements, \a m11, \a m12, \a m13, |

278 | \a m21, \a m22, \a m23, \a m31, \a m32, \a m33. |

279 | |

280 | \sa setMatrix() |

281 | */ |

282 | QTransform::QTransform(qreal h11, qreal h12, qreal h13, |

283 | qreal h21, qreal h22, qreal h23, |

284 | qreal h31, qreal h32, qreal h33) |

285 | : affine(h11, h12, h21, h22, h31, h32, true) |

286 | , m_13(h13), m_23(h23), m_33(h33) |

287 | , m_type(TxNone) |

288 | , m_dirty(TxProject) |

289 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |

290 | , d(nullptr) |

291 | #endif |

292 | { |

293 | } |

294 | |

295 | /*! |

296 | \fn QTransform::QTransform(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) |

297 | |

298 | Constructs a matrix with the elements, \a m11, \a m12, \a m21, \a m22, \a dx and \a dy. |

299 | |

300 | \sa setMatrix() |

301 | */ |

302 | QTransform::QTransform(qreal h11, qreal h12, qreal h21, |

303 | qreal h22, qreal dx, qreal dy) |

304 | : affine(h11, h12, h21, h22, dx, dy, true) |

305 | , m_13(0), m_23(0), m_33(1) |

306 | , m_type(TxNone) |

307 | , m_dirty(TxShear) |

308 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |

309 | , d(nullptr) |

310 | #endif |

311 | { |

312 | } |

313 | |

314 | /*! |

315 | \fn QTransform::QTransform(const QMatrix &matrix) |

316 | |

317 | Constructs a matrix that is a copy of the given \a matrix. |

318 | Note that the \c m13, \c m23, and \c m33 elements are set to 0, 0, |

319 | and 1 respectively. |

320 | */ |

321 | QTransform::QTransform(const QMatrix &mtx) |

322 | : affine(mtx._m11, mtx._m12, mtx._m21, mtx._m22, mtx._dx, mtx._dy, true), |

323 | m_13(0), m_23(0), m_33(1) |

324 | , m_type(TxNone) |

325 | , m_dirty(TxShear) |

326 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |

327 | , d(nullptr) |

328 | #endif |

329 | { |

330 | } |

331 | |

332 | /*! |

333 | Returns the adjoint of this matrix. |

334 | */ |

335 | QTransform QTransform::adjoint() const |

336 | { |

337 | qreal h11, h12, h13, |

338 | h21, h22, h23, |

339 | h31, h32, h33; |

340 | h11 = affine._m22*m_33 - m_23*affine._dy; |

341 | h21 = m_23*affine._dx - affine._m21*m_33; |

342 | h31 = affine._m21*affine._dy - affine._m22*affine._dx; |

343 | h12 = m_13*affine._dy - affine._m12*m_33; |

344 | h22 = affine._m11*m_33 - m_13*affine._dx; |

345 | h32 = affine._m12*affine._dx - affine._m11*affine._dy; |

346 | h13 = affine._m12*m_23 - m_13*affine._m22; |

347 | h23 = m_13*affine._m21 - affine._m11*m_23; |

348 | h33 = affine._m11*affine._m22 - affine._m12*affine._m21; |

349 | |

350 | return QTransform(h11, h12, h13, |

351 | h21, h22, h23, |

352 | h31, h32, h33, true); |

353 | } |

354 | |

355 | /*! |

356 | Returns the transpose of this matrix. |

357 | */ |

358 | QTransform QTransform::transposed() const |

359 | { |

360 | QTransform t(affine._m11, affine._m21, affine._dx, |

361 | affine._m12, affine._m22, affine._dy, |

362 | m_13, m_23, m_33, true); |

363 | return t; |

364 | } |

365 | |

366 | /*! |

367 | Returns an inverted copy of this matrix. |

368 | |

369 | If the matrix is singular (not invertible), the returned matrix is |

370 | the identity matrix. If \a invertible is valid (i.e. not 0), its |

371 | value is set to true if the matrix is invertible, otherwise it is |

372 | set to false. |

373 | |

374 | \sa isInvertible() |

375 | */ |

376 | QTransform QTransform::inverted(bool *invertible) const |

377 | { |

378 | QTransform invert(true); |

379 | bool inv = true; |

380 | |

381 | switch(inline_type()) { |

382 | case TxNone: |

383 | break; |

384 | case TxTranslate: |

385 | invert.affine._dx = -affine._dx; |

386 | invert.affine._dy = -affine._dy; |

387 | break; |

388 | case TxScale: |

389 | inv = !qFuzzyIsNull(affine._m11); |

390 | inv &= !qFuzzyIsNull(affine._m22); |

391 | if (inv) { |

392 | invert.affine._m11 = 1. / affine._m11; |

393 | invert.affine._m22 = 1. / affine._m22; |

394 | invert.affine._dx = -affine._dx * invert.affine._m11; |

395 | invert.affine._dy = -affine._dy * invert.affine._m22; |

396 | } |

397 | break; |

398 | case TxRotate: |

399 | case TxShear: |

400 | invert.affine = affine.inverted(&inv); |

401 | break; |

402 | default: |

403 | // general case |

404 | qreal det = determinant(); |

405 | inv = !qFuzzyIsNull(det); |

406 | if (inv) |

407 | invert = adjoint() / det; |

408 | break; |

409 | } |

410 | |

411 | if (invertible) |

412 | *invertible = inv; |

413 | |

414 | if (inv) { |

415 | // inverting doesn't change the type |

416 | invert.m_type = m_type; |

417 | invert.m_dirty = m_dirty; |

418 | } |

419 | |

420 | return invert; |

421 | } |

422 | |

423 | /*! |

424 | Moves the coordinate system \a dx along the x axis and \a dy along |

425 | the y axis, and returns a reference to the matrix. |

426 | |

427 | \sa setMatrix() |

428 | */ |

429 | QTransform &QTransform::translate(qreal dx, qreal dy) |

430 | { |

431 | if (dx == 0 && dy == 0) |

432 | return *this; |

433 | #ifndef QT_NO_DEBUG |

434 | if (qIsNaN(dx) | qIsNaN(dy)) { |

435 | nanWarning("translate"); |

436 | return *this; |

437 | } |

438 | #endif |

439 | |

440 | switch(inline_type()) { |

441 | case TxNone: |

442 | affine._dx = dx; |

443 | affine._dy = dy; |

444 | break; |

445 | case TxTranslate: |

446 | affine._dx += dx; |

447 | affine._dy += dy; |

448 | break; |

449 | case TxScale: |

450 | affine._dx += dx*affine._m11; |

451 | affine._dy += dy*affine._m22; |

452 | break; |

453 | case TxProject: |

454 | m_33 += dx*m_13 + dy*m_23; |

455 | Q_FALLTHROUGH(); |

456 | case TxShear: |

457 | case TxRotate: |

458 | affine._dx += dx*affine._m11 + dy*affine._m21; |

459 | affine._dy += dy*affine._m22 + dx*affine._m12; |

460 | break; |

461 | } |

462 | if (m_dirty < TxTranslate) |

463 | m_dirty = TxTranslate; |

464 | return *this; |

465 | } |

466 | |

467 | /*! |

468 | Creates a matrix which corresponds to a translation of \a dx along |

469 | the x axis and \a dy along the y axis. This is the same as |

470 | QTransform().translate(dx, dy) but slightly faster. |

471 | |

472 | \since 4.5 |

473 | */ |

474 | QTransform QTransform::fromTranslate(qreal dx, qreal dy) |

475 | { |

476 | #ifndef QT_NO_DEBUG |

477 | if (qIsNaN(dx) | qIsNaN(dy)) { |

478 | nanWarning("fromTranslate"); |

479 | return QTransform(); |

480 | } |

481 | #endif |

482 | QTransform transform(1, 0, 0, 0, 1, 0, dx, dy, 1, true); |

483 | if (dx == 0 && dy == 0) |

484 | transform.m_type = TxNone; |

485 | else |

486 | transform.m_type = TxTranslate; |

487 | transform.m_dirty = TxNone; |

488 | return transform; |

489 | } |

490 | |

491 | /*! |

492 | Scales the coordinate system by \a sx horizontally and \a sy |

493 | vertically, and returns a reference to the matrix. |

494 | |

495 | \sa setMatrix() |

496 | */ |

497 | QTransform & QTransform::scale(qreal sx, qreal sy) |

498 | { |

499 | if (sx == 1 && sy == 1) |

500 | return *this; |

501 | #ifndef QT_NO_DEBUG |

502 | if (qIsNaN(sx) | qIsNaN(sy)) { |

503 | nanWarning("scale"); |

504 | return *this; |

505 | } |

506 | #endif |

507 | |

508 | switch(inline_type()) { |

509 | case TxNone: |

510 | case TxTranslate: |

511 | affine._m11 = sx; |

512 | affine._m22 = sy; |

513 | break; |

514 | case TxProject: |

515 | m_13 *= sx; |

516 | m_23 *= sy; |

517 | Q_FALLTHROUGH(); |

518 | case TxRotate: |

519 | case TxShear: |

520 | affine._m12 *= sx; |

521 | affine._m21 *= sy; |

522 | Q_FALLTHROUGH(); |

523 | case TxScale: |

524 | affine._m11 *= sx; |

525 | affine._m22 *= sy; |

526 | break; |

527 | } |

528 | if (m_dirty < TxScale) |

529 | m_dirty = TxScale; |

530 | return *this; |

531 | } |

532 | |

533 | /*! |

534 | Creates a matrix which corresponds to a scaling of |

535 | \a sx horizontally and \a sy vertically. |

536 | This is the same as QTransform().scale(sx, sy) but slightly faster. |

537 | |

538 | \since 4.5 |

539 | */ |

540 | QTransform QTransform::fromScale(qreal sx, qreal sy) |

541 | { |

542 | #ifndef QT_NO_DEBUG |

543 | if (qIsNaN(sx) | qIsNaN(sy)) { |

544 | nanWarning("fromScale"); |

545 | return QTransform(); |

546 | } |

547 | #endif |

548 | QTransform transform(sx, 0, 0, 0, sy, 0, 0, 0, 1, true); |

549 | if (sx == 1. && sy == 1.) |

550 | transform.m_type = TxNone; |

551 | else |

552 | transform.m_type = TxScale; |

553 | transform.m_dirty = TxNone; |

554 | return transform; |

555 | } |

556 | |

557 | /*! |

558 | Shears the coordinate system by \a sh horizontally and \a sv |

559 | vertically, and returns a reference to the matrix. |

560 | |

561 | \sa setMatrix() |

562 | */ |

563 | QTransform & QTransform::shear(qreal sh, qreal sv) |

564 | { |

565 | if (sh == 0 && sv == 0) |

566 | return *this; |

567 | #ifndef QT_NO_DEBUG |

568 | if (qIsNaN(sh) | qIsNaN(sv)) { |

569 | nanWarning("shear"); |

570 | return *this; |

571 | } |

572 | #endif |

573 | |

574 | switch(inline_type()) { |

575 | case TxNone: |

576 | case TxTranslate: |

577 | affine._m12 = sv; |

578 | affine._m21 = sh; |

579 | break; |

580 | case TxScale: |

581 | affine._m12 = sv*affine._m22; |

582 | affine._m21 = sh*affine._m11; |

583 | break; |

584 | case TxProject: { |

585 | qreal tm13 = sv*m_23; |

586 | qreal tm23 = sh*m_13; |

587 | m_13 += tm13; |

588 | m_23 += tm23; |

589 | } |

590 | Q_FALLTHROUGH(); |

591 | case TxRotate: |

592 | case TxShear: { |

593 | qreal tm11 = sv*affine._m21; |

594 | qreal tm22 = sh*affine._m12; |

595 | qreal tm12 = sv*affine._m22; |

596 | qreal tm21 = sh*affine._m11; |

597 | affine._m11 += tm11; affine._m12 += tm12; |

598 | affine._m21 += tm21; affine._m22 += tm22; |

599 | break; |

600 | } |

601 | } |

602 | if (m_dirty < TxShear) |

603 | m_dirty = TxShear; |

604 | return *this; |

605 | } |

606 | |

607 | const qreal deg2rad = qreal(0.017453292519943295769); // pi/180 |

608 | const qreal inv_dist_to_plane = 1. / 1024.; |

609 | |

610 | /*! |

611 | \fn QTransform &QTransform::rotate(qreal angle, Qt::Axis axis) |

612 | |

613 | Rotates the coordinate system counterclockwise by the given \a angle |

614 | about the specified \a axis and returns a reference to the matrix. |

615 | |

616 | Note that if you apply a QTransform to a point defined in widget |

617 | coordinates, the direction of the rotation will be clockwise |

618 | because the y-axis points downwards. |

619 | |

620 | The angle is specified in degrees. |

621 | |

622 | \sa setMatrix() |

623 | */ |

624 | QTransform & QTransform::rotate(qreal a, Qt::Axis axis) |

625 | { |

626 | if (a == 0) |

627 | return *this; |

628 | #ifndef QT_NO_DEBUG |

629 | if (qIsNaN(a)) { |

630 | nanWarning("rotate"); |

631 | return *this; |

632 | } |

633 | #endif |

634 | |

635 | qreal sina = 0; |

636 | qreal cosa = 0; |

637 | if (a == 90. || a == -270.) |

638 | sina = 1.; |

639 | else if (a == 270. || a == -90.) |

640 | sina = -1.; |

641 | else if (a == 180.) |

642 | cosa = -1.; |

643 | else{ |

644 | qreal b = deg2rad*a; // convert to radians |

645 | sina = qSin(b); // fast and convenient |

646 | cosa = qCos(b); |

647 | } |

648 | |

649 | if (axis == Qt::ZAxis) { |

650 | switch(inline_type()) { |

651 | case TxNone: |

652 | case TxTranslate: |

653 | affine._m11 = cosa; |

654 | affine._m12 = sina; |

655 | affine._m21 = -sina; |

656 | affine._m22 = cosa; |

657 | break; |

658 | case TxScale: { |

659 | qreal tm11 = cosa*affine._m11; |

660 | qreal tm12 = sina*affine._m22; |

661 | qreal tm21 = -sina*affine._m11; |

662 | qreal tm22 = cosa*affine._m22; |

663 | affine._m11 = tm11; affine._m12 = tm12; |

664 | affine._m21 = tm21; affine._m22 = tm22; |

665 | break; |

666 | } |

667 | case TxProject: { |

668 | qreal tm13 = cosa*m_13 + sina*m_23; |

669 | qreal tm23 = -sina*m_13 + cosa*m_23; |

670 | m_13 = tm13; |

671 | m_23 = tm23; |

672 | Q_FALLTHROUGH(); |

673 | } |

674 | case TxRotate: |

675 | case TxShear: { |

676 | qreal tm11 = cosa*affine._m11 + sina*affine._m21; |

677 | qreal tm12 = cosa*affine._m12 + sina*affine._m22; |

678 | qreal tm21 = -sina*affine._m11 + cosa*affine._m21; |

679 | qreal tm22 = -sina*affine._m12 + cosa*affine._m22; |

680 | affine._m11 = tm11; affine._m12 = tm12; |

681 | affine._m21 = tm21; affine._m22 = tm22; |

682 | break; |

683 | } |

684 | } |

685 | if (m_dirty < TxRotate) |

686 | m_dirty = TxRotate; |

687 | } else { |

688 | QTransform result; |

689 | if (axis == Qt::YAxis) { |

690 | result.affine._m11 = cosa; |

691 | result.m_13 = -sina * inv_dist_to_plane; |

692 | } else { |

693 | result.affine._m22 = cosa; |

694 | result.m_23 = -sina * inv_dist_to_plane; |

695 | } |

696 | result.m_type = TxProject; |

697 | *this = result * *this; |

698 | } |

699 | |

700 | return *this; |

701 | } |

702 | |

703 | /*! |

704 | \fn QTransform & QTransform::rotateRadians(qreal angle, Qt::Axis axis) |

705 | |

706 | Rotates the coordinate system counterclockwise by the given \a angle |

707 | about the specified \a axis and returns a reference to the matrix. |

708 | |

709 | Note that if you apply a QTransform to a point defined in widget |

710 | coordinates, the direction of the rotation will be clockwise |

711 | because the y-axis points downwards. |

712 | |

713 | The angle is specified in radians. |

714 | |

715 | \sa setMatrix() |

716 | */ |

717 | QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis) |

718 | { |

719 | #ifndef QT_NO_DEBUG |

720 | if (qIsNaN(a)) { |

721 | nanWarning("rotateRadians"); |

722 | return *this; |

723 | } |

724 | #endif |

725 | qreal sina = qSin(a); |

726 | qreal cosa = qCos(a); |

727 | |

728 | if (axis == Qt::ZAxis) { |

729 | switch(inline_type()) { |

730 | case TxNone: |

731 | case TxTranslate: |

732 | affine._m11 = cosa; |

733 | affine._m12 = sina; |

734 | affine._m21 = -sina; |

735 | affine._m22 = cosa; |

736 | break; |

737 | case TxScale: { |

738 | qreal tm11 = cosa*affine._m11; |

739 | qreal tm12 = sina*affine._m22; |

740 | qreal tm21 = -sina*affine._m11; |

741 | qreal tm22 = cosa*affine._m22; |

742 | affine._m11 = tm11; affine._m12 = tm12; |

743 | affine._m21 = tm21; affine._m22 = tm22; |

744 | break; |

745 | } |

746 | case TxProject: { |

747 | qreal tm13 = cosa*m_13 + sina*m_23; |

748 | qreal tm23 = -sina*m_13 + cosa*m_23; |

749 | m_13 = tm13; |

750 | m_23 = tm23; |

751 | Q_FALLTHROUGH(); |

752 | } |

753 | case TxRotate: |

754 | case TxShear: { |

755 | qreal tm11 = cosa*affine._m11 + sina*affine._m21; |

756 | qreal tm12 = cosa*affine._m12 + sina*affine._m22; |

757 | qreal tm21 = -sina*affine._m11 + cosa*affine._m21; |

758 | qreal tm22 = -sina*affine._m12 + cosa*affine._m22; |

759 | affine._m11 = tm11; affine._m12 = tm12; |

760 | affine._m21 = tm21; affine._m22 = tm22; |

761 | break; |

762 | } |

763 | } |

764 | if (m_dirty < TxRotate) |

765 | m_dirty = TxRotate; |

766 | } else { |

767 | QTransform result; |

768 | if (axis == Qt::YAxis) { |

769 | result.affine._m11 = cosa; |

770 | result.m_13 = -sina * inv_dist_to_plane; |

771 | } else { |

772 | result.affine._m22 = cosa; |

773 | result.m_23 = -sina * inv_dist_to_plane; |

774 | } |

775 | result.m_type = TxProject; |

776 | *this = result * *this; |

777 | } |

778 | return *this; |

779 | } |

780 | |

781 | /*! |

782 | \fn bool QTransform::operator==(const QTransform &matrix) const |

783 | Returns \c true if this matrix is equal to the given \a matrix, |

784 | otherwise returns \c false. |

785 | */ |

786 | bool QTransform::operator==(const QTransform &o) const |

787 | { |

788 | return affine._m11 == o.affine._m11 && |

789 | affine._m12 == o.affine._m12 && |

790 | affine._m21 == o.affine._m21 && |

791 | affine._m22 == o.affine._m22 && |

792 | affine._dx == o.affine._dx && |

793 | affine._dy == o.affine._dy && |

794 | m_13 == o.m_13 && |

795 | m_23 == o.m_23 && |

796 | m_33 == o.m_33; |

797 | } |

798 | |

799 | /*! |

800 | \since 5.6 |

801 | \relates QTransform |

802 | |

803 | Returns the hash value for \a key, using |

804 | \a seed to seed the calculation. |

805 | */ |

806 | uint qHash(const QTransform &key, uint seed) noexcept |

807 | { |

808 | QtPrivate::QHashCombine hash; |

809 | seed = hash(seed, key.m11()); |

810 | seed = hash(seed, key.m12()); |

811 | seed = hash(seed, key.m21()); |

812 | seed = hash(seed, key.m22()); |

813 | seed = hash(seed, key.dx()); |

814 | seed = hash(seed, key.dy()); |

815 | seed = hash(seed, key.m13()); |

816 | seed = hash(seed, key.m23()); |

817 | seed = hash(seed, key.m33()); |

818 | return seed; |

819 | } |

820 | |

821 | |

822 | /*! |

823 | \fn bool QTransform::operator!=(const QTransform &matrix) const |

824 | Returns \c true if this matrix is not equal to the given \a matrix, |

825 | otherwise returns \c false. |

826 | */ |

827 | bool QTransform::operator!=(const QTransform &o) const |

828 | { |

829 | return !operator==(o); |

830 | } |

831 | |

832 | /*! |

833 | \fn QTransform & QTransform::operator*=(const QTransform &matrix) |

834 | \overload |

835 | |

836 | Returns the result of multiplying this matrix by the given \a |

837 | matrix. |

838 | */ |

839 | QTransform & QTransform::operator*=(const QTransform &o) |

840 | { |

841 | const TransformationType otherType = o.inline_type(); |

842 | if (otherType == TxNone) |

843 | return *this; |

844 | |

845 | const TransformationType thisType = inline_type(); |

846 | if (thisType == TxNone) |

847 | return operator=(o); |

848 | |

849 | TransformationType t = qMax(thisType, otherType); |

850 | switch(t) { |

851 | case TxNone: |

852 | break; |

853 | case TxTranslate: |

854 | affine._dx += o.affine._dx; |

855 | affine._dy += o.affine._dy; |

856 | break; |

857 | case TxScale: |

858 | { |

859 | qreal m11 = affine._m11*o.affine._m11; |

860 | qreal m22 = affine._m22*o.affine._m22; |

861 | |

862 | qreal m31 = affine._dx*o.affine._m11 + o.affine._dx; |

863 | qreal m32 = affine._dy*o.affine._m22 + o.affine._dy; |

864 | |

865 | affine._m11 = m11; |

866 | affine._m22 = m22; |

867 | affine._dx = m31; affine._dy = m32; |

868 | break; |

869 | } |

870 | case TxRotate: |

871 | case TxShear: |

872 | { |

873 | qreal m11 = affine._m11*o.affine._m11 + affine._m12*o.affine._m21; |

874 | qreal m12 = affine._m11*o.affine._m12 + affine._m12*o.affine._m22; |

875 | |

876 | qreal m21 = affine._m21*o.affine._m11 + affine._m22*o.affine._m21; |

877 | qreal m22 = affine._m21*o.affine._m12 + affine._m22*o.affine._m22; |

878 | |

879 | qreal m31 = affine._dx*o.affine._m11 + affine._dy*o.affine._m21 + o.affine._dx; |

880 | qreal m32 = affine._dx*o.affine._m12 + affine._dy*o.affine._m22 + o.affine._dy; |

881 | |

882 | affine._m11 = m11; affine._m12 = m12; |

883 | affine._m21 = m21; affine._m22 = m22; |

884 | affine._dx = m31; affine._dy = m32; |

885 | break; |

886 | } |

887 | case TxProject: |

888 | { |

889 | qreal m11 = affine._m11*o.affine._m11 + affine._m12*o.affine._m21 + m_13*o.affine._dx; |

890 | qreal m12 = affine._m11*o.affine._m12 + affine._m12*o.affine._m22 + m_13*o.affine._dy; |

891 | qreal m13 = affine._m11*o.m_13 + affine._m12*o.m_23 + m_13*o.m_33; |

892 | |

893 | qreal m21 = affine._m21*o.affine._m11 + affine._m22*o.affine._m21 + m_23*o.affine._dx; |

894 | qreal m22 = affine._m21*o.affine._m12 + affine._m22*o.affine._m22 + m_23*o.affine._dy; |

895 | qreal m23 = affine._m21*o.m_13 + affine._m22*o.m_23 + m_23*o.m_33; |

896 | |

897 | qreal m31 = affine._dx*o.affine._m11 + affine._dy*o.affine._m21 + m_33*o.affine._dx; |

898 | qreal m32 = affine._dx*o.affine._m12 + affine._dy*o.affine._m22 + m_33*o.affine._dy; |

899 | qreal m33 = affine._dx*o.m_13 + affine._dy*o.m_23 + m_33*o.m_33; |

900 | |

901 | affine._m11 = m11; affine._m12 = m12; m_13 = m13; |

902 | affine._m21 = m21; affine._m22 = m22; m_23 = m23; |

903 | affine._dx = m31; affine._dy = m32; m_33 = m33; |

904 | } |

905 | } |

906 | |

907 | m_dirty = t; |

908 | m_type = t; |

909 | |

910 | return *this; |

911 | } |

912 | |

913 | /*! |

914 | \fn QTransform QTransform::operator*(const QTransform &matrix) const |

915 | Returns the result of multiplying this matrix by the given \a |

916 | matrix. |

917 | |

918 | Note that matrix multiplication is not commutative, i.e. a*b != |

919 | b*a. |

920 | */ |

921 | QTransform QTransform::operator*(const QTransform &m) const |

922 | { |

923 | const TransformationType otherType = m.inline_type(); |

924 | if (otherType == TxNone) |

925 | return *this; |

926 | |

927 | const TransformationType thisType = inline_type(); |

928 | if (thisType == TxNone) |

929 | return m; |

930 | |

931 | QTransform t(true); |

932 | TransformationType type = qMax(thisType, otherType); |

933 | switch(type) { |

934 | case TxNone: |

935 | break; |

936 | case TxTranslate: |

937 | t.affine._dx = affine._dx + m.affine._dx; |

938 | t.affine._dy += affine._dy + m.affine._dy; |

939 | break; |

940 | case TxScale: |

941 | { |

942 | qreal m11 = affine._m11*m.affine._m11; |

943 | qreal m22 = affine._m22*m.affine._m22; |

944 | |

945 | qreal m31 = affine._dx*m.affine._m11 + m.affine._dx; |

946 | qreal m32 = affine._dy*m.affine._m22 + m.affine._dy; |

947 | |

948 | t.affine._m11 = m11; |

949 | t.affine._m22 = m22; |

950 | t.affine._dx = m31; t.affine._dy = m32; |

951 | break; |

952 | } |

953 | case TxRotate: |

954 | case TxShear: |

955 | { |

956 | qreal m11 = affine._m11*m.affine._m11 + affine._m12*m.affine._m21; |

957 | qreal m12 = affine._m11*m.affine._m12 + affine._m12*m.affine._m22; |

958 | |

959 | qreal m21 = affine._m21*m.affine._m11 + affine._m22*m.affine._m21; |

960 | qreal m22 = affine._m21*m.affine._m12 + affine._m22*m.affine._m22; |

961 | |

962 | qreal m31 = affine._dx*m.affine._m11 + affine._dy*m.affine._m21 + m.affine._dx; |

963 | qreal m32 = affine._dx*m.affine._m12 + affine._dy*m.affine._m22 + m.affine._dy; |

964 | |

965 | t.affine._m11 = m11; t.affine._m12 = m12; |

966 | t.affine._m21 = m21; t.affine._m22 = m22; |

967 | t.affine._dx = m31; t.affine._dy = m32; |

968 | break; |

969 | } |

970 | case TxProject: |

971 | { |

972 | qreal m11 = affine._m11*m.affine._m11 + affine._m12*m.affine._m21 + m_13*m.affine._dx; |

973 | qreal m12 = affine._m11*m.affine._m12 + affine._m12*m.affine._m22 + m_13*m.affine._dy; |

974 | qreal m13 = affine._m11*m.m_13 + affine._m12*m.m_23 + m_13*m.m_33; |

975 | |

976 | qreal m21 = affine._m21*m.affine._m11 + affine._m22*m.affine._m21 + m_23*m.affine._dx; |

977 | qreal m22 = affine._m21*m.affine._m12 + affine._m22*m.affine._m22 + m_23*m.affine._dy; |

978 | qreal m23 = affine._m21*m.m_13 + affine._m22*m.m_23 + m_23*m.m_33; |

979 | |

980 | qreal m31 = affine._dx*m.affine._m11 + affine._dy*m.affine._m21 + m_33*m.affine._dx; |

981 | qreal m32 = affine._dx*m.affine._m12 + affine._dy*m.affine._m22 + m_33*m.affine._dy; |

982 | qreal m33 = affine._dx*m.m_13 + affine._dy*m.m_23 + m_33*m.m_33; |

983 | |

984 | t.affine._m11 = m11; t.affine._m12 = m12; t.m_13 = m13; |

985 | t.affine._m21 = m21; t.affine._m22 = m22; t.m_23 = m23; |

986 | t.affine._dx = m31; t.affine._dy = m32; t.m_33 = m33; |

987 | } |

988 | } |

989 | |

990 | t.m_dirty = type; |

991 | t.m_type = type; |

992 | |

993 | return t; |

994 | } |

995 | |

996 | /*! |

997 | \fn QTransform & QTransform::operator*=(qreal scalar) |

998 | \overload |

999 | |

1000 | Returns the result of performing an element-wise multiplication of this |

1001 | matrix with the given \a scalar. |

1002 | */ |

1003 | |

1004 | /*! |

1005 | \fn QTransform & QTransform::operator/=(qreal scalar) |

1006 | \overload |

1007 | |

1008 | Returns the result of performing an element-wise division of this |

1009 | matrix by the given \a scalar. |

1010 | */ |

1011 | |

1012 | /*! |

1013 | \fn QTransform & QTransform::operator+=(qreal scalar) |

1014 | \overload |

1015 | |

1016 | Returns the matrix obtained by adding the given \a scalar to each |

1017 | element of this matrix. |

1018 | */ |

1019 | |

1020 | /*! |

1021 | \fn QTransform & QTransform::operator-=(qreal scalar) |

1022 | \overload |

1023 | |

1024 | Returns the matrix obtained by subtracting the given \a scalar from each |

1025 | element of this matrix. |

1026 | */ |

1027 | |

1028 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |

1029 | /*! |

1030 | Assigns the given \a matrix's values to this matrix. |

1031 | */ |

1032 | QTransform & QTransform::operator=(const QTransform &matrix) noexcept |

1033 | { |

1034 | affine._m11 = matrix.affine._m11; |

1035 | affine._m12 = matrix.affine._m12; |

1036 | affine._m21 = matrix.affine._m21; |

1037 | affine._m22 = matrix.affine._m22; |

1038 | affine._dx = matrix.affine._dx; |

1039 | affine._dy = matrix.affine._dy; |

1040 | m_13 = matrix.m_13; |

1041 | m_23 = matrix.m_23; |

1042 | m_33 = matrix.m_33; |

1043 | m_type = matrix.m_type; |

1044 | m_dirty = matrix.m_dirty; |

1045 | |

1046 | return *this; |

1047 | } |

1048 | #endif |

1049 | |

1050 | /*! |

1051 | Resets the matrix to an identity matrix, i.e. all elements are set |

1052 | to zero, except \c m11 and \c m22 (specifying the scale) and \c m33 |

1053 | which are set to 1. |

1054 | |

1055 | \sa QTransform(), isIdentity(), {QTransform#Basic Matrix |

1056 | Operations}{Basic Matrix Operations} |

1057 | */ |

1058 | void QTransform::reset() |

1059 | { |

1060 | affine._m11 = affine._m22 = m_33 = 1.0; |

1061 | affine._m12 = m_13 = affine._m21 = m_23 = affine._dx = affine._dy = 0; |

1062 | m_type = TxNone; |

1063 | m_dirty = TxNone; |

1064 | } |

1065 | |

1066 | #ifndef QT_NO_DATASTREAM |

1067 | /*! |

1068 | \fn QDataStream &operator<<(QDataStream &stream, const QTransform &matrix) |

1069 | \since 4.3 |

1070 | \relates QTransform |

1071 | |

1072 | Writes the given \a matrix to the given \a stream and returns a |

1073 | reference to the stream. |

1074 | |

1075 | \sa {Serializing Qt Data Types} |

1076 | */ |

1077 | QDataStream & operator<<(QDataStream &s, const QTransform &m) |

1078 | { |

1079 | s << double(m.m11()) |

1080 | << double(m.m12()) |

1081 | << double(m.m13()) |

1082 | << double(m.m21()) |

1083 | << double(m.m22()) |

1084 | << double(m.m23()) |

1085 | << double(m.m31()) |

1086 | << double(m.m32()) |

1087 | << double(m.m33()); |

1088 | return s; |

1089 | } |

1090 | |

1091 | /*! |

1092 | \fn QDataStream &operator>>(QDataStream &stream, QTransform &matrix) |

1093 | \since 4.3 |

1094 | \relates QTransform |

1095 | |

1096 | Reads the given \a matrix from the given \a stream and returns a |

1097 | reference to the stream. |

1098 | |

1099 | \sa {Serializing Qt Data Types} |

1100 | */ |

1101 | QDataStream & operator>>(QDataStream &s, QTransform &t) |

1102 | { |

1103 | double m11, m12, m13, |

1104 | m21, m22, m23, |

1105 | m31, m32, m33; |

1106 | |

1107 | s >> m11; |

1108 | s >> m12; |

1109 | s >> m13; |

1110 | s >> m21; |

1111 | s >> m22; |

1112 | s >> m23; |

1113 | s >> m31; |

1114 | s >> m32; |

1115 | s >> m33; |

1116 | t.setMatrix(m11, m12, m13, |

1117 | m21, m22, m23, |

1118 | m31, m32, m33); |

1119 | return s; |

1120 | } |

1121 | |

1122 | #endif // QT_NO_DATASTREAM |

1123 | |

1124 | #ifndef QT_NO_DEBUG_STREAM |

1125 | QDebug operator<<(QDebug dbg, const QTransform &m) |

1126 | { |

1127 | static const char typeStr[][12] = |

1128 | { |

1129 | "TxNone", |

1130 | "TxTranslate", |

1131 | "TxScale", |

1132 | "", |

1133 | "TxRotate", |

1134 | "", "", "", |

1135 | "TxShear", |

1136 | "", "", "", "", "", "", "", |

1137 | "TxProject" |

1138 | }; |

1139 | |

1140 | QDebugStateSaver saver(dbg); |

1141 | dbg.nospace() << "QTransform(type="<< typeStr[m.type()] << ',' |

1142 | << " 11="<< m.m11() |

1143 | << " 12="<< m.m12() |

1144 | << " 13="<< m.m13() |

1145 | << " 21="<< m.m21() |

1146 | << " 22="<< m.m22() |

1147 | << " 23="<< m.m23() |

1148 | << " 31="<< m.m31() |

1149 | << " 32="<< m.m32() |

1150 | << " 33="<< m.m33() |

1151 | << ')'; |

1152 | |

1153 | return dbg; |

1154 | } |

1155 | #endif |

1156 | |

1157 | /*! |

1158 | \fn QPoint operator*(const QPoint &point, const QTransform &matrix) |

1159 | \relates QTransform |

1160 | |

1161 | This is the same as \a{matrix}.map(\a{point}). |

1162 | |

1163 | \sa QTransform::map() |

1164 | */ |

1165 | QPoint QTransform::map(const QPoint &p) const |

1166 | { |

1167 | qreal fx = p.x(); |

1168 | qreal fy = p.y(); |

1169 | |

1170 | qreal x = 0, y = 0; |

1171 | |

1172 | TransformationType t = inline_type(); |

1173 | switch(t) { |

1174 | case TxNone: |

1175 | x = fx; |

1176 | y = fy; |

1177 | break; |

1178 | case TxTranslate: |

1179 | x = fx + affine._dx; |

1180 | y = fy + affine._dy; |

1181 | break; |

1182 | case TxScale: |

1183 | x = affine._m11 * fx + affine._dx; |

1184 | y = affine._m22 * fy + affine._dy; |

1185 | break; |

1186 | case TxRotate: |

1187 | case TxShear: |

1188 | case TxProject: |

1189 | x = affine._m11 * fx + affine._m21 * fy + affine._dx; |

1190 | y = affine._m12 * fx + affine._m22 * fy + affine._dy; |

1191 | if (t == TxProject) { |

1192 | qreal w = 1./(m_13 * fx + m_23 * fy + m_33); |

1193 | x *= w; |

1194 | y *= w; |

1195 | } |

1196 | } |

1197 | return QPoint(qRound(x), qRound(y)); |

1198 | } |

1199 | |

1200 | |

1201 | /*! |

1202 | \fn QPointF operator*(const QPointF &point, const QTransform &matrix) |

1203 | \relates QTransform |

1204 | |

1205 | Same as \a{matrix}.map(\a{point}). |

1206 | |

1207 | \sa QTransform::map() |

1208 | */ |

1209 | |

1210 | /*! |

1211 | \overload |

1212 | |

1213 | Creates and returns a QPointF object that is a copy of the given point, |

1214 | \a p, mapped into the coordinate system defined by this matrix. |

1215 | */ |

1216 | QPointF QTransform::map(const QPointF &p) const |

1217 | { |

1218 | qreal fx = p.x(); |

1219 | qreal fy = p.y(); |

1220 | |

1221 | qreal x = 0, y = 0; |

1222 | |

1223 | TransformationType t = inline_type(); |

1224 | switch(t) { |

1225 | case TxNone: |

1226 | x = fx; |

1227 | y = fy; |

1228 | break; |

1229 | case TxTranslate: |

1230 | x = fx + affine._dx; |

1231 | y = fy + affine._dy; |

1232 | break; |

1233 | case TxScale: |

1234 | x = affine._m11 * fx + affine._dx; |

1235 | y = affine._m22 * fy + affine._dy; |

1236 | break; |

1237 | case TxRotate: |

1238 | case TxShear: |

1239 | case TxProject: |

1240 | x = affine._m11 * fx + affine._m21 * fy + affine._dx; |

1241 | y = affine._m12 * fx + affine._m22 * fy + affine._dy; |

1242 | if (t == TxProject) { |

1243 | qreal w = 1./(m_13 * fx + m_23 * fy + m_33); |

1244 | x *= w; |

1245 | y *= w; |

1246 | } |

1247 | } |

1248 | return QPointF(x, y); |

1249 | } |

1250 | |

1251 | /*! |

1252 | \fn QPoint QTransform::map(const QPoint &point) const |

1253 | \overload |

1254 | |

1255 | Creates and returns a QPoint object that is a copy of the given \a |

1256 | point, mapped into the coordinate system defined by this |

1257 | matrix. Note that the transformed coordinates are rounded to the |

1258 | nearest integer. |

1259 | */ |

1260 | |

1261 | /*! |

1262 | \fn QLineF operator*(const QLineF &line, const QTransform &matrix) |

1263 | \relates QTransform |

1264 | |

1265 | This is the same as \a{matrix}.map(\a{line}). |

1266 | |

1267 | \sa QTransform::map() |

1268 | */ |

1269 | |

1270 | /*! |

1271 | \fn QLine operator*(const QLine &line, const QTransform &matrix) |

1272 | \relates QTransform |

1273 | |

1274 | This is the same as \a{matrix}.map(\a{line}). |

1275 | |

1276 | \sa QTransform::map() |

1277 | */ |

1278 | |

1279 | /*! |

1280 | \overload |

1281 | |

1282 | Creates and returns a QLineF object that is a copy of the given line, |

1283 | \a l, mapped into the coordinate system defined by this matrix. |

1284 | */ |

1285 | QLine QTransform::map(const QLine &l) const |

1286 | { |

1287 | qreal fx1 = l.x1(); |

1288 | qreal fy1 = l.y1(); |

1289 | qreal fx2 = l.x2(); |

1290 | qreal fy2 = l.y2(); |

1291 | |

1292 | qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0; |

1293 | |

1294 | TransformationType t = inline_type(); |

1295 | switch(t) { |

1296 | case TxNone: |

1297 | x1 = fx1; |

1298 | y1 = fy1; |

1299 | x2 = fx2; |

1300 | y2 = fy2; |

1301 | break; |

1302 | case TxTranslate: |

1303 | x1 = fx1 + affine._dx; |

1304 | y1 = fy1 + affine._dy; |

1305 | x2 = fx2 + affine._dx; |

1306 | y2 = fy2 + affine._dy; |

1307 | break; |

1308 | case TxScale: |

1309 | x1 = affine._m11 * fx1 + affine._dx; |

1310 | y1 = affine._m22 * fy1 + affine._dy; |

1311 | x2 = affine._m11 * fx2 + affine._dx; |

1312 | y2 = affine._m22 * fy2 + affine._dy; |

1313 | break; |

1314 | case TxRotate: |

1315 | case TxShear: |

1316 | case TxProject: |

1317 | x1 = affine._m11 * fx1 + affine._m21 * fy1 + affine._dx; |

1318 | y1 = affine._m12 * fx1 + affine._m22 * fy1 + affine._dy; |

1319 | x2 = affine._m11 * fx2 + affine._m21 * fy2 + affine._dx; |

1320 | y2 = affine._m12 * fx2 + affine._m22 * fy2 + affine._dy; |

1321 | if (t == TxProject) { |

1322 | qreal w = 1./(m_13 * fx1 + m_23 * fy1 + m_33); |

1323 | x1 *= w; |

1324 | y1 *= w; |

1325 | w = 1./(m_13 * fx2 + m_23 * fy2 + m_33); |

1326 | x2 *= w; |

1327 | y2 *= w; |

1328 | } |

1329 | } |

1330 | return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2)); |

1331 | } |

1332 | |

1333 | /*! |

1334 | \overload |

1335 | |

1336 | \fn QLineF QTransform::map(const QLineF &line) const |

1337 | |

1338 | Creates and returns a QLine object that is a copy of the given \a |

1339 | line, mapped into the coordinate system defined by this matrix. |

1340 | Note that the transformed coordinates are rounded to the nearest |

1341 | integer. |

1342 | */ |

1343 | |

1344 | QLineF QTransform::map(const QLineF &l) const |

1345 | { |

1346 | qreal fx1 = l.x1(); |

1347 | qreal fy1 = l.y1(); |

1348 | qreal fx2 = l.x2(); |

1349 | qreal fy2 = l.y2(); |

1350 | |

1351 | qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0; |

1352 | |

1353 | TransformationType t = inline_type(); |

1354 | switch(t) { |

1355 | case TxNone: |

1356 | x1 = fx1; |

1357 | y1 = fy1; |

1358 | x2 = fx2; |

1359 | y2 = fy2; |

1360 | break; |

1361 | case TxTranslate: |

1362 | x1 = fx1 + affine._dx; |

1363 | y1 = fy1 + affine._dy; |

1364 | x2 = fx2 + affine._dx; |

1365 | y2 = fy2 + affine._dy; |

1366 | break; |

1367 | case TxScale: |

1368 | x1 = affine._m11 * fx1 + affine._dx; |

1369 | y1 = affine._m22 * fy1 + affine._dy; |

1370 | x2 = affine._m11 * fx2 + affine._dx; |

1371 | y2 = affine._m22 * fy2 + affine._dy; |

1372 | break; |

1373 | case TxRotate: |

1374 | case TxShear: |

1375 | case TxProject: |

1376 | x1 = affine._m11 * fx1 + affine._m21 * fy1 + affine._dx; |

1377 | y1 = affine._m12 * fx1 + affine._m22 * fy1 + affine._dy; |

1378 | x2 = affine._m11 * fx2 + affine._m21 * fy2 + affine._dx; |

1379 | y2 = affine._m12 * fx2 + affine._m22 * fy2 + affine._dy; |

1380 | if (t == TxProject) { |

1381 | qreal w = 1./(m_13 * fx1 + m_23 * fy1 + m_33); |

1382 | x1 *= w; |

1383 | y1 *= w; |

1384 | w = 1./(m_13 * fx2 + m_23 * fy2 + m_33); |

1385 | x2 *= w; |

1386 | y2 *= w; |

1387 | } |

1388 | } |

1389 | return QLineF(x1, y1, x2, y2); |

1390 | } |

1391 | |

1392 | static QPolygonF mapProjective(const QTransform &transform, const QPolygonF &poly) |

1393 | { |

1394 | if (poly.size() == 0) |

1395 | return poly; |

1396 | |

1397 | if (poly.size() == 1) |

1398 | return QPolygonF() << transform.map(poly.at(0)); |

1399 | |

1400 | QPainterPath path; |

1401 | path.addPolygon(poly); |

1402 | |

1403 | path = transform.map(path); |

1404 | |

1405 | QPolygonF result; |

1406 | const int elementCount = path.elementCount(); |

1407 | result.reserve(elementCount); |

1408 | for (int i = 0; i < elementCount; ++i) |

1409 | result << path.elementAt(i); |

1410 | return result; |

1411 | } |

1412 | |

1413 | |

1414 | /*! |

1415 | \fn QPolygonF operator *(const QPolygonF &polygon, const QTransform &matrix) |

1416 | \since 4.3 |

1417 | \relates QTransform |

1418 | |

1419 | This is the same as \a{matrix}.map(\a{polygon}). |

1420 | |

1421 | \sa QTransform::map() |

1422 | */ |

1423 | |

1424 | /*! |

1425 | \fn QPolygon operator*(const QPolygon &polygon, const QTransform &matrix) |

1426 | \relates QTransform |

1427 | |

1428 | This is the same as \a{matrix}.map(\a{polygon}). |

1429 | |

1430 | \sa QTransform::map() |

1431 | */ |

1432 | |

1433 | /*! |

1434 | \fn QPolygonF QTransform::map(const QPolygonF &polygon) const |

1435 | \overload |

1436 | |

1437 | Creates and returns a QPolygonF object that is a copy of the given |

1438 | \a polygon, mapped into the coordinate system defined by this |

1439 | matrix. |

1440 | */ |

1441 | QPolygonF QTransform::map(const QPolygonF &a) const |

1442 | { |

1443 | TransformationType t = inline_type(); |

1444 | if (t <= TxTranslate) |

1445 | return a.translated(affine._dx, affine._dy); |

1446 | |

1447 | if (t >= QTransform::TxProject) |

1448 | return mapProjective(*this, a); |

1449 | |

1450 | int size = a.size(); |

1451 | int i; |

1452 | QPolygonF p(size); |

1453 | const QPointF *da = a.constData(); |

1454 | QPointF *dp = p.data(); |

1455 | |

1456 | for(i = 0; i < size; ++i) { |

1457 | MAP(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp); |

1458 | } |

1459 | return p; |

1460 | } |

1461 | |

1462 | /*! |

1463 | \fn QPolygon QTransform::map(const QPolygon &polygon) const |

1464 | \overload |

1465 | |

1466 | Creates and returns a QPolygon object that is a copy of the given |

1467 | \a polygon, mapped into the coordinate system defined by this |

1468 | matrix. Note that the transformed coordinates are rounded to the |

1469 | nearest integer. |

1470 | */ |

1471 | QPolygon QTransform::map(const QPolygon &a) const |

1472 | { |

1473 | TransformationType t = inline_type(); |

1474 | if (t <= TxTranslate) |

1475 | return a.translated(qRound(affine._dx), qRound(affine._dy)); |

1476 | |

1477 | if (t >= QTransform::TxProject) |

1478 | return mapProjective(*this, QPolygonF(a)).toPolygon(); |

1479 | |

1480 | int size = a.size(); |

1481 | int i; |

1482 | QPolygon p(size); |

1483 | const QPoint *da = a.constData(); |

1484 | QPoint *dp = p.data(); |

1485 | |

1486 | for(i = 0; i < size; ++i) { |

1487 | qreal nx = 0, ny = 0; |

1488 | MAP(da[i].xp, da[i].yp, nx, ny); |

1489 | dp[i].xp = qRound(nx); |

1490 | dp[i].yp = qRound(ny); |

1491 | } |

1492 | return p; |

1493 | } |

1494 | |

1495 | /*! |

1496 | \fn QRegion operator*(const QRegion ®ion, const QTransform &matrix) |

1497 | \relates QTransform |

1498 | |

1499 | This is the same as \a{matrix}.map(\a{region}). |

1500 | |

1501 | \sa QTransform::map() |

1502 | */ |

1503 | |

1504 | extern QPainterPath qt_regionToPath(const QRegion ®ion); |

1505 | |

1506 | /*! |

1507 | \fn QRegion QTransform::map(const QRegion ®ion) const |

1508 | \overload |

1509 | |

1510 | Creates and returns a QRegion object that is a copy of the given |

1511 | \a region, mapped into the coordinate system defined by this matrix. |

1512 | |

1513 | Calling this method can be rather expensive if rotations or |

1514 | shearing are used. |

1515 | */ |

1516 | QRegion QTransform::map(const QRegion &r) const |

1517 | { |

1518 | TransformationType t = inline_type(); |

1519 | if (t == TxNone) |

1520 | return r; |

1521 | |

1522 | if (t == TxTranslate) { |

1523 | QRegion copy(r); |

1524 | copy.translate(qRound(affine._dx), qRound(affine._dy)); |

1525 | return copy; |

1526 | } |

1527 | |

1528 | if (t == TxScale) { |

1529 | QRegion res; |

1530 | if (m11() < 0 || m22() < 0) { |

1531 | for (const QRect &rect : r) |

1532 | res += mapRect(rect); |

1533 | } else { |

1534 | QVarLengthArray<QRect, 32> rects; |

1535 | rects.reserve(r.rectCount()); |

1536 | for (const QRect &rect : r) { |

1537 | QRect nr = mapRect(rect); |

1538 | if (!nr.isEmpty()) |

1539 | rects.append(nr); |

1540 | } |

1541 | res.setRects(rects.constData(), rects.count()); |

1542 | } |

1543 | return res; |

1544 | } |

1545 | |

1546 | QPainterPath p = map(qt_regionToPath(r)); |

1547 | return p.toFillPolygon(QTransform()).toPolygon(); |

1548 | } |

1549 | |

1550 | struct QHomogeneousCoordinate |

1551 | { |

1552 | qreal x; |

1553 | qreal y; |

1554 | qreal w; |

1555 | |

1556 | QHomogeneousCoordinate() {} |

1557 | QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_) : x(x_), y(y_), w(w_) {} |

1558 | |

1559 | const QPointF toPoint() const { |

1560 | qreal iw = 1. / w; |

1561 | return QPointF(x * iw, y * iw); |

1562 | } |

1563 | }; |

1564 | |

1565 | static inline QHomogeneousCoordinate mapHomogeneous(const QTransform &transform, const QPointF &p) |

1566 | { |

1567 | QHomogeneousCoordinate c; |

1568 | c.x = transform.m11() * p.x() + transform.m21() * p.y() + transform.m31(); |

1569 | c.y = transform.m12() * p.x() + transform.m22() * p.y() + transform.m32(); |

1570 | c.w = transform.m13() * p.x() + transform.m23() * p.y() + transform.m33(); |

1571 | return c; |

1572 | } |

1573 | |

1574 | static inline bool lineTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, |

1575 | bool needsMoveTo, bool needsLineTo = true) |

1576 | { |

1577 | QHomogeneousCoordinate ha = mapHomogeneous(transform, a); |

1578 | QHomogeneousCoordinate hb = mapHomogeneous(transform, b); |

1579 | |

1580 | if (ha.w < Q_NEAR_CLIP && hb.w < Q_NEAR_CLIP) |

1581 | return false; |

1582 | |

1583 | if (hb.w < Q_NEAR_CLIP) { |

1584 | const qreal t = (Q_NEAR_CLIP - hb.w) / (ha.w - hb.w); |

1585 | |

1586 | hb.x += (ha.x - hb.x) * t; |

1587 | hb.y += (ha.y - hb.y) * t; |

1588 | hb.w = qreal(Q_NEAR_CLIP); |

1589 | } else if (ha.w < Q_NEAR_CLIP) { |

1590 | const qreal t = (Q_NEAR_CLIP - ha.w) / (hb.w - ha.w); |

1591 | |

1592 | ha.x += (hb.x - ha.x) * t; |

1593 | ha.y += (hb.y - ha.y) * t; |

1594 | ha.w = qreal(Q_NEAR_CLIP); |

1595 | |

1596 | const QPointF p = ha.toPoint(); |

1597 | if (needsMoveTo) { |

1598 | path.moveTo(p); |

1599 | needsMoveTo = false; |

1600 | } else { |

1601 | path.lineTo(p); |

1602 | } |

1603 | } |

1604 | |

1605 | if (needsMoveTo) |

1606 | path.moveTo(ha.toPoint()); |

1607 | |

1608 | if (needsLineTo) |

1609 | path.lineTo(hb.toPoint()); |

1610 | |

1611 | return true; |

1612 | } |

1613 | Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); |

1614 | |

1615 | static inline bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo) |

1616 | { |

1617 | // Convert projective xformed curves to line |

1618 | // segments so they can be transformed more accurately |

1619 | |

1620 | qreal scale; |

1621 | qt_scaleForTransform(transform, &scale); |

1622 | |

1623 | qreal curveThreshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale); |

1624 | |

1625 | QPolygonF segment = QBezier::fromPoints(a, b, c, d).toPolygon(curveThreshold); |

1626 | |

1627 | for (int i = 0; i < segment.size() - 1; ++i) |

1628 | if (lineTo_clipped(path, transform, segment.at(i), segment.at(i+1), needsMoveTo)) |

1629 | needsMoveTo = false; |

1630 | |

1631 | return !needsMoveTo; |

1632 | } |

1633 | |

1634 | static QPainterPath mapProjective(const QTransform &transform, const QPainterPath &path) |

1635 | { |

1636 | QPainterPath result; |

1637 | |

1638 | QPointF last; |

1639 | QPointF lastMoveTo; |

1640 | bool needsMoveTo = true; |

1641 | for (int i = 0; i < path.elementCount(); ++i) { |

1642 | switch (path.elementAt(i).type) { |

1643 | case QPainterPath::MoveToElement: |

1644 | if (i > 0 && lastMoveTo != last) |

1645 | lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo); |

1646 | |

1647 | lastMoveTo = path.elementAt(i); |

1648 | last = path.elementAt(i); |

1649 | needsMoveTo = true; |

1650 | break; |

1651 | case QPainterPath::LineToElement: |

1652 | if (lineTo_clipped(result, transform, last, path.elementAt(i), needsMoveTo)) |

1653 | needsMoveTo = false; |

1654 | last = path.elementAt(i); |

1655 | break; |

1656 | case QPainterPath::CurveToElement: |

1657 | if (cubicTo_clipped(result, transform, last, path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), needsMoveTo)) |

1658 | needsMoveTo = false; |

1659 | i += 2; |

1660 | last = path.elementAt(i); |

1661 | break; |

1662 | default: |

1663 | Q_ASSERT(false); |

1664 | } |

1665 | } |

1666 | |

1667 | if (path.elementCount() > 0 && lastMoveTo != last) |

1668 | lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo, false); |

1669 | |

1670 | result.setFillRule(path.fillRule()); |

1671 | return result; |

1672 | } |

1673 | |

1674 | /*! |

1675 | \fn QPainterPath operator *(const QPainterPath &path, const QTransform &matrix) |

1676 | \since 4.3 |

1677 | \relates QTransform |

1678 | |

1679 | This is the same as \a{matrix}.map(\a{path}). |

1680 | |

1681 | \sa QTransform::map() |

1682 | */ |

1683 | |

1684 | /*! |

1685 | \overload |

1686 | |

1687 | Creates and returns a QPainterPath object that is a copy of the |

1688 | given \a path, mapped into the coordinate system defined by this |

1689 | matrix. |

1690 | */ |

1691 | QPainterPath QTransform::map(const QPainterPath &path) const |

1692 | { |

1693 | TransformationType t = inline_type(); |

1694 | if (t == TxNone || path.elementCount() == 0) |

1695 | return path; |

1696 | |

1697 | if (t >= TxProject) |

1698 | return mapProjective(*this, path); |

1699 | |

1700 | QPainterPath copy = path; |

1701 | |

1702 | if (t == TxTranslate) { |

1703 | copy.translate(affine._dx, affine._dy); |

1704 | } else { |

1705 | copy.detach(); |

1706 | // Full xform |

1707 | for (int i=0; i<path.elementCount(); ++i) { |

1708 | QPainterPath::Element &e = copy.d_ptr->elements[i]; |

1709 | MAP(e.x, e.y, e.x, e.y); |

1710 | } |

1711 | } |

1712 | |

1713 | return copy; |

1714 | } |

1715 | |

1716 | /*! |

1717 | \fn QPolygon QTransform::mapToPolygon(const QRect &rectangle) const |

1718 | |

1719 | Creates and returns a QPolygon representation of the given \a |

1720 | rectangle, mapped into the coordinate system defined by this |

1721 | matrix. |

1722 | |

1723 | The rectangle's coordinates are transformed using the following |

1724 | formulas: |

1725 | |

1726 | \snippet code/src_gui_painting_qtransform.cpp 1 |

1727 | |

1728 | Polygons and rectangles behave slightly differently when |

1729 | transformed (due to integer rounding), so |

1730 | \c{matrix.map(QPolygon(rectangle))} is not always the same as |

1731 | \c{matrix.mapToPolygon(rectangle)}. |

1732 | |

1733 | \sa mapRect(), {QTransform#Basic Matrix Operations}{Basic Matrix |

1734 | Operations} |

1735 | */ |

1736 | QPolygon QTransform::mapToPolygon(const QRect &rect) const |

1737 | { |

1738 | TransformationType t = inline_type(); |

1739 | |

1740 | QPolygon a(4); |

1741 | qreal x[4] = { 0, 0, 0, 0 }, y[4] = { 0, 0, 0, 0 }; |

1742 | if (t <= TxScale) { |

1743 | x[0] = affine._m11*rect.x() + affine._dx; |

1744 | y[0] = affine._m22*rect.y() + affine._dy; |

1745 | qreal w = affine._m11*rect.width(); |

1746 | qreal h = affine._m22*rect.height(); |

1747 | if (w < 0) { |

1748 | w = -w; |

1749 | x[0] -= w; |

1750 | } |

1751 | if (h < 0) { |

1752 | h = -h; |

1753 | y[0] -= h; |

1754 | } |

1755 | x[1] = x[0]+w; |

1756 | x[2] = x[1]; |

1757 | x[3] = x[0]; |

1758 | y[1] = y[0]; |

1759 | y[2] = y[0]+h; |

1760 | y[3] = y[2]; |

1761 | } else { |

1762 | qreal right = rect.x() + rect.width(); |

1763 | qreal bottom = rect.y() + rect.height(); |

1764 | MAP(rect.x(), rect.y(), x[0], y[0]); |

1765 | MAP(right, rect.y(), x[1], y[1]); |

1766 | MAP(right, bottom, x[2], y[2]); |

1767 | MAP(rect.x(), bottom, x[3], y[3]); |

1768 | } |

1769 | |

1770 | // all coordinates are correctly, tranform to a pointarray |

1771 | // (rounding to the next integer) |

1772 | a.setPoints(4, qRound(x[0]), qRound(y[0]), |

1773 | qRound(x[1]), qRound(y[1]), |

1774 | qRound(x[2]), qRound(y[2]), |

1775 | qRound(x[3]), qRound(y[3])); |

1776 | return a; |

1777 | } |

1778 | |

1779 | /*! |

1780 | Creates a transformation matrix, \a trans, that maps a unit square |

1781 | to a four-sided polygon, \a quad. Returns \c true if the transformation |

1782 | is constructed or false if such a transformation does not exist. |

1783 | |

1784 | \sa quadToSquare(), quadToQuad() |

1785 | */ |

1786 | bool QTransform::squareToQuad(const QPolygonF &quad, QTransform &trans) |

1787 | { |

1788 | if (quad.count() != 4) |

1789 | return false; |

1790 | |

1791 | qreal dx0 = quad[0].x(); |

1792 | qreal dx1 = quad[1].x(); |

1793 | qreal dx2 = quad[2].x(); |

1794 | qreal dx3 = quad[3].x(); |

1795 | |

1796 | qreal dy0 = quad[0].y(); |

1797 | qreal dy1 = quad[1].y(); |

1798 | qreal dy2 = quad[2].y(); |

1799 | qreal dy3 = quad[3].y(); |

1800 | |

1801 | double ax = dx0 - dx1 + dx2 - dx3; |

1802 | double ay = dy0 - dy1 + dy2 - dy3; |

1803 | |

1804 | if (!ax && !ay) { //afine transform |

1805 | trans.setMatrix(dx1 - dx0, dy1 - dy0, 0, |

1806 | dx2 - dx1, dy2 - dy1, 0, |

1807 | dx0, dy0, 1); |

1808 | } else { |

1809 | double ax1 = dx1 - dx2; |

1810 | double ax2 = dx3 - dx2; |

1811 | double ay1 = dy1 - dy2; |

1812 | double ay2 = dy3 - dy2; |

1813 | |

1814 | /*determinants */ |

1815 | double gtop = ax * ay2 - ax2 * ay; |

1816 | double htop = ax1 * ay - ax * ay1; |

1817 | double bottom = ax1 * ay2 - ax2 * ay1; |

1818 | |

1819 | double a, b, c, d, e, f, g, h; /*i is always 1*/ |

1820 | |

1821 | if (!bottom) |

1822 | return false; |

1823 | |

1824 | g = gtop/bottom; |

1825 | h = htop/bottom; |

1826 | |

1827 | a = dx1 - dx0 + g * dx1; |

1828 | b = dx3 - dx0 + h * dx3; |

1829 | c = dx0; |

1830 | d = dy1 - dy0 + g * dy1; |

1831 | e = dy3 - dy0 + h * dy3; |

1832 | f = dy0; |

1833 | |

1834 | trans.setMatrix(a, d, g, |

1835 | b, e, h, |

1836 | c, f, 1.0); |

1837 | } |

1838 | |

1839 | return true; |

1840 | } |

1841 | |

1842 | /*! |

1843 | \fn bool QTransform::quadToSquare(const QPolygonF &quad, QTransform &trans) |

1844 | |

1845 | Creates a transformation matrix, \a trans, that maps a four-sided polygon, |

1846 | \a quad, to a unit square. Returns \c true if the transformation is constructed |

1847 | or false if such a transformation does not exist. |

1848 | |

1849 | \sa squareToQuad(), quadToQuad() |

1850 | */ |

1851 | bool QTransform::quadToSquare(const QPolygonF &quad, QTransform &trans) |

1852 | { |

1853 | if (!squareToQuad(quad, trans)) |

1854 | return false; |

1855 | |

1856 | bool invertible = false; |

1857 | trans = trans.inverted(&invertible); |

1858 | |

1859 | return invertible; |

1860 | } |

1861 | |

1862 | /*! |

1863 | Creates a transformation matrix, \a trans, that maps a four-sided |

1864 | polygon, \a one, to another four-sided polygon, \a two. |

1865 | Returns \c true if the transformation is possible; otherwise returns |

1866 | false. |

1867 | |

1868 | This is a convenience method combining quadToSquare() and |

1869 | squareToQuad() methods. It allows the input quad to be |

1870 | transformed into any other quad. |

1871 | |

1872 | \sa squareToQuad(), quadToSquare() |

1873 | */ |

1874 | bool QTransform::quadToQuad(const QPolygonF &one, |

1875 | const QPolygonF &two, |

1876 | QTransform &trans) |

1877 | { |

1878 | QTransform stq; |

1879 | if (!quadToSquare(one, trans)) |

1880 | return false; |

1881 | if (!squareToQuad(two, stq)) |

1882 | return false; |

1883 | trans *= stq; |

1884 | //qDebug()<<"Final = "<<trans; |

1885 | return true; |

1886 | } |

1887 | |

1888 | /*! |

1889 | Sets the matrix elements to the specified values, \a m11, |

1890 | \a m12, \a m13 \a m21, \a m22, \a m23 \a m31, \a m32 and |

1891 | \a m33. Note that this function replaces the previous values. |

1892 | QTransform provides the translate(), rotate(), scale() and shear() |

1893 | convenience functions to manipulate the various matrix elements |

1894 | based on the currently defined coordinate system. |

1895 | |

1896 | \sa QTransform() |

1897 | */ |

1898 | |

1899 | void QTransform::setMatrix(qreal m11, qreal m12, qreal m13, |

1900 | qreal m21, qreal m22, qreal m23, |

1901 | qreal m31, qreal m32, qreal m33) |

1902 | { |

1903 | affine._m11 = m11; affine._m12 = m12; m_13 = m13; |

1904 | affine._m21 = m21; affine._m22 = m22; m_23 = m23; |

1905 | affine._dx = m31; affine._dy = m32; m_33 = m33; |

1906 | m_type = TxNone; |

1907 | m_dirty = TxProject; |

1908 | } |

1909 | |

1910 | static inline bool needsPerspectiveClipping(const QRectF &rect, const QTransform &transform) |

1911 | { |

1912 | const qreal wx = qMin(transform.m13() * rect.left(), transform.m13() * rect.right()); |

1913 | const qreal wy = qMin(transform.m23() * rect.top(), transform.m23() * rect.bottom()); |

1914 | |

1915 | return wx + wy + transform.m33() < Q_NEAR_CLIP; |

1916 | } |

1917 | |

1918 | QRect QTransform::mapRect(const QRect &rect) const |

1919 | { |

1920 | TransformationType t = inline_type(); |

1921 | if (t <= TxTranslate) |

1922 | return rect.translated(qRound(affine._dx), qRound(affine._dy)); |

1923 | |

1924 | if (t <= TxScale) { |

1925 | int x = qRound(affine._m11*rect.x() + affine._dx); |

1926 | int y = qRound(affine._m22*rect.y() + affine._dy); |

1927 | int w = qRound(affine._m11*rect.width()); |

1928 | int h = qRound(affine._m22*rect.height()); |

1929 | if (w < 0) { |

1930 | w = -w; |

1931 | x -= w; |

1932 | } |

1933 | if (h < 0) { |

1934 | h = -h; |

1935 | y -= h; |

1936 | } |

1937 | return QRect(x, y, w, h); |

1938 | } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) { |

1939 | // see mapToPolygon for explanations of the algorithm. |

1940 | qreal x = 0, y = 0; |

1941 | MAP(rect.left(), rect.top(), x, y); |

1942 | qreal xmin = x; |

1943 | qreal ymin = y; |

1944 | qreal xmax = x; |

1945 | qreal ymax = y; |

1946 | MAP(rect.right() + 1, rect.top(), x, y); |

1947 | xmin = qMin(xmin, x); |

1948 | ymin = qMin(ymin, y); |

1949 | xmax = qMax(xmax, x); |

1950 | ymax = qMax(ymax, y); |

1951 | MAP(rect.right() + 1, rect.bottom() + 1, x, y); |

1952 | xmin = qMin(xmin, x); |

1953 | ymin = qMin(ymin, y); |

1954 | xmax = qMax(xmax, x); |

1955 | ymax = qMax(ymax, y); |

1956 | MAP(rect.left(), rect.bottom() + 1, x, y); |

1957 | xmin = qMin(xmin, x); |

1958 | ymin = qMin(ymin, y); |

1959 | xmax = qMax(xmax, x); |

1960 | ymax = qMax(ymax, y); |

1961 | return QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin)); |

1962 | } else { |

1963 | QPainterPath path; |

1964 | path.addRect(rect); |

1965 | return map(path).boundingRect().toRect(); |

1966 | } |

1967 | } |

1968 | |

1969 | /*! |

1970 | \fn QRectF QTransform::mapRect(const QRectF &rectangle) const |

1971 | |

1972 | Creates and returns a QRectF object that is a copy of the given \a |

1973 | rectangle, mapped into the coordinate system defined by this |

1974 | matrix. |

1975 | |

1976 | The rectangle's coordinates are transformed using the following |

1977 | formulas: |

1978 | |

1979 | \snippet code/src_gui_painting_qtransform.cpp 2 |

1980 | |

1981 | If rotation or shearing has been specified, this function returns |

1982 | the \e bounding rectangle. To retrieve the exact region the given |

1983 | \a rectangle maps to, use the mapToPolygon() function instead. |

1984 | |

1985 | \sa mapToPolygon(), {QTransform#Basic Matrix Operations}{Basic Matrix |

1986 | Operations} |

1987 | */ |

1988 | QRectF QTransform::mapRect(const QRectF &rect) const |

1989 | { |

1990 | TransformationType t = inline_type(); |

1991 | if (t <= TxTranslate) |

1992 | return rect.translated(affine._dx, affine._dy); |

1993 | |

1994 | if (t <= TxScale) { |

1995 | qreal x = affine._m11*rect.x() + affine._dx; |

1996 | qreal y = affine._m22*rect.y() + affine._dy; |

1997 | qreal w = affine._m11*rect.width(); |

1998 | qreal h = affine._m22*rect.height(); |

1999 | if (w < 0) { |

2000 | w = -w; |

2001 | x -= w; |

2002 | } |

2003 | if (h < 0) { |

2004 | h = -h; |

2005 | y -= h; |

2006 | } |

2007 | return QRectF(x, y, w, h); |

2008 | } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) { |

2009 | qreal x = 0, y = 0; |

2010 | MAP(rect.x(), rect.y(), x, y); |

2011 | qreal xmin = x; |

2012 | qreal ymin = y; |

2013 | qreal xmax = x; |

2014 | qreal ymax = y; |

2015 | MAP(rect.x() + rect.width(), rect.y(), x, y); |

2016 | xmin = qMin(xmin, x); |

2017 | ymin = qMin(ymin, y); |

2018 | xmax = qMax(xmax, x); |

2019 | ymax = qMax(ymax, y); |

2020 | MAP(rect.x() + rect.width(), rect.y() + rect.height(), x, y); |

2021 | xmin = qMin(xmin, x); |

2022 | ymin = qMin(ymin, y); |

2023 | xmax = qMax(xmax, x); |

2024 | ymax = qMax(ymax, y); |

2025 | MAP(rect.x(), rect.y() + rect.height(), x, y); |

2026 | xmin = qMin(xmin, x); |

2027 | ymin = qMin(ymin, y); |

2028 | xmax = qMax(xmax, x); |

2029 | ymax = qMax(ymax, y); |

2030 | return QRectF(xmin, ymin, xmax-xmin, ymax - ymin); |

2031 | } else { |

2032 | QPainterPath path; |

2033 | path.addRect(rect); |

2034 | return map(path).boundingRect(); |

2035 | } |

2036 | } |

2037 | |

2038 | /*! |

2039 | \fn QRect QTransform::mapRect(const QRect &rectangle) const |

2040 | \overload |

2041 | |

2042 | Creates and returns a QRect object that is a copy of the given \a |

2043 | rectangle, mapped into the coordinate system defined by this |

2044 | matrix. Note that the transformed coordinates are rounded to the |

2045 | nearest integer. |

2046 | */ |

2047 | |

2048 | /*! |

2049 | Maps the given coordinates \a x and \a y into the coordinate |

2050 | system defined by this matrix. The resulting values are put in *\a |

2051 | tx and *\a ty, respectively. |

2052 | |

2053 | The coordinates are transformed using the following formulas: |

2054 | |

2055 | \snippet code/src_gui_painting_qtransform.cpp 3 |

2056 | |

2057 | The point (x, y) is the original point, and (x', y') is the |

2058 | transformed point. |

2059 | |

2060 | \sa {QTransform#Basic Matrix Operations}{Basic Matrix Operations} |

2061 | */ |

2062 | void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const |

2063 | { |

2064 | TransformationType t = inline_type(); |

2065 | MAP(x, y, *tx, *ty); |

2066 | } |

2067 | |

2068 | /*! |

2069 | \overload |

2070 | |

2071 | Maps the given coordinates \a x and \a y into the coordinate |

2072 | system defined by this matrix. The resulting values are put in *\a |

2073 | tx and *\a ty, respectively. Note that the transformed coordinates |

2074 | are rounded to the nearest integer. |

2075 | */ |

2076 | void QTransform::map(int x, int y, int *tx, int *ty) const |

2077 | { |

2078 | TransformationType t = inline_type(); |

2079 | qreal fx = 0, fy = 0; |

2080 | MAP(x, y, fx, fy); |

2081 | *tx = qRound(fx); |

2082 | *ty = qRound(fy); |

2083 | } |

2084 | |

2085 | /*! |

2086 | Returns the QTransform as an affine matrix. |

2087 | |

2088 | \warning If a perspective transformation has been specified, |

2089 | then the conversion will cause loss of data. |

2090 | */ |

2091 | const QMatrix &QTransform::toAffine() const |

2092 | { |

2093 | return affine; |

2094 | } |

2095 | |

2096 | /*! |

2097 | Returns the transformation type of this matrix. |

2098 | |

2099 | The transformation type is the highest enumeration value |

2100 | capturing all of the matrix's transformations. For example, |

2101 | if the matrix both scales and shears, the type would be \c TxShear, |

2102 | because \c TxShear has a higher enumeration value than \c TxScale. |

2103 | |

2104 | Knowing the transformation type of a matrix is useful for optimization: |

2105 | you can often handle specific types more optimally than handling |

2106 | the generic case. |

2107 | */ |

2108 | QTransform::TransformationType QTransform::type() const |

2109 | { |

2110 | if(m_dirty == TxNone || m_dirty < m_type) |

2111 | return static_cast<TransformationType>(m_type); |

2112 | |

2113 | switch (static_cast<TransformationType>(m_dirty)) { |

2114 | case TxProject: |

2115 | if (!qFuzzyIsNull(m_13) || !qFuzzyIsNull(m_23) || !qFuzzyIsNull(m_33 - 1)) { |

2116 | m_type = TxProject; |

2117 | break; |

2118 | } |

2119 | Q_FALLTHROUGH(); |

2120 | case TxShear: |

2121 | case TxRotate: |

2122 | if (!qFuzzyIsNull(affine._m12) || !qFuzzyIsNull(affine._m21)) { |

2123 | const qreal dot = affine._m11 * affine._m12 + affine._m21 * affine._m22; |

2124 | if (qFuzzyIsNull(dot)) |

2125 | m_type = TxRotate; |

2126 | else |

2127 | m_type = TxShear; |

2128 | break; |

2129 | } |

2130 | Q_FALLTHROUGH(); |

2131 | case TxScale: |

2132 | if (!qFuzzyIsNull(affine._m11 - 1) || !qFuzzyIsNull(affine._m22 - 1)) { |

2133 | m_type = TxScale; |

2134 | break; |

2135 | } |

2136 | Q_FALLTHROUGH(); |

2137 | case TxTranslate: |

2138 | if (!qFuzzyIsNull(affine._dx) || !qFuzzyIsNull(affine._dy)) { |

2139 | m_type = TxTranslate; |

2140 | break; |

2141 | } |

2142 | Q_FALLTHROUGH(); |

2143 | case TxNone: |

2144 | m_type = TxNone; |

2145 | break; |

2146 | } |

2147 | |

2148 | m_dirty = TxNone; |

2149 | return static_cast<TransformationType>(m_type); |

2150 | } |

2151 | |

2152 | /*! |

2153 | |

2154 | Returns the transform as a QVariant. |

2155 | */ |

2156 | QTransform::operator QVariant() const |

2157 | { |

2158 | return QVariant(QVariant::Transform, this); |

2159 | } |

2160 | |

2161 | |

2162 | /*! |

2163 | \fn bool QTransform::isInvertible() const |

2164 | |

2165 | Returns \c true if the matrix is invertible, otherwise returns \c false. |

2166 | |

2167 | \sa inverted() |

2168 | */ |

2169 | |

2170 | #if QT_DEPRECATED_SINCE(5, 13) |

2171 | /*! |

2172 | \fn qreal QTransform::det() const |

2173 | \obsolete |

2174 | |

2175 | Returns the matrix's determinant. Use determinant() instead. |

2176 | */ |

2177 | #endif |

2178 | |

2179 | /*! |

2180 | \fn qreal QTransform::m11() const |

2181 | |

2182 | Returns the horizontal scaling factor. |

2183 | |

2184 | \sa scale(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2185 | Operations} |

2186 | */ |

2187 | |

2188 | /*! |

2189 | \fn qreal QTransform::m12() const |

2190 | |

2191 | Returns the vertical shearing factor. |

2192 | |

2193 | \sa shear(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2194 | Operations} |

2195 | */ |

2196 | |

2197 | /*! |

2198 | \fn qreal QTransform::m21() const |

2199 | |

2200 | Returns the horizontal shearing factor. |

2201 | |

2202 | \sa shear(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2203 | Operations} |

2204 | */ |

2205 | |

2206 | /*! |

2207 | \fn qreal QTransform::m22() const |

2208 | |

2209 | Returns the vertical scaling factor. |

2210 | |

2211 | \sa scale(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2212 | Operations} |

2213 | */ |

2214 | |

2215 | /*! |

2216 | \fn qreal QTransform::dx() const |

2217 | |

2218 | Returns the horizontal translation factor. |

2219 | |

2220 | \sa m31(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2221 | Operations} |

2222 | */ |

2223 | |

2224 | /*! |

2225 | \fn qreal QTransform::dy() const |

2226 | |

2227 | Returns the vertical translation factor. |

2228 | |

2229 | \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2230 | Operations} |

2231 | */ |

2232 | |

2233 | |

2234 | /*! |

2235 | \fn qreal QTransform::m13() const |

2236 | |

2237 | Returns the horizontal projection factor. |

2238 | |

2239 | \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2240 | Operations} |

2241 | */ |

2242 | |

2243 | |

2244 | /*! |

2245 | \fn qreal QTransform::m23() const |

2246 | |

2247 | Returns the vertical projection factor. |

2248 | |

2249 | \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2250 | Operations} |

2251 | */ |

2252 | |

2253 | /*! |

2254 | \fn qreal QTransform::m31() const |

2255 | |

2256 | Returns the horizontal translation factor. |

2257 | |

2258 | \sa dx(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2259 | Operations} |

2260 | */ |

2261 | |

2262 | /*! |

2263 | \fn qreal QTransform::m32() const |

2264 | |

2265 | Returns the vertical translation factor. |

2266 | |

2267 | \sa dy(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2268 | Operations} |

2269 | */ |

2270 | |

2271 | /*! |

2272 | \fn qreal QTransform::m33() const |

2273 | |

2274 | Returns the division factor. |

2275 | |

2276 | \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix |

2277 | Operations} |

2278 | */ |

2279 | |

2280 | /*! |

2281 | \fn qreal QTransform::determinant() const |

2282 | |

2283 | Returns the matrix's determinant. |

2284 | */ |

2285 | |

2286 | /*! |

2287 | \fn bool QTransform::isIdentity() const |

2288 | |

2289 | Returns \c true if the matrix is the identity matrix, otherwise |

2290 | returns \c false. |

2291 | |

2292 | \sa reset() |

2293 | */ |

2294 | |

2295 | /*! |

2296 | \fn bool QTransform::isAffine() const |

2297 | |

2298 | Returns \c true if the matrix represent an affine transformation, |

2299 | otherwise returns \c false. |

2300 | */ |

2301 | |

2302 | /*! |

2303 | \fn bool QTransform::isScaling() const |

2304 | |

2305 | Returns \c true if the matrix represents a scaling |

2306 | transformation, otherwise returns \c false. |

2307 | |

2308 | \sa reset() |

2309 | */ |

2310 | |

2311 | /*! |

2312 | \fn bool QTransform::isRotating() const |

2313 | |

2314 | Returns \c true if the matrix represents some kind of a |

2315 | rotating transformation, otherwise returns \c false. |

2316 | |

2317 | \note A rotation transformation of 180 degrees and/or 360 degrees is treated as a scaling transformation. |

2318 | |

2319 | \sa reset() |

2320 | */ |

2321 | |

2322 | /*! |

2323 | \fn bool QTransform::isTranslating() const |

2324 | |

2325 | Returns \c true if the matrix represents a translating |

2326 | transformation, otherwise returns \c false. |

2327 | |

2328 | \sa reset() |

2329 | */ |

2330 | |

2331 | /*! |

2332 | \fn bool qFuzzyCompare(const QTransform& t1, const QTransform& t2) |

2333 | |

2334 | \relates QTransform |

2335 | \since 4.6 |

2336 | |

2337 | Returns \c true if \a t1 and \a t2 are equal, allowing for a small |

2338 | fuzziness factor for floating-point comparisons; false otherwise. |

2339 | */ |

2340 | |

2341 | |

2342 | // returns true if the transform is uniformly scaling |

2343 | // (same scale in x and y direction) |

2344 | // scale is set to the max of x and y scaling factors |

2345 | Q_GUI_EXPORT |

2346 | bool qt_scaleForTransform(const QTransform &transform, qreal *scale) |

2347 | { |

2348 | const QTransform::TransformationType type = transform.type(); |

2349 | if (type <= QTransform::TxTranslate) { |

2350 | if (scale) |

2351 | *scale = 1; |

2352 | return true; |

2353 | } else if (type == QTransform::TxScale) { |

2354 | const qreal xScale = qAbs(transform.m11()); |

2355 | const qreal yScale = qAbs(transform.m22()); |

2356 | if (scale) |

2357 | *scale = qMax(xScale, yScale); |

2358 | return qFuzzyCompare(xScale, yScale); |

2359 | } |

2360 | |

2361 | // rotate then scale: compare columns |

2362 | const qreal xScale1 = transform.m11() * transform.m11() |

2363 | + transform.m21() * transform.m21(); |

2364 | const qreal yScale1 = transform.m12() * transform.m12() |

2365 | + transform.m22() * transform.m22(); |

2366 | |

2367 | // scale then rotate: compare rows |

2368 | const qreal xScale2 = transform.m11() * transform.m11() |

2369 | + transform.m12() * transform.m12(); |

2370 | const qreal yScale2 = transform.m21() * transform.m21() |

2371 | + transform.m22() * transform.m22(); |

2372 | |

2373 | // decide the order of rotate and scale operations |

2374 | if (qAbs(xScale1 - yScale1) > qAbs(xScale2 - yScale2)) { |

2375 | if (scale) |

2376 | *scale = qSqrt(qMax(xScale1, yScale1)); |

2377 | |

2378 | return type == QTransform::TxRotate && qFuzzyCompare(xScale1, yScale1); |

2379 | } else { |

2380 | if (scale) |

2381 | *scale = qSqrt(qMax(xScale2, yScale2)); |

2382 | |

2383 | return type == QTransform::TxRotate && qFuzzyCompare(xScale2, yScale2); |

2384 | } |

2385 | } |

2386 | |

2387 | QT_END_NAMESPACE |

2388 |