mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
* fix support for data-defined properties in SVG export * remove drawRects and let it fallback to drawPath and drawPolygon * close arcs
293 lines
7.2 KiB
C++
293 lines
7.2 KiB
C++
/***************************************************************************
|
|
qgsdxpaintengine.cpp
|
|
--------------------
|
|
begin : November 2013
|
|
copyright : (C) 2013 by Marco Hugentobler
|
|
email : marco at sourcepole dot ch
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "qgsdxfpaintengine.h"
|
|
#include "qgsdxfexport.h"
|
|
#include "qgsdxfpaintdevice.h"
|
|
#include "qgslogger.h"
|
|
|
|
QgsDxfPaintEngine::QgsDxfPaintEngine( const QgsDxfPaintDevice* dxfDevice, QgsDxfExport* dxf )
|
|
: QPaintEngine( QPaintEngine::AllFeatures /*QPaintEngine::PainterPaths | QPaintEngine::PaintOutsidePaintEvent*/ )
|
|
, mPaintDevice( dxfDevice )
|
|
, mDxf( dxf )
|
|
{
|
|
}
|
|
|
|
QgsDxfPaintEngine::~QgsDxfPaintEngine()
|
|
{
|
|
}
|
|
|
|
bool QgsDxfPaintEngine::begin( QPaintDevice* pdev )
|
|
{
|
|
Q_UNUSED( pdev );
|
|
return true;
|
|
}
|
|
|
|
bool QgsDxfPaintEngine::end()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
QPaintEngine::Type QgsDxfPaintEngine::type() const
|
|
{
|
|
return QPaintEngine::User;
|
|
}
|
|
|
|
void QgsDxfPaintEngine::drawPixmap( const QRectF& r, const QPixmap& pm, const QRectF& sr )
|
|
{
|
|
Q_UNUSED( r );
|
|
Q_UNUSED( pm );
|
|
Q_UNUSED( sr );
|
|
}
|
|
|
|
void QgsDxfPaintEngine::updateState( const QPaintEngineState& state )
|
|
{
|
|
if ( state.state() & QPaintEngine::DirtyTransform )
|
|
mTransform = state.transform();
|
|
|
|
if ( state.state() & QPaintEngine::DirtyPen )
|
|
mPen = state.pen();
|
|
|
|
if ( state.state() & QPaintEngine::DirtyBrush )
|
|
mBrush = state.brush();
|
|
}
|
|
|
|
void QgsDxfPaintEngine::setRing( QgsPolyline &polyline, const QPointF *points, int pointCount )
|
|
{
|
|
polyline.resize( pointCount );
|
|
for ( int i = 0; i < pointCount; ++i )
|
|
polyline[i] = toDxfCoordinates( points[i] );
|
|
}
|
|
|
|
void QgsDxfPaintEngine::drawPolygon( const QPointF *points, int pointCount, PolygonDrawMode mode )
|
|
{
|
|
Q_UNUSED( mode );
|
|
if ( !mDxf || !mPaintDevice )
|
|
return;
|
|
|
|
QgsPolygon polygon( 1 );
|
|
setRing( polygon[0], points, pointCount );
|
|
|
|
if ( mode == QPaintEngine::PolylineMode )
|
|
{
|
|
if ( mPen.style() != Qt::NoPen && mPen.brush().style() != Qt::NoBrush )
|
|
mDxf->writePolyline( polygon[0], mLayer, "CONTINUOUS", mPen.color(), currentWidth() );
|
|
}
|
|
else
|
|
{
|
|
if ( mBrush.style() != Qt::NoBrush )
|
|
mDxf->writePolygon( polygon, mLayer, "SOLID", mBrush.color() );
|
|
}
|
|
}
|
|
|
|
void QgsDxfPaintEngine::drawPath( const QPainterPath& path )
|
|
{
|
|
int pathLength = path.elementCount();
|
|
for ( int i = 0; i < pathLength; ++i )
|
|
{
|
|
const QPainterPath::Element& pathElem = path.elementAt( i );
|
|
if ( pathElem.type == QPainterPath::MoveToElement )
|
|
{
|
|
moveTo( pathElem.x, pathElem.y );
|
|
}
|
|
else if ( pathElem.type == QPainterPath::LineToElement )
|
|
{
|
|
lineTo( pathElem.x, pathElem.y );
|
|
}
|
|
else if ( pathElem.type == QPainterPath::CurveToElement )
|
|
{
|
|
curveTo( pathElem.x, pathElem.y );
|
|
}
|
|
else if ( pathElem.type == QPainterPath::CurveToDataElement )
|
|
{
|
|
mCurrentCurve.append( QPointF( pathElem.x, pathElem.y ) );
|
|
}
|
|
}
|
|
endCurve();
|
|
endPolygon();
|
|
|
|
if ( mPolygon.size() > 0 && mBrush.style() != Qt::NoBrush )
|
|
mDxf->writePolygon( mPolygon, mLayer, "SOLID", mBrush.color() );
|
|
|
|
mPolygon.clear();
|
|
}
|
|
|
|
void QgsDxfPaintEngine::moveTo( double dx, double dy )
|
|
{
|
|
endCurve();
|
|
endPolygon();
|
|
mCurrentPolygon.append( QPointF( dx, dy ) );
|
|
}
|
|
|
|
void QgsDxfPaintEngine::lineTo( double dx, double dy )
|
|
{
|
|
endCurve();
|
|
mCurrentPolygon.append( QPointF( dx, dy ) );
|
|
}
|
|
|
|
void QgsDxfPaintEngine::curveTo( double dx, double dy )
|
|
{
|
|
endCurve();
|
|
if ( mCurrentPolygon.size() > 0 )
|
|
mCurrentCurve.append( mCurrentPolygon.last() );
|
|
|
|
mCurrentCurve.append( QPointF( dx, dy ) );
|
|
}
|
|
|
|
void QgsDxfPaintEngine::endPolygon()
|
|
{
|
|
if ( mCurrentPolygon.size() > 1 )
|
|
{
|
|
if ( mPen.style() != Qt::NoPen )
|
|
drawPolygon( mCurrentPolygon.constData(), mCurrentPolygon.size(), QPaintEngine::PolylineMode );
|
|
|
|
mPolygon.resize( mPolygon.size() + 1 );
|
|
setRing( mPolygon[ mPolygon.size() - 1 ], mCurrentPolygon.constData(), mCurrentPolygon.size() );
|
|
}
|
|
mCurrentPolygon.clear();
|
|
}
|
|
|
|
void QgsDxfPaintEngine::endCurve()
|
|
{
|
|
if ( mCurrentCurve.size() < 1 )
|
|
return;
|
|
|
|
if ( mCurrentPolygon.size() < 1 )
|
|
{
|
|
mCurrentCurve.clear();
|
|
return;
|
|
}
|
|
|
|
if ( mCurrentCurve.size() >= 3 )
|
|
{
|
|
double t = 0.05;
|
|
for ( int i = 1; i <= 20; ++i ) //approximate curve with 20 segments
|
|
{
|
|
mCurrentPolygon.append( bezierPoint( mCurrentCurve, t ) );
|
|
t += 0.05;
|
|
}
|
|
}
|
|
else if ( mCurrentCurve.size() == 2 )
|
|
{
|
|
mCurrentPolygon.append( mCurrentCurve.at( 1 ) );
|
|
}
|
|
mCurrentCurve.clear();
|
|
}
|
|
|
|
void QgsDxfPaintEngine::drawLines( const QLineF* lines, int lineCount )
|
|
{
|
|
if ( !mDxf || !mPaintDevice || !lines || mPen.style() == Qt::NoPen )
|
|
return;
|
|
|
|
for ( int i = 0; i < lineCount; ++i )
|
|
{
|
|
QgsPoint pt1 = toDxfCoordinates( lines[i].p1() );
|
|
QgsPoint pt2 = toDxfCoordinates( lines[i].p2() );
|
|
mDxf->writeLine( pt1, pt2, mLayer, "CONTINUOUS", mPen.color(), currentWidth() );
|
|
}
|
|
}
|
|
|
|
QgsPoint QgsDxfPaintEngine::toDxfCoordinates( const QPointF& pt ) const
|
|
{
|
|
if ( !mPaintDevice || !mDxf )
|
|
return QgsPoint( pt.x(), pt.y() );
|
|
|
|
QPointF dxfPt = mPaintDevice->dxfCoordinates( mTransform.map( pt ) ) + mShift;
|
|
return QgsPoint( dxfPt.x(), dxfPt.y() );
|
|
}
|
|
|
|
|
|
double QgsDxfPaintEngine::currentWidth() const
|
|
{
|
|
if ( !mPaintDevice )
|
|
return 1;
|
|
|
|
return mPen.widthF() * mPaintDevice->widthScaleFactor();
|
|
}
|
|
|
|
QPointF QgsDxfPaintEngine::bezierPoint( const QList<QPointF>& controlPolygon, double t )
|
|
{
|
|
double x = 0;
|
|
double y = 0;
|
|
int cPolySize = controlPolygon.size();
|
|
double bPoly = 0;
|
|
|
|
QList<QPointF>::const_iterator it = controlPolygon.constBegin();
|
|
int i = 0;
|
|
for ( ; it != controlPolygon.constEnd(); ++it )
|
|
{
|
|
bPoly = bernsteinPoly( cPolySize - 1, i, t );
|
|
x += ( it->x() * bPoly );
|
|
y += ( it->y() * bPoly );
|
|
++i;
|
|
}
|
|
|
|
return QPointF( x, y );
|
|
}
|
|
|
|
double QgsDxfPaintEngine::bernsteinPoly( int n, int i, double t )
|
|
{
|
|
if ( i < 0 )
|
|
return 0;
|
|
|
|
return lower( n, i )*power( t, i )*power(( 1 - t ), ( n - i ) );
|
|
}
|
|
|
|
int QgsDxfPaintEngine::lower( int n, int i )
|
|
{
|
|
if ( i >= 0 && i <= n )
|
|
{
|
|
return faculty( n ) / ( faculty( i )*faculty( n - i ) );
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
double QgsDxfPaintEngine::power( double a, int b )
|
|
{
|
|
if ( b == 0 )
|
|
return 1;
|
|
|
|
double tmp = a;
|
|
for ( int i = 2; i <= qAbs(( double )b ); i++ )
|
|
a *= tmp;
|
|
|
|
if ( b > 0 )
|
|
return a;
|
|
else
|
|
return 1.0 / a;
|
|
}
|
|
|
|
int QgsDxfPaintEngine::faculty( int n )
|
|
{
|
|
if ( n < 0 )//Is faculty also defined for negative integers?
|
|
return 0;
|
|
|
|
int i;
|
|
int result = n;
|
|
|
|
if ( n == 0 || n == 1 )
|
|
return 1; //faculty of 0 is 1!
|
|
|
|
for ( i = n - 1; i >= 2; i-- )
|
|
result *= i;
|
|
|
|
return result;
|
|
}
|