KD Chart 2  [rev.2.5.1]
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
PaintingHelpers_p.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2013 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  diagramPrivate->reverseMapper.addLine( lineInfo.index.row(), lineInfo.index.column(),
230  lineInfo.value, lineInfo.nextValue );
231 
232  if ( points.count() && points.last() == lineInfo.value && curBrush == brush && curPen == pen ) {
233  // continue the current run of lines
234  } else {
235  // different painter settings or discontinuous line: start a new run of lines
236  if ( points.count() ) {
237  PaintingHelpers::paintPolyline( ctx, curBrush, curPen, points );
238  }
239  curBrush = brush;
240  curPen = pen;
241  points.clear();
242  points << lineInfo.value;
243  }
244  points << lineInfo.nextValue;
245  }
246  }
247  if ( points.count() ) {
248  // the last run of lines is yet to be painted - do it now
249  PaintingHelpers::paintPolyline( ctx, curBrush, curPen, points );
250  }
251 
252  KDAB_FOREACH ( const LineAttributesInfo& lineInfo, lineList ) {
253  const ValueTrackerAttributes vt = valueTrackerAttributes( diagram, lineInfo.index );
254  if ( vt.isEnabled() ) {
255  PaintingHelpers::paintValueTracker( ctx, vt, lineInfo.nextValue );
256  }
257  }
258 
259  // paint all data value texts and the point markers
260  diagramPrivate->paintDataValueTextsAndMarkers( ctx, lpc, true );
261 }
262 
263 void paintAreas( AbstractDiagram::Private* diagramPrivate, PaintContext* ctx, const QModelIndex& index,
264  const QList< QPolygonF >& areas, uint opacity )
265 {
266  AbstractDiagram* diagram = diagramPrivate->diagram;
267  QPainterPath path;
268  for ( int i = 0; i < areas.count(); ++i )
269  {
270  const QPolygonF& p = areas[ i ];
271  path.addPolygon( p );
272  diagramPrivate->reverseMapper.addPolygon( index.row(), index.column(), p );
273  path.closeSubpath();
274  }
275 
276  ThreeDLineAttributes threeDAttrs = threeDLineAttributes( diagram, index );
277  QBrush trans = diagram->brush( index );
278  if ( threeDAttrs.isEnabled() ) {
279  trans = threeDAttrs.threeDBrush( trans, path.boundingRect() );
280  }
281  QColor transColor = trans.color();
282  transColor.setAlpha( opacity );
283  trans.setColor(transColor);
284  QPen indexPen = diagram->pen(index);
285  indexPen.setBrush( trans );
286  const PainterSaver painterSaver( ctx->painter() );
287 
288  ctx->painter()->setRenderHint( QPainter::Antialiasing, diagram->antiAliasing() );
289  ctx->painter()->setPen( PrintingParameters::scalePen( indexPen ) );
290  ctx->painter()->setBrush( trans );
291 
292  ctx->painter()->drawPath( path );
293 }
294 
295 } // namespace PaintingHelpers
296 } // namespace KDChart

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