KD Chart 2  [rev.2.6]
PaintingHelpers_p.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2019 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include "PaintingHelpers_p.h"
24 
25 #include "KDChartGlobal.h"
26 
27 #include "KDChartAbstractDiagram.h"
28 #include "KDChartAbstractDiagram_p.h"
30 #include "KDChartLineDiagram.h"
32 #include "KDChartPaintContext.h"
33 #include "KDChartPainterSaver_p.h"
34 #include "KDChartPlotter.h"
37 #include "ReverseMapper.h"
38 
39 namespace KDChart {
40 namespace PaintingHelpers {
41 
47 const QPointF project( const QPointF& point, const ThreeDLineAttributes& tdAttributes )
48 {
49  //Pending Michel FIXME - the rotation does not work as expected atm
50  qreal xrad = DEGTORAD( tdAttributes.lineXRotation() );
51  qreal yrad = DEGTORAD( tdAttributes.lineYRotation() );
52  return QPointF( point.x() * cos( yrad ) + tdAttributes.depth() * sin( yrad ),
53  point.y() * cos( xrad ) - tdAttributes.depth() * sin( xrad ) );
54 }
55 
56 void paintPolyline( PaintContext* ctx, const QBrush& brush, const QPen& pen, const QPolygonF& points )
57 {
58  ctx->painter()->setBrush( brush );
59  ctx->painter()->setPen( PrintingParameters::scalePen(
60  QPen( pen.color(), pen.width(), pen.style(), Qt::FlatCap, Qt::MiterJoin ) ) );
61 #if QT_VERSION > 0x040299
62  ctx->painter()->drawPolyline( points );
63 #else
64  // FIXME (Mirko) verify, this sounds reverse-logical
65  // For Qt versions older than 4.3 drawPolyline is VERY slow
66  // so we use traditional line segments drawing instead then.
67  for ( int i = 0; i < points.size()-1; ++i ) {
68  ctx->painter()->drawLine( points.at( i ), points.at( i + 1 ) );
69  }
70 #endif
71 }
72 
73 void paintThreeDLines( PaintContext* ctx, AbstractDiagram *diagram, const QModelIndex& index,
74  const QPointF& from, const QPointF& to, const ThreeDLineAttributes& tdAttributes,
75  ReverseMapper* reverseMapper )
76 {
77  const QPointF topLeft = project( from, tdAttributes );
78  const QPointF topRight = project ( to, tdAttributes );
79  const QPolygonF segment = QPolygonF() << from << topLeft << topRight << to;
80 
81  QBrush indexBrush( diagram->brush( index ) );
82  indexBrush = tdAttributes.threeDBrush( indexBrush, QRectF(topLeft, topRight) );
83 
84  const PainterSaver painterSaver( ctx->painter() );
85 
86  ctx->painter()->setRenderHint( QPainter::Antialiasing, diagram->antiAliasing() );
87  ctx->painter()->setBrush( indexBrush );
88  ctx->painter()->setPen( PrintingParameters::scalePen( diagram->pen( index ) ) );
89 
90  reverseMapper->addPolygon( index.row(), index.column(), segment );
91  ctx->painter()->drawPolygon( segment );
92 }
93 
94 void paintValueTracker( PaintContext* ctx, const ValueTrackerAttributes& vt, const QPointF& at )
95 {
96  CartesianCoordinatePlane* plane = qobject_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() );
97  if ( !plane )
98  return;
99 
100  DataDimensionsList gridDimensions = ctx->coordinatePlane()->gridDimensionsList();
101  const QPointF bottomLeft( ctx->coordinatePlane()->translate(
102  QPointF( plane->isHorizontalRangeReversed() ?
103  gridDimensions.at( 0 ).end :
104  gridDimensions.at( 0 ).start,
105  plane->isVerticalRangeReversed() ?
106  gridDimensions.at( 1 ).end :
107  gridDimensions.at( 1 ).start ) ) );
108  const QPointF topRight( ctx->coordinatePlane()->translate(
109  QPointF( plane->isHorizontalRangeReversed() ?
110  gridDimensions.at( 0 ).start :
111  gridDimensions.at( 0 ).end,
112  plane->isVerticalRangeReversed() ?
113  gridDimensions.at( 1 ).start :
114  gridDimensions.at( 1 ).end ) ) );
115  const QPointF markerPoint = at;
116 
117  QPointF startPoint;
118  if ( vt.orientations() & Qt::Horizontal ) {
119  startPoint = QPointF( bottomLeft.x(), at.y() );
120  } else {
121  startPoint = QPointF( at.x(), topRight.y() );
122  }
123 
124  QPointF endPoint;
125  if ( vt.orientations() & Qt::Vertical ) {
126  endPoint = QPointF( at.x(), bottomLeft.y() );
127  } else {
128  endPoint = QPointF( topRight.x(), at.y() );
129  }
130 
131  const QSizeF markerSize = vt.markerSize();
132  const QRectF ellipseMarker = QRectF( at.x() - markerSize.width() / 2,
133  at.y() - markerSize.height() / 2,
134  markerSize.width(), markerSize.height() );
135 
136  QPointF startMarker[3];
137  if ( vt.orientations() & Qt::Horizontal ) {
138  startMarker[0] = startPoint + QPointF( 0, markerSize.height() / 2 );
139  startMarker[1] = startPoint + QPointF( markerSize.width() / 2, 0 );
140  startMarker[2] = startPoint - QPointF( 0, markerSize.height() / 2 );
141  } else {
142  startMarker[0] = startPoint + QPointF( 0, markerSize.height() / 2 );
143  startMarker[1] = startPoint + QPointF( markerSize.width() / 2, 0 );
144  startMarker[2] = startPoint - QPointF( markerSize.width() / 2, 0 );
145  }
146 
147  QPointF endMarker[3];
148 
149  if ( vt.orientations() & Qt::Vertical ) {
150  endMarker[0] = endPoint + QPointF( markerSize.width() / 2, 0 );
151  endMarker[1] = endPoint - QPointF( 0, markerSize.height() / 2 );
152  endMarker[2] = endPoint - QPointF( markerSize.width() / 2, 0 );
153  } else {
154  endMarker[0] = endPoint + QPointF( 0, markerSize.width() / 2 );
155  endMarker[1] = endPoint - QPointF( 0, markerSize.height() / 2 );
156  endMarker[2] = endPoint - QPointF( markerSize.width() / 2, 0 );
157  }
158 
159  QPointF topLeft = startPoint;
160  QPointF bottomRightOffset = endPoint - topLeft;
161  QSizeF size( bottomRightOffset.x(), bottomRightOffset.y() );
162  QRectF area( topLeft, size );
163 
164  PainterSaver painterSaver( ctx->painter() );
165  ctx->painter()->setPen( PrintingParameters::scalePen( vt.linePen() ) );
166  ctx->painter()->setBrush( QBrush() );
167  ctx->painter()->drawLine( markerPoint, startPoint );
168  ctx->painter()->drawLine( markerPoint, endPoint );
169 
170  ctx->painter()->fillRect( area, vt.areaBrush() );
171 
172  ctx->painter()->setPen( PrintingParameters::scalePen( vt.markerPen() ) );
173  ctx->painter()->setBrush( vt.markerBrush() );
174  ctx->painter()->drawEllipse( ellipseMarker );
175 
176  ctx->painter()->setPen( PrintingParameters::scalePen( vt.arrowBrush().color() ) );
177  ctx->painter()->setBrush( vt.arrowBrush() );
178  ctx->painter()->drawPolygon( startMarker, 3 );
179  ctx->painter()->drawPolygon( endMarker, 3 );
180 }
181 
182 // ### for BC reasons we cannot insert a common interface for LineDiagram and Plotter into the class
183 // hierarchy, so we have to use hacks to use their common methods
184 static ThreeDLineAttributes threeDLineAttributes( AbstractDiagram* diagram, const QModelIndex& index )
185 {
186  if ( Plotter *plotter = qobject_cast< Plotter* >( diagram ) ) {
187  return plotter->threeDLineAttributes( index );
188  } else if ( LineDiagram *lineDiagram = qobject_cast< LineDiagram* >( diagram ) ) {
189  return lineDiagram->threeDLineAttributes( index );
190  }
191  Q_ASSERT( false );
192  return ThreeDLineAttributes();
193 }
194 
195 static ValueTrackerAttributes valueTrackerAttributes( AbstractDiagram* diagram, const QModelIndex& index )
196 {
197  if ( Plotter *plotter = qobject_cast< Plotter* >( diagram ) ) {
198  return plotter->valueTrackerAttributes( index );
199  } else if ( LineDiagram *lineDiagram = qobject_cast< LineDiagram* >( diagram ) ) {
200  return lineDiagram->valueTrackerAttributes( index );
201  }
202  Q_ASSERT( false );
203  return ValueTrackerAttributes();
204 }
205 
206 void paintElements( AbstractDiagram::Private *diagramPrivate, PaintContext* ctx,
207  const LabelPaintCache& lpc, const LineAttributesInfoList& lineList )
208 {
209  AbstractDiagram* diagram = diagramPrivate->diagram;
210  // paint all lines and their attributes
211  const PainterSaver painterSaver( ctx->painter() );
212  ctx->painter()->setRenderHint( QPainter::Antialiasing, diagram->antiAliasing() );
213 
214  QBrush curBrush;
215  QPen curPen;
216  QPolygonF points;
217  KDAB_FOREACH ( const LineAttributesInfo& lineInfo, lineList ) {
218  const QModelIndex& index = lineInfo.index;
219  const ThreeDLineAttributes td = threeDLineAttributes( diagram, index );
220 
221  if ( td.isEnabled() ) {
222  PaintingHelpers::paintThreeDLines( ctx, diagram, index, lineInfo.value,
223  lineInfo.nextValue, td, &diagramPrivate->reverseMapper );
224  } else {
225  const QBrush brush( diagram->brush( index ) );
226  const QPen pen( diagram->pen( index ) );
227 
228  // line goes from lineInfo.value to lineInfo.nextValue
229  // We don't want it added if we're not drawing it, since the reverse mapper is used
230  // for lookup when trying to find e.g. tooltips. Having the line added when invisible gives
231  // us tooltips in empty areas.
232  if (pen.style() != Qt::NoPen)
233  diagramPrivate->reverseMapper.addLine( lineInfo.index.row(), lineInfo.index.column(),
234  lineInfo.value, lineInfo.nextValue );
235 
236  if ( points.count() && points.last() == lineInfo.value && curBrush == brush && curPen == pen ) {
237  // continue the current run of lines
238  } else {
239  // different painter settings or discontinuous line: start a new run of lines
240  if ( points.count() ) {
241  PaintingHelpers::paintPolyline( ctx, curBrush, curPen, points );
242  }
243  curBrush = brush;
244  curPen = pen;
245  points.clear();
246  points << lineInfo.value;
247  }
248  points << lineInfo.nextValue;
249  }
250  }
251  if ( points.count() ) {
252  // the last run of lines is yet to be painted - do it now
253  PaintingHelpers::paintPolyline( ctx, curBrush, curPen, points );
254  }
255 
256  KDAB_FOREACH ( const LineAttributesInfo& lineInfo, lineList ) {
257  const ValueTrackerAttributes vt = valueTrackerAttributes( diagram, lineInfo.index );
258  if ( vt.isEnabled() ) {
259  PaintingHelpers::paintValueTracker( ctx, vt, lineInfo.nextValue );
260  }
261  }
262 
263  // paint all data value texts and the point markers
264  diagramPrivate->paintDataValueTextsAndMarkers( ctx, lpc, true );
265 }
266 
267 void paintAreas( AbstractDiagram::Private* diagramPrivate, PaintContext* ctx, const QModelIndex& index,
268  const QList< QPolygonF >& areas, uint opacity )
269 {
270  AbstractDiagram* diagram = diagramPrivate->diagram;
271  QPainterPath path;
272  for ( int i = 0; i < areas.count(); ++i )
273  {
274  const QPolygonF& p = areas[ i ];
275  path.addPolygon( p );
276  diagramPrivate->reverseMapper.addPolygon( index.row(), index.column(), p );
277  path.closeSubpath();
278  }
279 
280  ThreeDLineAttributes threeDAttrs = threeDLineAttributes( diagram, index );
281  QBrush trans = diagram->brush( index );
282  if ( threeDAttrs.isEnabled() ) {
283  trans = threeDAttrs.threeDBrush( trans, path.boundingRect() );
284  }
285  QColor transColor = trans.color();
286  transColor.setAlpha( opacity );
287  trans.setColor(transColor);
288  QPen indexPen = diagram->pen(index);
289  indexPen.setBrush( trans );
290  const PainterSaver painterSaver( ctx->painter() );
291 
292  ctx->painter()->setRenderHint( QPainter::Antialiasing, diagram->antiAliasing() );
293  ctx->painter()->setPen( PrintingParameters::scalePen( indexPen ) );
294  ctx->painter()->setBrush( trans );
295 
296  ctx->painter()->drawPath( path );
297 }
298 
299 } // namespace PaintingHelpers
300 } // namespace KDChart
void paintElements(AbstractDiagram::Private *diagramPrivate, PaintContext *ctx, const LabelPaintCache &lpc, const LineAttributesInfoList &lineList)
const QPointF project(const QPointF &point, const ThreeDLineAttributes &tdAttributes)
virtual const QPointF translate(const QPointF &diagramPoint) const =0
Translate the given point in value space coordinates to a position in pixel space.
AbstractCoordinatePlane * coordinatePlane() const
static ValueTrackerAttributes valueTrackerAttributes(AbstractDiagram *diagram, const QModelIndex &index)
QPainter * painter() const
Plotter defines a diagram type plotting two-dimensional data.
AbstractDiagram defines the interface for diagram classes.
QBrush brush() const
Retrieve the brush to be used for painting datapoints globally.
The ReverseMapper stores information about objects on a chart and their respective model indexes...
Definition: ReverseMapper.h:44
void paintAreas(AbstractDiagram::Private *diagramPrivate, PaintContext *ctx, const QModelIndex &index, const QList< QPolygonF > &areas, uint opacity)
LineDiagram defines a common line diagram.
Cell-specific attributes regarding value tracking.
Stores information about painting diagrams.
QPen pen() const
Retrieve the pen to be used for painting datapoints globally.
virtual QBrush threeDBrush(const QBrush &brush, const QRectF &rect) const
DataDimensionsList gridDimensionsList()
Returns the dimensions used for drawing the grid lines.
void addPolygon(int row, int column, const QPolygonF &polygon)
void paintPolyline(PaintContext *ctx, const QBrush &brush, const QPen &pen, const QPolygonF &points)
static QPen scalePen(const QPen &pen)
void paintValueTracker(PaintContext *ctx, const ValueTrackerAttributes &vt, const QPointF &at)
void paintThreeDLines(PaintContext *ctx, AbstractDiagram *diagram, const QModelIndex &index, const QPointF &from, const QPointF &to, const ThreeDLineAttributes &tdAttributes, ReverseMapper *reverseMapper)
static ThreeDLineAttributes threeDLineAttributes(AbstractDiagram *diagram, const QModelIndex &index)
A set of 3D line attributes.

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
https://www.kdab.com/
https://www.kdab.com/products/kd-chart/