diff --git a/python/core/qgscomposeritem.sip b/python/core/qgscomposeritem.sip index dddf43f279f..4d9277a1c7c 100644 --- a/python/core/qgscomposeritem.sip +++ b/python/core/qgscomposeritem.sip @@ -145,7 +145,7 @@ class QgsComposerItem: QObject, QGraphicsRectItem double rotation() const; public slots: - void setRotation( double r); + virtual void setRotation( double r); protected: @@ -200,6 +200,13 @@ class QgsComposerItem: QObject, QGraphicsRectItem bool cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const; /**Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point*/ QPointF pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) const; + /**Calculates width / height of the bounding box of a rotated rectangle (mRotation)*/ + void sizeChangedByRotation(double& width, double& height); + /**Rotates a point / vector + @param angle rotation angle in degrees, counterclockwise + @param x in/out: x coordinate before / after the rotation + @param y in/out: y cooreinate before / after the rotation*/ + void rotate( double angle, double& x, double& y ) const; signals: /**Is emitted on rotation change to notify north arrow pictures*/ diff --git a/python/core/qgscomposerpicture.sip b/python/core/qgscomposerpicture.sip index 2c95690d3d5..5a0c9562bda 100644 --- a/python/core/qgscomposerpicture.sip +++ b/python/core/qgscomposerpicture.sip @@ -41,6 +41,9 @@ class QgsComposerPicture: QgsComposerItem /**True if the rotation is taken from a map item*/ bool useRotationMap() const; + public slots: + virtual void setRotation( double r ); + signals: /**Tell the configuration widget that the settings need to be updated*/ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c247ca9c411..4099b1d5506 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -202,6 +202,7 @@ composer/qgscomposerpicture.h composer/qgscomposerscalebar.h composer/qgscomposeritem.h composer/qgscomposeritemgroup.h +composer/qgscomposershape.h composer/qgscomposition.h composer/qgslegendmodel.h symbology/qgsmarkercatalogue.h diff --git a/src/core/composer/qgscomposeritem.cpp b/src/core/composer/qgscomposeritem.cpp index a83a76e77d7..ceb19e051f0 100644 --- a/src/core/composer/qgscomposeritem.cpp +++ b/src/core/composer/qgscomposeritem.cpp @@ -948,3 +948,50 @@ QPointF QgsComposerItem::pointOnLineWithDistance( const QPointF& startPoint, con double scaleFactor = distance / length; return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor ); } + +void QgsComposerItem::sizeChangedByRotation( double& width, double& height ) +{ + if ( mRotation == 0.0 ) + { + return; + } + + //vector to p1 + double x1 = -width / 2.0; + double y1 = -height / 2.0; + rotate( mRotation, x1, y1 ); + //vector to p2 + double x2 = width / 2.0; + double y2 = -height / 2.0; + rotate( mRotation, x2, y2 ); + //vector to p3 + double x3 = width / 2.0; + double y3 = height / 2.0; + rotate( mRotation, x3, y3 ); + //vector to p4 + double x4 = -width / 2.0; + double y4 = height / 2.0; + rotate( mRotation, x4, y4 ); + + //double midpoint + QPointF midpoint( width / 2.0, height / 2.0 ); + + QPolygonF rotatedRectPoly; + rotatedRectPoly << QPointF( midpoint.x() + x1, midpoint.y() + y1 ); + rotatedRectPoly << QPointF( midpoint.x() + x2, midpoint.y() + y2 ); + rotatedRectPoly << QPointF( midpoint.x() + x3, midpoint.y() + y3 ); + rotatedRectPoly << QPointF( midpoint.x() + x4, midpoint.y() + y4 ); + QRectF boundingRect = rotatedRectPoly.boundingRect(); + width = boundingRect.width(); + height = boundingRect.height(); +} + +void QgsComposerItem::rotate( double angle, double& x, double& y ) const +{ + double rotToRad = angle * M_PI / 180.0; + double xRot, yRot; + xRot = x * cos( rotToRad ) - y * sin( rotToRad ); + yRot = x * sin( rotToRad ) + y * cos( rotToRad ); + x = xRot; + y = yRot; +} diff --git a/src/core/composer/qgscomposeritem.h b/src/core/composer/qgscomposeritem.h index d22a89b15a0..fa8ea81a72d 100644 --- a/src/core/composer/qgscomposeritem.h +++ b/src/core/composer/qgscomposeritem.h @@ -172,7 +172,7 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem double rotation() const {return mRotation;} public slots: - void setRotation( double r ); + virtual void setRotation( double r ); protected: @@ -245,12 +245,21 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem @note: this function was introduced in version 1.2*/ double horizontalViewScaleFactor() const; + //some utility functions + /**Calculates width and hight of the picture (in mm) such that it fits into the item frame with the given rotation*/ bool imageSizeConsideringRotation( double& width, double& height ) const; /**Calculates corner point after rotation and scaling*/ bool cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const; /**Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point*/ QPointF pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) const; + /**Calculates width / height of the bounding box of a rotated rectangle (mRotation)*/ + void sizeChangedByRotation( double& width, double& height ); + /**Rotates a point / vector + @param angle rotation angle in degrees, counterclockwise + @param x in/out: x coordinate before / after the rotation + @param y in/out: y cooreinate before / after the rotation*/ + void rotate( double angle, double& x, double& y ) const; signals: /**Is emitted on rotation change to notify north arrow pictures*/ diff --git a/src/core/composer/qgscomposermap.cpp b/src/core/composer/qgscomposermap.cpp index 07dc1003fff..3fd2ab65427 100644 --- a/src/core/composer/qgscomposermap.cpp +++ b/src/core/composer/qgscomposermap.cpp @@ -1383,24 +1383,3 @@ QgsComposerMap::Border QgsComposerMap::borderForLineCoord( const QPointF& p ) co return Bottom; } } - -void QgsComposerMap::rotate( double angle, double& x, double& y ) const -{ - double rotToRad = angle * M_PI / 180.0; - double xRot, yRot; - xRot = x * cos( rotToRad ) - y * sin( rotToRad ); - yRot = x * sin( rotToRad ) + y * cos( rotToRad ); - x = xRot; - y = yRot; -} - -#if 0 -QPointF QgsComposerMap::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) const -{ - double dx = directionPoint.x() - startPoint.x(); - double dy = directionPoint.y() - startPoint.y(); - double length = sqrt( dx * dx + dy * dy ); - double scaleFactor = distance / length; - return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor ); -} -#endif //0 diff --git a/src/core/composer/qgscomposermap.h b/src/core/composer/qgscomposermap.h index 6e156acee3f..85c8b6cccfb 100644 --- a/src/core/composer/qgscomposermap.h +++ b/src/core/composer/qgscomposermap.h @@ -381,15 +381,6 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem QPointF mapToItemCoords( const QPointF& mapCoords ) const; /**Returns the item border of a point (in item coordinates)*/ Border borderForLineCoord( const QPointF& p ) const; - /**Rotates a point / vector - @param angle rotation angle in degrees, counterclockwise - @param x in/out: x coordinate before / after the rotation - @param y in/out: y cooreinate before / after the rotation*/ - void rotate( double angle, double& x, double& y ) const; -#if 0 - /**Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point*/ - QPointF pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) const; -#endif //0 }; #endif diff --git a/src/core/composer/qgscomposerpicture.cpp b/src/core/composer/qgscomposerpicture.cpp index c7603dfeae0..6901d843ec8 100644 --- a/src/core/composer/qgscomposerpicture.cpp +++ b/src/core/composer/qgscomposerpicture.cpp @@ -30,11 +30,12 @@ QgsComposerPicture::QgsComposerPicture( QgsComposition *composition ): QgsComposerItem( composition ), mMode( Unknown ), \ mSvgCacheUpToDate( false ), mCachedDpi( 0 ), mCachedRotation( 0 ), mCachedViewScaleFactor( -1 ), mRotationMap( 0 ) { + mPictureWidth = rect().width(); } QgsComposerPicture::QgsComposerPicture(): QgsComposerItem( 0 ), mMode( Unknown ), mSvgCacheUpToDate( false ), mCachedRotation( 0 ), mCachedViewScaleFactor( -1 ), mRotationMap( 0 ) { - + mPictureHeight = rect().height(); } QgsComposerPicture::~QgsComposerPicture() @@ -61,8 +62,8 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte if ( mMode != Unknown ) { - double rectPixelWidth = rect().width() * newDpi / 25.4; - double rectPixelHeight = rect().height() * newDpi / 25.4; + double rectPixelWidth = /*rect().width()*/mPictureWidth * newDpi / 25.4; + double rectPixelHeight = /*rect().height()*/ mPictureHeight * newDpi / 25.4; QRectF boundRect; if ( mMode == SVG ) { @@ -75,11 +76,8 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte double boundRectWidthMM = boundRect.width() / newDpi * 25.4; double boundRectHeightMM = boundRect.height() / newDpi * 25.4; - double rotatedBoundImageWidth = boundRect.width(); - double rotatedBoundImageHeight = boundRect.height(); - imageSizeConsideringRotation( rotatedBoundImageWidth, rotatedBoundImageHeight ); - double rotatedBoundImageWidthMM = rotatedBoundImageWidth / newDpi * 25.4; - double rotatedBoundImageHeightMM = rotatedBoundImageHeight / newDpi * 25.4; + double boundImageWidth = boundRect.width(); + double boundImageHeight = boundRect.height(); if ( mMode == SVG ) { @@ -88,20 +86,20 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte //make nicer preview if ( mComposition && mComposition->plotStyle() == QgsComposition::Preview ) { - rotatedBoundImageWidth *= std::min( viewScaleFactor, 10.0 ); - rotatedBoundImageHeight *= std::min( viewScaleFactor, 10.0 ); + boundImageWidth *= std::min( viewScaleFactor, 10.0 ); + boundImageHeight *= std::min( viewScaleFactor, 10.0 ); } - mImage = QImage( rotatedBoundImageWidth, rotatedBoundImageHeight, QImage::Format_ARGB32 ); + mImage = QImage( boundImageWidth, boundImageHeight, QImage::Format_ARGB32 ); updateImageFromSvg(); } } painter->save(); - painter->translate( boundRectWidthMM / 2.0, boundRectHeightMM / 2.0 ); + painter->translate( rect().width() / 2.0, rect().height() / 2.0 ); painter->rotate( mRotation ); - painter->translate( -rotatedBoundImageWidthMM / 2.0, -rotatedBoundImageHeightMM / 2.0 ); + painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 ); - painter->drawImage( QRectF( 0, 0, rotatedBoundImageWidthMM, rotatedBoundImageHeightMM ), mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) ); + painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) ); painter->restore(); } @@ -183,6 +181,24 @@ QRectF QgsComposerPicture::boundedImageRect( double deviceWidth, double deviceHe } } +QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeight ) +{ + double imageToSvgRatio; + if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() ) + { + imageToSvgRatio = deviceHeight / mDefaultSvgSize.height(); + double width = mDefaultSvgSize.width() * imageToSvgRatio; + return QRectF( 0, 0, width, deviceHeight ); + } + else + { + imageToSvgRatio = deviceWidth / mDefaultSvgSize.width(); + double height = mDefaultSvgSize.height() * imageToSvgRatio; + return QRectF( 0, 0, deviceWidth, height ); + } +} + +#if 0 QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeight ) { double imageToSvgRatio; @@ -199,6 +215,7 @@ QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeig return QRectF( 0, 0, width, deviceHeight ); } } +#endif //0 void QgsComposerPicture::updateImageFromSvg() { @@ -216,9 +233,32 @@ void QgsComposerPicture::setSceneRect( const QRectF& rectangle ) { mSvgCacheUpToDate = false; QgsComposerItem::setSceneRect( rectangle ); + + //consider to change size of the shape if the rectangle changes width and/or height + double newPictureWidth = rectangle.width(); + double newPictureHeight = rectangle.height(); + imageSizeConsideringRotation( newPictureWidth, newPictureHeight ); + mPictureWidth = newPictureWidth; + mPictureHeight = newPictureHeight; + emit settingsChanged(); } +void QgsComposerPicture::setRotation( double r ) +{ + //adapt rectangle size + double width = mPictureWidth; + double height = mPictureHeight; + sizeChangedByRotation( width, height ); + + //adapt scene rect to have the same center and the new width / height + double x = transform().dx() + rect().width() / 2.0 - width / 2.0; + double y = transform().dy() + rect().height() / 2.0 - height / 2.0; + QgsComposerItem::setSceneRect( QRectF( x, y, width, height ) ); + + QgsComposerItem::setRotation( r ); +} + void QgsComposerPicture::setRotationMap( int composerMapId ) { if ( !mComposition ) @@ -260,6 +300,8 @@ bool QgsComposerPicture::writeXML( QDomElement& elem, QDomDocument & doc ) const } QDomElement composerPictureElem = doc.createElement( "ComposerPicture" ); composerPictureElem.setAttribute( "file", QgsProject::instance()->writePath( mSourceFile.fileName() ) ); + composerPictureElem.setAttribute( "pictureWidth", mPictureWidth ); + composerPictureElem.setAttribute( "pictureHeight", mPictureHeight ); if ( !mRotationMap ) { composerPictureElem.setAttribute( "mapId", -1 ); @@ -281,6 +323,9 @@ bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocumen return false; } + mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble(); + mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble(); + QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" ); if ( composerItemList.size() > 0 ) { diff --git a/src/core/composer/qgscomposerpicture.h b/src/core/composer/qgscomposerpicture.h index 4722c8fea96..86b3a942f13 100644 --- a/src/core/composer/qgscomposerpicture.h +++ b/src/core/composer/qgscomposerpicture.h @@ -39,7 +39,7 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem QString pictureFile() const; /**Sets this items bound in scene coordinates such that 1 item size units - corresponds to 1 scene size unit*/ + corresponds to 1 scene size unit and resizes the svg symbol / image*/ void setSceneRect( const QRectF& rectangle ); /** stores state in Dom node @@ -60,6 +60,10 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem /**True if the rotation is taken from a map item*/ bool useRotationMap() const {return mRotationMap;} + public slots: + /**Sets the rotation and adapts the item rect*/ + virtual void setRotation( double r ); + private: enum Mode //SVG or raster graphic format @@ -94,7 +98,10 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem QSize mDefaultSvgSize; /**Map that sets the rotation (or 0 if this picture uses map independent rotation)*/ const QgsComposerMap* mRotationMap; - + /**Width of the picture (in mm)*/ + double mPictureWidth; + /**Height of the picture (in mm)*/ + double mPictureHeight; signals: /**Tell the configuration widget that the settings need to be updated*/ diff --git a/src/core/composer/qgscomposershape.cpp b/src/core/composer/qgscomposershape.cpp index 552b336f8b1..3b50da6c001 100755 --- a/src/core/composer/qgscomposershape.cpp +++ b/src/core/composer/qgscomposershape.cpp @@ -26,6 +26,8 @@ QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerIt QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ): QgsComposerItem( x, y, width, height, composition ), mShape( Ellipse ) { setSceneRect( QRectF( x, y, width, height ) ); + mShapeWidth = width; + mShapeHeight = height; initGraphicsSettings(); } @@ -44,7 +46,6 @@ void QgsComposerShape::paint( QPainter* painter, const QStyleOptionGraphicsItem* double width = rect().width(); double height = rect().height(); - imageSizeConsideringRotation( width, height ); painter->save(); painter->setRenderHint( QPainter::Antialiasing ); @@ -53,23 +54,23 @@ void QgsComposerShape::paint( QPainter* painter, const QStyleOptionGraphicsItem* painter->translate( rect().width() / 2.0, rect().height() / 2.0 ); painter->rotate( mRotation ); - painter->translate( -width / 2.0, -height / 2.0 ); + painter->translate( -mShapeWidth / 2.0, -mShapeHeight / 2.0 ); double halfPenWidth = mPen.widthF() / 2.0; switch ( mShape ) { case Ellipse: - painter->drawEllipse( QRectF( halfPenWidth, halfPenWidth , width - mPen.widthF(), height - mPen.widthF() ) ); + painter->drawEllipse( QRectF( halfPenWidth, halfPenWidth , mShapeWidth - mPen.widthF(), mShapeHeight - mPen.widthF() ) ); break; case Rectangle: - painter->drawRect( QRectF( halfPenWidth, halfPenWidth , width - mPen.widthF(), height - mPen.widthF() ) ); + painter->drawRect( QRectF( halfPenWidth, halfPenWidth , mShapeWidth - mPen.widthF(), mShapeHeight - mPen.widthF() ) ); break; case Triangle: QPolygonF triangle; - triangle << QPointF( halfPenWidth, height - halfPenWidth ); - triangle << QPointF( width - halfPenWidth, height - halfPenWidth ); - triangle << QPointF( width / 2.0, halfPenWidth ); + triangle << QPointF( halfPenWidth, mShapeHeight - halfPenWidth ); + triangle << QPointF( mShapeWidth - halfPenWidth, mShapeHeight - halfPenWidth ); + triangle << QPointF( mShapeWidth / 2.0, halfPenWidth ); painter->drawPolygon( triangle ); break; } @@ -89,6 +90,8 @@ bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const composerShapeElem.setAttribute( "shapeType", mShape ); composerShapeElem.setAttribute( "outlineWidth", mPen.widthF() ); composerShapeElem.setAttribute( "transparentFill", mBrush.style() == Qt::NoBrush ); + composerShapeElem.setAttribute( "shapeWidth", mShapeWidth ); + composerShapeElem.setAttribute( "shapeHeight", mShapeHeight ); QDomElement outlineColorElem = doc.createElement( "OutlineColor" ); outlineColorElem.setAttribute( "red", mPen.color().red() ); outlineColorElem.setAttribute( "green", mPen.color().green() ); @@ -108,6 +111,8 @@ bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument& doc ) { mShape = QgsComposerShape::Shape( itemElem.attribute( "shapeType", "0" ).toInt() ); + mShapeWidth = itemElem.attribute( "shapeWidth", "10" ).toDouble(); + mShapeHeight = itemElem.attribute( "shapeHeight", "10" ).toDouble(); mPen.setWidthF( itemElem.attribute( "outlineWidth", "0.4" ).toDouble() ); //transparent fill @@ -215,3 +220,30 @@ void QgsComposerShape::initGraphicsSettings() setPen( QPen( QColor( 255, 255, 255, 0 ) ) ); setBrush( QBrush( QColor( 255, 255, 255, 0 ) ) ); } + +void QgsComposerShape::setRotation( double r ) +{ + //adapt rectangle size + double width = mShapeWidth; + double height = mShapeHeight; + sizeChangedByRotation( width, height ); + + //adapt scene rect to have the same center and the new width / height + double x = transform().dx() + rect().width() / 2.0 - width / 2.0; + double y = transform().dy() + rect().height() / 2.0 - height / 2.0; + QgsComposerItem::setSceneRect( QRectF( x, y, width, height ) ); + + QgsComposerItem::setRotation( r ); +} + +void QgsComposerShape::setSceneRect( const QRectF& rectangle ) +{ + QgsComposerItem::setSceneRect( rectangle ); + + //consider to change size of the shape if the rectangle changes width and/or height + double newShapeWidth = rectangle.width(); + double newShapeHeight = rectangle.height(); + imageSizeConsideringRotation( newShapeWidth, newShapeHeight ); + mShapeWidth = newShapeWidth; + mShapeHeight = newShapeHeight; +} diff --git a/src/core/composer/qgscomposershape.h b/src/core/composer/qgscomposershape.h index fc6e125dd7b..7c91bd7a8f4 100755 --- a/src/core/composer/qgscomposershape.h +++ b/src/core/composer/qgscomposershape.h @@ -23,6 +23,7 @@ /**A composer items that draws common shapes (ellipse, triangle, rectangle)*/ class CORE_EXPORT QgsComposerShape: public QgsComposerItem { + Q_OBJECT public: enum Shape @@ -62,6 +63,14 @@ class CORE_EXPORT QgsComposerShape: public QgsComposerItem bool transparentFill() const; void setTransparentFill( bool transparent ); + /**Sets this items bound in scene coordinates such that 1 item size units + corresponds to 1 scene size unit. Also, the shape is scaled*/ + void setSceneRect( const QRectF& rectangle ); + + public slots: + /**Sets item rotation and resizes item bounds such that the shape always has the same size*/ + virtual void setRotation( double r ); + private: /**Ellipse, rectangle or triangle*/ @@ -70,6 +79,8 @@ class CORE_EXPORT QgsComposerShape: public QgsComposerItem QPen mPen; /**Shape fill*/ QBrush mBrush; + double mShapeWidth; + double mShapeHeight; /**Apply default graphics settings*/ void initGraphicsSettings();