Skip to content

Commit

Permalink
Start porting item rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jul 18, 2017
1 parent c345613 commit 00405fd
Show file tree
Hide file tree
Showing 5 changed files with 460 additions and 115 deletions.
39 changes: 36 additions & 3 deletions python/core/layout/qgslayoutitem.sip
Expand Up @@ -125,6 +125,11 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem
:rtype: QgsLayoutSize
%End

double itemRotation() const;
%Docstring
:rtype: float
%End

public slots:

virtual void refresh();
Expand All @@ -142,6 +147,20 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem
refreshed.
%End

virtual void setItemRotation( const double rotation );
%Docstring
Sets the layout item's ``rotation``, in degrees clockwise. This rotation occurs around the center of the item.
.. seealso:: itemRotation()
.. seealso:: rotateItem()
%End

virtual void rotateItem( const double angle, const QPointF &transformOrigin );
%Docstring
Rotates the item by a specified ``angle`` in degrees clockwise around a specified reference point.
.. seealso:: setItemRotation()
.. seealso:: itemRotation()
%End

protected:

virtual void drawDebugRect( QPainter *painter );
Expand Down Expand Up @@ -188,10 +207,24 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem
.. seealso:: refreshItemSize()
%End

QPointF adjustPointForReferencePosition( const QPointF &point, const QSizeF &size ) const;
void refreshItemRotation();
%Docstring
Refreshes an item's rotation by rechecking it against any possible overrides
such as data defined rotation.
.. seealso:: refreshItemSize()
.. seealso:: refreshItemPosition()
%End

QPointF adjustPointForReferencePosition( const QPointF &point, const QSizeF &size, const ReferencePoint &reference ) const;
%Docstring
Adjusts the specified ``point`` at which a ``reference`` position of the item
sits and returns the top left corner of the item, if reference point where placed at the specified position.
:rtype: QPointF
%End

QPointF positionAtReferencePoint( const ReferencePoint &reference ) const;
%Docstring
Adjusts the specified ``point`` at which the reference position of the item
sits and returns the top left corner of the item.
Returns the current position (in layout units) of a ``reference`` point for the item.
:rtype: QPointF
%End

Expand Down
114 changes: 97 additions & 17 deletions src/core/layout/qgslayoutitem.cpp
Expand Up @@ -66,8 +66,7 @@ void QgsLayoutItem::setReferencePoint( const QgsLayoutItem::ReferencePoint &poin
mReferencePoint = point;

//also need to adjust stored position
QPointF positionReferencePointLayoutUnits = adjustPointForReferencePosition( pos(), QSizeF( -rect().width(), -rect().height() ) );
mItemPosition = mLayout->convertFromLayoutUnits( positionReferencePointLayoutUnits, mItemPosition.units() );
updateStoredItemPosition();
refreshItemPosition();
}

Expand Down Expand Up @@ -108,8 +107,8 @@ void QgsLayoutItem::attemptMove( const QgsLayoutPoint &point )

QgsLayoutPoint evaluatedPoint = applyDataDefinedPosition( point );
QPointF evaluatedPointLayoutUnits = mLayout->convertToLayoutUnits( evaluatedPoint );
QPointF topLeftPointLayoutUnits = adjustPointForReferencePosition( evaluatedPointLayoutUnits, rect().size() );
if ( topLeftPointLayoutUnits == pos() && point.units() == mItemPosition.units() )
QPointF topLeftPointLayoutUnits = adjustPointForReferencePosition( evaluatedPointLayoutUnits, rect().size(), mReferencePoint );
if ( topLeftPointLayoutUnits == scenePos() && point.units() == mItemPosition.units() )
{
//TODO - add test for second condition
return;
Expand All @@ -118,7 +117,20 @@ void QgsLayoutItem::attemptMove( const QgsLayoutPoint &point )
QgsLayoutPoint referencePointTargetUnits = mLayout->convertFromLayoutUnits( evaluatedPointLayoutUnits, point.units() );
mItemPosition = referencePointTargetUnits;

setPos( topLeftPointLayoutUnits );
setScenePos( topLeftPointLayoutUnits );
}

void QgsLayoutItem::setScenePos( const QPointF &destinationPos )
{
//since setPos does not account for item rotation, use difference between
//current scenePos (which DOES account for rotation) and destination pos
//to calculate how much the item needs to move
setPos( pos() + ( destinationPos - scenePos() ) );
}

double QgsLayoutItem::itemRotation() const
{
return rotation();
}

QgsLayoutPoint QgsLayoutItem::applyDataDefinedPosition( const QgsLayoutPoint &position )
Expand Down Expand Up @@ -147,6 +159,18 @@ QgsLayoutSize QgsLayoutItem::applyDataDefinedSize( const QgsLayoutSize &size )
return QgsLayoutSize( evaluatedWidth, evaluatedHeight, size.units() );
}

double QgsLayoutItem::applyDataDefinedRotation( const double rotation )
{
if ( !mLayout )
{
return rotation;
}

QgsExpressionContext context = createExpressionContext();
double evaluatedRotation = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::ItemRotation, context, rotation );
return evaluatedRotation;
}

void QgsLayoutItem::refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property )
{
//update data defined properties and update item to match
Expand All @@ -162,6 +186,44 @@ void QgsLayoutItem::refreshDataDefinedProperty( const QgsLayoutObject::DataDefin
{
refreshItemPosition();
}
if ( property == QgsLayoutObject::ItemRotation || property == QgsLayoutObject::AllProperties )
{
refreshItemRotation();
}
}

void QgsLayoutItem::setItemRotation( const double angle )
{
QPointF itemCenter = positionAtReferencePoint( QgsLayoutItem::Middle );
double rotationRequired = angle - itemRotation();
rotateItem( rotationRequired, itemCenter );
}

void QgsLayoutItem::updateStoredItemPosition()
{
QPointF layoutPosReferencePoint = positionAtReferencePoint( mReferencePoint );
mItemPosition = mLayout->convertFromLayoutUnits( layoutPosReferencePoint, mItemPosition.units() );
}

void QgsLayoutItem::rotateItem( const double angle, const QPointF &transformOrigin )
{
double evaluatedAngle = angle + rotation();
evaluatedAngle = QgsLayoutUtils::normalizedAngle( evaluatedAngle, true );
evaluatedAngle = applyDataDefinedRotation( evaluatedAngle );

QPointF itemTransformOrigin = mapFromScene( transformOrigin );
setTransformOriginPoint( itemTransformOrigin );
setRotation( evaluatedAngle );

//adjust stored position of item to match scene pos of reference point
updateStoredItemPosition();

//TODO
// emit itemRotationChanged( rotation );

//TODO
//update bounds of scene, since rotation may affect this
//mLayout->updateBounds();
}


Expand Down Expand Up @@ -210,31 +272,44 @@ void QgsLayoutItem::refreshItemPosition()
attemptMove( mItemPosition );
}

QPointF QgsLayoutItem::adjustPointForReferencePosition( const QPointF &position, const QSizeF &size ) const
QPointF QgsLayoutItem::itemPositionAtReferencePoint( const ReferencePoint reference, const QSizeF &size ) const
{
switch ( mReferencePoint )
switch ( reference )
{
case UpperMiddle:
return QPointF( position.x() - size.width() / 2.0, position.y() );
return QPointF( size.width() / 2.0, 0 );
case UpperRight:
return QPointF( position.x() - size.width(), position.y() );
return QPointF( size.width(), 0 );
case MiddleLeft:
return QPointF( position.x(), position.y() - size.height() / 2.0 );
return QPointF( 0, size.height() / 2.0 );
case Middle:
return QPointF( position.x() - size.width() / 2.0, position.y() - size.height() / 2.0 );
return QPointF( size.width() / 2.0, size.height() / 2.0 );
case MiddleRight:
return QPointF( position.x() - size.width(), position.y() - size.height() / 2.0 );
return QPointF( size.width(), size.height() / 2.0 );
case LowerLeft:
return QPointF( position.x(), position.y() - size.height() );
return QPointF( 0, size.height() );
case LowerMiddle:
return QPointF( position.x() - size.width() / 2.0, position.y() - size.height() );
return QPointF( size.width() / 2.0, size.height() );
case LowerRight:
return QPointF( position.x() - size.width(), position.y() - size.height() );
return QPointF( size.width(), size.height() );
case UpperLeft:
return position;
return QPointF( 0, 0 );
}
// no warnings
return position;
return QPointF( 0, 0 );
}

