Adjust the arrow points to fit inside the specified bounding

box, as opposed to adjusting the bounding box for the arrow points (except
 when bounding box is too small)
This commit is contained in:
Sandro Mani 2014-04-06 23:50:32 +02:00
parent e71930fd45
commit e214741c62
2 changed files with 69 additions and 26 deletions

View File

@ -26,6 +26,8 @@ QgsComposerArrow::QgsComposerArrow( QgsComposition* c )
: QgsComposerItem( c )
, mStartPoint( 0, 0 )
, mStopPoint( 0, 0 )
, mStartXIdx( 0 )
, mStartYIdx( 0 )
, mMarkerMode( DefaultMarker )
, mArrowColor( QColor( 0, 0, 0 ) )
{
@ -39,6 +41,8 @@ QgsComposerArrow::QgsComposerArrow( const QPointF& startPoint, const QPointF& st
, mMarkerMode( DefaultMarker )
, mArrowColor( QColor( 0, 0, 0 ) )
{
mStartXIdx = mStopPoint.x() < mStartPoint.x();
mStartYIdx = mStopPoint.y() < mStartPoint.y();
initGraphicsSettings();
adaptItemSceneRect();
}
@ -97,18 +101,31 @@ void QgsComposerArrow::paint( QPainter* painter, const QStyleOptionGraphicsItem
void QgsComposerArrow::setSceneRect( const QRectF& rectangle )
{
//maintain the relative position of start and stop point in the rectangle
double startPointXPos = ( mStartPoint.x() - pos().x() ) / rect().width();
double startPointYPos = ( mStartPoint.y() - pos().y() ) / rect().height();
double stopPointXPos = ( mStopPoint.x() - pos().x() ) / rect().width();
double stopPointYPos = ( mStopPoint.y() - pos().y() ) / rect().height();
if ( rectangle.width() < 0 )
{
mStartXIdx = 1 - mStartXIdx;
}
if ( rectangle.height() < 0 )
{
mStartYIdx = 1 - mStartYIdx;
}
mStartPoint.setX( rectangle.left() + startPointXPos * rectangle.width() );
mStartPoint.setY( rectangle.top() + startPointYPos * rectangle.height() );
mStopPoint.setX( rectangle.left() + stopPointXPos * rectangle.width() );
mStopPoint.setY( rectangle.top() + stopPointYPos * rectangle.height() );
double margin = computeMarkerMargin();
adaptItemSceneRect();
// Ensure the rectangle is at least as large as needed to include the markers
QRectF rect = rectangle.unite( QRectF( rectangle.x(), rectangle.y(), 2. * margin, 2. * margin ) );
// Compute new start and stop positions
double x[2] = {rect.x(), rect.x() + rect.width()};
double y[2] = {rect.y(), rect.y() + rect.height()};
double xsign = x[mStartXIdx] < x[1 - mStartXIdx] ? 1.0 : -1.0;
double ysign = y[mStartYIdx] < y[1 - mStartYIdx] ? 1.0 : -1.0;
mStartPoint = QPointF( x[mStartXIdx] + xsign * margin, y[mStartYIdx] + ysign * margin );
mStopPoint = QPointF( x[1 - mStartXIdx] - xsign * margin, y[1 - mStartYIdx] - ysign * margin );
QgsComposerItem::setSceneRect( rect );
}
void QgsComposerArrow::drawHardcodedMarker( QPainter *p, MarkerType type )
@ -258,30 +275,41 @@ void QgsComposerArrow::setArrowHeadWidth( double width )
adaptItemSceneRect();
}
double QgsComposerArrow::computeMarkerMargin() const
{
double margin = 0;
if ( mMarkerMode == DefaultMarker )
{
margin = mPen.widthF() / 2.0 + mArrowHeadWidth / 2.0;
}
else if ( mMarkerMode == NoMarker )
{
margin = mPen.widthF() / 2.0;
}
else if ( mMarkerMode == SVGMarker )
{
double maxArrowHeight = qMax( mStartArrowHeadHeight, mStopArrowHeadHeight );
margin = mPen.widthF() / 2 + qMax( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 );
}
return margin;
}
void QgsComposerArrow::adaptItemSceneRect()
{
//rectangle containing start and end point
QRectF rect = QRectF( qMin( mStartPoint.x(), mStopPoint.x() ), qMin( mStartPoint.y(), mStopPoint.y() ),
qAbs( mStopPoint.x() - mStartPoint.x() ), qAbs( mStopPoint.y() - mStartPoint.y() ) );
double enlarge = 0;
if ( mMarkerMode == DefaultMarker )
{
enlarge = mPen.widthF() / 2.0 + mArrowHeadWidth / 2.0;
}
else if ( mMarkerMode == NoMarker )
{
enlarge = mPen.widthF() / 2.0;
}
else if ( mMarkerMode == SVGMarker )
{
double maxArrowHeight = qMax( mStartArrowHeadHeight, mStopArrowHeadHeight );
enlarge = mPen.widthF() / 2 + qMax( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 );
}
double enlarge = computeMarkerMargin();
rect.adjust( -enlarge, -enlarge, enlarge, enlarge );
QgsComposerItem::setSceneRect( rect );
}
void QgsComposerArrow::setMarkerMode( MarkerMode mode )
{
mMarkerMode = mode;
adaptItemSceneRect();
}
bool QgsComposerArrow::writeXML( QDomElement& elem, QDomDocument & doc ) const
{
QDomElement composerArrowElem = doc.createElement( "ComposerArrow" );
@ -362,7 +390,12 @@ bool QgsComposerArrow::readXML( const QDomElement& itemElem, const QDomDocument&
mStopPoint.setY( stopPointElem.attribute( "y", "0.0" ).toDouble() );
}
mStartXIdx = mStopPoint.x() < mStartPoint.x();
mStartYIdx = mStopPoint.y() < mStartPoint.y();
adaptItemSceneRect();
emit itemChanged();
return true;
}

View File

@ -63,7 +63,7 @@ class CORE_EXPORT QgsComposerArrow: public QgsComposerItem
void setArrowColor( const QColor& c ) { mArrowColor = c; }
MarkerMode markerMode() const { return mMarkerMode;}
void setMarkerMode( MarkerMode mode ) {mMarkerMode = mode;}
void setMarkerMode( MarkerMode mode );
/** stores state in Dom element
* @param elem is Dom element corresponding to 'Composer' tag
@ -88,6 +88,12 @@ class CORE_EXPORT QgsComposerArrow: public QgsComposerItem
QPointF mStartPoint;
QPointF mStopPoint;
/**Considering the rectangle as spanning [x[0], x[1]] x [y[0], y[1]], these
* indices specify which index {0, 1} corresponds to the start point
* coordinate of the respective dimension*/
int mStartXIdx;
int mStartYIdx;
QPen mPen;
QBrush mBrush;
@ -110,6 +116,8 @@ class CORE_EXPORT QgsComposerArrow: public QgsComposerItem
/**Adapts the item scene rect to contain the start point, the stop point including the arrow marker and the outline.
Needs to be called whenever the arrow width/height, the outline with or the endpoints are changed*/
void adaptItemSceneRect();
/**Computes the margin around the line necessary to include the markers */
double computeMarkerMargin() const;
/**Draws the default marker at the line end*/
void drawHardcodedMarker( QPainter* p, MarkerType type );
/**Draws a user-defined marker (must be an svg file)*/
@ -119,3 +127,5 @@ class CORE_EXPORT QgsComposerArrow: public QgsComposerItem
};
#endif // QGSCOMPOSERARROW_H