qwt_plot_vectorfield.cpp
Go to the documentation of this file.
1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2  * Qwt Widget Library
3  * Copyright (C) 1997 Josef Wilgen
4  * Copyright (C) 2002 Uwe Rathmann
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the Qwt License, Version 1.0
8  *****************************************************************************/
9 
10 #include "qwt_plot_vectorfield.h"
11 #include "qwt_vectorfield_symbol.h"
12 #include "qwt_scale_map.h"
13 #include "qwt_color_map.h"
14 #include "qwt_painter.h"
15 #include "qwt_text.h"
16 #include "qwt_graphic.h"
17 #include "qwt_math.h"
18 
19 #include <qpainter.h>
20 #include <qpainterpath.h>
21 #include <qdebug.h>
22 #include <cstdlib>
23 
24 #define DEBUG_RENDER 0
25 
26 #if DEBUG_RENDER
27 #include <qelapsedtimer.h>
28 #endif
29 
30 
31 static inline double qwtVector2Radians( double vx, double vy )
32 {
33  if ( vx == 0.0 )
34  return ( vy >= 0 ) ? M_PI_2 : 3 * M_PI_2;
35 
36  return std::atan2( vy, vx );
37 }
38 
39 static inline double qwtVector2Magnitude( double vx, double vy )
40 {
41  return sqrt( vx * vx + vy * vy );
42 }
43 
46 {
47  if ( series->size() == 0 )
48  return QwtInterval( 0, 1 );
49 
50  const QwtVectorFieldSample s0 = series->sample( 0 );
51 
52  double min = s0.vx * s0.vx + s0.vy * s0.vy;
53  double max = min;
54 
55  for ( uint i = 1; i < series->size(); i++ )
56  {
57  const QwtVectorFieldSample s = series->sample( i );
58  const double l = s.vx * s.vx + s.vy * s.vy;
59 
60  if ( l < min )
61  min = l;
62 
63  if ( l > max )
64  max = l;
65  }
66 
67  min = std::sqrt( min );
68  max = std::sqrt( max );
69 
70  if ( max == min )
71  max += 1.0;
72 
73  return QwtInterval( min, max );
74 }
75 
76 static inline QTransform qwtSymbolTransformation(
77  const QTransform& oldTransform, double x, double y,
78  double vx, double vy, double magnitude )
79 {
80  QTransform transform = oldTransform;
81 
82  if ( !transform.isIdentity() )
83  {
84  transform.translate( x, y );
85 
86  const double radians = qwtVector2Radians( vx, vy );
87  transform.rotateRadians( radians );
88  }
89  else
90  {
91  /*
92  When starting with no transformation ( f.e on screen )
93  the matrix can be found without having to use
94  trigonometric functions
95  */
96 
97  qreal sin, cos;
98  if ( magnitude == 0.0 )
99  {
100  // something
101  sin = 1.0;
102  cos = 0.0;
103  }
104  else
105  {
106  sin = vy / magnitude;
107  cos = vx / magnitude;
108  }
109 
110  transform.setMatrix( cos, sin, 0.0, -sin, cos, 0.0, x, y, 1.0 );
111  }
112 
113  return transform;
114 }
115 
116 namespace
117 {
118  class FilterMatrix
119  {
120  public:
121  class Entry
122  {
123  public:
124  inline void addSample( double sx, double sy,
125  double svx, double svy )
126  {
127  x += sx;
128  y += sy;
129 
130  vx += svx;
131  vy += svy;
132 
133  count++;
134  }
135 
136  quint32 count;
137 
138  // screen positions -> float is good enough
139  float x;
140  float y;
141  float vx;
142  float vy;
143  };
144 
145  FilterMatrix( const QRectF& dataRect,
146  const QRectF& canvasRect, const QSizeF& cellSize )
147  {
148  d_dx = cellSize.width();
149  d_dy = cellSize.height();
150 
151  d_x0 = dataRect.x();
152  if ( d_x0 < canvasRect.x() )
153  d_x0 += int ( ( canvasRect.x() - d_x0 ) / d_dx ) * d_dx;
154 
155  d_y0 = dataRect.y();
156  if ( d_y0 < canvasRect.y() )
157  d_y0 += int ( ( canvasRect.y() - d_y0 ) / d_dy ) * d_dy;
158 
159  d_numColumns = canvasRect.width() / d_dx + 1;
160  d_numRows = canvasRect.height() / d_dy + 1;
161 
162 #if 1
163  /*
164  limit column and row count to a maximum of 1000000,
165  so that memory usage is not an issue
166  */
167  if ( d_numColumns > 1000 )
168  {
169  d_dx = canvasRect.width() / 1000;
170  d_numColumns = canvasRect.width() / d_dx + 1;
171  }
172 
173  if ( d_numRows > 1000 )
174  {
175  d_dy = canvasRect.height() / 1000;
176  d_numRows = canvasRect.height() / d_dx + 1;
177  }
178 #endif
179 
180  d_x1 = d_x0 + d_numColumns * d_dx;
181  d_y1 = d_y0 + d_numRows * d_dy;
182 
183  d_entries = ( Entry* )::calloc( d_numRows * d_numColumns, sizeof( Entry ) );
184  if ( d_entries == NULL )
185  {
186  qWarning() << "QwtPlotVectorField: raster for filtering too fine - running out of memory";
187  }
188  }
189 
190  ~FilterMatrix()
191  {
192  if ( d_entries )
193  std::free( d_entries );
194  }
195 
196  inline int numColumns() const
197  {
198  return d_numColumns;
199  }
200 
201  inline int numRows() const
202  {
203  return d_numRows;
204  }
205 
206  inline void addSample( double x, double y,
207  double u, double v )
208  {
209  if ( x >= d_x0 && x < d_x1
210  && y >= d_y0 && y < d_y1 )
211  {
212  Entry &entry = d_entries[ indexOf( x, y ) ];
213  entry.addSample( x, y, u, v );
214  }
215  }
216 
217  const FilterMatrix::Entry* entries() const
218  {
219  return d_entries;
220  }
221 
222  private:
223  inline int indexOf( qreal x, qreal y ) const
224  {
225  const int col = ( x - d_x0 ) / d_dx;
226  const int row = ( y - d_y0 ) / d_dy;
227 
228  return row * d_numColumns + col;
229  }
230 
231  qreal d_x0, d_x1, d_y0, d_y1, d_dx, d_dy;
232  int d_numColumns;
233  int d_numRows;
234 
235  Entry* d_entries;
236  };
237 }
238 
240 {
241 public:
243  pen( Qt::black ),
244  brush( Qt::black ),
245  indicatorOrigin( QwtPlotVectorField::OriginHead ),
246  magnitudeScaleFactor( 1.0 ),
247  rasterSize( 20, 20 ),
248  magnitudeModes( MagnitudeAsLength )
249  {
250  colorMap = NULL;
251  symbol = new QwtVectorFieldThinArrow();
252  }
253 
255  {
256  delete colorMap;
257  delete symbol;
258  }
259 
260  QPen pen;
261  QBrush brush;
262 
266 
267  /*
268  Stores the range of magnitudes to be used for the color map.
269  If invalid (min=max or negative values), the range is determined
270  from the data samples themselves.
271  */
274 
276  QSizeF rasterSize;
277 
280 };
281 
287  QwtPlotSeriesItem( title )
288 {
289  init();
290 }
291 
297  QwtPlotSeriesItem( QwtText( title ) )
298 {
299  init();
300 }
301 
304 {
305  delete d_data;
306 }
307 
312 {
315 
316  d_data = new PrivateData;
317  setData( new QwtVectorFieldData() );
318 
319  setZ( 20.0 );
320 }
321 
322 void QwtPlotVectorField::setPen( const QPen &pen )
323 {
324  if ( d_data->pen != pen )
325  {
326  d_data->pen = pen;
327 
328  itemChanged();
329  legendChanged();
330  }
331 }
332 
334 {
335  return d_data->pen;
336 }
337 
338 void QwtPlotVectorField::setBrush( const QBrush &brush )
339 {
340  if ( d_data->brush != brush )
341  {
342  d_data->brush = brush;
343 
344  itemChanged();
345  legendChanged();
346  }
347 }
348 
350 {
351  return d_data->brush;
352 }
353 
355 {
356  d_data->indicatorOrigin = origin;
357  if ( d_data->indicatorOrigin != origin )
358  {
359  d_data->indicatorOrigin = origin;
360  itemChanged();
361  }
362 }
363 
365 {
366  return d_data->indicatorOrigin;
367 }
368 
370 {
371  if ( factor != d_data->magnitudeScaleFactor )
372  {
373  d_data->magnitudeScaleFactor = factor;
374  itemChanged();
375  }
376 }
377 
379 {
381 }
382 
383 void QwtPlotVectorField::setRasterSize( const QSizeF& size )
384 {
385  if ( size != d_data->rasterSize )
386  {
387  d_data->rasterSize = size;
388  itemChanged();
389  }
390 }
391 
393 {
394  return d_data->rasterSize;
395 }
396 
405  PaintAttribute attribute, bool on )
406 {
407  PaintAttributes attributes = d_data->paintAttributes;
408 
409  if ( on )
410  attributes |= attribute;
411  else
412  attributes &= ~attribute;
413 
414  if ( d_data->paintAttributes != attributes )
415  {
416  d_data->paintAttributes = attributes;
417  itemChanged();
418  }
419 }
420 
426  PaintAttribute attribute ) const
427 {
428  return ( d_data->paintAttributes & attribute );
429 }
430 
433 {
435 }
436 
438 {
439  if ( on == testMagnitudeMode( mode ) )
440  return;
441 
442  if ( on )
443  d_data->magnitudeModes |= mode;
444  else
445  d_data->magnitudeModes &= ~mode;
446 
447  itemChanged();
448 }
449 
451 {
452  return d_data->magnitudeModes & mode;
453 }
454 
456 {
457  if ( d_data->magnitudeModes != modes )
458  {
459  d_data->magnitudeModes = modes;
460  itemChanged();
461  }
462 }
463 
469 {
470  if ( d_data->symbol == symbol )
471  return;
472 
473  delete d_data->symbol;
474  d_data->symbol = symbol;
475 
476  itemChanged();
477  legendChanged();
478 }
479 
481 {
482  return d_data->symbol;
483 }
484 
486 {
487  return d_data->magnitudeModes;
488 }
489 
495 {
496  setData( new QwtVectorFieldData( samples ) );
497 }
498 
510 {
511  setData( data );
512 }
513 
526 {
527  if ( colorMap == NULL )
528  return;
529 
530  if ( colorMap != d_data->colorMap )
531  {
532  delete d_data->colorMap;
534  }
535 
536  legendChanged();
537  itemChanged();
538 }
539 
545 {
546  return d_data->colorMap;
547 }
548 
555 {
556  d_data->magnitudeRange = magnitudeRange;
557 }
558 
568 double QwtPlotVectorField::arrowLength( double magnitude ) const
569 {
570 #if 0
571  /*
572  Normalize magnitude with respect to value range.
573  Then, magnitudeScaleFactor is the number of pixels to draw (times the arrow tail width) for
574  a vector of length equal to magnitudeRange.maxValue().
575  The relative scaling ensures that change of data samples of very different magnitudes
576  will always lead to a reasonable display on screen.
577  */
578  const QwtVectorFieldData * vectorData = dynamic_cast<const QwtVectorFieldData *>(data());
579  if (d_data->magnitudeRange.maxValue() > 0)
580  magnitude /= d_data->magnitudeRange.maxValue();
581 #endif
582  double l = magnitude * d_data->magnitudeScaleFactor;
584  {
585  // TODO : make 30 a parameter? or leave this to user code and remove LimitLength altogether?
586  l = qMin(l, 50.0);
587  // ensure non-zero arrows are always at least 3 pixels long
588  if (l != 0)
589  l = qMax(l, 3.0);
590  }
591  return l;
592 }
593 
595 {
596 #if 0
597  /*
598  The bounding rectangle of the samples comes from the origins
599  only, but as we know the scaling factor for the magnitude
600  ( qwtVector2Magnitude ) here, we could try to include it ?
601  */
602 #endif
603 
605 }
606 
608  int index, const QSizeF &size ) const
609 {
610  Q_UNUSED( index );
611 
612  QwtGraphic icon;
613  icon.setDefaultSize( size );
614 
615  if ( size.isEmpty() )
616  return icon;
617 
618  QPainter painter( &icon );
619  painter.setRenderHint( QPainter::Antialiasing,
621 
622  painter.translate( -size.width(), -0.5 * size.height() );
623 
624  painter.setPen( d_data->pen );
625  painter.setBrush( d_data->brush );
626 
627  d_data->symbol->setLength( size.width() - 2 );
628  d_data->symbol->paint( &painter );
629 
630  return icon;
631 }
632 
646 void QwtPlotVectorField::drawSeries( QPainter *painter,
647  const QwtScaleMap &xMap, const QwtScaleMap &yMap,
648  const QRectF &canvasRect, int from, int to ) const
649 {
650  if ( !painter || dataSize() <= 0 )
651  return;
652 
653  if ( to < 0 )
654  to = dataSize() - 1;
655 
656  if ( from < 0 )
657  from = 0;
658 
659  if ( from > to )
660  return;
661 
662 #if DEBUG_RENDER
663  QElapsedTimer timer;
664  timer.start();
665 #endif
666 
667  drawSymbols( painter, xMap, yMap, canvasRect, from, to );
668 
669 #if DEBUG_RENDER
670  qDebug() << timer.elapsed();
671 #endif
672 }
673 
674 void QwtPlotVectorField::drawSymbols( QPainter *painter,
675  const QwtScaleMap &xMap, const QwtScaleMap &yMap,
676  const QRectF &canvasRect, int from, int to ) const
677 {
678  const bool doAlign = QwtPainter::roundingAlignment( painter );
679  const bool doClip = false;
680 
681  const bool isInvertingX = xMap.isInverting();
682  const bool isInvertingY = yMap.isInverting();
683 
684  const QwtSeriesData<QwtVectorFieldSample> *series = data();
685 
687  {
688  // user input error, can't draw without color map
689  // TODO: Discuss! Without colormap, silently fall back to uniform colors?
690  if ( d_data->colorMap == NULL)
691  return;
692  }
693  else
694  {
695  painter->setPen( d_data->pen );
696  painter->setBrush( d_data->brush );
697  }
698 
699  if ( ( d_data->paintAttributes & FilterVectors ) && !d_data->rasterSize.isEmpty() )
700  {
701  const QRectF dataRect = QwtScaleMap::transform(
702  xMap, yMap, boundingRect() );
703 
704  // TODO: Discuss. How to handle raster size when switching from screen to print size!
705  // DPI-aware adjustment of rastersize? Or make "rastersize in screen coordinate"
706  // or "rastersize in plotcoordinetes" a user option?
707 #if 1
708  // define filter matrix based on screen/print coordinates
709  FilterMatrix matrix( dataRect, canvasRect, d_data->rasterSize );
710 #else
711  // define filter matrix based on real coordinates
712 
713  // get scale factor from real coordinates to screen coordinates
714  double xScale = 1;
715  if (xMap.sDist() != 0)
716  xScale = xMap.pDist() / xMap.sDist();
717 
718  double yScale = 1;
719  if (yMap.sDist() != 0)
720  yScale = yMap.pDist() / yMap.sDist();
721 
722  QSizeF canvasRasterSize(xScale*d_data->rasterSize.width(), yScale*d_data->rasterSize.height());
723  FilterMatrix matrix( dataRect, canvasRect, canvasRasterSize );
724 #endif
725 
726  for ( int i = from; i <= to; i++ )
727  {
728  const QwtVectorFieldSample sample = series->sample( i );
729  if ( !sample.isNull() )
730  {
731  matrix.addSample( xMap.transform( sample.x ),
732  yMap.transform( sample.y ), sample.vx, sample.vy );
733  }
734  }
735 
736  const int numEntries = matrix.numRows() * matrix.numColumns();
737  const FilterMatrix::Entry* entries = matrix.entries();
738 
739  for ( int i = 0; i < numEntries; i++ )
740  {
741  const FilterMatrix::Entry &entry = entries[i];
742 
743  if ( entry.count == 0 )
744  continue;
745 
746  double xi = entry.x / entry.count;
747  double yi = entry.y / entry.count;
748 
749  if ( doAlign )
750  {
751  xi = qRound( xi );
752  yi = qRound( yi );
753  }
754 
755  const double vx = entry.vx / entry.count;
756  const double vy = entry.vy / entry.count;
757 
758  drawSymbol( painter, xi, yi,
759  isInvertingX ? -vx : vx, isInvertingY ? -vy : vy );
760  }
761  }
762  else
763  {
764  for ( int i = from; i <= to; i++ )
765  {
766  const QwtVectorFieldSample sample = series->sample( i );
767 
768  // arrows with zero length are never drawn
769  if ( sample.isNull() )
770  continue;
771 
772  double xi = xMap.transform( sample.x );
773  double yi = yMap.transform( sample.y );
774 
775  if ( doAlign )
776  {
777  xi = qRound( xi );
778  yi = qRound( yi );
779  }
780 
781  if ( doClip )
782  {
783  if ( !canvasRect.contains( xi, yi ) )
784  continue;
785  }
786 
787  drawSymbol( painter, xi, yi,
788  isInvertingX ? -sample.vx : sample.vx,
789  isInvertingY ? -sample.vy : sample.vy );
790  }
791  }
792 }
793 
794 void QwtPlotVectorField::drawSymbol( QPainter *painter,
795  double x, double y, double vx, double vy ) const
796 {
797  const double magnitude = qwtVector2Magnitude( vx, vy );
798 
799  const QTransform oldTransform = painter->transform();
800 
801  QTransform transform = qwtSymbolTransformation( oldTransform,
802  x, y, vx, vy, magnitude );
803 
805 
806  double length = 0.0;
807 
809  {
810  length = arrowLength( magnitude );
811  }
812 
813  symbol->setLength( length );
814 
816  {
817  const qreal dx = symbol->length();
818  transform.translate( dx, 0.0 );
819  }
820  else if ( d_data->indicatorOrigin == OriginCenter )
821  {
822  const qreal dx = symbol->length();
823  transform.translate( 0.5 * dx, 0.0 );
824  }
825 
827  {
828  // Determine color for arrow if colored by magnitude.
829 
831 
832  if ( !range.isValid() )
833  {
836 
838  }
839 
840  const QColor c = d_data->colorMap->rgb( range, magnitude );
841 
842 #if 1
843  painter->setBrush( c );
844  painter->setPen( c );
845 #endif
846  }
847 
848  painter->setWorldTransform( transform, false );
849  symbol->paint( painter );
850  painter->setWorldTransform( oldTransform, false );
851 }
852 
854 {
857 }
virtual void legendChanged()
virtual QwtGraphic legendIcon(int index, const QSizeF &) const QWT_OVERRIDE
IndicatorOrigin indicatorOrigin() const
void setBrush(const QBrush &)
virtual qreal length() const =0
Enable antialiasing.
void setSamples(const QVector< QwtVectorFieldSample > &)
virtual int rtti() const QWT_OVERRIDE
A class representing an interval.
Definition: qwt_interval.h:22
void init()
Initialize data members.
virtual void drawSymbols(QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to) const
double sDist() const
void setColorMap(QwtColorMap *)
virtual QRectF dataRect() const QWT_OVERRIDE
#define M_PI_2
Definition: qwt_math.h:60
QwtPlotVectorField(const QString &title=QString())
void setRasterSize(const QSizeF &)
virtual double arrowLength(double magnitude) const
void setData(QwtSeriesData< QwtVectorFieldSample > *series)
void setMagnitudeModes(MagnitudeModes)
static double qwtVector2Radians(double vx, double vy)
MagnitudeModes magnitudeModes() const
virtual QRectF boundingRect() const QWT_OVERRIDE
QwtSeriesData< QwtVectorFieldSample > * data()
void setMagnitudeScaleFactor(double factor)
static QwtInterval qwtMagnitudeRange(const QwtSeriesData< QwtVectorFieldSample > *series)
void setIndicatorOrigin(IndicatorOrigin)
double maxValue() const
Definition: qwt_interval.h:196
#define free(x)
Definition: Heap.h:55
void setPaintAttribute(PaintAttribute, bool on=true)
bool testPaintAttribute(PaintAttribute) const
void setSymbol(QwtVectorFieldSymbol *)
void setDefaultSize(const QSizeF &)
Set a default size.
The item is represented on the legend.
constexpr size_t count()
Definition: core.h:960
double magnitudeScaleFactor() const
static QTransform qwtSymbolTransformation(const QTransform &oldTransform, double x, double y, double vx, double vy, double magnitude)
#define min(A, B)
Definition: Log.c:64
#define max(A, B)
Definition: Socket.h:88
bool testRenderHint(RenderHint) const
const QwtVectorFieldSymbol * symbol() const
bool isNull() const
Definition: qwt_samples.h:283
virtual void drawSeries(QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to) const QWT_OVERRIDE
bool isValid() const
Definition: qwt_interval.h:208
A class representing a text.
Definition: qwt_text.h:51
virtual void drawSymbol(QPainter *, double x, double y, double vx, double vy) const
void setZ(double z)
Set the z value.
QwtColorMap is used to map values into colors.
Definition: qwt_color_map.h:34
QFlags< PaintAttribute > PaintAttributes
Paint attributes.
A paint device for scalable graphics.
Definition: qwt_graphic.h:75
A scale map.
Definition: qwt_scale_map.h:26
QwtVectorFieldSample sample(int index) const
virtual void itemChanged()
const QwtColorMap * colorMap() const
double pDist() const
bool testMagnitudeMode(MagnitudeMode) const
void invalidate()
Definition: qwt_interval.h:323
MQTTClient c
Definition: test10.c:1656
virtual QRgb rgb(const QwtInterval &interval, double value) const =0
virtual size_t size() const =0
virtual void paint(QPainter *) const =0
void setPen(const QPen &)
virtual T sample(size_t i) const =0
void setMagnitudeRange(const QwtInterval &magnitudeRange)
For QwtPlotVectorField.
virtual size_t dataSize() const QWT_OVERRIDE
virtual void dataChanged() QWT_OVERRIDE
dataChanged() indicates, that the series has been changed.
Base class for plot items representing a series of samples.
void setItemAttribute(ItemAttribute, bool on=true)
bool isInverting() const
double transform(double s) const
QFlags< MagnitudeMode > MagnitudeModes
Paint attributes.
virtual void dataChanged() QWT_OVERRIDE
dataChanged() indicates, that the series has been changed.
virtual ~QwtPlotVectorField()
Destructor.
static double qwtVector2Magnitude(double vx, double vy)
static bool roundingAlignment()
Definition: qwt_painter.h:181
virtual QRectF boundingRect() const QWT_OVERRIDE
virtual void setLength(qreal length)=0
void setMagnitudeMode(MagnitudeMode, bool on=true)
const QwtText & title() const


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Dec 6 2020 03:48:10