26using namespace Qt::StringLiterals;
29 : QPaintEngine( QPaintEngine::AllFeatures )
30 , mPaintDevice( dxfDevice )
47 return QPaintEngine::User;
59 if ( state.state() & QPaintEngine::DirtyTransform )
60 mTransform = state.transform();
62 if ( state.state() & QPaintEngine::DirtyPen )
65 if ( state.state() & QPaintEngine::DirtyBrush )
66 mBrush = state.brush();
68 if ( state.state() & QPaintEngine::DirtyOpacity )
70 mOpacity = state.opacity();
78 if ( state.state() & QPaintEngine::DirtyClipRegion )
80 if ( state.clipOperation() == Qt::NoClip )
82 mClipPath = QPainterPath();
87 p.addRegion( state.clipRegion() );
88 mClipPath = state.transform().map( p );
91 if ( state.state() & QPaintEngine::DirtyClipPath )
93 if ( state.clipOperation() == Qt::NoClip )
95 mClipPath = QPainterPath();
99 mClipPath = state.transform().map( state.clipPath() );
102 if ( state.state() & ( QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled ) )
104 mClipEnabled = state.isClipEnabled() && !mClipPath.isEmpty();
108QList<QPolygonF> QgsDxfPaintEngine::clipPolygonLogical(
const QPolygonF &polyLogical )
const
110 if ( !mClipEnabled || mClipPath.isEmpty() || polyLogical.isEmpty() )
111 return { polyLogical };
117 const QPolygonF polyDevice = mTransform.map( polyLogical );
118 QPainterPath polyPath;
119 polyPath.addPolygon( polyDevice );
120 const QPainterPath clipped = polyPath.intersected( mClipPath );
121 if ( clipped.isEmpty() )
124 bool invertible =
false;
125 const QTransform inv = mTransform.inverted( &invertible );
127 return { polyLogical };
129 QList<QPolygonF> result;
130 const QList<QPolygonF> subs = clipped.toSubpathPolygons();
131 result.reserve( subs.size() );
132 for (
const QPolygonF &sub : subs )
134 if ( sub.size() < 2 )
136 result << inv.map( sub );
141void QgsDxfPaintEngine::setRing(
QgsPointSequence &polyline,
const QPointF *points,
int pointCount )
144 for (
int i = 0; i < pointCount; ++i )
145 polyline.append( toDxfCoordinates( points[i] ) );
151 if ( !mDxf || !mPaintDevice )
155 inputPoly.reserve( pointCount );
156 for (
int i = 0; i < pointCount; ++i )
157 inputPoly << points[i];
159 const QList<QPolygonF> polys = clipPolygonLogical( inputPoly );
160 for (
const QPolygonF &sub : polys )
162 if ( sub.size() < 2 )
167 setRing( polygon.last(), sub.constData(), sub.size() );
169 if ( mode == QPaintEngine::PolylineMode )
171 if ( mPen.style() != Qt::NoPen && mPen.brush().style() != Qt::NoBrush )
172 mDxf->writePolyline( polygon.at( 0 ), mLayer, u
"CONTINUOUS"_s, penColor(), currentWidth() );
176 if ( mBrush.style() != Qt::NoBrush )
177 mDxf->writePolygon( polygon, mLayer, u
"SOLID"_s, brushColor() );
184 const int pathLength = path.elementCount();
185 for (
int i = 0; i < pathLength; ++i )
187 const QPainterPath::Element &pathElem = path.elementAt( i );
188 if ( pathElem.type == QPainterPath::MoveToElement )
190 moveTo( pathElem.x, pathElem.y );
192 else if ( pathElem.type == QPainterPath::LineToElement )
194 lineTo( pathElem.x, pathElem.y );
196 else if ( pathElem.type == QPainterPath::CurveToElement )
198 curveTo( pathElem.x, pathElem.y );
200 else if ( pathElem.type == QPainterPath::CurveToDataElement )
202 mCurrentCurve.append( QPointF( pathElem.x, pathElem.y ) );
208 if ( !mPolygon.isEmpty() && mBrush.style() != Qt::NoBrush )
209 mDxf->writePolygon( mPolygon, mLayer, u
"SOLID"_s, brushColor() );
214void QgsDxfPaintEngine::moveTo(
double dx,
double dy )
218 mCurrentPolygon.append( QPointF( dx, dy ) );
221void QgsDxfPaintEngine::lineTo(
double dx,
double dy )
224 mCurrentPolygon.append( QPointF( dx, dy ) );
227void QgsDxfPaintEngine::curveTo(
double dx,
double dy )
230 if ( !mCurrentPolygon.isEmpty() )
231 mCurrentCurve.append( mCurrentPolygon.last() );
233 mCurrentCurve.append( QPointF( dx, dy ) );
236void QgsDxfPaintEngine::endPolygon()
238 if ( mCurrentPolygon.size() > 1 )
240 if ( mPen.style() != Qt::NoPen )
241 drawPolygon( mCurrentPolygon.constData(), mCurrentPolygon.size(), QPaintEngine::PolylineMode );
243 const QList<QPolygonF> clipped = clipPolygonLogical( mCurrentPolygon );
244 for (
const QPolygonF &sub : clipped )
246 if ( sub.size() < 2 )
249 setRing( mPolygon.last(), sub.constData(), sub.size() );
252 mCurrentPolygon.clear();
255void QgsDxfPaintEngine::endCurve()
257 if ( mCurrentCurve.empty() )
260 if ( mCurrentPolygon.empty() )
262 mCurrentCurve.clear();
266 if ( mCurrentCurve.size() >= 3 )
269 for (
int i = 1; i <= 20; ++i )
271 mCurrentPolygon.append( bezierPoint( mCurrentCurve, t ) );
275 else if ( mCurrentCurve.size() == 2 )
277 mCurrentPolygon.append( mCurrentCurve.at( 1 ) );
279 mCurrentCurve.clear();
284 if ( !mDxf || !mPaintDevice || !lines || mPen.style() == Qt::NoPen )
287 for (
int i = 0; i < lineCount; ++i )
289 mDxf->writeLine( toDxfCoordinates( lines[i].p1() ), toDxfCoordinates( lines[i].p2() ), mLayer, u
"CONTINUOUS"_s, penColor(), currentWidth() );
293QgsPoint QgsDxfPaintEngine::toDxfCoordinates( QPointF pt )
const
295 if ( !mPaintDevice || !mDxf )
298 const QPointF dxfPt = mPaintDevice->
dxfCoordinates( mTransform.map( pt ) ) + mShift;
299 return QgsPoint( dxfPt.x(), dxfPt.y() );
303double QgsDxfPaintEngine::currentWidth()
const
308 return mPen.widthF() * mPaintDevice->widthScaleFactor();
311QPointF QgsDxfPaintEngine::bezierPoint(
const QList<QPointF> &controlPolygon,
double t )
315 const int cPolySize = controlPolygon.size();
318 QList<QPointF>::const_iterator it = controlPolygon.constBegin();
320 for ( ; it != controlPolygon.constEnd(); ++it )
322 bPoly = bernsteinPoly( cPolySize - 1, i, t );
323 x += ( it->x() * bPoly );
324 y += ( it->y() * bPoly );
328 return QPointF( x, y );
331double QgsDxfPaintEngine::bernsteinPoly(
int n,
int i,
double t )
336 return lower( n, i ) * power( t, i ) * power( ( 1 - t ), ( n - i ) );
339int QgsDxfPaintEngine::lower(
int n,
int i )
341 if ( i >= 0 && i <= n )
343 return faculty( n ) / ( faculty( i ) * faculty( n - i ) );
351double QgsDxfPaintEngine::power(
double a,
int b )
356 const double tmp = a;
357 for (
int i = 2; i <= std::abs( b ); i++ )
366int QgsDxfPaintEngine::faculty(
int n )
374 if ( n == 0 || n == 1 )
377 for ( i = n - 1; i >= 2; i-- )
383QColor QgsDxfPaintEngine::penColor()
const
389 QColor
c = mPen.color();
390 c.setAlphaF(
c.alphaF() * mOpacity );
394QColor QgsDxfPaintEngine::brushColor()
const
397 switch ( mBrush.style() )
401 case Qt::LinearGradientPattern:
402 case Qt::RadialGradientPattern:
403 case Qt::ConicalGradientPattern:
405 const QGradientStops stops = mBrush.gradient() ? mBrush.gradient()->stops() : QGradientStops();
406 if ( !stops.isEmpty() )
408 c = stops.at( stops.size() / 2 ).second;
423 c.setAlphaF(
static_cast<float>(
c.alphaF() * mOpacity ) );
Exports QGIS layers to the DXF format.
A paint device for drawing into dxf files.
QPointF dxfCoordinates(QPointF pt) const
Converts a point from device coordinates to dxf coordinates.
void drawPath(const QPainterPath &path) override
bool begin(QPaintDevice *pdev) override
QgsDxfPaintEngine(const QgsDxfPaintDevice *dxfDevice, QgsDxfExport *dxf)
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override
void updateState(const QPaintEngineState &state) override
void drawLines(const QLineF *lines, int lineCount) override
QPaintEngine::Type type() const override
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override
Point geometry type, with support for z-dimension and m-values.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence