KD Chart 2  [rev.2.5.1]
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
KDChartPolarCoordinatePlane.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 
24 #include "KDChartPolarCoordinatePlane_p.h"
25 
26 #include "KDChartPainterSaver_p.h"
27 #include "KDChartChart.h"
28 #include "KDChartPaintContext.h"
29 #include "KDChartAbstractDiagram.h"
31 #include "KDChartPolarDiagram.h"
32 
33 #include <math.h>
34 
35 #include <QFont>
36 #include <QList>
37 #include <QtDebug>
38 #include <QPainter>
39 #include <QTimer>
40 
41 #include <KDABLibFakes>
42 
43 using namespace KDChart;
44 
45 #define d d_func()
46 
48  : AbstractCoordinatePlane ( new Private(), parent )
49 {
50  // this bloc left empty intentionally
51 }
52 
54 {
55  // this bloc left empty intentionally
56 }
57 
58 void PolarCoordinatePlane::init()
59 {
60  // this bloc left empty intentionally
61 }
62 
64 {
65  Q_ASSERT_X ( dynamic_cast<AbstractPolarDiagram*> ( diagram ),
66  "PolarCoordinatePlane::addDiagram", "Only polar"
67  "diagrams can be added to a polar coordinate plane!" );
69  connect ( diagram, SIGNAL ( layoutChanged ( AbstractDiagram* ) ),
70  SLOT ( slotLayoutChanged ( AbstractDiagram* ) ) );
71 
72 }
73 
74 void PolarCoordinatePlane::paint ( QPainter* painter )
75 {
76  AbstractDiagramList diags = diagrams();
77  if ( d->coordinateTransformations.size() != diags.size() ) {
78  // diagrams have not been set up yet
79  return;
80  }
81  // need at least one so d->currentTransformation can be a valid pointer
82  Q_ASSERT( !d->coordinateTransformations.isEmpty() );
83 
84  PaintContext ctx;
85  ctx.setPainter ( painter );
86  ctx.setCoordinatePlane ( this );
87  ctx.setRectangle ( geometry() /*d->contentRect*/ );
88 
89  // 1. ask (only!) PolarDiagrams if they need additional space for data labels / data comments
90 
91  const qreal oldZoomX = zoomFactorX();
92  const qreal oldZoomY = zoomFactorY();
93  d->newZoomX = oldZoomX;
94  d->newZoomY = oldZoomY;
95  for ( int i = 0; i < diags.size(); i++ ) {
96  d->currentTransformation = & ( d->coordinateTransformations[i] );
97  qreal zoomX;
98  qreal zoomY;
99  PolarDiagram* polarDia = dynamic_cast<PolarDiagram*> ( diags[i] );
100  if ( polarDia ) {
101  polarDia->paint( &ctx, true, zoomX, zoomY );
102  d->newZoomX = qMin( d->newZoomX, zoomX );
103  d->newZoomY = qMin( d->newZoomY, zoomY );
104  }
105  }
106 
107  if ( d->newZoomX != oldZoomX || d->newZoomY != oldZoomY ) {
108  //qDebug() << "new zoom:" << d->newZoomY << " old zoom:" << oldZoomY;
109  d->currentTransformation = 0; // not painting anymore until we get called again
110  QMetaObject::invokeMethod( this, "adjustZoomAndRepaint", Qt::QueuedConnection );
111  return;
112  }
113 
114  // 2. there was room enough for the labels, so start drawing
115 
116  // paint the coordinate system rulers:
117  d->currentTransformation = &d->coordinateTransformations.first();
118  d->grid->drawGrid( &ctx );
119 
120  // paint the diagrams which will re-use their DataValueTextInfoList(s) filled in step 1:
121  for ( int i = 0; i < diags.size(); i++ ) {
122  d->currentTransformation = & ( d->coordinateTransformations[i] );
123  PainterSaver painterSaver( painter );
124  PolarDiagram* polarDia = dynamic_cast<PolarDiagram*>( diags[i] );
125  if ( polarDia ) {
126  qreal dummy1, dummy2;
127  polarDia->paint( &ctx, false, dummy1, dummy2 );
128  } else {
129  diags[i]->paint( &ctx );
130  }
131  }
132  d->currentTransformation = 0;
133 }
134 
135 
137 {
138  const qreal newZoom = qMin(d->newZoomX, d->newZoomY);
139  setZoomFactors(newZoom, newZoom);
140  update();
141 }
142 
143 
144 void PolarCoordinatePlane::resizeEvent ( QResizeEvent* )
145 {
146  d->initialResizeEventReceived = true;
147  layoutDiagrams();
148 }
149 
151 {
152  // the rectangle the diagrams cover in the *plane*:
153  // (Why -3? We save 1px on each side for the antialiased drawing, and
154  // respect the way QPainter calculates the width of a painted rect (the
155  // size is the rectangle size plus the pen width). This way, most clipping
156  // for regular pens should be avoided. When pens with a penWidth or larger
157  // than 1 are used, this may not b sufficient.
158  const QRect rect( areaGeometry() );
159  d->contentRect = QRectF ( 1, 1, rect.width() - 3, rect.height() - 3 );
160 
161  const ZoomParameters zoom = d->coordinateTransformations.isEmpty() ? ZoomParameters()
162  : d->coordinateTransformations.front().zoom;
163  // FIXME distribute space according to options:
164  const qreal oldStartPosition = startPosition();
165  d->coordinateTransformations.clear();
166  Q_FOREACH( AbstractDiagram* diagram, diagrams() )
167  {
168  AbstractPolarDiagram *polarDiagram = dynamic_cast<AbstractPolarDiagram*>( diagram );
169  Q_ASSERT( polarDiagram );
170  QPair<QPointF, QPointF> dataBoundariesPair = polarDiagram->dataBoundaries();
171 
172  const qreal angleUnit = 360 / polarDiagram->valueTotals();
173 //qDebug() << "--------------------------------------------------------";
174  const qreal radius = qAbs( dataBoundariesPair.first.y() ) + dataBoundariesPair.second.y();
175 //qDebug() << radius <<"="<<dataBoundariesPair.second.y();
176  const qreal diagramWidth = radius * 2; // == height
177  const qreal planeWidth = d->contentRect.width();
178  const qreal planeHeight = d->contentRect.height();
179  const qreal radiusUnit = qMin( planeWidth, planeHeight ) / diagramWidth;
180 //qDebug() << radiusUnit <<"=" << "qMin( "<<planeWidth<<","<< planeHeight <<") / "<<diagramWidth;
181  QPointF coordinateOrigin = QPointF ( planeWidth / 2, planeHeight / 2 );
182  coordinateOrigin += d->contentRect.topLeft();
183 
184  CoordinateTransformation diagramTransposition;
185  diagramTransposition.originTranslation = coordinateOrigin;
186  diagramTransposition.radiusUnit = radiusUnit;
187  diagramTransposition.angleUnit = angleUnit;
188  diagramTransposition.startPosition = oldStartPosition;
189  diagramTransposition.zoom = zoom;
190  diagramTransposition.minValue = dataBoundariesPair.first.y() < 0 ? dataBoundariesPair.first.y() : 0.0;
191  d->coordinateTransformations.append( diagramTransposition );
192  }
193  update();
194 }
195 
196 const QPointF PolarCoordinatePlane::translate( const QPointF& diagramPoint ) const
197 {
198  Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate",
199  "Only call translate() from within paint()." );
200  return d->currentTransformation->translate ( diagramPoint );
201 }
202 
203 const QPointF PolarCoordinatePlane::translatePolar( const QPointF& diagramPoint ) const
204 {
205  Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate",
206  "Only call translate() from within paint()." );
207  return d->currentTransformation->translatePolar ( diagramPoint );
208 }
209 
211 {
212  Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::angleUnit",
213  "Only call angleUnit() from within paint()." );
214  return d->currentTransformation->angleUnit;
215 }
216 
218 {
219  Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::radiusUnit",
220  "Only call radiusUnit() from within paint()." );
221  return d->currentTransformation->radiusUnit;
222 }
223 
225 {
226  if ( d->initialResizeEventReceived ) layoutDiagrams();
227 }
228 
230 {
231  Q_ASSERT_X ( diagram(), "PolarCoordinatePlane::setStartPosition",
232  "setStartPosition() needs a diagram to be associated to the plane." );
233  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
234  it != d->coordinateTransformations.end();
235  ++it )
236  {
237  CoordinateTransformation& trans = *it;
238  trans.startPosition = degrees;
239  }
240 }
241 
243 {
244  return d->coordinateTransformations.isEmpty()
245  ? 0.0
246  : d->coordinateTransformations.first().startPosition;
247 }
248 
250 {
251  return d->coordinateTransformations.isEmpty()
252  ? 1.0
253  : d->coordinateTransformations.first().zoom.xFactor;
254 }
255 
257 {
258  return d->coordinateTransformations.isEmpty()
259  ? 1.0
260  : d->coordinateTransformations.first().zoom.yFactor;
261 }
262 
263 void PolarCoordinatePlane::setZoomFactors( qreal factorX, qreal factorY )
264 {
265  setZoomFactorX( factorX );
266  setZoomFactorY( factorY );
267 }
268 
270 {
271  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
272  it != d->coordinateTransformations.end();
273  ++it )
274  {
275  CoordinateTransformation& trans = *it;
276  trans.zoom.xFactor = factor;
277  }
278 }
279 
281 {
282  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
283  it != d->coordinateTransformations.end();
284  ++it )
285  {
286  CoordinateTransformation& trans = *it;
287  trans.zoom.yFactor = factor;
288  }
289 }
290 
292 {
293  return d->coordinateTransformations.isEmpty()
294  ? QPointF( 0.5, 0.5 )
295  : QPointF( d->coordinateTransformations.first().zoom.xCenter, d->coordinateTransformations.first().zoom.yCenter );
296 }
297 
298 void PolarCoordinatePlane::setZoomCenter( const QPointF& center )
299 {
300  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
301  it != d->coordinateTransformations.end();
302  ++it )
303  {
304  CoordinateTransformation& trans = *it;
305  trans.zoom.xCenter = center.x();
306  trans.zoom.yCenter = center.y();
307  }
308 }
309 
311 {
313 
314  //FIXME(khz): do the real calculation
315 
316  return l;
317 }
318 
320  bool circular,
321  const GridAttributes& a )
322 {
323  if ( circular )
324  d->gridAttributesCircular = a;
325  else
326  d->gridAttributesSagittal = a;
327  setHasOwnGridAttributes( circular, true );
328  update();
329  emit propertiesChanged();
330 }
331 
333  bool circular )
334 {
335  setHasOwnGridAttributes( circular, false );
336  update();
337 }
338 
340  bool circular ) const
341 {
342  if ( hasOwnGridAttributes( circular ) ) {
343  if ( circular )
344  return d->gridAttributesCircular;
345  else
346  return d->gridAttributesSagittal;
347  } else {
348  return globalGridAttributes();
349  }
350 }
351 
352 QRectF KDChart::PolarCoordinatePlane::Private::contentsRect( const KDChart::PolarCoordinatePlane* plane )
353 {
354  QRectF contentsRect;
355  QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) );
356  QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop;
357  const qreal offset = temp.y();
358  referencePointAtTop.setX( referencePointAtTop.x() - offset );
359  contentsRect.setTopLeft( referencePointAtTop );
360  contentsRect.setBottomRight( referencePointAtTop + QPointF( 2.0 * offset, 2.0 * offset) );
361  return contentsRect;
362 }
363 
364 void KDChart::PolarCoordinatePlane::setHasOwnGridAttributes(
365  bool circular, bool on )
366 {
367  if ( circular )
368  d->hasOwnGridAttributesCircular = on;
369  else
370  d->hasOwnGridAttributesSagittal = on;
371  emit propertiesChanged();
372 }
373 
375  bool circular ) const
376 {
377  return
378  ( circular )
379  ? d->hasOwnGridAttributesCircular
380  : d->hasOwnGridAttributesSagittal;
381 }

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