KD Chart API Documentation  3.1
KDChartPolarCoordinatePlane.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Chart library.
4 **
5 ** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 
12 #include "KDChartPolarCoordinatePlane_p.h"
13 
14 #include "KDChartAbstractDiagram.h"
16 #include "KDChartChart.h"
17 #include "KDChartPaintContext.h"
18 #include "KDChartPainterSaver_p.h"
19 #include "KDChartPolarDiagram.h"
20 
21 #include <math.h>
22 
23 #include <QFont>
24 #include <QList>
25 #include <QPainter>
26 #include <QTimer>
27 #include <QtDebug>
28 
29 #include <KDABLibFakes>
30 
31 using namespace KDChart;
32 
33 #define d d_func()
34 
36  : AbstractCoordinatePlane(new Private(), parent)
37 {
38  // this block left empty intentionally
39 }
40 
42 {
43  // this block left empty intentionally
44 }
45 
46 void PolarCoordinatePlane::init()
47 {
48  // this block left empty intentionally
49 }
50 
52 {
53  Q_ASSERT_X(dynamic_cast<AbstractPolarDiagram *>(diagram),
54  "PolarCoordinatePlane::addDiagram", "Only polar"
55  "diagrams can be added to a polar coordinate plane!");
58 }
59 
60 void PolarCoordinatePlane::paint(QPainter *painter)
61 {
62  AbstractDiagramList diags = diagrams();
63  if (d->coordinateTransformations.size() != diags.size()) {
64  // diagrams have not been set up yet
65  return;
66  }
67  // need at least one so d->currentTransformation can be a valid pointer
68  Q_ASSERT(!d->coordinateTransformations.isEmpty());
69 
70  PaintContext ctx;
71  ctx.setPainter(painter);
72  ctx.setCoordinatePlane(this);
73  ctx.setRectangle(geometry() /*d->contentRect*/);
74 
75  // 1. ask (only!) PolarDiagrams if they need additional space for data labels / data comments
76 
77  const qreal oldZoomX = zoomFactorX();
78  const qreal oldZoomY = zoomFactorY();
79  d->newZoomX = oldZoomX;
80  d->newZoomY = oldZoomY;
81  for (int i = 0; i < diags.size(); i++) {
82  d->currentTransformation = &(d->coordinateTransformations[i]);
83  qreal zoomX;
84  qreal zoomY;
85  auto *polarDia = dynamic_cast<PolarDiagram *>(diags[i]);
86  if (polarDia) {
87  polarDia->paint(&ctx, true, zoomX, zoomY);
88  d->newZoomX = qMin(d->newZoomX, zoomX);
89  d->newZoomY = qMin(d->newZoomY, zoomY);
90  }
91  }
92 
93  if (d->newZoomX != oldZoomX || d->newZoomY != oldZoomY) {
94  // qDebug() << "new zoom:" << d->newZoomY << " old zoom:" << oldZoomY;
95  d->currentTransformation = nullptr; // not painting anymore until we get called again
96  QMetaObject::invokeMethod(this, "adjustZoomAndRepaint", Qt::QueuedConnection);
97  return;
98  }
99 
100  // 2. there was room enough for the labels, so start drawing
101 
102  // paint the coordinate system rulers:
103  d->currentTransformation = &d->coordinateTransformations.first();
104  d->grid->drawGrid(&ctx);
105 
106  // paint the diagrams which will re-use their DataValueTextInfoList(s) filled in step 1:
107  for (int i = 0; i < diags.size(); i++) {
108  d->currentTransformation = &(d->coordinateTransformations[i]);
109  PainterSaver painterSaver(painter);
110  auto *polarDia = dynamic_cast<PolarDiagram *>(diags[i]);
111  if (polarDia) {
112  qreal dummy1, dummy2;
113  polarDia->paint(&ctx, false, dummy1, dummy2);
114  } else {
115  diags[i]->paint(&ctx);
116  }
117  }
118  d->currentTransformation = nullptr;
119 }
120 
122 {
123  const qreal newZoom = qMin(d->newZoomX, d->newZoomY);
124  setZoomFactors(newZoom, newZoom);
125  update();
126 }
127 
129 {
130  d->initialResizeEventReceived = true;
131  layoutDiagrams();
132 }
133 
135 {
136  // the rectangle the diagrams cover in the *plane*:
137  // (Why -3? We save 1px on each side for the antialiased drawing, and
138  // respect the way QPainter calculates the width of a painted rect (the
139  // size is the rectangle size plus the pen width). This way, most clipping
140  // for regular pens should be avoided. When pens with a penWidth or larger
141  // than 1 are used, this may not b sufficient.
142  const QRect rect(areaGeometry());
143  d->contentRect = QRectF(1, 1, rect.width() - 3, rect.height() - 3);
144 
145  const ZoomParameters zoom = d->coordinateTransformations.isEmpty() ? ZoomParameters()
146  : d->coordinateTransformations.front().zoom;
147  // FIXME distribute space according to options:
148  const qreal oldStartPosition = startPosition();
149  d->coordinateTransformations.clear();
150  const auto constDiagrams = diagrams();
151  for (AbstractDiagram *diagram : constDiagrams) {
152  auto *polarDiagram = dynamic_cast<AbstractPolarDiagram *>(diagram);
153  Q_ASSERT(polarDiagram);
154  QPair<QPointF, QPointF> dataBoundariesPair = polarDiagram->dataBoundaries();
155 
156  const qreal angleUnit = 360 / polarDiagram->valueTotals();
157  // qDebug() << "--------------------------------------------------------";
158  const qreal radius = qAbs(dataBoundariesPair.first.y()) + dataBoundariesPair.second.y();
159  // qDebug() << radius <<"="<<dataBoundariesPair.second.y();
160  const qreal diagramWidth = radius * 2; // == height
161  const qreal planeWidth = d->contentRect.width();
162  const qreal planeHeight = d->contentRect.height();
163  const qreal radiusUnit = qMin(planeWidth, planeHeight) / diagramWidth;
164  // qDebug() << radiusUnit <<"=" << "qMin( "<<planeWidth<<","<< planeHeight <<") / "<<diagramWidth;
165  QPointF coordinateOrigin = QPointF(planeWidth / 2, planeHeight / 2);
166  coordinateOrigin += d->contentRect.topLeft();
167 
168  CoordinateTransformation diagramTransposition;
169  diagramTransposition.originTranslation = coordinateOrigin;
170  diagramTransposition.radiusUnit = radiusUnit;
171  diagramTransposition.angleUnit = angleUnit;
172  diagramTransposition.startPosition = oldStartPosition;
173  diagramTransposition.zoom = zoom;
174  diagramTransposition.minValue = dataBoundariesPair.first.y() < 0 ? dataBoundariesPair.first.y() : 0.0;
175  d->coordinateTransformations.append(diagramTransposition);
176  }
177  update();
178 }
179 
180 const QPointF PolarCoordinatePlane::translate(const QPointF &diagramPoint) const
181 {
182  Q_ASSERT_X(d->currentTransformation != nullptr, "PolarCoordinatePlane::translate",
183  "Only call translate() from within paint().");
184  return d->currentTransformation->translate(diagramPoint);
185 }
186 
187 const QPointF PolarCoordinatePlane::translatePolar(const QPointF &diagramPoint) const
188 {
189  Q_ASSERT_X(d->currentTransformation != nullptr, "PolarCoordinatePlane::translate",
190  "Only call translate() from within paint().");
191  return d->currentTransformation->translatePolar(diagramPoint);
192 }
193 
195 {
196  Q_ASSERT_X(d->currentTransformation != nullptr, "PolarCoordinatePlane::angleUnit",
197  "Only call angleUnit() from within paint().");
198  return d->currentTransformation->angleUnit;
199 }
200 
202 {
203  Q_ASSERT_X(d->currentTransformation != nullptr, "PolarCoordinatePlane::radiusUnit",
204  "Only call radiusUnit() from within paint().");
205  return d->currentTransformation->radiusUnit;
206 }
207 
209 {
210  if (d->initialResizeEventReceived)
211  layoutDiagrams();
212 }
213 
215 {
216  Q_ASSERT_X(diagram(), "PolarCoordinatePlane::setStartPosition",
217  "setStartPosition() needs a diagram to be associated to the plane.");
218  for (CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
219  it != d->coordinateTransformations.end();
220  ++it) {
221  CoordinateTransformation &trans = *it;
222  trans.startPosition = degrees;
223  }
224 }
225 
227 {
228  return d->coordinateTransformations.isEmpty()
229  ? 0.0
230  : d->coordinateTransformations.first().startPosition;
231 }
232 
234 {
235  return d->coordinateTransformations.isEmpty()
236  ? 1.0
237  : d->coordinateTransformations.first().zoom.xFactor;
238 }
239 
241 {
242  return d->coordinateTransformations.isEmpty()
243  ? 1.0
244  : d->coordinateTransformations.first().zoom.yFactor;
245 }
246 
247 void PolarCoordinatePlane::setZoomFactors(qreal factorX, qreal factorY)
248 {
249  setZoomFactorX(factorX);
250  setZoomFactorY(factorY);
251 }
252 
254 {
255  for (CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
256  it != d->coordinateTransformations.end();
257  ++it) {
258  CoordinateTransformation &trans = *it;
259  trans.zoom.xFactor = factor;
260  }
261 }
262 
264 {
265  for (CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
266  it != d->coordinateTransformations.end();
267  ++it) {
268  CoordinateTransformation &trans = *it;
269  trans.zoom.yFactor = factor;
270  }
271 }
272 
274 {
275  return d->coordinateTransformations.isEmpty()
276  ? QPointF(0.5, 0.5)
277  : QPointF(d->coordinateTransformations.first().zoom.xCenter, d->coordinateTransformations.first().zoom.yCenter);
278 }
279 
280 void PolarCoordinatePlane::setZoomCenter(const QPointF &center)
281 {
282  for (CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
283  it != d->coordinateTransformations.end();
284  ++it) {
285  CoordinateTransformation &trans = *it;
286  trans.zoom.xCenter = center.x();
287  trans.zoom.yCenter = center.y();
288  }
289 }
290 
292 {
294 
295  // FIXME(khz): do the real calculation
296 
297  return l;
298 }
299 
301  bool circular,
302  const GridAttributes &a)
303 {
304  if (circular)
305  d->gridAttributesCircular = a;
306  else
307  d->gridAttributesSagittal = a;
308  setHasOwnGridAttributes(circular, true);
309  update();
310  Q_EMIT propertiesChanged();
311 }
312 
314  bool circular)
315 {
316  setHasOwnGridAttributes(circular, false);
317  update();
318 }
319 
321  bool circular) const
322 {
323  if (hasOwnGridAttributes(circular)) {
324  if (circular)
325  return d->gridAttributesCircular;
326  else
327  return d->gridAttributesSagittal;
328  } else {
329  return globalGridAttributes();
330  }
331 }
332 
333 QRectF KDChart::PolarCoordinatePlane::Private::contentsRect(const KDChart::PolarCoordinatePlane *plane)
334 {
335  QRectF contentsRect;
336  QPointF referencePointAtTop = plane->translate(QPointF(1, 0));
337  QPointF temp = plane->translate(QPointF(0, 0)) - referencePointAtTop;
338  const qreal offset = temp.y();
339  referencePointAtTop.setX(referencePointAtTop.x() - offset);
340  contentsRect.setTopLeft(referencePointAtTop);
341  contentsRect.setBottomRight(referencePointAtTop + QPointF(2.0 * offset, 2.0 * offset));
342  return contentsRect;
343 }
344 
345 void KDChart::PolarCoordinatePlane::setHasOwnGridAttributes(
346  bool circular, bool on)
347 {
348  if (circular)
349  d->hasOwnGridAttributesCircular = on;
350  else
351  d->hasOwnGridAttributesSagittal = on;
352  Q_EMIT propertiesChanged();
353 }
354 
356  bool circular) const
357 {
358  return (circular)
359  ? d->hasOwnGridAttributesCircular
360  : d->hasOwnGridAttributesSagittal;
361 }
QRect areaGeometry() const override
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
virtual void addDiagram(AbstractDiagram *diagram)
AbstractDiagram defines the interface for diagram classes.
void layoutChanged(AbstractDiagram *)
Base class for diagrams based on a polar coordinate system.
A chart with one or more diagrams.
Definition: KDChartChart.h:84
A set of attributes controlling the appearance of grids.
Stores information about painting diagrams.
void setPainter(QPainter *painter)
void setCoordinatePlane(AbstractCoordinatePlane *plane)
void setRectangle(const QRectF &rect)
const GridAttributes gridAttributes(bool circular) const
void setZoomFactorY(qreal factor) override
void addDiagram(AbstractDiagram *diagram) override
void setZoomCenter(const QPointF &center) override
void setZoomFactorX(qreal factor) override
DataDimensionsList getDataDimensionsList() const override
void setStartPosition(qreal degrees)
Specify the rotation of the coordinate plane.
const QPointF translate(const QPointF &diagramPoint) const override
bool hasOwnGridAttributes(bool circular) const
void setZoomFactors(qreal factorX, qreal factorY) override
void setGridAttributes(bool circular, const GridAttributes &)
void slotLayoutChanged(AbstractDiagram *diagram)
const QPointF translatePolar(const QPointF &diagramPoint) const
PolarDiagram defines a common polar diagram.
void paint(PaintContext *paintContext) override
QList< AbstractDiagram * > AbstractDiagramList
QList< DataDimension > DataDimensionsList

© 2001 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/
Generated by doxygen 1.9.1