diff options
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 512 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.h | 37 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem_p.h | 56 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 10 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsview.cpp | 2 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicswidget.h | 9 | ||||
-rw-r--r-- | tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 199 |
7 files changed, 716 insertions, 109 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 2a062ab..d962554 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -133,12 +133,8 @@ \section1 Transformation QGraphicsItem supports affine transformations in addition to its base - position, pos(). To change the item's transformation, you can either pass - a transformation matrix to setTransform(), or set the different transformation - properties (transformOrigin, x/y/zRotation, x/yScale, horizontal/verticalShear). - Note that setting the transformation matrix conflicts with using the properties. - Setting the properties will overwrite the transformation set with setTransform, - and using setTransform will reset the properties. + position, pos(). To change the item's transformation, you can pass + a transformation matrix to setTransform() Item transformations accumulate from parent to child, so if both a parent and child item are rotated 90 degrees, the child's total transformation will be 180 degrees. @@ -152,19 +148,18 @@ and scenePos(), which returns its position in scene coordinates. To reset an item's matrix, call resetTransform(). + Another way to apply transformation to an item is to use the , or set the + different transformation properties (transformOrigin, x/y/zRotation, x/yScale, + horizontal/verticalShear). Those properties come in addition to the base transformation + The order you set the transformation properties does not affect the resulting transformation The resulting transformation is always computed in the following order \code - [Origin] [RotateX] [RotateY] [RotateZ] [Shear] [Scale] [-Origin] + [Origin] [Base] [RotateX] [RotateY] [RotateZ] [Shear] [Scale] [-Origin] \endcode - So the transformation is equivalent to the following code - - \code - QTransform().translate(xOrigin, yOrigin).rotate(xRotation, Qt::XAxis).rotate(yRotation, Qt::YAxis).rotate(zRotation, Qt::ZAxis) - .shear(horizontalShear, verticalShear).scale(xScale, yScale).translate(-xOrigin, -yOrigin); - \endcode + Where [Base] is the stransformation set by setTransform \section1 Painting @@ -788,8 +783,8 @@ void QGraphicsItemPrivate::combineTransformToParent(QTransform *x, const QTransf if (itemIsUntransformable() && viewTransform) { *x = q_ptr->deviceTransform(*viewTransform); } else { - if (transform) - *x *= *transform; + if (transformData) + *x *= transformData->computedFullTransform(); if (!pos.isNull()) *x *= QTransform::fromTranslate(pos.x(), pos.y()); } @@ -811,8 +806,8 @@ void QGraphicsItemPrivate::combineTransformFromParent(QTransform *x, const QTran *x = q_ptr->deviceTransform(*viewTransform); } else { x->translate(pos.x(), pos.y()); - if (transform) - *x = *transform * *x; + if (transformData) + *x = transformData->computedFullTransform() * *x; } } @@ -975,16 +970,9 @@ void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rec QGraphicsItem *child = children.at(i); QGraphicsItemPrivate *childd = child->d_ptr; bool hasPos = !childd->pos.isNull(); - if (hasPos || childd->transform) { + if (hasPos || childd->transformData) { // COMBINE - QTransform matrix; - if (childd->transform) - matrix = *childd->transform; - if (hasPos) { - const QPointF &p = childd->pos; - matrix *= QTransform::fromTranslate(p.x(), p.y()); - } - matrix *= *x; + QTransform matrix = childd->transformToParent() * *x; *rect |= matrix.mapRect(child->boundingRect()); if (!childd->children.isEmpty()) childd->childrenBoundingRectHelper(&matrix, rect); @@ -1136,8 +1124,7 @@ QGraphicsItem::~QGraphicsItem() if (d_ptr->scene) d_ptr->scene->d_func()->_q_removeItemLater(this); - if (d_ptr->transform) - delete d_ptr->transform; + delete d_ptr->transformData; delete d_ptr; @@ -2747,12 +2734,364 @@ QMatrix QGraphicsItem::matrix() const */ QTransform QGraphicsItem::transform() const { - if (!d_ptr->transform) + if (!d_ptr->transformData) return QTransform(); - return *d_ptr->transform; + return d_ptr->transformData->transform; +} + +/*! + \since 4.6 + + Returns the rotation around the X axis. + + The default is 0 + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa setXRotation(), {Transformations} +*/ +qreal QGraphicsItem::xRotation() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->xRotation; } /*! + \since 4.6 + + Sets the rotation around the X axis to \a angle degrees. + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa xRotation(), setTransformOrigin(), {Transformations} +*/ +void QGraphicsItem::setXRotation(qreal angle) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xRotation = angle; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the rotation around the Y axis. + + The default is 0 + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa setYRotation(), {Transformations} +*/ +qreal QGraphicsItem::yRotation() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->yRotation; +} + +/*! + \since 4.6 + + Sets the rotation around the Y axis to \a angle degrees. + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa yRotation(), setTransformOrigin() {Transformations} +*/ +void QGraphicsItem::setYRotation(qreal angle) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->yRotation = angle; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the rotation around the Z axis. + + The default is 0 + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa setZRotation(), {Transformations} +*/ +qreal QGraphicsItem::zRotation() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->zRotation; +} + +/*! + \since 4.6 + + Sets the rotation around the Z axis to \a angle degrees. + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa zRotation(), setTransformOrigin(), {Transformations} +*/ +void QGraphicsItem::setZRotation(qreal angle) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->zRotation = angle; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + This convenience function set the rotation angles around the 3 axes + to \a x, \a y and \a z. + + \sa setXRotation(), setYRotation(), setZRotation(), {Transformations} +*/ +void QGraphicsItem::setRotation(qreal x, qreal y, qreal z) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xRotation = x; + d_ptr->transformData->yRotation = y; + d_ptr->transformData->zRotation = z; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the scale factor on the X axis. + + The default is 1 + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa setXScale(), {Transformations} +*/ +qreal QGraphicsItem::xScale() const +{ + if (!d_ptr->transformData) + return 1; + return d_ptr->transformData->xScale; +} + +/*! + \since 4.6 + + Sets the scale factor on the X axis to \a factor. + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa xScale(), setTransformOrigin(), {Transformations} +*/ +void QGraphicsItem::setXScale(qreal factor) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xScale = factor; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the scale factor on the Y axis. + + The default is 1 + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa setYScale(), {Transformations} +*/ +qreal QGraphicsItem::yScale() const +{ + if (!d_ptr->transformData) + return 1; + return d_ptr->transformData->yScale; +} + +/*! + \since 4.6 + + Sets the scale factor on the Y axis to \a factor. + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa yScale(), setTransformOrigin(), {Transformations} +*/ +void QGraphicsItem::setYScale(qreal factor) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->yScale = factor; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + This convenience function set the scaling factors on X and Y axis to \a sx and \a sy. + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa setXScale(), setYScale(), {Transformations} +*/ +void QGraphicsItem::setScale(qreal sx, qreal sy) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xScale = sx; + d_ptr->transformData->yScale = sy; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the horizontal shear. + + The default is 0 + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa setHorizontalShear(), {Transformations} +*/ +qreal QGraphicsItem::horizontalShear() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->horizontalShear; +} + +/*! + \since 4.6 + + Sets the horizontal shear to \a shear. + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa horizontalShear(), {Transformations} +*/ +void QGraphicsItem::setHorizontalShear(qreal shear) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->horizontalShear = shear; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the vertical shear. + + The default is 0 + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa setHorizontalShear(), {Transformations} +*/ +qreal QGraphicsItem::verticalShear() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->verticalShear; +} + +/*! + \since 4.6 + + Sets the vertical shear to \a shear. + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa verticalShear(), {Transformations} +*/ +void QGraphicsItem::setVerticalShear(qreal shear) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->verticalShear = shear; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + This convenience function sets the horizontal shear to \a sh and the vertical shear to \a sv. + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa setHorizontalShear(), setVerticalShear() +*/ +void QGraphicsItem::setShear(qreal sh, qreal sv) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->horizontalShear = sh; + d_ptr->transformData->verticalShear = sv; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the origin point using for transformation in item coordinate. + + The default is QPointF(0,0). + + \sa setTransformOrigin(), {Transformations} +*/ +QPointF QGraphicsItem::transformOrigin() const +{ + if (!d_ptr->transformData) + return QPointF(0,0); + return QPointF(d_ptr->transformData->xOrigin, d_ptr->transformData->yOrigin); +} + +/*! + \since 4.6 + + Sets the origin for transformation in item coordinate + + \sa transformOrigin(), {Transformations} +*/ +void QGraphicsItem::setTransformOrigin(const QPointF &origin) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xOrigin = origin.x(); + d_ptr->transformData->yOrigin = origin.y(); + d_ptr->dirtySceneTransform = 1; +} + +/*! + \fn inline void setTransformOrigin(qreal x, qreal y) + + \since 4.6 + \overload + + \sa setTransformOrigin(), {Transformations} +*/ + + +/*! \obsolete Use sceneTransform() instead. @@ -2778,9 +3117,9 @@ QMatrix QGraphicsItem::sceneMatrix() const \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 4 Unlike transform(), which returns only an item's local transformation, this - function includes the item's (and any parents') position. + function includes the item's (and any parents') position, and all the transfomation properties. - \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System} + \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System}, {Transformations} */ QTransform QGraphicsItem::sceneTransform() const { @@ -2848,7 +3187,8 @@ QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) c // COMBINE QTransform matrix; matrix.translate(mappedPoint.x(), mappedPoint.y()); - matrix = untransformedAncestor->transform() * matrix; + if (untransformedAncestor->d_ptr->transformData) + matrix = untransformedAncestor->d_ptr->transformData->computedFullTransform() * matrix; // Then transform and translate all children. for (int i = 0; i < parents.size(); ++i) { @@ -2904,7 +3244,7 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co // This is other's parent if (otherParent == this) { const QPointF &otherPos = other->d_ptr->pos; - if (other->d_ptr->transform) { + if (other->d_ptr->transformData) { QTransform otherToParent; other->d_ptr->combineTransformFromParent(&otherToParent); return otherToParent.inverted(ok); @@ -2919,7 +3259,7 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co // COMBINE const QPointF &itemPos = d_ptr->pos; const QPointF &otherPos = other->d_ptr->pos; - if (!d_ptr->transform && !other->d_ptr->transform) { + if (!d_ptr->transformData && !other->d_ptr->transformData) { QPointF delta = itemPos - otherPos; if (ok) *ok = true; @@ -2987,11 +3327,11 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co */ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) { - if (!d_ptr->transform) - d_ptr->transform = new QTransform; + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; - QTransform newTransform(combine ? QTransform(matrix) * *d_ptr->transform : QTransform(matrix)); - if (*d_ptr->transform == newTransform) + QTransform newTransform(combine ? QTransform(matrix) * d_ptr->transformData->transform : QTransform(matrix)); + if (d_ptr->transformData->transform == newTransform) return; // Notify the item that the transformation matrix is changing. @@ -2999,13 +3339,13 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) if (notify) { newTransform = QTransform(qVariantValue<QMatrix>(itemChange(ItemMatrixChange, qVariantFromValue<QMatrix>(newTransform.toAffine())))); - if (*d_ptr->transform == newTransform) + if (d_ptr->transformData->transform == newTransform) return; } // Update and set the new transformation. prepareGeometryChange(); - *d_ptr->transform = newTransform; + d_ptr->transformData->transform = newTransform; d_ptr->dirtySceneTransform = 1; // Send post-notification. @@ -3028,31 +3368,30 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) to map an item coordiate to a scene coordinate, or mapFromScene() to map from scene coordinates to item coordinates. - \warning using this function conflicts with using the transformation properties. - If you set a transformation, getting the properties will return default values. + \warning using this function doesnt affect the value of the transformation properties - \sa transform(), setRotation(), setScale(), setShear(), setTransformOrigin() {The Graphics View Coordinate System} + \sa transform(), setRotation(), setScale(), setShear(), setTransformOrigin(), {The Graphics View Coordinate System}, {Transformations} */ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) { - if (!d_ptr->transform) - d_ptr->transform = new QTransform; + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; - QTransform newTransform(combine ? matrix * *d_ptr->transform : matrix); - if (*d_ptr->transform == newTransform) + QTransform newTransform(combine ? matrix * d_ptr->transformData->transform : matrix); + if (d_ptr->transformData->transform == newTransform) return; // Notify the item that the transformation matrix is changing. bool notify = itemChangeEnabled(ItemTransformChange); if (notify) { newTransform = qVariantValue<QTransform>(itemChange(ItemTransformChange, qVariantFromValue<QTransform>(newTransform))); - if (*d_ptr->transform == newTransform) + if (d_ptr->transformData->transform == newTransform) return; } // Update and set the new transformation. prepareGeometryChange(); - *d_ptr->transform = newTransform; + d_ptr->transformData->transform = newTransform; d_ptr->dirtySceneTransform = 1; // Send post-notification. @@ -3096,8 +3435,7 @@ void QGraphicsItem::resetTransform() \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 6 - \warning using this function conflicts with using the transformation properties. - Getting those properties after using this function will return default values. + \warning using this functionhas no effect on the zRotation value \sa setTransform(), transform(), scale(), shear(), translate() */ @@ -3118,8 +3456,7 @@ void QGraphicsItem::rotate(qreal angle) \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 7 - \warning using this function conflicts with using the transformation properties. - Getting those properties after using this function will return default values. + \warning using this function has no effect on the xScale or yScale value \sa setTransform(), transform() */ @@ -3134,8 +3471,7 @@ void QGraphicsItem::scale(qreal sx, qreal sy) Shears the current item transformation by (\a sh, \a sv). - \warning using this function conflicts with using the transformation properties. - Getting those properties after using this function will return default values. + \warning using this function has no effect on the horizontalShear or verticalShear value \sa setTransform(), transform() */ @@ -3154,9 +3490,6 @@ void QGraphicsItem::shear(qreal sh, qreal sv) setPos() instead; this function changes the item's translation, which is conceptually separate from its position. - \warning using this function conflicts with using the transformation properties. - Getting those properties after using this function will return default values. - \sa setTransform(), transform() */ void QGraphicsItem::translate(qreal dx, qreal dy) @@ -3330,7 +3663,7 @@ QRectF QGraphicsItem::sceneBoundingRect() const const QGraphicsItemPrivate *itemd; do { itemd = parentItem->d_ptr; - if (itemd->transform) + if (itemd->transformData) break; offset += itemd->pos; } while ((parentItem = itemd->parent)); @@ -4030,18 +4363,10 @@ void QGraphicsItemPrivate::updateCachedClipPathFromSetPosHelper(const QPointF &n // Find closest clip ancestor and transform. Q_Q(QGraphicsItem); // COMBINE - QTransform thisToParentTransform = transform - ? *transform * QTransform::fromTranslate(newPos.x(), newPos.y()) - : QTransform::fromTranslate(newPos.x(), newPos.y()); + QTransform thisToParentTransform = transformToParent(); QGraphicsItem *clipParent = parent; while (clipParent && !(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)) { - // COMBINE - if (clipParent->d_ptr->transform) - thisToParentTransform *= *clipParent->d_ptr->transform; - if (!clipParent->d_ptr->pos.isNull()) { - thisToParentTransform *= QTransform::fromTranslate(clipParent->d_ptr->pos.x(), - clipParent->d_ptr->pos.y()); - } + thisToParentTransform *= clipParent->d_ptr->transformToParent(); clipParent = clipParent->d_ptr->parent; } @@ -4428,9 +4753,9 @@ QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPointF &point QPointF QGraphicsItem::mapToParent(const QPointF &point) const { // COMBINE - if (!d_ptr->transform) + if (!d_ptr->transformData) return point + d_ptr->pos; - return d_ptr->transform->map(point) + d_ptr->pos; + return d_ptr->transformToParent().map(point); } /*! @@ -4496,9 +4821,9 @@ QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QRectF &rect QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const { // COMBINE - if (!d_ptr->transform) + if (!d_ptr->transformData) return rect.translated(d_ptr->pos); - return d_ptr->transform->map(rect).translated(d_ptr->pos); + return d_ptr->transformToParent().map(rect); } /*! @@ -4566,8 +4891,9 @@ QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, const QRectF &rec QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const { // COMBINE - QRectF r = !d_ptr->transform ? rect : d_ptr->transform->mapRect(rect); - return r.translated(d_ptr->pos); + if (!d_ptr->transformData) + return rect.translated(d_ptr->pos); + return d_ptr->transformToParent().mapRect(rect); } /*! @@ -4639,8 +4965,9 @@ QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, const QRectF &r QRectF QGraphicsItem::mapRectFromParent(const QRectF &rect) const { // COMBINE - QRectF r = rect.translated(-d_ptr->pos); - return d_ptr->transform ? d_ptr->transform->inverted().mapRect(r) : r; + if (!d_ptr->transformData) + return rect.translated(-d_ptr->pos); + return d_ptr->transformToParent().inverted().mapRect(rect); } /*! @@ -4700,9 +5027,9 @@ QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPolygonF &p QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const { // COMBINE - if (!d_ptr->transform) + if (!d_ptr->transformData) return polygon.translated(d_ptr->pos); - return d_ptr->transform->map(polygon).translated(d_ptr->pos); + return d_ptr->transformToParent().map(polygon); } /*! @@ -4745,9 +5072,9 @@ QPainterPath QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPainterP QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const { // COMBINE - if (!d_ptr->transform) + if (!d_ptr->transformData) return path.translated(d_ptr->pos); - return d_ptr->transform->map(path).translated(d_ptr->pos); + return d_ptr->transformToParent().map(path); } /*! @@ -4797,8 +5124,8 @@ QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPointF &poi QPointF QGraphicsItem::mapFromParent(const QPointF &point) const { // COMBINE - if (d_ptr->transform) - return d_ptr->transform->inverted().map(point - d_ptr->pos); + if (d_ptr->transformData) + return d_ptr->transformToParent().inverted().map(point); return point - d_ptr->pos; } @@ -4866,8 +5193,9 @@ QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QRectF &re QPolygonF QGraphicsItem::mapFromParent(const QRectF &rect) const { // COMBINE - QRectF r = rect.translated(-d_ptr->pos); - return d_ptr->transform ? d_ptr->transform->inverted().map(r) : r; + if (!d_ptr->transformData) + return rect.translated(-d_ptr->pos); + return d_ptr->transformToParent().inverted().map(rect); } /*! @@ -4923,9 +5251,9 @@ QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPolygonF QPolygonF QGraphicsItem::mapFromParent(const QPolygonF &polygon) const { // COMBINE - QPolygonF p = polygon; - p.translate(-d_ptr->pos); - return d_ptr->transform ? d_ptr->transform->inverted().map(p) : p; + if (!d_ptr->transformData) + return polygon.translated(-d_ptr->pos); + return d_ptr->transformToParent().inverted().map(polygon); } /*! @@ -4966,9 +5294,9 @@ QPainterPath QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPainte QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const { // COMBINE - QPainterPath p(path); - p.translate(-d_ptr->pos); - return d_ptr->transform ? d_ptr->transform->inverted().map(p) : p; + if (!d_ptr->transformData) + return path.translated(-d_ptr->pos); + return d_ptr->transformToParent().inverted().map(path); } /*! diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 7c87176..67b57e0 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -248,10 +248,39 @@ public: void setTransform(const QTransform &matrix, bool combine = false); void resetTransform(); - void rotate(qreal angle); - void scale(qreal sx, qreal sy); - void shear(qreal sh, qreal sv); - void translate(qreal dx, qreal dy); + void rotate(qreal angle); // ### obsolete + void scale(qreal sx, qreal sy); // ### obsolete + void shear(qreal sh, qreal sv); // ### obsolete + void translate(qreal dx, qreal dy); // ### obsolete + + qreal xRotation() const; + void setXRotation(qreal angle); + + qreal yRotation() const; + void setYRotation(qreal angle); + + qreal zRotation() const; + void setZRotation(qreal angle); + void setRotation(qreal x, qreal y, qreal z); + + qreal xScale() const; + void setXScale(qreal factor); + + qreal yScale() const; + void setYScale(qreal factor); + void setScale(qreal sx, qreal sy); + + qreal horizontalShear() const; + void setHorizontalShear(qreal shear); + + qreal verticalShear() const; + void setVerticalShear(qreal shear); + void setShear(qreal sh, qreal sv); + + QPointF transformOrigin() const; + void setTransformOrigin(const QPointF &origin); + inline void setTransformOrigin(qreal x, qreal y) + { setTransformOrigin(QPointF(x,y)); } virtual void advance(int phase); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index d884b16..62ef36a 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -115,7 +115,7 @@ public: opacity(1.), scene(0), parent(0), - transform(0), + transformData(0), index(-1), depth(0), acceptedMouseButtons(0x1f), @@ -147,8 +147,8 @@ public: dirtyChildrenBoundingRect(1), paintedViewBoundingRectsNeedRepaint(0), dirtySceneTransform(1), - itemChangesEnabled(0xffff), geometryChanged(0), + itemChangesEnabled(0xffff), globalStackingOrder(-1), q_ptr(0) { @@ -352,6 +352,8 @@ public: || (childrenCombineOpacity() && isFullyTransparent()); } + inline QTransform transformToParent() const; + QPainterPath cachedClipPath; QRectF childrenBoundingRect; QRectF needsRepaint; @@ -362,7 +364,8 @@ public: QGraphicsScene *scene; QGraphicsItem *parent; QList<QGraphicsItem *> children; - QTransform *transform; + class TransformData; + TransformData *transformData; QTransform sceneTransform; int index; int depth; @@ -399,16 +402,57 @@ public: quint32 dirtyChildrenBoundingRect : 1; quint32 paintedViewBoundingRectsNeedRepaint : 1; quint32 dirtySceneTransform : 1; - quint32 itemChangesEnabled : 16; quint32 geometryChanged : 1; - quint32 padding : 2; // feel free to use + quint32 unused : 2; // feel free to use + quint32 itemChangesEnabled : 16; // Optional stacking order int globalStackingOrder; - QGraphicsItem *q_ptr; }; +struct QGraphicsItemPrivate::TransformData { + QTransform transform; + qreal xScale; + qreal yScale; + qreal xRotation; + qreal yRotation; + qreal zRotation; + qreal horizontalShear; + qreal verticalShear; + qreal xOrigin; + qreal yOrigin; + + TransformData() : + xScale(1.0), yScale(1.0), xRotation(0.0), yRotation(0.0), zRotation(0.0), + horizontalShear(0.0), verticalShear(0.0), xOrigin(0.0), yOrigin(0.0) + {} + + QTransform computedFullTransform() const + { + QTransform x; + x.translate(xOrigin, yOrigin); + x = transform * x; + x.rotate(xRotation, Qt::XAxis); + x.rotate(yRotation, Qt::YAxis); + x.rotate(zRotation, Qt::ZAxis); + x.shear(horizontalShear, verticalShear); + x.scale(xScale, yScale); + x.translate(-xOrigin, -yOrigin); + return x; + } +}; + +/* + return the full transform of the item to the parent. This include the position and all the transform data +*/ +inline QTransform QGraphicsItemPrivate::transformToParent() const +{ + QTransform matrix; + combineTransformToParent(&matrix); + return matrix; +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index ad392b0..1014550 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -1737,7 +1737,7 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, QList<QGraphicsItem *> &children = parent->d_ptr->children; for (int i = 0; i < children.size(); ++i) { QGraphicsItem *item = children.at(i); - if (item->d_ptr->transform && !item->d_ptr->transform->isInvertible()) + if (item->d_ptr->transformData && !item->d_ptr->transformData->computedFullTransform().isInvertible()) continue; // Skip invisible items and all their children. @@ -1777,7 +1777,7 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, QList<QGraphicsItem *> &children = parent->d_ptr->children; for (int i = 0; i < children.size(); ++i) { QGraphicsItem *item = children.at(i); - if (item->d_ptr->transform && !item->d_ptr->transform->isInvertible()) + if (item->d_ptr->transformData && !item->d_ptr->transformData->computedFullTransform().isInvertible()) continue; // Skip invisible items and all their children. @@ -1813,7 +1813,7 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { // Recurse into children. - if (!item->d_ptr->transform || item->d_ptr->transform->type() <= QTransform::TxScale) { + if (!item->d_ptr->transformData || item->d_ptr->transformData->computedFullTransform().type() <= QTransform::TxScale) { // Rect childItems_helper(items, item, item->mapRectFromParent(rect), mode); } else { @@ -1843,7 +1843,7 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, QList<QGraphicsItem *> &children = parent->d_ptr->children; for (int i = 0; i < children.size(); ++i) { QGraphicsItem *item = children.at(i); - if (item->d_ptr->transform && !item->d_ptr->transform->isInvertible()) + if (item->d_ptr->transformData && !item->d_ptr->transformData->computedFullTransform().isInvertible()) continue; // Skip invisible items. @@ -1902,7 +1902,7 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, QList<QGraphicsItem *> &children = parent->d_ptr->children; for (int i = 0; i < children.size(); ++i) { QGraphicsItem *item = children.at(i); - if (item->d_ptr->transform && !item->d_ptr->transform->isInvertible()) + if (item->d_ptr->transformData && !item->d_ptr->transformData->computedFullTransform().isInvertible()) continue; // Skip invisible items. diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index a9fc563..166bcb9 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -752,7 +752,7 @@ QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRect const QGraphicsItemPrivate *itemd; do { itemd = parentItem->d_ptr; - if (itemd->transform) + if (itemd->transformData) break; offset += itemd->pos; } while ((parentItem = itemd->parent)); diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h index 34f1c5f..a5c9068 100644 --- a/src/gui/graphicsview/qgraphicswidget.h +++ b/src/gui/graphicsview/qgraphicswidget.h @@ -81,7 +81,14 @@ class Q_GUI_EXPORT QGraphicsWidget : public QObject, public QGraphicsItem, publi Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) Q_PROPERTY(QPointF pos READ pos WRITE setPos) Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry) - + Q_PROPERTY(QPointF transformOrigin READ transformOrigin WRITE setTransformOrigin) + Q_PROPERTY(qreal xRotation READ xRotation WRITE setXRotation) + Q_PROPERTY(qreal yRotation READ yRotation WRITE setYRotation) + Q_PROPERTY(qreal zRotation READ zRotation WRITE setZRotation) + Q_PROPERTY(qreal xScale READ xScale WRITE setXScale) + Q_PROPERTY(qreal yScale READ yScale WRITE setYScale) + Q_PROPERTY(qreal horizontalShear READ horizontalShear WRITE setHorizontalShear) + Q_PROPERTY(qreal verticalShear READ verticalShear WRITE setVerticalShear) public: QGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); ~QGraphicsWidget(); diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index 41593b5..82c173b 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -220,6 +220,8 @@ private slots: void deviceTransform_data(); void deviceTransform(); void update(); + void setTransformProperties_data(); + void setTransformProperties(); // task specific tests below me void task141694_textItemEnsureVisible(); @@ -6484,5 +6486,202 @@ void tst_QGraphicsItem::update() QCOMPARE(view.paintedRegion, expectedRegion); } +void tst_QGraphicsItem::setTransformProperties_data() +{ + QTest::addColumn<QPointF>("origin"); + QTest::addColumn<qreal>("rotationX"); + QTest::addColumn<qreal>("rotationY"); + QTest::addColumn<qreal>("rotationZ"); + QTest::addColumn<qreal>("scaleX"); + QTest::addColumn<qreal>("scaleY"); + QTest::addColumn<qreal>("shearX"); + QTest::addColumn<qreal>("shearY"); + + QTest::newRow("nothing") << QPointF() << qreal(0.0) << qreal(0.0) << qreal(0.0) + << qreal(1.0) << qreal(1.0) << qreal(0.0) << qreal(0.0); + + QTest::newRow("rotationZ") << QPointF() << qreal(0.0) << qreal(0.0) << qreal(42.2) + << qreal(1.0) << qreal(1.0) << qreal(0.0) << qreal(0.0); + + QTest::newRow("rotationXY") << QPointF() << qreal(12.5) << qreal(53.6) << qreal(0.0) + << qreal(1.0) << qreal(1.0) << qreal(0.0) << qreal(0.0); + + QTest::newRow("rotationXYZ") << QPointF() << qreal(-25) << qreal(12) << qreal(556) + << qreal(1.0) << qreal(1.0) << qreal(0.0) << qreal(0.0); + + QTest::newRow("rotationXYZ dicentred") << QPointF(-53, 25.2) + << qreal(-2578.2) << qreal(4565.2) << qreal(56) + << qreal(1.0) << qreal(1.0) << qreal(0.0) << qreal(0.0); + + QTest::newRow("Scale") << QPointF() << qreal(0.0) << qreal(0.0) << qreal(0.0) + << qreal(6) << qreal(0.5) << qreal(0.0) << qreal(0.0); + + QTest::newRow("Shear") << QPointF() << qreal(0.0) << qreal(0.0) << qreal(0.0) + << qreal(1.0) << qreal(1.0) << qreal(2.2) << qreal(0.5); + + QTest::newRow("Scale and Shear") << QPointF() << qreal(0.0) << qreal(0.0) << qreal(0.0) + << qreal(5.2) << qreal(2.1) << qreal(5.2) << qreal(5.5); + + QTest::newRow("Everything") << QPointF() << qreal(41) << qreal(-23) << qreal(0.56) + << qreal(8.2) << qreal(-0.2) << qreal(-12) << qreal(-0.8); + + QTest::newRow("Everything dicentred") << QPointF(qreal(22.3), qreal(-56.2)) << qreal(-175) << qreal(196) << qreal(-1260) + << qreal(4) << qreal(2) << qreal(2.56) << qreal(0.8); +} + +/** + * the normal QCOMPARE doesn't work because it doesn't use qFuzzyCompare + */ +#define QCOMPARE_TRANSFORM(X1, X2) QVERIFY(((X1)*(X2).inverted()).isIdentity()) + +void tst_QGraphicsItem::setTransformProperties() +{ + QFETCH(QPointF,origin); + QFETCH(qreal,rotationX); + QFETCH(qreal,rotationY); + QFETCH(qreal,rotationZ); + QFETCH(qreal,scaleX); + QFETCH(qreal,scaleY); + QFETCH(qreal,shearX); + QFETCH(qreal,shearY); + + QTransform result; + result.translate(origin.x(), origin.y()); + result.rotate(rotationX, Qt::XAxis); + result.rotate(rotationY, Qt::YAxis); + result.rotate(rotationZ, Qt::ZAxis); + result.shear(shearX, shearY); + result.scale(scaleX, scaleY); + result.translate(-origin.x(), -origin.y()); + + QGraphicsScene scene; + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + scene.addItem(item); + + item->setRotation(rotationX, rotationY, rotationZ); + item->setScale(scaleX, scaleY); + item->setShear(shearX, shearY); + item->setTransformOrigin(origin); + + QCOMPARE(item->xRotation(), rotationX); + QCOMPARE(item->yRotation(), rotationY); + QCOMPARE(item->zRotation(), rotationZ); + QCOMPARE(item->xScale(), scaleX); + QCOMPARE(item->yScale(), scaleY); + QCOMPARE(item->horizontalShear(), shearX); + QCOMPARE(item->verticalShear(), shearY); + QCOMPARE(item->transformOrigin(), origin); + + QCOMPARE(QTransform(), item->transform()); + QCOMPARE(result, item->sceneTransform()); + + //----------------------------------------------------------------- + //Change the rotation Z + item->setZRotation(45); + QTransform result2; + result2.translate(origin.x(), origin.y()); + result2.rotate(rotationX, Qt::XAxis); + result2.rotate(rotationY, Qt::YAxis); + result2.rotate(45, Qt::ZAxis); + result2.shear(shearX, shearY); + result2.scale(scaleX, scaleY); + result2.translate(-origin.x(), -origin.y()); + + QCOMPARE(item->xRotation(), rotationX); + QCOMPARE(item->yRotation(), rotationY); + QCOMPARE(item->zRotation(), 45.0); + QCOMPARE(item->xScale(), scaleX); + QCOMPARE(item->yScale(), scaleY); + QCOMPARE(item->horizontalShear(), shearX); + QCOMPARE(item->verticalShear(), shearY); + QCOMPARE(item->transformOrigin(), origin); + + QCOMPARE(QTransform(), item->transform()); + QCOMPARE(result2, item->sceneTransform()); + + //----------------------------------------------------------------- + // calling setTransform() and setPos shoukld change the sceneTransform + item->setTransform(result); + item->setPos(100, -150.5); + + QCOMPARE(item->xRotation(), rotationX); + QCOMPARE(item->yRotation(), rotationY); + QCOMPARE(item->zRotation(), 45.0); + QCOMPARE(item->xScale(), scaleX); + QCOMPARE(item->yScale(), scaleY); + QCOMPARE(item->horizontalShear(), shearX); + QCOMPARE(item->verticalShear(), shearY); + QCOMPARE(item->transformOrigin(), origin); + QCOMPARE(result, item->transform()); + + QTransform result3; + + result3.translate(origin.x(), origin.y()); + result3 = result * result3; + result3.rotate(rotationX, Qt::XAxis); + result3.rotate(rotationY, Qt::YAxis); + result3.rotate(45, Qt::ZAxis); + result3.shear(shearX, shearY); + result3.scale(scaleX, scaleY); + result3.translate(-origin.x(), -origin.y()); + + result3 *= QTransform::fromTranslate(100, -150.5); //the pos; + + QCOMPARE(result3, item->sceneTransform()); + + //----------------------------------------------------- + // setting the propertiees should be the same as setting a transform + {//with center origin on the matrix + QGraphicsRectItem *item1 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item1); + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item2); + + item1->setPos(12.3, -5); + item2->setPos(12.3, -5); + item1->setRotation(rotationX, rotationY, rotationZ); + item1->setScale(scaleX, scaleY); + item1->setShear(shearX, shearY); + item1->setTransformOrigin(origin); + + item2->setTransform(result); + + QCOMPARE_TRANSFORM(item1->sceneTransform(), item2->sceneTransform()); + + QCOMPARE_TRANSFORM(item1->itemTransform(item2), QTransform()); + QCOMPARE_TRANSFORM(item2->itemTransform(item1), QTransform()); + } + + {//with center origin on the item + QGraphicsRectItem *item1 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item1); + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item2); + + item1->setPos(12.3, -5); + item2->setPos(12.3, -5); + item1->setTransformOrigin(origin); + item2->setTransformOrigin(origin); + + item1->setRotation(rotationX, rotationY, rotationZ); + item1->setScale(scaleX, scaleY); + item1->setShear(shearX, shearY); + + QTransform tr; + tr.rotate(rotationX, Qt::XAxis); + tr.rotate(rotationY, Qt::YAxis); + tr.rotate(rotationZ, Qt::ZAxis); + tr.shear(shearX, shearY); + tr.scale(scaleX, scaleY); + + item2->setTransform(tr); + + QCOMPARE_TRANSFORM(item1->sceneTransform(), item2->sceneTransform()); + + QCOMPARE_TRANSFORM(item1->itemTransform(item2), QTransform()); + QCOMPARE_TRANSFORM(item2->itemTransform(item1), QTransform()); + } +} + QTEST_MAIN(tst_QGraphicsItem) #include "tst_qgraphicsitem.moc" |