QPointF QgsLayoutItem::adjustPointForReferencePosition( const QPointF &position, const QSizeF &size, const ReferencePoint &reference ) const
{
QPointF itemPosition = mapFromScene( position ); //need to map from scene to handle item rotation
QPointF adjustedPointInsideItem = itemPosition - itemPositionAtReferencePoint( reference, size );
return mapToScene( adjustedPointInsideItem );
}

QPointF QgsLayoutItem::positionAtReferencePoint( const QgsLayoutItem::ReferencePoint &reference ) const
{
QPointF pointWithinItem = itemPositionAtReferencePoint( reference, rect().size() );
return mapToScene( pointWithinItem );
}

void QgsLayoutItem::initConnectionsToLayout()
Expand Down Expand Up @@ -287,3 +362,8 @@ QSizeF QgsLayoutItem::applyFixedSize( const QSizeF &targetSize )
QSizeF fixedSizeLayoutUnits = mLayout->convertToLayoutUnits( fixedSize() );
return targetSize.expandedTo( fixedSizeLayoutUnits );
}

void QgsLayoutItem::refreshItemRotation()
{
setItemRotation( itemRotation() );
}
45 changes: 42 additions & 3 deletions src/core/layout/qgslayoutitem.h
Expand Up @@ -138,6 +138,13 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
QgsLayoutSize sizeWithUnits() const { return mItemSize; }

/**
* Returns the current rotation for the item, in degrees clockwise.
* \see setItemRotation()
*/
//TODO
double itemRotation() const;

public slots:

/**
Expand All @@ -154,6 +161,20 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
virtual void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );

/**
* Sets the layout item's \a rotation, in degrees clockwise. This rotation occurs around the center of the item.
* \see itemRotation()
* \see rotateItem()
*/
virtual void setItemRotation( const double rotation );

/**
* Rotates the item by a specified \a angle in degrees clockwise around a specified reference point.
* \see setItemRotation()
* \see itemRotation()
*/
virtual void rotateItem( const double angle, const QPointF &transformOrigin );

protected:

/** Draws a debugging rectangle of the item's current bounds within the specified
Expand Down Expand Up @@ -200,10 +221,23 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
void refreshItemPosition();

/**
* Adjusts the specified \a point at which the reference position of the item
* sits and returns the top left corner of the item.
* Refreshes an item's rotation by rechecking it against any possible overrides
* such as data defined rotation.
* \see refreshItemSize()
* \see refreshItemPosition()
*/
void refreshItemRotation();

/**
* Adjusts the specified \a point at which a \a reference position of the item
* sits and returns the top left corner of the item, if reference point where placed at the specified position.
*/
QPointF adjustPointForReferencePosition( const QPointF &point, const QSizeF &size ) const;
QPointF adjustPointForReferencePosition( const QPointF &point, const QSizeF &size, const ReferencePoint &reference ) const;

/**
* Returns the current position (in layout units) of a \a reference point for the item.
*/
QPointF positionAtReferencePoint( const ReferencePoint &reference ) const;

private:

Expand All @@ -213,6 +247,7 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt

QgsLayoutSize mItemSize;
QgsLayoutPoint mItemPosition;
double mItemRotation = 0.0;

void initConnectionsToLayout();

Expand All @@ -225,6 +260,10 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
QSizeF applyFixedSize( const QSizeF &targetSize );
QgsLayoutPoint applyDataDefinedPosition( const QgsLayoutPoint &position );
QgsLayoutSize applyDataDefinedSize( const QgsLayoutSize &size );
double applyDataDefinedRotation( const double rotation );
void updateStoredItemPosition();
QPointF itemPositionAtReferencePoint( const ReferencePoint reference, const QSizeF &size ) const;
void setScenePos( const QPointF &destinationPos );

friend class TestQgsLayoutItem;
};
Expand Down

0 comments on commit 00405fd

Please sign in to comment.