Skip to content
Permalink
Browse files
Merge linepattern offset
  • Loading branch information
mhugent committed Aug 30, 2011
2 parents 26aa007 + 03c13ad commit 6c0f56c
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 89 deletions.
@@ -326,8 +326,7 @@ class QgsComposerItem: QObject, QGraphicsRectItem
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
@@ -30,6 +30,7 @@
#include "qgsapplication.h"
#include "qgsrectangle.h" //just for debugging
#include "qgslogger.h"
#include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance

#include <cmath>

@@ -954,7 +955,7 @@ bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& heigh

//assume points 1 and 3 are on the rectangle boundaries. Calculate 2 and 4.
double distM1 = sqrt(( x1 - midX ) * ( x1 - midX ) + ( y1 - midY ) * ( y1 - midY ) );
QPointF p2 = pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x2, y2 ), distM1 );
QPointF p2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x2, y2 ), distM1 );

if ( p2.x() < width && p2.x() > 0 && p2.y() < height && p2.y() > 0 )
{
@@ -965,8 +966,8 @@ bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& heigh

//else assume that points 2 and 4 are on the rectangle boundaries. Calculate 1 and 3
double distM2 = sqrt(( x2 - midX ) * ( x2 - midX ) + ( y2 - midY ) * ( y2 - midY ) );
QPointF p1 = pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x1, y1 ), distM2 );
QPointF p3 = pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x3, y3 ), distM2 );
QPointF p1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x1, y1 ), distM2 );
QPointF p3 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x3, y3 ), distM2 );
width = sqrt(( x2 - p1.x() ) * ( x2 - p1.x() ) + ( y2 - p1.y() ) * ( y2 - p1.y() ) );
height = sqrt(( p3.x() - x2 ) * ( p3.x() - x2 ) + ( p3.y() - y2 ) * ( p3.y() - y2 ) );
return true;
@@ -1036,15 +1037,6 @@ bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, d
return false;
}

QPointF QgsComposerItem::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 );
}

void QgsComposerItem::sizeChangedByRotation( double& width, double& height )
{
if ( mRotation == 0.0 )
@@ -315,8 +315,7 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
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
@@ -31,6 +31,7 @@

#include "qgslabel.h"
#include "qgslabelattributes.h"
#include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance

#include <QGraphicsScene>
#include <QGraphicsView>
@@ -881,7 +882,7 @@ void QgsComposerMap::drawGrid( QPainter* p )
for ( ; vIt != verticalLines.constEnd(); ++vIt )
{
//start mark
crossEnd1 = pointOnLineWithDistance( vIt->second.p1(), vIt->second.p2(), mCrossLength );
crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( vIt->second.p1(), vIt->second.p2(), mCrossLength );
p->drawLine( vIt->second.p1(), crossEnd1 );

//test for intersection with every horizontal line
@@ -890,35 +891,35 @@ void QgsComposerMap::drawGrid( QPainter* p )
{
if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
{
crossEnd1 = pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength );
crossEnd2 = pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength );
crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength );
crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength );
p->drawLine( crossEnd1, crossEnd2 );
}
}
//end mark
QPointF crossEnd2 = pointOnLineWithDistance( vIt->second.p2(), vIt->second.p1(), mCrossLength );
QPointF crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( vIt->second.p2(), vIt->second.p1(), mCrossLength );
p->drawLine( vIt->second.p2(), crossEnd2 );
}

hIt = horizontalLines.constBegin();
for ( ; hIt != horizontalLines.constEnd(); ++hIt )
{
//start mark
crossEnd1 = pointOnLineWithDistance( hIt->second.p1(), hIt->second.p2(), mCrossLength );
crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( hIt->second.p1(), hIt->second.p2(), mCrossLength );
p->drawLine( hIt->second.p1(), crossEnd1 );

vIt = verticalLines.constBegin();
for ( ; vIt != verticalLines.constEnd(); ++vIt )
{
if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
{
crossEnd1 = pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength );
crossEnd2 = pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength );
crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength );
crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength );
p->drawLine( crossEnd1, crossEnd2 );
}
}
//end mark
crossEnd1 = pointOnLineWithDistance( hIt->second.p2(), hIt->second.p1(), mCrossLength );
crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( hIt->second.p2(), hIt->second.p1(), mCrossLength );
p->drawLine( hIt->second.p2(), crossEnd1 );
}

