10#include "qwt_point_mapper.h"
11#include "qwt_scale_map.h"
12#include "qwt_pixel_matrix.h"
13#include "qwt_series_data.h"
23#include <qtconcurrentrun.h>
25#if !defined( QT_NO_QFUTURE )
26#define QWT_USE_THREADS 1
29static QRectF qwtInvalidRect( 0.0, 0.0, -1.0, -1.0 );
31static inline int qwtRoundValue(
double value )
33 return qRound( value );
36static inline double qwtRoundValueF(
double value )
40 return ( value >= 0.0 ) ? std::floor( value + 0.5 ) : std::ceil( value - 0.5 );
42 return nearbyint( value );
46static Qt::Orientation qwtProbeOrientation(
52 return Qt::Horizontal;
55 const double x0 = series->
sample( from ).x();
56 const double xn = series->
sample( to ).x();
61 const int step = ( to - from ) / 10;
62 const bool isIncreasing = xn > x0;
65 for (
int i = from + step; i < to; i += step )
67 const double x2 = series->
sample( i ).x();
70 if ( ( x2 > x1 ) != isIncreasing )
77 return Qt::Horizontal;
82 template<
class Polygon,
class Po
int >
83 class QwtPolygonQuadrupelX
86 inline void start(
int x,
int y )
89 m_y1 = m_yMin = m_yMax = m_y2 = y;
92 inline bool append(
int x,
int y )
99 else if ( y > m_yMax )
107 inline void flush( Polygon& polyline )
109 appendTo( m_y1, polyline );
112 qSwap( m_yMin, m_yMax );
114 if ( m_yMax != m_y1 )
115 appendTo( m_yMax, polyline );
117 if ( m_yMin != m_yMax )
118 appendTo( m_yMin, polyline );
120 if ( m_y2 != m_yMin )
121 appendTo( m_y2, polyline );
125 inline void appendTo(
int y, Polygon& polyline )
127 polyline += Point( m_x0, y );
131 int m_x0, m_y1, m_yMin, m_yMax, m_y2;
134 template<
class Polygon,
class Po
int >
135 class QwtPolygonQuadrupelY
138 inline void start(
int x,
int y )
141 m_x1 = m_xMin = m_xMax = m_x2 = x;
144 inline bool append(
int x,
int y )
151 else if ( x > m_xMax )
159 inline void flush( Polygon& polyline )
161 appendTo( m_x1, polyline );
164 qSwap( m_xMin, m_xMax );
166 if ( m_xMax != m_x1 )
167 appendTo( m_xMax, polyline );
169 if ( m_xMin != m_xMax )
170 appendTo( m_xMin, polyline );
172 if ( m_x2 != m_xMin )
173 appendTo( m_x2, polyline );
177 inline void appendTo(
int x, Polygon& polyline )
179 polyline += Point( x, m_y0 );
182 int m_y0, m_x1, m_xMin, m_xMax, m_x2;
186template<
class Polygon,
class Po
int,
class PolygonQuadrupel >
190 const QPointF sample0 = series->
sample( from );
193 q.start( qwtRoundValue( xMap.
transform( sample0.x() ) ),
194 qwtRoundValue( yMap.
transform( sample0.y() ) ) );
197 for (
int i = from; i <= to; i++ )
199 const QPointF sample = series->
sample( i );
201 const int x = qwtRoundValue( xMap.
transform( sample.x() ) );
202 const int y = qwtRoundValue( yMap.
transform( sample.y() ) );
204 if ( !q.append( x, y ) )
215template<
class Polygon,
class Po
int,
class PolygonQuadrupel >
216static Polygon qwtMapPointsQuad(
const Polygon& polyline )
218 const int numPoints = polyline.size();
223 const Point* points = polyline.constData();
228 q.start( points[0].x(), points[0].y() );
230 for (
int i = 0; i < numPoints; i++ )
232 const int x = points[i].x();
233 const int y = points[i].y();
235 if ( !q.append( x, y ) )
237 q.flush( polylineXY );
241 q.flush( polylineXY );
247template<
class Polygon,
class Po
int >
259 const Qt::Orientation orientation = qwtProbeOrientation( series, from, to );
261 if ( orientation == Qt::Horizontal )
263 polyline = qwtMapPointsQuad< Polygon, Point,
264 QwtPolygonQuadrupelY< Polygon, Point > >( xMap, yMap, series, from, to );
266 polyline = qwtMapPointsQuad< Polygon, Point,
267 QwtPolygonQuadrupelX< Polygon, Point > >( polyline );
271 polyline = qwtMapPointsQuad< Polygon, Point,
272 QwtPolygonQuadrupelX< Polygon, Point > >( xMap, yMap, series, from, to );
274 polyline = qwtMapPointsQuad< Polygon, Point,
275 QwtPolygonQuadrupelY< Polygon, Point > >( polyline );
292static void qwtRenderDots(
294 const QwtDotsCommand& command,
const QPoint& pos, QImage* image )
296 const QRgb rgb = command.rgb;
297 QRgb* bits =
reinterpret_cast< QRgb*
>( image->bits() );
299 const int w = image->width();
300 const int h = image->height();
302 const int x0 = pos.x();
303 const int y0 = pos.y();
305 for (
int i = command.from; i <= command.to; i++ )
307 const QPointF sample = command.series->
sample( i );
309 const int x =
static_cast< int >( xMap.
transform( sample.x() ) + 0.5 ) - x0;
310 const int y =
static_cast< int >( yMap.
transform( sample.y() ) + 0.5 ) - y0;
312 if ( x >= 0 && x < w && y >= 0 && y < h )
313 bits[ y * w + x ] = rgb;
320 inline int operator()(
double value )
const
322 return qwtRoundValue( value );
328 inline double operator()(
double value )
const
330 return qwtRoundValueF( value );
336 inline double operator()(
double value )
const
345template<
class Polygon,
class Po
int,
class Round >
346static inline Polygon qwtToPoints(
347 const QRectF& boundingRect,
350 int from,
int to, Round round )
352 Polygon polyline( to - from + 1 );
353 Point* points = polyline.data();
357 if ( boundingRect.isValid() )
363 for (
int i = from; i <= to; i++ )
365 const QPointF sample = series->
sample( i );
367 const double x = xMap.
transform( sample.x() );
368 const double y = yMap.
transform( sample.y() );
370 if ( boundingRect.contains( x, y ) )
372 points[ numPoints ].rx() = round( x );
373 points[ numPoints ].ry() = round( y );
379 polyline.resize( numPoints );
386 for (
int i = from; i <= to; i++ )
388 const QPointF sample = series->
sample( i );
390 const double x = xMap.
transform( sample.x() );
391 const double y = yMap.
transform( sample.y() );
393 points[ numPoints ].rx() = round( x );
394 points[ numPoints ].ry() = round( y );
403static inline QPolygon qwtToPointsI(
404 const QRectF& boundingRect,
409 return qwtToPoints< QPolygon, QPoint >(
410 boundingRect, xMap, yMap, series, from, to, QwtRoundI() );
413template<
class Round >
414static inline QPolygonF qwtToPointsF(
415 const QRectF& boundingRect,
418 int from,
int to, Round round )
420 return qwtToPoints< QPolygonF, QPointF >(
421 boundingRect, xMap, yMap, series, from, to, round );
427template<
class Polygon,
class Po
int,
class Round >
428static inline Polygon qwtToPolylineFiltered(
431 int from,
int to, Round round )
438 Polygon polyline( to - from + 1 );
439 Point* points = polyline.data();
441 const QPointF sample0 = series->
sample( from );
443 points[0].rx() = round( xMap.
transform( sample0.x() ) );
444 points[0].ry() = round( yMap.
transform( sample0.y() ) );
447 for (
int i = from + 1; i <= to; i++ )
449 const QPointF sample = series->
sample( i );
451 const Point p( round( xMap.
transform( sample.x() ) ),
454 if ( points[pos] != p )
458 polyline.resize( pos + 1 );
462static inline QPolygon qwtToPolylineFilteredI(
467 return qwtToPolylineFiltered< QPolygon, QPoint >(
468 xMap, yMap, series, from, to, QwtRoundI() );
471template<
class Round >
472static inline QPolygonF qwtToPolylineFilteredF(
475 int from,
int to, Round round )
477 return qwtToPolylineFiltered< QPolygonF, QPointF >(
478 xMap, yMap, series, from, to, round );
481template<
class Polygon,
class Po
int >
482static inline Polygon qwtToPointsFiltered(
483 const QRectF& boundingRect,
490 Polygon polygon( to - from + 1 );
491 Point* points = polygon.data();
496 for (
int i = from; i <= to; i++ )
498 const QPointF sample = series->
sample( i );
500 const int x = qwtRoundValue( xMap.
transform( sample.x() ) );
501 const int y = qwtRoundValue( yMap.
transform( sample.y() ) );
503 if ( pixelMatrix.testAndSetPixel( x, y,
true ) ==
false )
505 points[ numPoints ].rx() = x;
506 points[ numPoints ].ry() = y;
512 polygon.resize( numPoints );
516static inline QPolygon qwtToPointsFilteredI(
517 const QRectF& boundingRect,
521 return qwtToPointsFiltered< QPolygon, QPoint >(
522 boundingRect, xMap, yMap, series, from, to );
525static inline QPolygonF qwtToPointsFilteredF(
526 const QRectF& boundingRect,
530 return qwtToPointsFiltered< QPolygonF, QPointF >(
531 boundingRect, xMap, yMap, series, from, to );
534class QwtPointMapper::PrivateData
538 : boundingRect( qwtInvalidRect )
549 m_data =
new PrivateData();
566 m_data->flags =
flags;
575 return m_data->flags;
589 m_data->flags |= flag;
591 m_data->flags &= ~flag;
601 return m_data->flags & flag;
614 m_data->boundingRect = rect;
623 return m_data->boundingRect;
657 polyline = qwtMapPointsQuad< QPolygonF, QPointF >(
658 xMap, yMap, series, from, to );
662 polyline = qwtToPolylineFilteredF(
663 xMap, yMap, series, from, to, QwtRoundF() );
667 polyline = qwtToPointsF( qwtInvalidRect,
668 xMap, yMap, series, from, to, QwtRoundF() );
675 polyline = qwtToPolylineFilteredF(
676 xMap, yMap, series, from, to, QwtNoRoundF() );
680 polyline = qwtToPointsF( qwtInvalidRect,
681 xMap, yMap, series, from, to, QwtNoRoundF() );
711 polyline = qwtMapPointsQuad< QPolygon, QPoint >(
712 xMap, yMap, series, from, to );
716 polyline = qwtToPolylineFilteredI(
717 xMap, yMap, series, from, to );
721 polyline = qwtToPointsI(
722 qwtInvalidRect, xMap, yMap, series, from, to );
769 if ( m_data->boundingRect.isValid() )
771 points = qwtToPointsFilteredF( m_data->boundingRect,
772 xMap, yMap, series, from, to );
780 points = qwtToPolylineFilteredF(
781 xMap, yMap, series, from, to, QwtRoundF() );
789 points = qwtToPolylineFilteredF(
790 xMap, yMap, series, from, to, QwtNoRoundF() );
797 points = qwtToPointsF( m_data->boundingRect,
798 xMap, yMap, series, from, to, QwtRoundF() );
802 points = qwtToPointsF( m_data->boundingRect,
803 xMap, yMap, series, from, to, QwtNoRoundF() );
841 if ( m_data->boundingRect.isValid() )
843 points = qwtToPointsFilteredI( m_data->boundingRect,
844 xMap, yMap, series, from, to );
851 points = qwtToPolylineFilteredI(
852 xMap, yMap, series, from, to );
857 points = qwtToPointsI(
858 m_data->boundingRect, xMap, yMap, series, from, to );
886 const QPen& pen,
bool antialiased, uint numThreads )
const
888 Q_UNUSED( antialiased )
891 if ( numThreads == 0 )
892 numThreads = QThread::idealThreadCount();
894 if ( numThreads <= 0 )
897 Q_UNUSED( numThreads )
903 const QRect rect = m_data->boundingRect.toAlignedRect();
905 QImage image( rect.size(), QImage::Format_ARGB32 );
906 image.fill( Qt::transparent );
908 if ( pen.width() <= 1 && pen.color().alpha() == 255 )
910 QwtDotsCommand command;
911 command.series = series;
912 command.rgb = pen.color().rgba();
915 const int numPoints = ( to - from + 1 ) / numThreads;
918 for ( uint i = 0; i < numThreads; i++ )
920 const QPoint pos = rect.topLeft();
922 const int index0 = from + i * numPoints;
923 if ( i == numThreads - 1 )
925 command.from = index0;
928 qwtRenderDots( xMap, yMap, command, pos, &image );
932 command.from = index0;
933 command.to = index0 + numPoints - 1;
935 futures += QtConcurrent::run( &qwtRenderDots,
936 xMap, yMap, command, pos, &image );
939 for (
int i = 0; i < futures.size(); i++ )
940 futures[i].waitForFinished();
945 qwtRenderDots( xMap, yMap, command, rect.topLeft(), &image );
953 QPainter painter( &image );
954 painter.setPen( pen );
955 painter.setRenderHint( QPainter::Antialiasing, antialiased );
957 const int chunkSize = 1000;
958 for (
int i = from; i <= to; i += chunkSize )
960 const int indexTo = qMin( i + chunkSize - 1, to );
962 xMap, yMap, series, i, indexTo );
964 painter.drawPoints( points );
A bit field corresponding to the pixels of a rectangle.
void setBoundingRect(const QRectF &)
QRectF boundingRect() const
QPolygonF toPolygonF(const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QwtSeriesData< QPointF > *series, int from, int to) const
Translate a series of points into a QPolygonF.
TransformationFlags flags() const
QImage toImage(const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QwtSeriesData< QPointF > *series, int from, int to, const QPen &, bool antialiased, uint numThreads) const
Translate a series into a QImage.
bool testFlag(TransformationFlag) const
QPolygonF toPointsF(const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QwtSeriesData< QPointF > *series, int from, int to) const
Translate a series into a QPolygonF.
void setFlag(TransformationFlag, bool on=true)
QwtPointMapper()
Constructor.
~QwtPointMapper()
Destructor.
TransformationFlag
Flags affecting the transformation process.
@ RoundPoints
Round points to integer values.
@ WeedOutIntermediatePoints
QPolygon toPolygon(const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QwtSeriesData< QPointF > *series, int from, int to) const
Translate a series of points into a QPolygon.
void setFlags(TransformationFlags)
QFlags< TransformationFlag > TransformationFlags
QPolygon toPoints(const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QwtSeriesData< QPointF > *series, int from, int to) const
Translate a series of points into a QPolygon.
double transform(double s) const
virtual T sample(size_t i) const =0