Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
qwt_scale_draw.cpp
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_scale_draw.h"
11#include "qwt_scale_div.h"
12#include "qwt_scale_map.h"
13#include "qwt_math.h"
14#include "qwt_painter.h"
15#include "qwt_text.h"
16
17#include <qpainter.h>
18#include <qpaintengine.h>
19#include <qmath.h>
20
21static inline double qwtEffectivePenWidth( const QwtAbstractScaleDraw* scaleDraw )
22{
23 return qwtMaxF( scaleDraw->penWidthF(), 1.0 );
24}
25
26namespace QwtScaleRendererReal
27{
28 inline qreal penWidth( const QPainter* painter, const QwtScaleDraw* scaleDraw )
29 {
30 qreal width = scaleDraw->penWidthF();
31#if 1
32 if ( width <= 0.0 )
33 width = 1.0;
34#endif
35
36 if ( painter->pen().isCosmetic() )
37 {
38 const QTransform& transform = painter->transform();
39
40 switch ( scaleDraw->alignment() )
41 {
44 {
45 width /= transform.m11();
46 break;
47 }
50 {
51 width /= transform.m22();
52 break;
53 }
54 }
55 }
56
57 return width;
58 }
59
60 inline void drawBackbone( QPainter* painter, const QwtScaleDraw* scaleDraw )
61 {
62 const qreal pw2 = 0.5 * penWidth( painter, scaleDraw );
63
64 const QPointF pos = scaleDraw->pos();
65 const qreal length = scaleDraw->length();
66
67 switch ( scaleDraw->alignment() )
68 {
70 {
71 const qreal x = pos.x() + 1.0 - pw2;
72 QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + length );
73
74 break;
75 }
77 {
78 const qreal x = pos.x() - 1.0 + pw2;
79 QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + length );
80
81 break;
82 }
84 {
85 const qreal y = pos.y() + 1.0 - pw2;
86 QwtPainter::drawLine( painter, pos.x(), y, pos.x() + length, y );
87
88 break;
89 }
91 {
92 const qreal y = pos.y() - 1.0 + pw2;
93 QwtPainter::drawLine( painter, pos.x(), y, pos.x() + length, y );
94
95 break;
96 }
97 }
98 }
99
100 inline void drawTick( QPainter* painter,
101 const QwtScaleDraw* scaleDraw, qreal tickPos, qreal tickLength )
102 {
103 const QPointF pos = scaleDraw->pos();
104
105 qreal pw = 0.0;
106
107 if ( scaleDraw->hasComponent( QwtScaleDraw::Backbone ) )
108 pw = penWidth( painter, scaleDraw );
109
110 const qreal length = tickLength + pw;
111
112 /*
113 Those correction offsets have been found by try and error.
114 They need to be understood and replaced by a calculation,
115 that makes sense. TODO ...
116 */
117 const qreal off1 = 1.0;
118 const qreal off2 = ( scaleDraw->penWidthF() <= 0.0 ) ? 0.5 : 0.0;
119
120 switch ( scaleDraw->alignment() )
121 {
123 {
124 const qreal x = pos.x() + off1 - off2;
125 QwtPainter::drawLine( painter, x, tickPos, x - length, tickPos );
126
127 break;
128 }
130 {
131 const qreal x = pos.x() - off1 + off2;
132 QwtPainter::drawLine( painter, x, tickPos, x + length, tickPos );
133 break;
134 }
136 {
137 const qreal y = pos.y() + off1 - 2 * off2;
138 QwtPainter::drawLine( painter, tickPos, y, tickPos, y - length );
139
140 break;
141 }
143 {
144 const qreal y = pos.y() - off1 + off2;
145 QwtPainter::drawLine( painter, tickPos, y, tickPos, y + length );
146
147 break;
148 }
149 }
150 }
151}
152
153namespace QwtScaleRendererInt
154{
155 inline void drawBackbone( QPainter* painter, const QwtScaleDraw* scaleDraw )
156 {
157 const int pw = qMax( qRound( scaleDraw->penWidthF() ), 1 );
158
159 const qreal length = scaleDraw->length();
160 const QPointF pos = scaleDraw->pos();
161
162 switch ( scaleDraw->alignment() )
163 {
165 {
166 const qreal x = qRound( pos.x() - ( pw - 1 ) / 2 );
167 QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + length );
168
169 break;
170 }
172 {
173 const qreal x = qRound( pos.x() + pw / 2 );
174 QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + length );
175
176 break;
177 }
179 {
180 const qreal y = qRound( pos.y() - ( pw - 1 ) / 2 );
181 QwtPainter::drawLine( painter, pos.x(), y, pos.x() + length, y );
182
183 break;
184 }
186 {
187 const qreal y = qRound( pos.y() + pw / 2 );
188 QwtPainter::drawLine( painter, pos.x(), y, pos.x() + length, y );
189
190 break;
191 }
192 }
193 }
194
195 inline void drawTick( QPainter* painter,
196 const QwtScaleDraw* scaleDraw, qreal tickPos, qreal tickLength )
197 {
198 const QPointF pos = scaleDraw->pos();
199 tickPos = qRound( tickPos );
200
201 int pw = 0;
202 if ( scaleDraw->hasComponent( QwtScaleDraw::Backbone ) )
203 pw = qMax( qRound( scaleDraw->penWidthF() ), 1 );
204
205 int len = qMax( qRound( tickLength ), 1 );
206
207 // the width of ticks at the borders might extent the backbone
208 len += pw;
209
210 if ( painter->pen().capStyle() == Qt::FlatCap )
211 len++; // the end point is not rendered
212
213 qreal off = 0.0;
214
215 if ( painter->paintEngine()->type() == QPaintEngine::X11 )
216 {
217 if ( pw == 1 )
218 {
219 // In opposite to raster, X11 paints the end point
220 off = 1.0;
221 }
222 }
223
224 switch ( scaleDraw->alignment() )
225 {
227 {
228 const qreal x1 = qRound( pos.x() ) + 1;
229 const qreal x2 = x1 - len + 1;
230
231 QwtPainter::drawLine( painter, x2, tickPos, x1 - off, tickPos );
232
233 break;
234 }
236 {
237 const qreal x1 = qRound( pos.x() );
238 const qreal x2 = x1 + len - 1;
239
240 QwtPainter::drawLine( painter, x1, tickPos, x2 - off, tickPos );
241
242 break;
243 }
245 {
246 const qreal y1 = qRound( pos.y() );
247 const qreal y2 = y1 + len - 1;
248
249 QwtPainter::drawLine( painter, tickPos, y1, tickPos, y2 - off );
250
251 break;
252 }
254 {
255 const qreal y1 = qRound( pos.y() );
256 const qreal y2 = y1 - len + 1;
257
258 QwtPainter::drawLine( painter, tickPos, y2 + 1, tickPos, y1 + 1 - off );
259
260 break;
261 }
262 }
263 }
264}
265
266class QwtScaleDraw::PrivateData
267{
268 public:
269 PrivateData()
270 : len( 0 )
271 , alignment( QwtScaleDraw::BottomScale )
272 , labelRotation( 0.0 )
273 {
274 }
275
276 QPointF pos;
277 double len;
278
279 Alignment alignment;
280
281 Qt::Alignment labelAlignment;
282 double labelRotation;
283};
284
293{
294 m_data = new QwtScaleDraw::PrivateData;
295 setLength( 100 );
296}
297
300{
301 delete m_data;
302}
303
310{
311 return m_data->alignment;
312}
313
323{
324 m_data->alignment = align;
325}
326
337Qt::Orientation QwtScaleDraw::orientation() const
338{
339 switch ( m_data->alignment )
340 {
341 case TopScale:
342 case BottomScale:
343 return Qt::Horizontal;
344 case LeftScale:
345 case RightScale:
346 default:
347 return Qt::Vertical;
348 }
349}
350
362 const QFont& font, int& start, int& end ) const
363{
364 start = 0;
365 end = 1.0;
366
368 return;
369
371 if ( ticks.count() == 0 )
372 return;
373
374 // Find the ticks, that are mapped to the borders.
375 // minTick is the tick, that is mapped to the top/left-most position
376 // in widget coordinates.
377
378 double minTick = ticks[0];
379 double minPos = scaleMap().transform( minTick );
380 double maxTick = minTick;
381 double maxPos = minPos;
382
383 for ( int i = 1; i < ticks.count(); i++ )
384 {
385 const double tickPos = scaleMap().transform( ticks[i] );
386 if ( tickPos < minPos )
387 {
388 minTick = ticks[i];
389 minPos = tickPos;
390 }
391 if ( tickPos > scaleMap().transform( maxTick ) )
392 {
393 maxTick = ticks[i];
394 maxPos = tickPos;
395 }
396 }
397
398 double e = 0.0;
399 double s = 0.0;
400 if ( orientation() == Qt::Vertical )
401 {
402 s = -labelRect( font, minTick ).top();
403 s -= qAbs( minPos - qRound( scaleMap().p2() ) );
404
405 e = labelRect( font, maxTick ).bottom();
406 e -= qAbs( maxPos - scaleMap().p1() );
407 }
408 else
409 {
410 s = -labelRect( font, minTick ).left();
411 s -= qAbs( minPos - scaleMap().p1() );
412
413 e = labelRect( font, maxTick ).right();
414 e -= qAbs( maxPos - scaleMap().p2() );
415 }
416
417 if ( s < 0.0 )
418 s = 0.0;
419 if ( e < 0.0 )
420 e = 0.0;
421
422 start = qwtCeil( s );
423 end = qwtCeil( e );
424}
425
436int QwtScaleDraw::minLabelDist( const QFont& font ) const
437{
439 return 0;
440
442 if ( ticks.isEmpty() )
443 return 0;
444
445 const QFontMetrics fm( font );
446
447 const bool vertical = ( orientation() == Qt::Vertical );
448
449 QRectF bRect1;
450 QRectF bRect2 = labelRect( font, ticks[0] );
451 if ( vertical )
452 {
453 bRect2.setRect( -bRect2.bottom(), 0.0, bRect2.height(), bRect2.width() );
454 }
455
456 double maxDist = 0.0;
457
458 for ( int i = 1; i < ticks.count(); i++ )
459 {
460 bRect1 = bRect2;
461 bRect2 = labelRect( font, ticks[i] );
462 if ( vertical )
463 {
464 bRect2.setRect( -bRect2.bottom(), 0.0,
465 bRect2.height(), bRect2.width() );
466 }
467
468 double dist = fm.leading(); // space between the labels
469 if ( bRect1.right() > 0 )
470 dist += bRect1.right();
471 if ( bRect2.left() < 0 )
472 dist += -bRect2.left();
473
474 if ( dist > maxDist )
475 maxDist = dist;
476 }
477
478 double angle = qwtRadians( labelRotation() );
479 if ( vertical )
480 angle += M_PI / 2;
481
482 const double sinA = qFastSin( angle ); // qreal -> double
483 if ( qFuzzyCompare( sinA + 1.0, 1.0 ) )
484 return qCeil( maxDist );
485
486 const int fmHeight = fm.ascent() - 2;
487
488 // The distance we need until there is
489 // the height of the label font. This height is needed
490 // for the neighbored label.
491
492 double labelDist = fmHeight / qFastSin( angle ) * qFastCos( angle );
493 if ( labelDist < 0 )
494 labelDist = -labelDist;
495
496 // For text orientations close to the scale orientation
497
498 if ( labelDist > maxDist )
499 labelDist = maxDist;
500
501 // For text orientations close to the opposite of the
502 // scale orientation
503
504 if ( labelDist < fmHeight )
505 labelDist = fmHeight;
506
507 return qCeil( labelDist );
508}
509
523double QwtScaleDraw::extent( const QFont& font ) const
524{
525 double d = 0;
526
528 {
529 if ( orientation() == Qt::Vertical )
530 d = maxLabelWidth( font );
531 else
532 d = maxLabelHeight( font );
533
534 if ( d > 0 )
535 d += spacing();
536 }
537
539 {
540 d += maxTickLength();
541 }
542
544 {
545 d += qwtEffectivePenWidth( this );
546 }
547
548 d = qwtMaxF( d, minimumExtent() );
549 return d;
550}
551
560int QwtScaleDraw::minLength( const QFont& font ) const
561{
562 int startDist, endDist;
563 getBorderDistHint( font, startDist, endDist );
564
565 const QwtScaleDiv& sd = scaleDiv();
566
567 const uint minorCount =
568 sd.ticks( QwtScaleDiv::MinorTick ).count() +
569 sd.ticks( QwtScaleDiv::MediumTick ).count();
570 const uint majorCount =
571 sd.ticks( QwtScaleDiv::MajorTick ).count();
572
573 int lengthForLabels = 0;
575 lengthForLabels = minLabelDist( font ) * majorCount;
576
577 int lengthForTicks = 0;
579 {
580 const double pw = qwtEffectivePenWidth( this );
581 lengthForTicks = qCeil( ( majorCount + minorCount ) * ( pw + 1.0 ) );
582 }
583
584 return startDist + endDist + qMax( lengthForLabels, lengthForTicks );
585}
586
596QPointF QwtScaleDraw::labelPosition( double value ) const
597{
598 const double tval = scaleMap().transform( value );
599 double dist = spacing();
601 dist += qwtEffectivePenWidth( this );
602
605
606 double px = 0;
607 double py = 0;
608
609 switch ( alignment() )
610 {
611 case RightScale:
612 {
613 px = m_data->pos.x() + dist;
614 py = tval;
615 break;
616 }
617 case LeftScale:
618 {
619 px = m_data->pos.x() - dist;
620 py = tval;
621 break;
622 }
623 case BottomScale:
624 {
625 px = tval;
626 py = m_data->pos.y() + dist;
627 break;
628 }
629 case TopScale:
630 {
631 px = tval;
632 py = m_data->pos.y() - dist;
633 break;
634 }
635 }
636
637 return QPointF( px, py );
638}
639
649void QwtScaleDraw::drawTick( QPainter* painter, double value, double len ) const
650{
651 if ( len <= 0 )
652 return;
653
654 const double tval = scaleMap().transform( value );
655
656 if ( QwtPainter::roundingAlignment( painter ) )
657 QwtScaleRendererInt::drawTick( painter, this, tval, len );
658 else
659 QwtScaleRendererReal::drawTick( painter, this, tval, len );
660}
661
668void QwtScaleDraw::drawBackbone( QPainter* painter ) const
669{
670 if ( QwtPainter::roundingAlignment( painter ) )
671 QwtScaleRendererInt::drawBackbone( painter, this );
672 else
673 QwtScaleRendererReal::drawBackbone( painter, this );
674}
675
707void QwtScaleDraw::move( const QPointF& pos )
708{
709 m_data->pos = pos;
710 updateMap();
711}
712
717QPointF QwtScaleDraw::pos() const
718{
719 return m_data->pos;
720}
721
732void QwtScaleDraw::setLength( double length )
733{
734 if ( length >= 0 && length < 10 )
735 length = 10;
736
737 // f.e the left/bottom scales of a polar plot
738 if ( length < 0 && length > -10 )
739 length = -10;
740
741 m_data->len = length;
742 updateMap();
743}
744
750{
751 return m_data->len;
752}
753
762void QwtScaleDraw::drawLabel( QPainter* painter, double value ) const
763{
764 QwtText lbl = tickLabel( painter->font(), value );
765 if ( lbl.isEmpty() )
766 return;
767
768 QPointF pos = labelPosition( value );
769
770 QSizeF labelSize = lbl.textSize( painter->font() );
771
772 const QTransform transform = labelTransformation( pos, labelSize );
773
774 painter->save();
775 painter->setWorldTransform( transform, true );
776
777 lbl.draw ( painter, QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
778
779 painter->restore();
780}
781
794QRect QwtScaleDraw::boundingLabelRect( const QFont& font, double value ) const
795{
796 QwtText lbl = tickLabel( font, value );
797 if ( lbl.isEmpty() )
798 return QRect();
799
800 const QPointF pos = labelPosition( value );
801 QSizeF labelSize = lbl.textSize( font );
802
803 const QTransform transform = labelTransformation( pos, labelSize );
804 return transform.mapRect( QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
805}
806
818 const QPointF& pos, const QSizeF& size ) const
819{
820 QTransform transform;
821 transform.translate( pos.x(), pos.y() );
822 transform.rotate( labelRotation() );
823
824 int flags = labelAlignment();
825 if ( flags == 0 )
826 {
827 switch ( alignment() )
828 {
829 case RightScale:
830 {
831 if ( flags == 0 )
832 flags = Qt::AlignRight | Qt::AlignVCenter;
833 break;
834 }
835 case LeftScale:
836 {
837 if ( flags == 0 )
838 flags = Qt::AlignLeft | Qt::AlignVCenter;
839 break;
840 }
841 case BottomScale:
842 {
843 if ( flags == 0 )
844 flags = Qt::AlignHCenter | Qt::AlignBottom;
845 break;
846 }
847 case TopScale:
848 {
849 if ( flags == 0 )
850 flags = Qt::AlignHCenter | Qt::AlignTop;
851 break;
852 }
853 }
854 }
855
856 double x, y;
857
858 if ( flags & Qt::AlignLeft )
859 x = -size.width();
860 else if ( flags & Qt::AlignRight )
861 x = 0.0;
862 else // Qt::AlignHCenter
863 x = -( 0.5 * size.width() );
864
865 if ( flags & Qt::AlignTop )
866 y = -size.height();
867 else if ( flags & Qt::AlignBottom )
868 y = 0;
869 else // Qt::AlignVCenter
870 y = -( 0.5 * size.height() );
871
872 transform.translate( x, y );
873
874 return transform;
875}
876
887QRectF QwtScaleDraw::labelRect( const QFont& font, double value ) const
888{
889 QwtText lbl = tickLabel( font, value );
890 if ( lbl.isEmpty() )
891 return QRectF( 0.0, 0.0, 0.0, 0.0 );
892
893 const QPointF pos = labelPosition( value );
894
895 const QSizeF labelSize = lbl.textSize( font );
896 const QTransform transform = labelTransformation( pos, labelSize );
897
898 QRectF br = transform.mapRect( QRectF( QPointF( 0, 0 ), labelSize ) );
899 br.translate( -pos.x(), -pos.y() );
900
901 return br;
902}
903
912QSizeF QwtScaleDraw::labelSize( const QFont& font, double value ) const
913{
914 return labelRect( font, value ).size();
915}
916
930void QwtScaleDraw::setLabelRotation( double rotation )
931{
932 m_data->labelRotation = rotation;
933}
934
940{
941 return m_data->labelRotation;
942}
943
969void QwtScaleDraw::setLabelAlignment( Qt::Alignment alignment )
970{
971 m_data->labelAlignment = alignment;
972}
973
978Qt::Alignment QwtScaleDraw::labelAlignment() const
979{
980 return m_data->labelAlignment;
981}
982
987int QwtScaleDraw::maxLabelWidth( const QFont& font ) const
988{
989 double maxWidth = 0.0;
990
992 for ( int i = 0; i < ticks.count(); i++ )
993 {
994 const double v = ticks[i];
995 if ( scaleDiv().contains( v ) )
996 {
997 const double w = labelSize( font, ticks[i] ).width();
998 if ( w > maxWidth )
999 maxWidth = w;
1000 }
1001 }
1002
1003 return qCeil( maxWidth );
1004}
1005
1010int QwtScaleDraw::maxLabelHeight( const QFont& font ) const
1011{
1012 double maxHeight = 0.0;
1013
1015 for ( int i = 0; i < ticks.count(); i++ )
1016 {
1017 const double v = ticks[i];
1018 if ( scaleDiv().contains( v ) )
1019 {
1020 const double h = labelSize( font, ticks[i] ).height();
1021 if ( h > maxHeight )
1022 maxHeight = h;
1023 }
1024 }
1025
1026 return qCeil( maxHeight );
1027}
1028
1029void QwtScaleDraw::updateMap()
1030{
1031 const QPointF pos = m_data->pos;
1032 double len = m_data->len;
1033
1034 QwtScaleMap& sm = scaleMap();
1035 if ( orientation() == Qt::Vertical )
1036 sm.setPaintInterval( pos.y() + len, pos.y() );
1037 else
1038 sm.setPaintInterval( pos.x(), pos.x() + len );
1039}
A abstract base class for drawing scales.
const QwtScaleMap & scaleMap() const
@ Backbone
Backbone = the line where the ticks are located.
double tickLength(QwtScaleDiv::TickType) const
bool hasComponent(ScaleComponent) const
const QwtText & tickLabel(const QFont &, double value) const
Convert a value into its representing label and cache it.
const QwtScaleDiv & scaleDiv() const
double spacing() const
Get the spacing.
static bool roundingAlignment()
static void drawLine(QPainter *, qreal x1, qreal y1, qreal x2, qreal y2)
Wrapper for QPainter::drawLine()
A class representing a scale division.
QList< double > ticks(int tickType) const
@ MediumTick
Medium ticks.
@ MinorTick
Minor ticks.
@ MajorTick
Major ticks.
A class for drawing scales.
void setLength(double length)
QRect boundingLabelRect(const QFont &, double value) const
Find the bounding rectangle for the label.
QTransform labelTransformation(const QPointF &, const QSizeF &) const
Qt::Alignment labelAlignment() const
void move(double x, double y)
double labelRotation() const
virtual void drawLabel(QPainter *, double value) const override
virtual double extent(const QFont &) const override
void getBorderDistHint(const QFont &, int &start, int &end) const
Determine the minimum border distance.
void setLabelAlignment(Qt::Alignment)
Change the label flags.
int minLength(const QFont &) const
virtual void drawTick(QPainter *, double value, double len) const override
double length() const
QPointF pos() const
QPointF labelPosition(double value) const
void setAlignment(Alignment)
virtual void drawBackbone(QPainter *) const override
Alignment alignment() const
virtual ~QwtScaleDraw()
Destructor.
int maxLabelHeight(const QFont &) const
Qt::Orientation orientation() const
void setLabelRotation(double rotation)
int maxLabelWidth(const QFont &) const
@ BottomScale
The scale is below.
@ TopScale
The scale is above.
@ RightScale
The scale is right.
@ LeftScale
The scale is left.
int minLabelDist(const QFont &) const
QwtScaleDraw()
Constructor.
QRectF labelRect(const QFont &, double value) const
QSizeF labelSize(const QFont &, double value) const
A scale map.
double transform(double s) const
void setPaintInterval(double p1, double p2)
Specify the borders of the paint device interval.
A class representing a text.
Definition qwt_text.h:52
QSizeF textSize() const
Definition qwt_text.cpp:570
bool isEmpty() const
Definition qwt_text.cpp:739
void draw(QPainter *painter, const QRectF &rect) const
Definition qwt_text.cpp:615