@@ -386,6 +386,7 @@ QgsSymbolLayerV2* QgsLinePatternFillSymbolLayer::create( const QgsStringMap& pro
double distance = 5;
double lineWidth = 0.5;
QColor color( Qt::black );
double offset = 0.0;

if ( properties.contains( "lineangle" ) )
{
@@ -410,6 +411,12 @@ QgsSymbolLayerV2* QgsLinePatternFillSymbolLayer::create( const QgsStringMap& pro
color = QgsSymbolLayerV2Utils::decodeColor( properties["color"] );
}
patternLayer->setColor( color );

if ( properties.contains( "offset" ) )
{
offset = properties["offset"].toDouble();
}
patternLayer->setOffset( offset );
return patternLayer;
}

@@ -420,87 +427,101 @@ QString QgsLinePatternFillSymbolLayer::layerType() const

void QgsLinePatternFillSymbolLayer::startRender( QgsSymbolV2RenderContext& context )
{
double outlinePixelWidth = context.outputPixelSize( mLineWidth );
double outputPixelDist = context.outputPixelSize( mDistance );
double outputPixelOffset = context.outputPixelSize( mOffset );

//create image
int height, width;
if ( doubleNear( mLineAngle, 0 ) || doubleNear( mLineAngle, 360 ) || doubleNear( mLineAngle, 90 ) || doubleNear( mLineAngle, 180 ) || doubleNear( mLineAngle, 270 ) )
{
height = context.outputPixelSize( mDistance );
height = outputPixelDist;
width = height; //width can be set to arbitrary value
}
else
{
height = fabs( context.outputPixelSize( mDistance ) / cos( mLineAngle * M_PI / 180 ) ); //keep perpendicular distance between lines constant
width = fabs( height / tan( mLineAngle * M_PI / 180 ) );
height = qAbs( outputPixelDist / cos( mLineAngle * M_PI / 180 ) ); //keep perpendicular distance between lines constant
width = qAbs( height / tan( mLineAngle * M_PI / 180 ) );
}

double outlinePixelWidth = context.outputPixelSize( mLineWidth );

//find a suitable multiple of width and heigh
//depending on the angle, we might need to render into a larger image and use a subset of it
int dx = 0;
int dy = 0;

QImage patternImage( qAbs( width ), height, QImage::Format_ARGB32 );
QImage patternImage( width, height, QImage::Format_ARGB32 );
patternImage.fill( 0 );
QPainter p( &patternImage );

p.setRenderHint( QPainter::Antialiasing, true );
QPen pen( mColor );
pen.setWidthF( outlinePixelWidth );
pen.setCapStyle( Qt::FlatCap );
p.setPen( pen );

//draw line and dots in the border
QPoint p1, p2, p3, p4, p5, p6;
if ( doubleNear( mLineAngle, 0.0 ) || doubleNear( mLineAngle, 360.0 ) || doubleNear( mLineAngle, 180.0 ) )
{
p.drawLine( QPointF( 0, height / 2.0 ), QPointF( width, height / 2.0 ) );
p1 = QPoint( 0, height );
p2 = QPoint( width, height );
p3 = QPoint( 0, 0 );
p4 = QPoint( width, 0 );
p5 = QPoint( 0, 2 * height );
p6 = QPoint( width, 2 * height );
}
else if ( doubleNear( mLineAngle, 90.0 ) || doubleNear( mLineAngle, 270.0 ) )
{
p.drawLine( QPointF( width / 2.0, 0 ), QPointF( width / 2.0, height ) );
p1 = QPoint( 0, height );
p2 = QPoint( 0, 0 );
p3 = QPoint( width, height );
p4 = QPoint( width, 0 );
p5 = QPoint( -width, height );
p6 = QPoint( -width, 0 );
}
else if (( mLineAngle > 0 && mLineAngle < 90 ) || ( mLineAngle > 180 && mLineAngle < 270 ) )
{
p.drawLine( QPointF( 0, height ), QPointF( width, 0 ) );
dx = outputPixelDist * cos(( 90 - mLineAngle ) * M_PI / 180.0 );
dy = outputPixelDist * sin(( 90 - mLineAngle ) * M_PI / 180.0 );
p1 = QPoint( 0, height );
p2 = QPoint( width, 0 );
p3 = QPoint( -dx, height - dy );
p4 = QPoint( width - dx, -dy ); //p4 = QPoint( p3.x() + width, p3.y() - height );
p5 = QPoint( dx, height + dy );
p6 = QPoint( width + dx, dy ); //p6 = QPoint( p5.x() + width, p5.y() - height );
}
else if (( mLineAngle < 180 ) || ( mLineAngle > 270 && mLineAngle < 360 ) )
{
p.drawLine( QPointF( width, height ), QPointF( 0, 0 ) );
}

//todo: calculate triangles more accurately
double d1 = 0;
double d2 = 0;
QPolygonF triangle1, triangle2;
if ( mLineAngle > 0 && mLineAngle < 90 )
{
d1 = ( outlinePixelWidth / 2.0 ) / cos( mLineAngle * M_PI / 180 );
d2 = ( outlinePixelWidth / 2.0 ) / cos(( 90 - mLineAngle ) * M_PI / 180 );
triangle1 << QPointF( 0, 0 ) << QPointF( 0, d1 ) << QPointF( d2, 0 ) << QPointF( 0, 0 );
triangle2 << QPointF( width, height ) << QPointF( width - d2, height ) << QPointF( width, height - d1 ) << QPointF( width, height );
}
else if ( mLineAngle > 90 && mLineAngle < 180 )
{
d1 = ( outlinePixelWidth / 2.0 ) / cos(( mLineAngle - 90 ) * M_PI / 180 );
d2 = ( outlinePixelWidth / 2.0 ) / cos(( 180 - mLineAngle ) * M_PI / 180 );
triangle1 << QPointF( width, 0 ) << QPointF( width - d1, 0 ) << QPointF( width, d2 ) << QPointF( width, 0 );
triangle2 << QPointF( 0, height ) << QPointF( 0, height - d2 ) << QPointF( d1, height ) << QPointF( 0, height );
}
else if ( mLineAngle > 180 && mLineAngle < 270 )
{
d1 = ( outlinePixelWidth / 2.0 ) / cos(( mLineAngle - 180 ) * M_PI / 180 );
d2 = ( outlinePixelWidth / 2.0 ) / cos(( 270 - mLineAngle ) * M_PI / 180 );
triangle1 << QPointF( 0, 0 ) << QPointF( 0, d1 ) << QPointF( d2, 0 ) << QPointF( 0, 0 );
triangle2 << QPointF( width, height ) << QPointF( width - d2, height ) << QPointF( width, height - d1 ) << QPointF( width, height );
}
else if ( mLineAngle > 270 && mLineAngle < 360 )
{
d1 = ( outlinePixelWidth / 2.0 ) / cos(( mLineAngle - 270 ) * M_PI / 180 );
d2 = ( outlinePixelWidth / 2.0 ) / cos(( 360 - mLineAngle ) * M_PI / 180 );
triangle1 << QPointF( width, 0 ) << QPointF( width - d1, 0 ) << QPointF( width, d2 ) << QPointF( width, 0 );
triangle2 << QPointF( 0, height ) << QPointF( 0, height - d2 ) << QPointF( d1, height ) << QPointF( 0, height );
}

p.setPen( QPen( Qt::NoPen ) );
p.setBrush( QBrush( mColor ) );
p.drawPolygon( triangle1 );
p.drawPolygon( triangle2 );
dy = outputPixelDist * cos(( 180 - mLineAngle ) * M_PI / 180 );
dx = outputPixelDist * sin(( 180 - mLineAngle ) * M_PI / 180 );
p1 = QPoint( width, height );
p2 = QPoint( 0, 0 );
p5 = QPoint( width + dx, height - dy );
p6 = QPoint( p5.x() - width, p5.y() - height ); //p6 = QPoint( dx, -dy );
p3 = QPoint( width - dx, height + dy );
p4 = QPoint( p3.x() - width, p3.y() - height ); //p4 = QPoint( -dx, dy );
}

if ( !doubleNear( mOffset, 0.0 ) ) //shift everything
{
QPointF tempPt;
tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
p3 = QPoint( tempPt.x(), tempPt.y() );
tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
p4 = QPoint( tempPt.x(), tempPt.y() );
tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
p5 = QPoint( tempPt.x(), tempPt.y() );
tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
p6 = QPoint( tempPt.x(), tempPt.y() );

//update p1, p2 last
tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelOffset ).toPoint();
p1 = QPoint( tempPt.x(), tempPt.y() );
tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelOffset ).toPoint();
p2 = QPoint( tempPt.x(), tempPt.y() );;
}

