KD Chart 2  [rev.2.7]
KDChartPolarCoordinatePlane.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2020 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 }
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane.
void setZoomCenter(const QPointF &center) override
Set the point (in value coordinates) to be used as the center point in zoom operations.
void setStartPosition(qreal degrees)
Specify the rotation of the coordinate plane.
void resetGridAttributes(bool circular)
Reset the attributes to be used for grid lines drawn in circular direction (or in sagittal direction...
void setPainter(QPainter *painter)
bool hasOwnGridAttributes(bool circular) const
const QPointF translate(const QPointF &diagramPoint) const override
Translate the given point in value space coordinates to a position in pixel space.
QRect geometry() const override
pure virtual in QLayoutItem
virtual qreal valueTotals() const =0
A chart with one or more diagrams.
Definition: KDChartChart.h:98
ZoomParameters stores the center and the factor of zooming internally.
DataDimensionsList getDataDimensionsList() const override
PolarDiagram defines a common polar diagram.
Base class for diagrams based on a polar coordinate system.
void update()
Calling update() on the plane triggers the global KDChart::Chart::update()
AbstractDiagram defines the interface for diagram classes.
const GridAttributes gridAttributes(bool circular) const
void setZoomFactors(qreal factorX, qreal factorY) override
Sets both zoom factors in one go.
void addDiagram(AbstractDiagram *diagram) override
Adds a diagram to this coordinate plane.
const QPair< QPointF, QPointF > dataBoundaries() const
Return the bottom left and top right data point, that the diagram will display (unless the grid adjus...
void paint(PaintContext *paintContext) override
[reimplemented]
void setZoomFactorY(qreal factor) override
Sets the zoom factor in vertical direction, that is applied to all coordinate transformations.
Stores information about painting diagrams.
void layoutDiagrams() override
Distribute the available space among the diagrams and axes.
A set of attributes controlling the appearance of grids.
void paint(QPainter *) override
reimpl
qreal startPosition() const
Retrieve the rotation of the coordinate plane.
void propertiesChanged()
Emitted upon change of a property of the Coordinate Plane or any of its components.
void setZoomFactorX(qreal factor) override
Sets the zoom factor in horizontal direction, that is applied to all coordinate transformations.
virtual void addDiagram(AbstractDiagram *diagram)
Adds a diagram to this coordinate plane.
void slotLayoutChanged(AbstractDiagram *diagram)
QRect areaGeometry() const override
const QPointF translatePolar(const QPointF &diagramPoint) const
void setGridAttributes(bool circular, const GridAttributes &)
Set the attributes to be used for grid lines drawn in circular direction (or in sagittal direction...

Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/

https://www.kdab.com/development-resources/qt-tools/kd-chart/