QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsdxfpaintengine.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdxpaintengine.cpp
3  --------------------
4  begin : November 2013
5  copyright : (C) 2013 by Marco Hugentobler
6  email : marco at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsdxfpaintengine.h"
19 #include "qgsdxfexport.h"
20 #include "qgsdxfpaintdevice.h"
21 #include "qgslogger.h"
22 
24  : QPaintEngine( QPaintEngine::AllFeatures /*QPaintEngine::PainterPaths | QPaintEngine::PaintOutsidePaintEvent*/ )
25  , mPaintDevice( dxfDevice )
26  , mDxf( dxf )
27 {
28 }
29 
30 bool QgsDxfPaintEngine::begin( QPaintDevice *pdev )
31 {
32  Q_UNUSED( pdev )
33  return true;
34 }
35 
37 {
38  return true;
39 }
40 
41 QPaintEngine::Type QgsDxfPaintEngine::type() const
42 {
43  return QPaintEngine::User;
44 }
45 
46 void QgsDxfPaintEngine::drawPixmap( const QRectF &r, const QPixmap &pm, const QRectF &sr )
47 {
48  Q_UNUSED( r )
49  Q_UNUSED( pm )
50  Q_UNUSED( sr )
51 }
52 
53 void QgsDxfPaintEngine::updateState( const QPaintEngineState &state )
54 {
55  if ( state.state() & QPaintEngine::DirtyTransform )
56  mTransform = state.transform();
57 
58  if ( state.state() & QPaintEngine::DirtyPen )
59  mPen = state.pen();
60 
61  if ( state.state() & QPaintEngine::DirtyBrush )
62  mBrush = state.brush();
63 
64  if ( state.state() & QPaintEngine::DirtyOpacity )
65  {
66  mOpacity = state.opacity();
67  }
68 }
69 
70 void QgsDxfPaintEngine::setRing( QgsPointSequence &polyline, const QPointF *points, int pointCount )
71 {
72  polyline.clear();
73  for ( int i = 0; i < pointCount; ++i )
74  polyline.append( toDxfCoordinates( points[i] ) );
75 }
76 
77 void QgsDxfPaintEngine::drawPolygon( const QPointF *points, int pointCount, PolygonDrawMode mode )
78 {
79  Q_UNUSED( mode )
80  if ( !mDxf || !mPaintDevice )
81  return;
82 
83  QgsRingSequence polygon;
84  polygon << QgsPointSequence();
85  setRing( polygon.last(), points, pointCount );
86 
87  if ( mode == QPaintEngine::PolylineMode )
88  {
89  if ( mPen.style() != Qt::NoPen && mPen.brush().style() != Qt::NoBrush )
90  mDxf->writePolyline( polygon.at( 0 ), mLayer, QStringLiteral( "CONTINUOUS" ), penColor(), currentWidth() );
91  }
92  else
93  {
94  if ( mBrush.style() != Qt::NoBrush )
95  mDxf->writePolygon( polygon, mLayer, QStringLiteral( "SOLID" ), brushColor() );
96  }
97 }
98 
99 void QgsDxfPaintEngine::drawPath( const QPainterPath &path )
100 {
101  int pathLength = path.elementCount();
102  for ( int i = 0; i < pathLength; ++i )
103  {
104  const QPainterPath::Element &pathElem = path.elementAt( i );
105  if ( pathElem.type == QPainterPath::MoveToElement )
106  {
107  moveTo( pathElem.x, pathElem.y );
108  }
109  else if ( pathElem.type == QPainterPath::LineToElement )
110  {
111  lineTo( pathElem.x, pathElem.y );
112  }
113  else if ( pathElem.type == QPainterPath::CurveToElement )
114  {
115  curveTo( pathElem.x, pathElem.y );
116  }
117  else if ( pathElem.type == QPainterPath::CurveToDataElement )
118  {
119  mCurrentCurve.append( QPointF( pathElem.x, pathElem.y ) );
120  }
121  }
122  endCurve();
123  endPolygon();
124 
125  if ( !mPolygon.isEmpty() && mBrush.style() != Qt::NoBrush )
126  mDxf->writePolygon( mPolygon, mLayer, QStringLiteral( "SOLID" ), brushColor() );
127 
128  mPolygon.clear();
129 }
130 
131 void QgsDxfPaintEngine::moveTo( double dx, double dy )
132 {
133  endCurve();
134  endPolygon();
135  mCurrentPolygon.append( QPointF( dx, dy ) );
136 }
137 
138 void QgsDxfPaintEngine::lineTo( double dx, double dy )
139 {
140  endCurve();
141  mCurrentPolygon.append( QPointF( dx, dy ) );
142 }
143 
144 void QgsDxfPaintEngine::curveTo( double dx, double dy )
145 {
146  endCurve();
147  if ( !mCurrentPolygon.isEmpty() )
148  mCurrentCurve.append( mCurrentPolygon.last() );
149 
150  mCurrentCurve.append( QPointF( dx, dy ) );
151 }
152 
153 void QgsDxfPaintEngine::endPolygon()
154 {
155  if ( mCurrentPolygon.size() > 1 )
156  {
157  if ( mPen.style() != Qt::NoPen )
158  drawPolygon( mCurrentPolygon.constData(), mCurrentPolygon.size(), QPaintEngine::PolylineMode );
159 
160  mPolygon << QgsPointSequence();
161  setRing( mPolygon.last(), mCurrentPolygon.constData(), mCurrentPolygon.size() );
162  }
163  mCurrentPolygon.clear();
164 }
165 
166 void QgsDxfPaintEngine::endCurve()
167 {
168  if ( mCurrentCurve.empty() )
169  return;
170 
171  if ( mCurrentPolygon.empty() )
172  {
173  mCurrentCurve.clear();
174  return;
175  }
176 
177  if ( mCurrentCurve.size() >= 3 )
178  {
179  double t = 0.05;
180  for ( int i = 1; i <= 20; ++i ) //approximate curve with 20 segments
181  {
182  mCurrentPolygon.append( bezierPoint( mCurrentCurve, t ) );
183  t += 0.05;
184  }
185  }
186  else if ( mCurrentCurve.size() == 2 )
187  {
188  mCurrentPolygon.append( mCurrentCurve.at( 1 ) );
189  }
190  mCurrentCurve.clear();
191 }
192 
193 void QgsDxfPaintEngine::drawLines( const QLineF *lines, int lineCount )
194 {
195  if ( !mDxf || !mPaintDevice || !lines || mPen.style() == Qt::NoPen )
196  return;
197 
198  for ( int i = 0; i < lineCount; ++i )
199  {
200  mDxf->writeLine( toDxfCoordinates( lines[i].p1() ),
201  toDxfCoordinates( lines[i].p2() ),
202  mLayer, QStringLiteral( "CONTINUOUS" ), penColor(), currentWidth() );
203  }
204 }
205 
206 QgsPoint QgsDxfPaintEngine::toDxfCoordinates( QPointF pt ) const
207 {
208  if ( !mPaintDevice || !mDxf )
209  return QgsPoint( pt.x(), pt.y() );
210 
211  QPointF dxfPt = mPaintDevice->dxfCoordinates( mTransform.map( pt ) ) + mShift;
212  return QgsPoint( dxfPt.x(), dxfPt.y() );
213 }
214 
215 
216 double QgsDxfPaintEngine::currentWidth() const
217 {
218  if ( !mPaintDevice )
219  return 1;
220 
221  return mPen.widthF() * mPaintDevice->widthScaleFactor();
222 }
223 
224 QPointF QgsDxfPaintEngine::bezierPoint( const QList<QPointF> &controlPolygon, double t )
225 {
226  double x = 0;
227  double y = 0;
228  int cPolySize = controlPolygon.size();
229  double bPoly = 0;
230 
231  QList<QPointF>::const_iterator it = controlPolygon.constBegin();
232  int i = 0;
233  for ( ; it != controlPolygon.constEnd(); ++it )
234  {
235  bPoly = bernsteinPoly( cPolySize - 1, i, t );
236  x += ( it->x() * bPoly );
237  y += ( it->y() * bPoly );
238  ++i;
239  }
240 
241  return QPointF( x, y );
242 }
243 
244 double QgsDxfPaintEngine::bernsteinPoly( int n, int i, double t )
245 {
246  if ( i < 0 )
247  return 0;
248 
249  return lower( n, i ) * power( t, i ) * power( ( 1 - t ), ( n - i ) );
250 }
251 
252 int QgsDxfPaintEngine::lower( int n, int i )
253 {
254  if ( i >= 0 && i <= n )
255  {
256  return faculty( n ) / ( faculty( i ) * faculty( n - i ) );
257  }
258  else
259  {
260  return 0;
261  }
262 }
263 
264 double QgsDxfPaintEngine::power( double a, int b )
265 {
266  if ( b == 0 )
267  return 1;
268 
269  double tmp = a;
270  for ( int i = 2; i <= std::abs( b ); i++ )
271  a *= tmp;
272 
273  if ( b > 0 )
274  return a;
275  else
276  return 1.0 / a;
277 }
278 
279 int QgsDxfPaintEngine::faculty( int n )
280 {
281  if ( n < 0 )//Is faculty also defined for negative integers?
282  return 0;
283 
284  int i;
285  int result = n;
286 
287  if ( n == 0 || n == 1 )
288  return 1; //faculty of 0 is 1!
289 
290  for ( i = n - 1; i >= 2; i-- )
291  result *= i;
292 
293  return result;
294 }
295 
296 QColor QgsDxfPaintEngine::penColor() const
297 {
298  if ( qgsDoubleNear( mOpacity, 1.0 ) )
299  {
300  return mPen.color();
301  }
302  QColor c = mPen.color();
303  c.setAlphaF( c.alphaF() * mOpacity );
304  return c;
305 }
306 
307 QColor QgsDxfPaintEngine::brushColor() const
308 {
309  if ( qgsDoubleNear( mOpacity, 1.0 ) )
310  {
311  return mBrush.color();
312  }
313  QColor c = mBrush.color();
314  c.setAlphaF( c.alphaF() * mOpacity );
315  return c;
316 }
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
A paint device for drawing into dxf files.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
void drawPath(const QPainterPath &path) override
void updateState(const QPaintEngineState &state) override
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
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
QgsDxfPaintEngine(const QgsDxfPaintDevice *dxfDevice, QgsDxfExport *dxf)
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QVector< QgsPoint > QgsPointSequence
QVector< QgsPointSequence > QgsRingSequence
void drawLines(const QLineF *lines, int lineCount) override
QPointF dxfCoordinates(QPointF pt) const
Converts a point from device coordinates to dxf coordinates.
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override
QPaintEngine::Type type() const override
bool begin(QPaintDevice *pdev) override
bool end() override
double widthScaleFactor() const
Returns scale factor for line width.