p.drawLine( p1, p2 );
p.drawLine( p3, p4 );
p.drawLine( p5, p6 );
p.end();

//set image to mBrush
@@ -536,6 +557,7 @@ QgsStringMap QgsLinePatternFillSymbolLayer::properties() const
map.insert( "distance", QString::number( mDistance ) );
map.insert( "linewidth", QString::number( mLineWidth ) );
map.insert( "color", QgsSymbolLayerV2Utils::encodeColor( mColor ) );
map.insert( "offset", QString::number( mOffset ) );
return map;
}

@@ -157,6 +157,8 @@ class CORE_EXPORT QgsLinePatternFillSymbolLayer: public QgsImageFillSymbolLayer
double lineWidth() const { return mLineWidth; }
void setColor( const QColor& c ) { mColor = c; }
QColor color() const { return mColor; }
void setOffset( double offset ) { mOffset = offset; }
double offset() const { return mOffset; }

protected:
/**Distance (in mm or map units) between lines*/
@@ -166,6 +168,8 @@ class CORE_EXPORT QgsLinePatternFillSymbolLayer: public QgsImageFillSymbolLayer
QColor mColor;
/**Vector line angle in degrees (0 = horizontal, counterclockwise)*/
double mLineAngle;
/**Offset perpendicular to line direction*/
double mOffset;
};

class CORE_EXPORT QgsPointPatternFillSymbolLayer: public QgsImageFillSymbolLayer
@@ -809,3 +809,12 @@ void QgsSymbolLayerV2Utils::sortVariantList( QList<QVariant>& list, Qt::SortOrde
qSort( list.begin(), list.end(), _QVariantGreaterThan );
}
}

QPointF QgsSymbolLayerV2Utils::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance )
{
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 );
}
@@ -83,8 +83,10 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
/**Multiplies opacity of image pixel values with a (global) transparency value*/
static void multiplyImageOpacity( QImage* image, qreal alpha );

/**Sorts the passed list in requested order*/
static void sortVariantList( QList<QVariant>& list, Qt::SortOrder order );
/**Sorts the passed list in requested order*/
static void sortVariantList( QList<QVariant>& list, Qt::SortOrder order );
/**Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point*/
static QPointF pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance );
};

class QPolygonF;
@@ -930,6 +930,7 @@ void QgsLinePatternFillSymbolLayerWidget::setSymbolLayer( QgsSymbolLayerV2* laye
mAngleSpinBox->setValue( mLayer->lineAngle() );
mDistanceSpinBox->setValue( mLayer->distance() );
mLineWidthSpinBox->setValue( mLayer->lineWidth() );
mOffsetSpinBox->setValue( mLayer->offset() );
}
}

@@ -965,6 +966,15 @@ void QgsLinePatternFillSymbolLayerWidget::on_mLineWidthSpinBox_valueChanged( dou
}
}

void QgsLinePatternFillSymbolLayerWidget::on_mOffsetSpinBox_valueChanged( double d )
{
if ( mLayer )
{
mLayer->setOffset( d );
emit changed();
}
}

void QgsLinePatternFillSymbolLayerWidget::on_mColorPushButton_clicked()
{
if ( mLayer )

0 comments on commit 6c0f56c

Please sign in to comment.