From e780030ca6990e73444646e6deae48145ca49972 Mon Sep 17 00:00:00 2001 From: Thierry Bastian Date: Wed, 12 Aug 2009 12:40:30 +0200 Subject: QGraphicsRotation and QGraphicsRotation3D are now merged into 1 class You can now also set the axis following hte Qt::Axis enum Note: I'm not 100% sure about the maths in QGraphicsRotation::applyTo Feel free to fix it. Reviewed-by: ogoffart --- demos/sub-attaq/submarine.cpp | 2 +- demos/sub-attaq/submarine.h | 4 +- demos/sub-attaq/submarine_p.h | 5 +- src/gui/graphicsview/qgraphicstransform.cpp | 182 ++++++++++----------- src/gui/graphicsview/qgraphicstransform.h | 31 +--- .../qgraphicstransform/tst_qgraphicstransform.cpp | 59 ++++--- 6 files changed, 138 insertions(+), 145 deletions(-) diff --git a/demos/sub-attaq/submarine.cpp b/demos/sub-attaq/submarine.cpp index 857b009..383005d 100644 --- a/demos/sub-attaq/submarine.cpp +++ b/demos/sub-attaq/submarine.cpp @@ -111,7 +111,7 @@ SubMarine::SubMarine(int type, const QString &name, int points, QGraphicsItem * resize(pixmapItem->boundingRect().width(),pixmapItem->boundingRect().height()); setTransformOriginPoint(boundingRect().center()); - graphicsRotation = new QGraphicsRotation3D(this); + graphicsRotation = new QGraphicsRotation(this); graphicsRotation->setAxis(QVector3D(0, 1, 0)); graphicsRotation->setOrigin(QPointF(size().width()/2, size().height()/2)); QList r; diff --git a/demos/sub-attaq/submarine.h b/demos/sub-attaq/submarine.h index 0e4d074..1e33ba0 100644 --- a/demos/sub-attaq/submarine.h +++ b/demos/sub-attaq/submarine.h @@ -76,7 +76,7 @@ public: virtual int type() const; - QGraphicsRotation3D *rotation3d() const { return graphicsRotation; } + QGraphicsRotation *rotation() const { return graphicsRotation; } signals: void subMarineDestroyed(); @@ -90,7 +90,7 @@ private: int speed; Movement direction; PixmapItem *pixmapItem; - QGraphicsRotation3D *graphicsRotation; + QGraphicsRotation *graphicsRotation; }; #endif //__SUBMARINE__H__ diff --git a/demos/sub-attaq/submarine_p.h b/demos/sub-attaq/submarine_p.h index 1af820f..520fe2f 100644 --- a/demos/sub-attaq/submarine_p.h +++ b/demos/sub-attaq/submarine_p.h @@ -109,7 +109,8 @@ class ReturnState : public QAnimationState public: ReturnState(SubMarine *submarine, QState *parent = 0) : QAnimationState(parent) { - returnAnimation = new QPropertyAnimation(submarine->rotation3d(), "angle"); + returnAnimation = new QPropertyAnimation(submarine->rotation(), "angle"); + returnAnimation->setDuration(500); AnimationManager::self()->registerAnimation(returnAnimation); setAnimation(returnAnimation); this->submarine = submarine; @@ -119,9 +120,7 @@ protected: void onEntry(QEvent *e) { returnAnimation->stop(); - returnAnimation->setStartValue(submarine->rotation3d()->angle()); returnAnimation->setEndValue(submarine->currentDirection() == SubMarine::Right ? 360. : 180.); - returnAnimation->setDuration(500); QAnimationState::onEntry(e); } diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp index 8f04850..95f137d 100644 --- a/src/gui/graphicsview/qgraphicstransform.cpp +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -73,7 +73,7 @@ QGraphicsTransform can be used together with QGraphicsItem::setTransform(), QGraphicsItem::setRotation(), and QGraphicsItem::setScale(). - \sa QGraphicsItem::transform(), QGraphicsScale, QGraphicsRotation, QGraphicsRotation3D + \sa QGraphicsItem::transform(), QGraphicsScale, QGraphicsRotation */ #include "qgraphicstransform.h" @@ -326,9 +326,17 @@ void QGraphicsScale::applyTo(QTransform *transform) const /*! \class QGraphicsRotation - \brief The QGraphicsRotation class provides a rotation transformation. + \brief The QGraphicsRotation class provides a rotation transformation around + a given axis. \since 4.6 + You can provide the desired axis by assigning a QVector3D to the axis property + or by passing a member if Qt::Axis to the setAxis convenience function. + By default the axis is (0, 0, 1) i.e., rotation around the Z axis. + + The angle property, which is provided by QGraphicsRotation, now + describes the number of degrees to rotate around this axis. + QGraphicsRotation provides certain parameters to help control how the rotation should be applied. @@ -342,16 +350,28 @@ void QGraphicsScale::applyTo(QTransform *transform) const provide rotation angles exceeding (-360, 360) degrees, for instance to animate how an item rotates several times. + Note: the final rotation is the combined effect of a rotation in + 3D space followed by a projection back to 2D. If several rotations + are performed in succession, they will not behave as expected unless + they were all around the Z axis. + \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate() */ +#define VECTOR_FOR_AXIS_X QVector3D(1, 0, 0) +#define VECTOR_FOR_AXIS_Y QVector3D(0, 1, 0) +#define VECTOR_FOR_AXIS_Z QVector3D(0, 0, 1) + + class QGraphicsRotationPrivate : public QGraphicsTransformPrivate { public: QGraphicsRotationPrivate() - : angle(0) {} + : angle(0), axis(VECTOR_FOR_AXIS_Z), simpleAxis(Qt::ZAxis) {} QPointF origin; qreal angle; + QVector3D axis; + int simpleAxis; }; /*! @@ -363,14 +383,6 @@ QGraphicsRotation::QGraphicsRotation(QObject *parent) } /*! - \internal -*/ -QGraphicsRotation::QGraphicsRotation(QGraphicsRotationPrivate &p, QObject *parent) - : QGraphicsTransform(p, parent) -{ -} - -/*! Destroys the graphics rotation. */ QGraphicsRotation::~QGraphicsRotation() @@ -427,19 +439,6 @@ void QGraphicsRotation::setAngle(qreal angle) } /*! - \reimp -*/ -void QGraphicsRotation::applyTo(QTransform *t) const -{ - Q_D(const QGraphicsRotation); - if (d->angle) { - t->translate(d->origin.x(), d->origin.y()); - t->rotate(d->angle); - t->translate(-d->origin.x(), -d->origin.y()); - } -} - -/*! \fn QGraphicsRotation::originChanged() This signal is emitted whenever the origin has changed. @@ -456,93 +455,92 @@ void QGraphicsRotation::applyTo(QTransform *t) const */ /*! - \class QGraphicsRotation3D - \brief The QGraphicsRotation3D class provides rotation in 3 dimensions. - \since 4.6 - - QGraphicsRotation3D extends QGraphicsRotation with the ability to rotate - around a given axis. - - You can provide the desired axis by assigning a QVector3D to the axis - property. The angle property, which is provided by QGraphicsRotation, now - describes the number of degrees to rotate around this axis. - - By default the axis is (0, 0, 1), giving QGraphicsRotation3D the same - default behavior as QGraphicsRotation (i.e., rotation around the Z axis). - - Note: the final rotation is the combined effect of a rotation in - 3D space followed by a projection back to 2D. If several rotations - are performed in succession, they will not behave as expected unless - they were all around the Z axis. - - \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate() -*/ - -class QGraphicsRotation3DPrivate : public QGraphicsRotationPrivate -{ -public: - QGraphicsRotation3DPrivate() : axis(0, 0, 1) {} - - QVector3D axis; -}; - -/*! - Constructs a new QGraphicsRotation3D with the given \a parent. -*/ -QGraphicsRotation3D::QGraphicsRotation3D(QObject *parent) - : QGraphicsRotation(*new QGraphicsRotation3DPrivate, parent) -{ -} - -/*! - Destroys the 3D graphics rotation. -*/ -QGraphicsRotation3D::~QGraphicsRotation3D() -{ -} - -/*! - \property QGraphicsRotation3D::axis + \property QGraphicsRotation::axis \brief a rotation axis, specified by a vector in 3D space. This can be any axis in 3D space. By default the axis is (0, 0, 1), - which is aligned with the Z axis and provides the same behavior - for the rotation angle as QGraphicsRotation. If you provide another - axis, QGraphicsRotation3D will provide a transformation that rotates + which is aligned with the Z axis. If you provide another axis, + QGraphicsRotation will provide a transformation that rotates around this axis. For example, if you would like to rotate an item around its X axis, you could pass (1, 0, 0) as the axis. \sa QTransform, QGraphicsRotation::angle */ -QVector3D QGraphicsRotation3D::axis() +QVector3D QGraphicsRotation::axis() const { - Q_D(QGraphicsRotation3D); + Q_D(const QGraphicsRotation); return d->axis; } -void QGraphicsRotation3D::setAxis(const QVector3D &axis) +void QGraphicsRotation::setAxis(const QVector3D &axis) { - Q_D(QGraphicsRotation3D); - if (d->axis == axis) - return; - d->axis = axis; + Q_D(QGraphicsRotation); + if (d->axis == axis) + return; + d->axis = axis; + if (axis == VECTOR_FOR_AXIS_X) { + d->simpleAxis = Qt::XAxis; + } else if (axis == VECTOR_FOR_AXIS_Y) { + d->simpleAxis = Qt::YAxis; + } else if (axis == VECTOR_FOR_AXIS_Z) { + d->simpleAxis = Qt::ZAxis; + } else { + d->simpleAxis = -1; // no predefined axis + } update(); emit axisChanged(); } +/*! + \fn void QGraphicsRotation::setAxis(Qt::Axis axis) + + Convenience function to set the axis to \a axis. +*/ + +void QGraphicsRotation::setAxis(Qt::Axis axis) +{ + switch (axis) + { + case Qt::XAxis: + setAxis(VECTOR_FOR_AXIS_X); + break; + case Qt::YAxis: + setAxis(VECTOR_FOR_AXIS_Y); + break; + case Qt::ZAxis: + setAxis(VECTOR_FOR_AXIS_Z); + break; + } +} + + const qreal deg2rad = qreal(0.017453292519943295769); // pi/180 static const qreal inv_dist_to_plane = 1. / 1024.; /*! \reimp */ -void QGraphicsRotation3D::applyTo(QTransform *t) const +void QGraphicsRotation::applyTo(QTransform *t) const { - Q_D(const QGraphicsRotation3D); + Q_D(const QGraphicsRotation); qreal a = d->angle; - if (a == 0. || - (d->axis.z() == 0. && d->axis.y() == 0 && d->axis.x() == 0)) + if (a == 0.) + return; + + if (d->simpleAxis != -1) { + //that's an optimization for simple axis + t->translate(d->origin.x(), d->origin.y()); + t->rotate(a, Qt::Axis(d->simpleAxis)); + t->translate(-d->origin.x(), -d->origin.y()); + return; + } + + qreal x = d->axis.x(); + qreal y = d->axis.y(); + qreal z = d->axis.z(); + + if (x == 0. && y == 0 && z == 0) return; qreal c, s; @@ -561,27 +559,23 @@ void QGraphicsRotation3D::applyTo(QTransform *t) const c = qCos(b); } - qreal x = d->axis.x(); - qreal y = d->axis.y(); - qreal z = d->axis.z(); - qreal len = x * x + y * y + z * z; if (len != 1.) { - len = 1./::sqrt(len); + len = 1. / qSqrt(len); x *= len; y *= len; z *= len; } t->translate(d->origin.x(), d->origin.y()); - *t = QTransform(x*x*(1-c)+c, x*y*(1-c)-z*s, x*z*(1-c)+y*s*inv_dist_to_plane, - y*x*(1-c)+z*s, y*y*(1-c)+c, y*z*(1-c)-x*s*inv_dist_to_plane, - 0, 0, 1) * *t; + *t = QTransform(x*x*(1-c)+c, x*y*(1-c)+z*s, x*z*(1-c)-y*s*inv_dist_to_plane, + y*x*(1-c)-z*s, y*y*(1-c)+c, y*z*(1-c)-x*s*inv_dist_to_plane, + 0, 0, 1) * *t; t->translate(-d->origin.x(), -d->origin.y()); } /*! - \fn void QGraphicsRotation3D::axisChanged() + \fn void QGraphicsRotation::axisChanged() This signal is emitted whenever the axis of the object changes. */ diff --git a/src/gui/graphicsview/qgraphicstransform.h b/src/gui/graphicsview/qgraphicstransform.h index 2e0d511..8ccc258 100644 --- a/src/gui/graphicsview/qgraphicstransform.h +++ b/src/gui/graphicsview/qgraphicstransform.h @@ -117,6 +117,7 @@ class Q_GUI_EXPORT QGraphicsRotation : public QGraphicsTransform Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY originChanged) Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged) public: QGraphicsRotation(QObject *parent = 0); ~QGraphicsRotation(); @@ -127,39 +128,19 @@ public: qreal angle() const; void setAngle(qreal); - void applyTo(QTransform *transform) const; - -Q_SIGNALS: - void originChanged(); - void angleChanged(); - -protected: - QGraphicsRotation(QGraphicsRotationPrivate &p, QObject *parent); -private: - Q_DECLARE_PRIVATE(QGraphicsRotation) -}; - -class QGraphicsRotation3DPrivate; - -class Q_GUI_EXPORT QGraphicsRotation3D : public QGraphicsRotation -{ - Q_OBJECT - - Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged) -public: - QGraphicsRotation3D(QObject *parent = 0); - ~QGraphicsRotation3D(); - - QVector3D axis(); + QVector3D axis() const; void setAxis(const QVector3D &axis); + void setAxis(Qt::Axis axis); void applyTo(QTransform *transform) const; Q_SIGNALS: + void originChanged(); + void angleChanged(); void axisChanged(); private: - Q_DECLARE_PRIVATE(QGraphicsRotation3D) + Q_DECLARE_PRIVATE(QGraphicsRotation) }; QT_END_NAMESPACE diff --git a/tests/auto/qgraphicstransform/tst_qgraphicstransform.cpp b/tests/auto/qgraphicstransform/tst_qgraphicstransform.cpp index 932062f..bfd346b 100644 --- a/tests/auto/qgraphicstransform/tst_qgraphicstransform.cpp +++ b/tests/auto/qgraphicstransform/tst_qgraphicstransform.cpp @@ -57,6 +57,7 @@ public slots: private slots: void scale(); void rotation(); + void rotation3d_data(); void rotation3d(); }; @@ -112,6 +113,11 @@ void tst_QGraphicsTransform::scale() void tst_QGraphicsTransform::rotation() { QGraphicsRotation rotation; + QCOMPARE(rotation.axis().x(), (qreal)0); + QCOMPARE(rotation.axis().y(), (qreal)0); + QCOMPARE(rotation.axis().z(), (qreal)1); + QCOMPARE(rotation.angle(), (qreal)0); + rotation.setOrigin(QPointF(10, 10)); QTransform t; @@ -134,46 +140,59 @@ void tst_QGraphicsTransform::rotation() QCOMPARE(rotation.transform().map(QPointF(20, 10)), QPointF(10, 20)); } +Q_DECLARE_METATYPE(Qt::Axis); +void tst_QGraphicsTransform::rotation3d_data() +{ + QTest::addColumn("axis"); + QTest::addColumn("angle"); + + for (int angle = 0; angle <= 360; angle++) { + QTest::newRow("test rotation on X") << Qt::XAxis << qreal(angle); + QTest::newRow("test rotation on Y") << Qt::YAxis << qreal(angle); + QTest::newRow("test rotation on Z") << Qt::ZAxis << qreal(angle); + } +} + void tst_QGraphicsTransform::rotation3d() { - QGraphicsRotation3D rotation; - QCOMPARE(rotation.axis().x(), (qreal)0); - QCOMPARE(rotation.axis().y(), (qreal)0); - QCOMPARE(rotation.axis().z(), (qreal)1); - QCOMPARE(rotation.angle(), (qreal)0); + QFETCH(Qt::Axis, axis); + QFETCH(qreal, angle); + + QGraphicsRotation rotation; + rotation.setAxis(axis); QTransform t; rotation.applyTo(&t); - QCOMPARE(t, QTransform()); - QCOMPARE(rotation.transform(), QTransform()); + QVERIFY(t.isIdentity()); + QVERIFY(rotation.transform().isIdentity()); - rotation.setAngle(180); + rotation.setAngle(angle); - QTransform t180; - t180.rotate(180.0f); + QTransform expected; + expected.rotate(angle, axis); - QCOMPARE(t, QTransform()); - QVERIFY(qFuzzyCompare(rotation.transform(), t180)); + QVERIFY(qFuzzyCompare(rotation.transform(), expected)); + //now let's check that a null vector will not change the transform rotation.setAxis(QVector3D(0, 0, 0)); rotation.setOrigin(QPointF(10, 10)); - t = QTransform(); + t.reset(); rotation.applyTo(&t); - QCOMPARE(t, QTransform()); - QCOMPARE(rotation.transform(), QTransform()); + QVERIFY(t.isIdentity()); + QVERIFY(rotation.transform().isIdentity()); - rotation.setAngle(180); + rotation.setAngle(angle); - QCOMPARE(t, QTransform()); - QCOMPARE(rotation.transform(), QTransform()); + QVERIFY(t.isIdentity()); + QVERIFY(rotation.transform().isIdentity()); rotation.setOrigin(QPointF(0, 0)); - QCOMPARE(t, QTransform()); - QCOMPARE(rotation.transform(), QTransform()); + QVERIFY(t.isIdentity()); + QVERIFY(rotation.transform().isIdentity()); } -- cgit v0.12