source: ntrip/trunk/BNC/qwt/qwt_plot_vectorfield.cpp

Last change on this file was 11158, checked in by stuerze, 4 days ago

deprecated functions exchanged libs updated

File size: 25.4 KB
RevLine 
[11158]1/******************************************************************************
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#include <limits>
24
25#define DEBUG_RENDER 0
26
27#if DEBUG_RENDER
28#include <qelapsedtimer.h>
29#endif
30
31
32static inline double qwtVector2Radians( double vx, double vy )
33{
34 if ( vx == 0.0 )
35 return ( vy >= 0 ) ? M_PI_2 : 3 * M_PI_2;
36
37 return std::atan2( vy, vx );
38}
39
40static inline double qwtVector2Magnitude( double vx, double vy )
41{
42 return sqrt( vx * vx + vy * vy );
43}
44
45static QwtInterval qwtMagnitudeRange(
46 const QwtSeriesData< QwtVectorFieldSample >* series )
47{
48 if ( series->size() == 0 )
49 return QwtInterval( 0, 1 );
50
51 const QwtVectorFieldSample s0 = series->sample( 0 );
52
53 double min = s0.vx * s0.vx + s0.vy * s0.vy;
54 double max = min;
55
56 for ( uint i = 1; i < series->size(); i++ )
57 {
58 const QwtVectorFieldSample s = series->sample( i );
59 const double l = s.vx * s.vx + s.vy * s.vy;
60
61 if ( l < min )
62 min = l;
63
64 if ( l > max )
65 max = l;
66 }
67
68 min = std::sqrt( min );
69 max = std::sqrt( max );
70
71 if ( max == min )
72 max += 1.0;
73
74 return QwtInterval( min, max );
75}
76
77static inline QTransform qwtSymbolTransformation(
78 const QTransform& oldTransform, double x, double y,
79 double vx, double vy, double magnitude )
80{
81 QTransform transform = oldTransform;
82
83 if ( !transform.isIdentity() )
84 {
85 transform.translate( x, y );
86
87 const double radians = qwtVector2Radians( vx, vy );
88 transform.rotateRadians( radians );
89 }
90 else
91 {
92 /*
93 When starting with no transformation ( f.e on screen )
94 the matrix can be found without having to use
95 trigonometric functions
96 */
97
98 qreal sin, cos;
99 if ( magnitude == 0.0 )
100 {
101 // something
102 sin = 1.0;
103 cos = 0.0;
104 }
105 else
106 {
107 sin = vy / magnitude;
108 cos = vx / magnitude;
109 }
110
111 transform.setMatrix( cos, sin, 0.0, -sin, cos, 0.0, x, y, 1.0 );
112 }
113
114 return transform;
115}
116
117namespace
118{
119 class FilterMatrix
120 {
121 public:
122 class Entry
123 {
124 public:
125 inline void addSample( double sx, double sy,
126 double svx, double svy )
127 {
128 x += sx;
129 y += sy;
130
131 vx += svx;
132 vy += svy;
133
134 count++;
135 }
136
137 quint32 count;
138
139 // screen positions -> float is good enough
140 float x;
141 float y;
142 float vx;
143 float vy;
144 };
145
146 FilterMatrix( const QRectF& dataRect,
147 const QRectF& canvasRect, const QSizeF& cellSize )
148 {
149 m_dx = cellSize.width();
150 m_dy = cellSize.height();
151
152 m_x0 = dataRect.x();
153 if ( m_x0 < canvasRect.x() )
154 m_x0 += int( ( canvasRect.x() - m_x0 ) / m_dx ) * m_dx;
155
156 m_y0 = dataRect.y();
157 if ( m_y0 < canvasRect.y() )
158 m_y0 += int( ( canvasRect.y() - m_y0 ) / m_dy ) * m_dy;
159
160 m_numColumns = canvasRect.width() / m_dx + 1;
161 m_numRows = canvasRect.height() / m_dy + 1;
162
163#if 1
164 /*
165 limit column and row count to a maximum of 1000000,
166 so that memory usage is not an issue
167 */
168 if ( m_numColumns > 1000 )
169 {
170 m_dx = canvasRect.width() / 1000;
171 m_numColumns = canvasRect.width() / m_dx + 1;
172 }
173
174 if ( m_numRows > 1000 )
175 {
176 m_dy = canvasRect.height() / 1000;
177 m_numRows = canvasRect.height() / m_dx + 1;
178 }
179#endif
180
181 m_x1 = m_x0 + m_numColumns * m_dx;
182 m_y1 = m_y0 + m_numRows * m_dy;
183
184 m_entries = ( Entry* )::calloc( m_numRows * m_numColumns, sizeof( Entry ) );
185 if ( m_entries == NULL )
186 {
187 qWarning() << "QwtPlotVectorField: raster for filtering too fine - running out of memory";
188 }
189 }
190
191 ~FilterMatrix()
192 {
193 if ( m_entries )
194 std::free( m_entries );
195 }
196
197 inline int numColumns() const
198 {
199 return m_numColumns;
200 }
201
202 inline int numRows() const
203 {
204 return m_numRows;
205 }
206
207 inline void addSample( double x, double y,
208 double u, double v )
209 {
210 if ( x >= m_x0 && x < m_x1
211 && y >= m_y0 && y < m_y1 )
212 {
213 Entry& entry = m_entries[ indexOf( x, y ) ];
214 entry.addSample( x, y, u, v );
215 }
216 }
217
218 const FilterMatrix::Entry* entries() const
219 {
220 return m_entries;
221 }
222
223 private:
224 inline int indexOf( qreal x, qreal y ) const
225 {
226 const int col = ( x - m_x0 ) / m_dx;
227 const int row = ( y - m_y0 ) / m_dy;
228
229 return row * m_numColumns + col;
230 }
231
232 qreal m_x0, m_x1, m_y0, m_y1, m_dx, m_dy;
233 int m_numColumns;
234 int m_numRows;
235
236 Entry* m_entries;
237 };
238}
239
240class QwtPlotVectorField::PrivateData
241{
242 public:
243 PrivateData()
244 : pen( Qt::black )
245 , brush( Qt::black )
246 , indicatorOrigin( QwtPlotVectorField::OriginHead )
247 , magnitudeScaleFactor( 1.0 )
248 , rasterSize( 20, 20 )
249 , minArrowLength( 0.0 )
250 , maxArrowLength( std::numeric_limits< short >::max() )
251 , magnitudeModes( MagnitudeAsLength )
252 {
253 colorMap = NULL;
254 symbol = new QwtVectorFieldThinArrow();
255 }
256
257 ~PrivateData()
258 {
259 delete colorMap;
260 delete symbol;
261 }
262
263 QPen pen;
264 QBrush brush;
265
266 IndicatorOrigin indicatorOrigin;
267 QwtVectorFieldSymbol* symbol;
268 QwtColorMap* colorMap;
269
270 /*
271 Stores the range of magnitudes to be used for the color map.
272 If invalid (min=max or negative values), the range is determined
273 from the data samples themselves.
274 */
275 QwtInterval magnitudeRange;
276 QwtInterval boundingMagnitudeRange;
277
278 qreal magnitudeScaleFactor;
279 QSizeF rasterSize;
280
281 double minArrowLength;
282 double maxArrowLength;
283
284 PaintAttributes paintAttributes;
285 MagnitudeModes magnitudeModes;
286};
287
288/*!
289 Constructor
290 \param title Title of the curve
291 */
292QwtPlotVectorField::QwtPlotVectorField( const QwtText& title )
293 : QwtPlotSeriesItem( title )
294{
295 init();
296}
297
298/*!
299 Constructor
300 \param title Title of the curve
301 */
302QwtPlotVectorField::QwtPlotVectorField( const QString& title )
303 : QwtPlotSeriesItem( QwtText( title ) )
304{
305 init();
306}
307
308//! Destructor
309QwtPlotVectorField::~QwtPlotVectorField()
310{
311 delete m_data;
312}
313
314/*!
315 \brief Initialize data members
316 */
317void QwtPlotVectorField::init()
318{
319 setItemAttribute( QwtPlotItem::Legend );
320 setItemAttribute( QwtPlotItem::AutoScale );
321
322 m_data = new PrivateData;
323 setData( new QwtVectorFieldData() );
324
325 setZ( 20.0 );
326}
327
328/*!
329 Assign a pen
330
331 \param pen New pen
332 \sa pen(), brush()
333
334 \note the pen is ignored in MagnitudeAsColor mode
335 */
336void QwtPlotVectorField::setPen( const QPen& pen )
337{
338 if ( m_data->pen != pen )
339 {
340 m_data->pen = pen;
341
342 itemChanged();
343 legendChanged();
344 }
345}
346
347/*!
348 \return Pen used to draw the lines
349 \sa setPen(), brush()
350 */
351QPen QwtPlotVectorField::pen() const
352{
353 return m_data->pen;
354}
355
356/*!
357 \brief Assign a brush.
358
359 \param brush New brush
360 \sa brush(), pen()
361
362 \note the brush is ignored in MagnitudeAsColor mode
363 */
364void QwtPlotVectorField::setBrush( const QBrush& brush )
365{
366 if ( m_data->brush != brush )
367 {
368 m_data->brush = brush;
369
370 itemChanged();
371 legendChanged();
372 }
373}
374
375/*!
376 \return Brush used to fill the symbol
377 \sa setBrush(), pen()
378 */
379QBrush QwtPlotVectorField::brush() const
380{
381 return m_data->brush;
382}
383
384/*!
385 Set the origin for the symbols/arrows
386
387 \param origin Origin
388 \sa indicatorOrigin()
389 */
390void QwtPlotVectorField::setIndicatorOrigin( IndicatorOrigin origin )
391{
392 m_data->indicatorOrigin = origin;
393 if ( m_data->indicatorOrigin != origin )
394 {
395 m_data->indicatorOrigin = origin;
396 itemChanged();
397 }
398}
399
400//! \return origin for the symbols/arrows
401QwtPlotVectorField::IndicatorOrigin QwtPlotVectorField::indicatorOrigin() const
402{
403 return m_data->indicatorOrigin;
404}
405
406/*!
407 \brief Set the magnitudeScaleFactor
408
409 The length of the arrow in screen coordinate units is calculated by
410 scaling the magnitude by the magnitudeScaleFactor.
411
412 \param factor Scale factor
413
414 \sa magnitudeScaleFactor(), arrowLength()
415 \note Has no effect when QwtPlotVectorField::MagnitudeAsLength is not enabled
416 */
417void QwtPlotVectorField::setMagnitudeScaleFactor( double factor )
418{
419 if ( factor != m_data->magnitudeScaleFactor )
420 {
421 m_data->magnitudeScaleFactor = factor;
422 itemChanged();
423 }
424}
425
426/*!
427 \return Scale factor used to calculate the arrow length from the magnitude
428
429 The length of the arrow in screen coordinate units is calculated by
430 scaling the magnitude by the magnitudeScaleFactor.
431
432 Default implementation simply scales the vector using the magnitudeScaleFactor
433 property. Re-implement this function to provide special handling for
434 zero/non-zero magnitude arrows, or impose minimum/maximum arrow length limits.
435
436 \return Length of arrow to be drawn in dependence of vector magnitude.
437 \sa magnitudeScaleFactor
438 \note Has no effect when QwtPlotVectorField::MagnitudeAsLength is not enabled
439 */
440double QwtPlotVectorField::magnitudeScaleFactor() const
441{
442 return m_data->magnitudeScaleFactor;
443}
444
445/*!
446 Set the raster size used for filtering samples
447
448 \sa rasterSize(), QwtPlotVectorField::FilterVectors
449 */
450void QwtPlotVectorField::setRasterSize( const QSizeF& size )
451{
452 if ( size != m_data->rasterSize )
453 {
454 m_data->rasterSize = size;
455 itemChanged();
456 }
457}
458
459/*!
460 \return raster size used for filtering samples
461 \sa setRasterSize(), QwtPlotVectorField::FilterVectors
462 */
463QSizeF QwtPlotVectorField::rasterSize() const
464{
465 return m_data->rasterSize;
466}
467
468/*!
469 Specify an attribute how to draw the curve
470
471 \param attribute Paint attribute
472 \param on On/Off
473 \sa testPaintAttribute()
474 */
475void QwtPlotVectorField::setPaintAttribute(
476 PaintAttribute attribute, bool on )
477{
478 PaintAttributes attributes = m_data->paintAttributes;
479
480 if ( on )
481 attributes |= attribute;
482 else
483 attributes &= ~attribute;
484
485 if ( m_data->paintAttributes != attributes )
486 {
487 m_data->paintAttributes = attributes;
488 itemChanged();
489 }
490}
491
492/*!
493 \return True, when attribute is enabled
494 \sa PaintAttribute, setPaintAttribute()
495 */
496bool QwtPlotVectorField::testPaintAttribute(
497 PaintAttribute attribute ) const
498{
499 return ( m_data->paintAttributes & attribute );
500}
501
502//! \return QwtPlotItem::Rtti_PlotField
503int QwtPlotVectorField::rtti() const
504{
505 return QwtPlotItem::Rtti_PlotVectorField;
506}
507
508/*!
509 Sets a new arrow symbol (implementation of arrow drawing code).
510
511 \param symbol Arrow symbol
512
513 \sa symbol(), drawSymbol()
514 \note Ownership is transferred to QwtPlotVectorField.
515 */
516void QwtPlotVectorField::setSymbol( QwtVectorFieldSymbol* symbol )
517{
518 if ( m_data->symbol == symbol )
519 return;
520
521 delete m_data->symbol;
522 m_data->symbol = symbol;
523
524 itemChanged();
525 legendChanged();
526}
527
528/*!
529 \return arrow symbol
530 \sa setSymbol(), drawSymbol()
531 */
532const QwtVectorFieldSymbol* QwtPlotVectorField::symbol() const
533{
534 return m_data->symbol;
535}
536
537/*!
538 Initialize data with an array of samples.
539 \param samples Vector of points
540 */
541void QwtPlotVectorField::setSamples( const QVector< QwtVectorFieldSample >& samples )
542{
543 setData( new QwtVectorFieldData( samples ) );
544}
545
546/*!
547 Assign a series of samples
548
549 setSamples() is just a wrapper for setData() without any additional
550 value - beside that it is easier to find for the developer.
551
552 \param data Data
553 \warning The item takes ownership of the data object, deleting
554 it when its not used anymore.
555 */
556void QwtPlotVectorField::setSamples( QwtVectorFieldData* data )
557{
558 setData( data );
559}
560
561/*!
562 Change the color map
563
564 The color map is used to map the magnitude of a sample into
565 a color using a known range for the magnitudes.
566
567 \param colorMap Color Map
568
569 \sa colorMap(), magnitudeRange()
570 */
571void QwtPlotVectorField::setColorMap( QwtColorMap* colorMap )
572{
573 if ( colorMap == NULL )
574 return;
575
576 if ( colorMap != m_data->colorMap )
577 {
578 delete m_data->colorMap;
579 m_data->colorMap = colorMap;
580 }
581
582 legendChanged();
583 itemChanged();
584}
585
586/*!
587 \return Color Map used for mapping the intensity values to colors
588 \sa setColorMap()
589 */
590const QwtColorMap* QwtPlotVectorField::colorMap() const
591{
592 return m_data->colorMap;
593}
594
595/*!
596 Specify a mode how to represent the magnitude a n arrow/symbol
597
598 \param mode Mode
599 \param on On/Off
600 \sa testMagnitudeMode()
601 */
602void QwtPlotVectorField::setMagnitudeMode( MagnitudeMode mode, bool on )
603{
604 if ( on == testMagnitudeMode( mode ) )
605 return;
606
607 if ( on )
608 m_data->magnitudeModes |= mode;
609 else
610 m_data->magnitudeModes &= ~mode;
611
612 itemChanged();
613}
614
615/*!
616 \return True, when mode is enabled
617 \sa MagnitudeMode, setMagnitudeMode()
618 */
619bool QwtPlotVectorField::testMagnitudeMode( MagnitudeMode mode ) const
620{
621 return m_data->magnitudeModes & mode;
622}
623
624/*!
625 Sets the min/max magnitudes to be used for color map lookups.
626
627 If invalid (min=max=0 or negative values), the range is determined from
628 the current range of magnitudes in the vector samples.
629
630 \sa magnitudeRange(), colorMap()
631 */
632void QwtPlotVectorField::setMagnitudeRange( const QwtInterval& magnitudeRange )
633{
634 if ( m_data->magnitudeRange != magnitudeRange )
635 {
636 m_data->magnitudeRange = magnitudeRange;
637 itemChanged();
638 }
639}
640
641/*!
642 \return min/max magnitudes to be used for color map lookups
643 \sa setMagnitudeRange(), colorMap()
644 */
645QwtInterval QwtPlotVectorField::magnitudeRange() const
646{
647 return m_data->magnitudeRange;
648}
649
650/*!
651 Set a minimum for the arrow length of non zero vectors
652
653 \param length Minimum for the arrow length in pixels
654
655 \sa minArrowLength(), setMaxArrowLength(), arrowLength()
656 \note Has no effect when QwtPlotVectorField::MagnitudeAsLength is not enabled
657 */
658void QwtPlotVectorField::setMinArrowLength( double length )
659{
660 length = qMax( length, 0.0 );
661
662 if ( m_data->minArrowLength != length )
663 {
664 m_data->minArrowLength = length;
665 itemChanged();
666 }
667}
668
669/*!
670 \return minimum for the arrow length of non zero vectors
671
672 \sa setMinArrowLength(), maxArrowLength(), arrowLength()
673 \note Has no effect when QwtPlotVectorField::MagnitudeAsLength is not enabled
674 */
675double QwtPlotVectorField::minArrowLength() const
676{
677 return m_data->minArrowLength;
678}
679
680/*!
681 Set a maximum for the arrow length
682
683 \param length Maximum for the arrow length in pixels
684
685 \sa maxArrowLength(), setMinArrowLength(), arrowLength()
686 \note Has no effect when QwtPlotVectorField::MagnitudeAsLength is not enabled
687 */
688void QwtPlotVectorField::setMaxArrowLength( double length )
689{
690 length = qMax( length, 0.0 );
691
692 if ( m_data->maxArrowLength != length )
693 {
694 m_data->maxArrowLength = length;
695 itemChanged();
696 }
697}
698
699/*!
700 \return maximum for the arrow length
701
702 \sa setMinArrowLength(), maxArrowLength(), arrowLength()
703 \note Has no effect when QwtPlotVectorField::MagnitudeAsLength is not enabled
704 */
705double QwtPlotVectorField::maxArrowLength() const
706{
707 return m_data->maxArrowLength;
708}
709
710/*!
711 Computes length of the arrow in screen coordinate units based on its magnitude.
712
713 Default implementation simply scales the vector using the magnitudeScaleFactor()
714 If the result is not null, the length is then bounded into the interval
715 [ minArrowLength(), maxArrowLength() ].
716
717 Re-implement this function to provide special handling for
718 zero/non-zero magnitude arrows, or impose minimum/maximum arrow length limits.
719
720 \param magnitude Magnitude
721 \return Length of arrow to be drawn in dependence of vector magnitude.
722
723 \sa magnitudeScaleFactor, minArrowLength(), maxArrowLength()
724 \note Has no effect when QwtPlotVectorField::MagnitudeAsLength is not enabled
725 */
726double QwtPlotVectorField::arrowLength( double magnitude ) const
727{
728#if 0
729 /*
730 Normalize magnitude with respect to value range. Then, magnitudeScaleFactor
731 is the number of pixels to draw for a vector of length equal to
732 magnitudeRange.maxValue(). The relative scaling ensures that change of data
733 samples of very different magnitudes will always lead to a reasonable
734 display on screen.
735 */
736 const QwtVectorFieldData* vectorData = dynamic_cast< const QwtVectorFieldData* >( data() );
737 if ( m_data->magnitudeRange.maxValue() > 0 )
738 magnitude /= m_data->magnitudeRange.maxValue();
739#endif
740
741 double length = magnitude * m_data->magnitudeScaleFactor;
742
743 if ( length > 0.0 )
744 length = qBound( m_data->minArrowLength, length, m_data->maxArrowLength );
745
746 return length;
747}
748
749QRectF QwtPlotVectorField::boundingRect() const
750{
751#if 0
752 /*
753 The bounding rectangle of the samples comes from the origins
754 only, but as we know the scaling factor for the magnitude
755 ( qwtVector2Magnitude ) here, we could try to include it ?
756 */
757#endif
758
759 return QwtPlotSeriesItem::boundingRect();
760}
761
762/*!
763 \return Icon representing the vector fields on the legend
764
765 \param index Index of the legend entry ( ignored as there is only one )
766 \param size Icon size
767
768 \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
769 */
770QwtGraphic QwtPlotVectorField::legendIcon(
771 int index, const QSizeF& size ) const
772{
773 Q_UNUSED( index );
774
775 QwtGraphic icon;
776 icon.setDefaultSize( size );
777
778 if ( size.isEmpty() )
779 return icon;
780
781 QPainter painter( &icon );
782 painter.setRenderHint( QPainter::Antialiasing,
783 testRenderHint( QwtPlotItem::RenderAntialiased ) );
784
785 painter.translate( -size.width(), -0.5 * size.height() );
786
787 painter.setPen( m_data->pen );
788 painter.setBrush( m_data->brush );
789
790 m_data->symbol->setLength( size.width() - 2 );
791 m_data->symbol->paint( &painter );
792
793 return icon;
794}
795
796/*!
797 Draw a subset of the points
798
799 \param painter Painter
800 \param xMap Maps x-values into pixel coordinates.
801 \param yMap Maps y-values into pixel coordinates.
802 \param canvasRect Contents rectangle of the canvas
803 \param from Index of the first sample to be painted
804 \param to Index of the last sample to be painted. If to < 0 the
805 series will be painted to its last sample.
806 */
807void QwtPlotVectorField::drawSeries( QPainter* painter,
808 const QwtScaleMap& xMap, const QwtScaleMap& yMap,
809 const QRectF& canvasRect, int from, int to ) const
810{
811 if ( !painter || dataSize() <= 0 )
812 return;
813
814 if ( to < 0 )
815 to = dataSize() - 1;
816
817 if ( from < 0 )
818 from = 0;
819
820 if ( from > to )
821 return;
822
823#if DEBUG_RENDER
824 QElapsedTimer timer;
825 timer.start();
826#endif
827
828 drawSymbols( painter, xMap, yMap, canvasRect, from, to );
829
830#if DEBUG_RENDER
831 qDebug() << timer.elapsed();
832#endif
833}
834
835/*!
836 Draw symbols
837
838 \param painter Painter
839 \param xMap x map
840 \param yMap y map
841 \param canvasRect Contents rectangle of the canvas
842 \param from Index of the first sample to be painted
843 \param to Index of the last sample to be painted
844
845 \sa setSymbol(), drawSymbol(), drawSeries()
846 */
847void QwtPlotVectorField::drawSymbols( QPainter* painter,
848 const QwtScaleMap& xMap, const QwtScaleMap& yMap,
849 const QRectF& canvasRect, int from, int to ) const
850{
851 const bool doAlign = QwtPainter::roundingAlignment( painter );
852 const bool doClip = false;
853
854 const bool isInvertingX = xMap.isInverting();
855 const bool isInvertingY = yMap.isInverting();
856
857 const QwtSeriesData< QwtVectorFieldSample >* series = data();
858
859 if ( m_data->magnitudeModes & MagnitudeAsColor )
860 {
861 // user input error, can't draw without color map
862 // TODO: Discuss! Without colormap, silently fall back to uniform colors?
863 if ( m_data->colorMap == NULL)
864 return;
865 }
866 else
867 {
868 painter->setPen( m_data->pen );
869 painter->setBrush( m_data->brush );
870 }
871
872 if ( ( m_data->paintAttributes & FilterVectors ) && !m_data->rasterSize.isEmpty() )
873 {
874 const QRectF dataRect = QwtScaleMap::transform(
875 xMap, yMap, boundingRect() );
876
877 // TODO: Discuss. How to handle raster size when switching from screen to print size!
878 // DPI-aware adjustment of rastersize? Or make "rastersize in screen coordinate"
879 // or "rastersize in plotcoordinetes" a user option?
880#if 1
881 // define filter matrix based on screen/print coordinates
882 FilterMatrix matrix( dataRect, canvasRect, m_data->rasterSize );
883#else
884 // define filter matrix based on real coordinates
885
886 // get scale factor from real coordinates to screen coordinates
887 double xScale = 1;
888 if (xMap.sDist() != 0)
889 xScale = xMap.pDist() / xMap.sDist();
890
891 double yScale = 1;
892 if (yMap.sDist() != 0)
893 yScale = yMap.pDist() / yMap.sDist();
894
895 QSizeF canvasRasterSize(xScale * m_data->rasterSize.width(), yScale * m_data->rasterSize.height() );
896 FilterMatrix matrix( dataRect, canvasRect, canvasRasterSize );
897#endif
898
899 for ( int i = from; i <= to; i++ )
900 {
901 const QwtVectorFieldSample sample = series->sample( i );
902 if ( !sample.isNull() )
903 {
904 matrix.addSample( xMap.transform( sample.x ),
905 yMap.transform( sample.y ), sample.vx, sample.vy );
906 }
907 }
908
909 const int numEntries = matrix.numRows() * matrix.numColumns();
910 const FilterMatrix::Entry* entries = matrix.entries();
911
912 for ( int i = 0; i < numEntries; i++ )
913 {
914 const FilterMatrix::Entry& entry = entries[i];
915
916 if ( entry.count == 0 )
917 continue;
918
919 double xi = entry.x / entry.count;
920 double yi = entry.y / entry.count;
921
922 if ( doAlign )
923 {
924 xi = qRound( xi );
925 yi = qRound( yi );
926 }
927
928 const double vx = entry.vx / entry.count;
929 const double vy = entry.vy / entry.count;
930
931 drawSymbol( painter, xi, yi,
932 isInvertingX ? -vx : vx, isInvertingY ? -vy : vy );
933 }
934 }
935 else
936 {
937 for ( int i = from; i <= to; i++ )
938 {
939 const QwtVectorFieldSample sample = series->sample( i );
940
941 // arrows with zero length are never drawn
942 if ( sample.isNull() )
943 continue;
944
945 double xi = xMap.transform( sample.x );
946 double yi = yMap.transform( sample.y );
947
948 if ( doAlign )
949 {
950 xi = qRound( xi );
951 yi = qRound( yi );
952 }
953
954 if ( doClip )
955 {
956 if ( !canvasRect.contains( xi, yi ) )
957 continue;
958 }
959
960 drawSymbol( painter, xi, yi,
961 isInvertingX ? -sample.vx : sample.vx,
962 isInvertingY ? -sample.vy : sample.vy );
963 }
964 }
965}
966
967/*!
968 Draw a arrow/symbols at a specific position
969
970 x, y, are paint device coordinates, while vx, vy are from
971 the corresponding sample.
972
973 \sa setSymbol(), drawSeries()
974 */
975void QwtPlotVectorField::drawSymbol( QPainter* painter,
976 double x, double y, double vx, double vy ) const
977{
978 const double magnitude = qwtVector2Magnitude( vx, vy );
979
980 const QTransform oldTransform = painter->transform();
981
982 QTransform transform = qwtSymbolTransformation( oldTransform,
983 x, y, vx, vy, magnitude );
984
985 QwtVectorFieldSymbol* symbol = m_data->symbol;
986
987 double length = 0.0;
988
989 if ( m_data->magnitudeModes & MagnitudeAsLength )
990 {
991 length = arrowLength( magnitude );
992 }
993
994 symbol->setLength( length );
995
996 if( m_data->indicatorOrigin == OriginTail )
997 {
998 const qreal dx = symbol->length();
999 transform.translate( dx, 0.0 );
1000 }
1001 else if ( m_data->indicatorOrigin == OriginCenter )
1002 {
1003 const qreal dx = symbol->length();
1004 transform.translate( 0.5 * dx, 0.0 );
1005 }
1006
1007 if ( m_data->magnitudeModes & MagnitudeAsColor )
1008 {
1009 // Determine color for arrow if colored by magnitude.
1010
1011 QwtInterval range = m_data->magnitudeRange;
1012
1013 if ( !range.isValid() )
1014 {
1015 if ( !m_data->boundingMagnitudeRange.isValid() )
1016 m_data->boundingMagnitudeRange = qwtMagnitudeRange( data() );
1017
1018 range = m_data->boundingMagnitudeRange;
1019 }
1020
1021 const QColor c = m_data->colorMap->rgb( range, magnitude );
1022
1023#if 1
1024 painter->setBrush( c );
1025 painter->setPen( c );
1026#endif
1027 }
1028
1029 painter->setWorldTransform( transform, false );
1030 symbol->paint( painter );
1031 painter->setWorldTransform( oldTransform, false );
1032}
1033
1034void QwtPlotVectorField::dataChanged()
1035{
1036 m_data->boundingMagnitudeRange.invalidate();
1037 QwtPlotSeriesItem::dataChanged();
1038}
Note: See TracBrowser for help on using the repository browser.