util/src/gui/graphicsview/qgraphicstransform.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /*!
       
    43     \class QGraphicsTransform
       
    44     \brief The QGraphicsTransform class is an abstract base class for building
       
    45     advanced transformations on QGraphicsItems.
       
    46     \since 4.6
       
    47     \ingroup graphicsview-api
       
    48 
       
    49     As an alternative to QGraphicsItem::transform, QGraphicsTransform lets you
       
    50     create and control advanced transformations that can be configured
       
    51     independently using specialized properties.
       
    52 
       
    53     QGraphicsItem allows you to assign any number of QGraphicsTransform
       
    54     instances to one QGraphicsItem. Each QGraphicsTransform is applied in
       
    55     order, one at a time, to the QGraphicsItem it's assigned to.
       
    56 
       
    57     QGraphicsTransform is particularily useful for animations. Whereas
       
    58     QGraphicsItem::setTransform() lets you assign any transform directly to an
       
    59     item, there is no direct way to interpolate between two different
       
    60     transformations (e.g., when transitioning between two states, each for
       
    61     which the item has a different arbitrary transform assigned). Using
       
    62     QGraphicsTransform you can interpolate the property values of each
       
    63     independent transformation. The resulting operation is then combined into a
       
    64     single transform which is applied to QGraphicsItem.
       
    65 
       
    66     Transformations are computed in true 3D space using QMatrix4x4.
       
    67     When the transformation is applied to a QGraphicsItem, it will be
       
    68     projected back to a 2D QTransform.  When multiple QGraphicsTransform
       
    69     objects are applied to a QGraphicsItem, all of the transformations
       
    70     are computed in true 3D space, with the projection back to 2D
       
    71     only occurring after the last QGraphicsTransform is applied.
       
    72     The exception to this is QGraphicsRotation, which projects back to
       
    73     2D after each rotation to preserve the perspective effect around
       
    74     the X and Y axes.
       
    75 
       
    76     If you want to create your own configurable transformation, you can create
       
    77     a subclass of QGraphicsTransform (or any or the existing subclasses), and
       
    78     reimplement the pure virtual applyTo() function, which takes a pointer to a
       
    79     QMatrix4x4. Each operation you would like to apply should be exposed as
       
    80     properties (e.g., customTransform->setVerticalShear(2.5)). Inside you
       
    81     reimplementation of applyTo(), you can modify the provided transform
       
    82     respectively.
       
    83 
       
    84     QGraphicsTransform can be used together with QGraphicsItem::setTransform(),
       
    85     QGraphicsItem::setRotation(), and QGraphicsItem::setScale().
       
    86 
       
    87     \sa QGraphicsItem::transform(), QGraphicsScale, QGraphicsRotation
       
    88 */
       
    89 
       
    90 #include "qgraphicstransform.h"
       
    91 #include "qgraphicsitem_p.h"
       
    92 #include "qgraphicstransform_p.h"
       
    93 #include <QDebug>
       
    94 #include <QtCore/qmath.h>
       
    95 
       
    96 #ifndef QT_NO_GRAPHICSVIEW
       
    97 QT_BEGIN_NAMESPACE
       
    98 void QGraphicsTransformPrivate::setItem(QGraphicsItem *i)
       
    99 {
       
   100     if (item == i)
       
   101         return;
       
   102 
       
   103     if (item) {
       
   104         Q_Q(QGraphicsTransform);
       
   105         QGraphicsItemPrivate *d_ptr = item->d_ptr.data();
       
   106 
       
   107         item->prepareGeometryChange();
       
   108         Q_ASSERT(d_ptr->transformData);
       
   109         d_ptr->transformData->graphicsTransforms.removeAll(q);
       
   110         d_ptr->dirtySceneTransform = 1;
       
   111         item = 0;
       
   112     }
       
   113 
       
   114     item = i;
       
   115 }
       
   116 
       
   117 void QGraphicsTransformPrivate::updateItem(QGraphicsItem *item)
       
   118 {
       
   119     item->prepareGeometryChange();
       
   120     item->d_ptr->dirtySceneTransform = 1;
       
   121 }
       
   122 
       
   123 /*!
       
   124     Constructs a new QGraphicsTransform with the given \a parent.
       
   125 */
       
   126 QGraphicsTransform::QGraphicsTransform(QObject *parent)
       
   127     : QObject(*new QGraphicsTransformPrivate, parent)
       
   128 {
       
   129 }
       
   130 
       
   131 /*!
       
   132     Destroys the graphics transform.
       
   133 */
       
   134 QGraphicsTransform::~QGraphicsTransform()
       
   135 {
       
   136     Q_D(QGraphicsTransform);
       
   137     d->setItem(0);
       
   138 }
       
   139 
       
   140 /*!
       
   141     \internal
       
   142 */
       
   143 QGraphicsTransform::QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *parent)
       
   144     : QObject(p, parent)
       
   145 {
       
   146 }
       
   147 
       
   148 /*!
       
   149     \fn void QGraphicsTransform::applyTo(QMatrix4x4 *matrix) const
       
   150 
       
   151     This pure virtual method has to be reimplemented in derived classes.
       
   152 
       
   153     It applies this transformation to \a matrix.
       
   154 
       
   155     \sa QGraphicsItem::transform(), QMatrix4x4::toTransform()
       
   156 */
       
   157 
       
   158 /*!
       
   159     Notifies that this transform operation has changed its parameters in such a
       
   160     way that applyTo() will return a different result than before.
       
   161 
       
   162     When implementing you own custom graphics transform, you must call this
       
   163     function every time you change a parameter, to let QGraphicsItem know that
       
   164     its transformation needs to be updated.
       
   165 
       
   166     \sa applyTo()
       
   167 */
       
   168 void QGraphicsTransform::update()
       
   169 {
       
   170     Q_D(QGraphicsTransform);
       
   171     if (d->item)
       
   172         d->updateItem(d->item);
       
   173 }
       
   174 
       
   175 /*!
       
   176   \class QGraphicsScale
       
   177   \brief The QGraphicsScale class provides a scale transformation.
       
   178   \since 4.6
       
   179 
       
   180   QGraphicsScene provides certain parameters to help control how the scale
       
   181   should be applied.
       
   182 
       
   183   The origin is the point that the item is scaled from (i.e., it stays fixed
       
   184   relative to the parent as the rest of the item grows). By default the
       
   185   origin is QPointF(0, 0).
       
   186 
       
   187   The parameters xScale, yScale, and zScale describe the scale factors to
       
   188   apply in horizontal, vertical, and depth directions. They can take on any
       
   189   value, including 0 (to collapse the item to a point) or negative value.
       
   190   A negative xScale value will mirror the item horizontally. A negative yScale
       
   191   value will flip the item vertically. A negative zScale will flip the
       
   192   item end for end.
       
   193 
       
   194   \sa QGraphicsTransform, QGraphicsItem::setScale(), QTransform::scale()
       
   195 */
       
   196 
       
   197 class QGraphicsScalePrivate : public QGraphicsTransformPrivate
       
   198 {
       
   199 public:
       
   200     QGraphicsScalePrivate()
       
   201         : xScale(1), yScale(1), zScale(1) {}
       
   202     QVector3D origin;
       
   203     qreal xScale;
       
   204     qreal yScale;
       
   205     qreal zScale;
       
   206 };
       
   207 
       
   208 /*!
       
   209     Constructs an empty QGraphicsScale object with the given \a parent.
       
   210 */
       
   211 QGraphicsScale::QGraphicsScale(QObject *parent)
       
   212     : QGraphicsTransform(*new QGraphicsScalePrivate, parent)
       
   213 {
       
   214 }
       
   215 
       
   216 /*!
       
   217     Destroys the graphics scale.
       
   218 */
       
   219 QGraphicsScale::~QGraphicsScale()
       
   220 {
       
   221 }
       
   222 
       
   223 /*!
       
   224     \property QGraphicsScale::origin
       
   225     \brief the origin of the scale in 3D space.
       
   226 
       
   227     All scaling will be done relative to this point (i.e., this point
       
   228     will stay fixed, relative to the parent, when the item is scaled).
       
   229 
       
   230     \sa xScale, yScale, zScale
       
   231 */
       
   232 QVector3D QGraphicsScale::origin() const
       
   233 {
       
   234     Q_D(const QGraphicsScale);
       
   235     return d->origin;
       
   236 }
       
   237 void QGraphicsScale::setOrigin(const QVector3D &point)
       
   238 {
       
   239     Q_D(QGraphicsScale);
       
   240     if (d->origin == point)
       
   241         return;
       
   242     d->origin = point;
       
   243     update();
       
   244     emit originChanged();
       
   245 }
       
   246 
       
   247 /*!
       
   248     \property QGraphicsScale::xScale
       
   249     \brief the horizontal scale factor.
       
   250 
       
   251     The scale factor can be any real number; the default value is 1.0. If you
       
   252     set the factor to 0.0, the item will be collapsed to a single point. If you
       
   253     provide a negative value, the item will be mirrored horizontally around its
       
   254     origin.
       
   255 
       
   256     \sa yScale, zScale, origin
       
   257 */
       
   258 qreal QGraphicsScale::xScale() const
       
   259 {
       
   260     Q_D(const QGraphicsScale);
       
   261     return d->xScale;
       
   262 }
       
   263 void QGraphicsScale::setXScale(qreal scale)
       
   264 {
       
   265     Q_D(QGraphicsScale);
       
   266     if (d->xScale == scale)
       
   267         return;
       
   268     d->xScale = scale;
       
   269     update();
       
   270     emit scaleChanged();
       
   271 }
       
   272 
       
   273 /*!
       
   274     \property QGraphicsScale::yScale
       
   275     \brief the vertical scale factor.
       
   276 
       
   277     The scale factor can be any real number; the default value is 1.0. If you
       
   278     set the factor to 0.0, the item will be collapsed to a single point. If you
       
   279     provide a negative value, the item will be flipped vertically around its
       
   280     origin.
       
   281 
       
   282     \sa xScale, zScale, origin
       
   283 */
       
   284 qreal QGraphicsScale::yScale() const
       
   285 {
       
   286     Q_D(const QGraphicsScale);
       
   287     return d->yScale;
       
   288 }
       
   289 void QGraphicsScale::setYScale(qreal scale)
       
   290 {
       
   291     Q_D(QGraphicsScale);
       
   292     if (d->yScale == scale)
       
   293         return;
       
   294     d->yScale = scale;
       
   295     update();
       
   296     emit scaleChanged();
       
   297 }
       
   298 
       
   299 /*!
       
   300     \property QGraphicsScale::zScale
       
   301     \brief the depth scale factor.
       
   302 
       
   303     The scale factor can be any real number; the default value is 1.0. If you
       
   304     set the factor to 0.0, the item will be collapsed to a single point. If you
       
   305     provide a negative value, the item will be flipped end for end around its
       
   306     origin.
       
   307 
       
   308     \sa xScale, yScale, origin
       
   309 */
       
   310 qreal QGraphicsScale::zScale() const
       
   311 {
       
   312     Q_D(const QGraphicsScale);
       
   313     return d->zScale;
       
   314 }
       
   315 void QGraphicsScale::setZScale(qreal scale)
       
   316 {
       
   317     Q_D(QGraphicsScale);
       
   318     if (d->zScale == scale)
       
   319         return;
       
   320     d->zScale = scale;
       
   321     update();
       
   322     emit scaleChanged();
       
   323 }
       
   324 
       
   325 /*!
       
   326     \reimp
       
   327 */
       
   328 void QGraphicsScale::applyTo(QMatrix4x4 *matrix) const
       
   329 {
       
   330     Q_D(const QGraphicsScale);
       
   331     matrix->translate(d->origin);
       
   332     matrix->scale(d->xScale, d->yScale, d->zScale);
       
   333     matrix->translate(-d->origin);
       
   334 }
       
   335 
       
   336 /*!
       
   337     \fn QGraphicsScale::originChanged()
       
   338 
       
   339     QGraphicsScale emits this signal when its origin changes.
       
   340 
       
   341     \sa QGraphicsScale::origin
       
   342 */
       
   343 
       
   344 /*!
       
   345     \fn QGraphicsScale::scaleChanged()
       
   346 
       
   347     This signal is emitted whenever the xScale, yScale, or zScale
       
   348     of the object changes.
       
   349 
       
   350     \sa QGraphicsScale::xScale, QGraphicsScale::yScale
       
   351     \sa QGraphicsScale::zScale
       
   352 */
       
   353 
       
   354 /*!
       
   355     \class QGraphicsRotation
       
   356     \brief The QGraphicsRotation class provides a rotation transformation around
       
   357     a given axis.
       
   358     \since 4.6
       
   359 
       
   360     You can provide the desired axis by assigning a QVector3D to the axis property
       
   361     or by passing a member if Qt::Axis to the setAxis convenience function.
       
   362     By default the axis is (0, 0, 1) i.e., rotation around the Z axis.
       
   363 
       
   364     The angle property, which is provided by QGraphicsRotation, now
       
   365     describes the number of degrees to rotate around this axis.
       
   366 
       
   367     QGraphicsRotation provides certain parameters to help control how the
       
   368     rotation should be applied.
       
   369 
       
   370     The origin is the point that the item is rotated around (i.e., it stays
       
   371     fixed relative to the parent as the rest of the item is rotated). By
       
   372     default the origin is QPointF(0, 0).
       
   373 
       
   374     The angle property provides the number of degrees to rotate the item
       
   375     clockwise around the origin. This value also be negative, indicating a
       
   376     counter-clockwise rotation. For animation purposes it may also be useful to
       
   377     provide rotation angles exceeding (-360, 360) degrees, for instance to
       
   378     animate how an item rotates several times.
       
   379 
       
   380     Note: the final rotation is the combined effect of a rotation in
       
   381     3D space followed by a projection back to 2D.  If several rotations
       
   382     are performed in succession, they will not behave as expected unless
       
   383     they were all around the Z axis.
       
   384 
       
   385     \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate()
       
   386 */
       
   387 
       
   388 class QGraphicsRotationPrivate : public QGraphicsTransformPrivate
       
   389 {
       
   390 public:
       
   391     QGraphicsRotationPrivate()
       
   392         : angle(0), axis(0, 0, 1) {}
       
   393     QVector3D origin;
       
   394     qreal angle;
       
   395     QVector3D axis;
       
   396 };
       
   397 
       
   398 /*!
       
   399     Constructs a new QGraphicsRotation with the given \a parent.
       
   400 */
       
   401 QGraphicsRotation::QGraphicsRotation(QObject *parent)
       
   402     : QGraphicsTransform(*new QGraphicsRotationPrivate, parent)
       
   403 {
       
   404 }
       
   405 
       
   406 /*!
       
   407     Destroys the graphics rotation.
       
   408 */
       
   409 QGraphicsRotation::~QGraphicsRotation()
       
   410 {
       
   411 }
       
   412 
       
   413 /*!
       
   414     \property QGraphicsRotation::origin
       
   415     \brief the origin of the rotation in 3D space.
       
   416 
       
   417     All rotations will be done relative to this point (i.e., this point
       
   418     will stay fixed, relative to the parent, when the item is rotated).
       
   419 
       
   420     \sa angle
       
   421 */
       
   422 QVector3D QGraphicsRotation::origin() const
       
   423 {
       
   424     Q_D(const QGraphicsRotation);
       
   425     return d->origin;
       
   426 }
       
   427 void QGraphicsRotation::setOrigin(const QVector3D &point)
       
   428 {
       
   429     Q_D(QGraphicsRotation);
       
   430     if (d->origin == point)
       
   431         return;
       
   432     d->origin = point;
       
   433     update();
       
   434     emit originChanged();
       
   435 }
       
   436 
       
   437 /*!
       
   438     \property QGraphicsRotation::angle
       
   439     \brief the angle for clockwise rotation, in degrees.
       
   440 
       
   441     The angle can be any real number; the default value is 0.0. A value of 180
       
   442     will rotate 180 degrees, clockwise. If you provide a negative number, the
       
   443     item will be rotated counter-clockwise. Normally the rotation angle will be
       
   444     in the range (-360, 360), but you can also provide numbers outside of this
       
   445     range (e.g., a angle of 370 degrees gives the same result as 10 degrees).
       
   446 
       
   447     \sa origin
       
   448 */
       
   449 qreal QGraphicsRotation::angle() const
       
   450 {
       
   451     Q_D(const QGraphicsRotation);
       
   452     return d->angle;
       
   453 }
       
   454 void QGraphicsRotation::setAngle(qreal angle)
       
   455 {
       
   456     Q_D(QGraphicsRotation);
       
   457     if (d->angle == angle)
       
   458         return;
       
   459     d->angle = angle;
       
   460     update();
       
   461     emit angleChanged();
       
   462 }
       
   463 
       
   464 /*!
       
   465     \fn QGraphicsRotation::originChanged()
       
   466 
       
   467     This signal is emitted whenever the origin has changed.
       
   468 
       
   469     \sa QGraphicsRotation::origin
       
   470 */
       
   471 
       
   472 /*!
       
   473     \fn void QGraphicsRotation::angleChanged()
       
   474 
       
   475     This signal is emitted whenever the angle has changed.
       
   476 
       
   477     \sa QGraphicsRotation::angle
       
   478 */
       
   479 
       
   480 /*!
       
   481     \property QGraphicsRotation::axis
       
   482     \brief a rotation axis, specified by a vector in 3D space.
       
   483 
       
   484     This can be any axis in 3D space. By default the axis is (0, 0, 1),
       
   485     which is aligned with the Z axis. If you provide another axis,
       
   486     QGraphicsRotation will provide a transformation that rotates
       
   487     around this axis. For example, if you would like to rotate an item
       
   488     around its X axis, you could pass (1, 0, 0) as the axis.
       
   489 
       
   490     \sa QTransform, QGraphicsRotation::angle
       
   491 */
       
   492 QVector3D QGraphicsRotation::axis() const
       
   493 {
       
   494     Q_D(const QGraphicsRotation);
       
   495     return d->axis;
       
   496 }
       
   497 void QGraphicsRotation::setAxis(const QVector3D &axis)
       
   498 {
       
   499     Q_D(QGraphicsRotation);
       
   500     if (d->axis == axis)
       
   501          return;
       
   502     d->axis = axis;
       
   503     update();
       
   504     emit axisChanged();
       
   505 }
       
   506 
       
   507 /*!
       
   508     \fn void QGraphicsRotation::setAxis(Qt::Axis axis)
       
   509 
       
   510     Convenience function to set the axis to \a axis.
       
   511 
       
   512     Note: the Qt::YAxis rotation for QTransform is inverted from the
       
   513     correct mathematical rotation in 3D space.  The QGraphicsRotation
       
   514     class implements a correct mathematical rotation.  The following
       
   515     two sequences of code will perform the same transformation:
       
   516 
       
   517     \code
       
   518     QTransform t;
       
   519     t.rotate(45, Qt::YAxis);
       
   520 
       
   521     QGraphicsRotation r;
       
   522     r.setAxis(Qt::YAxis);
       
   523     r.setAngle(-45);
       
   524     \endcode
       
   525 */
       
   526 void QGraphicsRotation::setAxis(Qt::Axis axis)
       
   527 {
       
   528     switch (axis)
       
   529     {
       
   530     case Qt::XAxis:
       
   531         setAxis(QVector3D(1, 0, 0));
       
   532         break;
       
   533     case Qt::YAxis:
       
   534         setAxis(QVector3D(0, 1, 0));
       
   535         break;
       
   536     case Qt::ZAxis:
       
   537         setAxis(QVector3D(0, 0, 1));
       
   538         break;
       
   539     }
       
   540 }
       
   541 
       
   542 /*!
       
   543     \reimp
       
   544 */
       
   545 void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const
       
   546 {
       
   547     Q_D(const QGraphicsRotation);
       
   548 
       
   549     if (d->angle == 0. || d->axis.isNull())
       
   550         return;
       
   551 
       
   552     matrix->translate(d->origin);
       
   553     matrix->projectedRotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z());
       
   554     matrix->translate(-d->origin);
       
   555 }
       
   556 
       
   557 /*!
       
   558     \fn void QGraphicsRotation::axisChanged()
       
   559 
       
   560     This signal is emitted whenever the axis of the object changes.
       
   561 
       
   562     \sa QGraphicsRotation::axis
       
   563 */
       
   564 
       
   565 #include "moc_qgraphicstransform.cpp"
       
   566 
       
   567 QT_END_NAMESPACE
       
   568 #endif //QT_NO_GRAPHICSVIEW