KChart

KChartPolarCoordinatePlane.cpp
1 /*
2  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3  *
4  * This file is part of the KD Chart library.
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "KChartPolarCoordinatePlane.h"
10 #include "KChartPolarCoordinatePlane_p.h"
11 
12 #include "KChartPainterSaver_p.h"
13 #include "KChartChart.h"
14 #include "KChartPaintContext.h"
15 #include "KChartAbstractDiagram.h"
16 #include "KChartAbstractPolarDiagram.h"
17 #include "KChartPolarDiagram.h"
18 #include "KChartMath_p.h"
19 
20 #include <QFont>
21 #include <QList>
22 #include <QtDebug>
23 #include <QPainter>
24 #include <QTimer>
25 
26 
27 using namespace KChart;
28 
29 #define d d_func()
30 
31 PolarCoordinatePlane::PolarCoordinatePlane ( Chart* parent )
32  : AbstractCoordinatePlane ( new Private(), parent )
33 {
34  // this block left empty intentionally
35 }
36 
37 PolarCoordinatePlane::~PolarCoordinatePlane()
38 {
39  // this block left empty intentionally
40 }
41 
42 void PolarCoordinatePlane::init()
43 {
44  // this block left empty intentionally
45 }
46 
48 {
49  Q_ASSERT_X ( dynamic_cast<AbstractPolarDiagram*> ( diagram ),
50  "PolarCoordinatePlane::addDiagram", "Only polar"
51  "diagrams can be added to a polar coordinate plane!" );
53  connect ( diagram, SIGNAL (layoutChanged(KChart::AbstractDiagram*)),
54  SLOT (slotLayoutChanged(KChart::AbstractDiagram*)) );
55 
56 }
57 
59 {
60  AbstractDiagramList diags = diagrams();
61  if ( d->coordinateTransformations.size() != diags.size() ) {
62  // diagrams have not been set up yet
63  return;
64  }
65  // need at least one so d->currentTransformation can be a valid pointer
66  Q_ASSERT( !d->coordinateTransformations.isEmpty() );
67 
68  PaintContext ctx;
69  ctx.setPainter ( painter );
70  ctx.setCoordinatePlane ( this );
71  ctx.setRectangle ( geometry() /*d->contentRect*/ );
72 
73  // 1. ask (only!) PolarDiagrams if they need additional space for data labels / data comments
74 
75  const qreal oldZoomX = zoomFactorX();
76  const qreal oldZoomY = zoomFactorY();
77  d->newZoomX = oldZoomX;
78  d->newZoomY = oldZoomY;
79  for ( int i = 0; i < diags.size(); i++ ) {
80  d->currentTransformation = & ( d->coordinateTransformations[i] );
81  qreal zoomX;
82  qreal zoomY;
83  PolarDiagram* polarDia = dynamic_cast<PolarDiagram*> ( diags[i] );
84  if ( polarDia ) {
85  polarDia->paint( &ctx, true, zoomX, zoomY );
86  d->newZoomX = qMin( d->newZoomX, zoomX );
87  d->newZoomY = qMin( d->newZoomY, zoomY );
88  }
89  }
90 
91  if ( d->newZoomX != oldZoomX || d->newZoomY != oldZoomY ) {
92  //qDebug() << "new zoom:" << d->newZoomY << " old zoom:" << oldZoomY;
93  d->currentTransformation = nullptr; // not painting anymore until we get called again
94  QMetaObject::invokeMethod( this, "adjustZoomAndRepaint", Qt::QueuedConnection );
95  return;
96  }
97 
98  // 2. there was room enough for the labels, so start drawing
99 
100  // paint the coordinate system rulers:
101  d->currentTransformation = &d->coordinateTransformations.first();
102  d->grid->drawGrid( &ctx );
103 
104  // paint the diagrams which will re-use their DataValueTextInfoList(s) filled in step 1:
105  for ( int i = 0; i < diags.size(); i++ ) {
106  d->currentTransformation = & ( d->coordinateTransformations[i] );
107  PainterSaver painterSaver( painter );
108  PolarDiagram* polarDia = dynamic_cast<PolarDiagram*>( diags[i] );
109  if ( polarDia ) {
110  qreal dummy1, dummy2;
111  polarDia->paint( &ctx, false, dummy1, dummy2 );
112  } else {
113  diags[i]->paint( &ctx );
114  }
115  }
116  d->currentTransformation = nullptr;
117 }
118 
119 
120 void PolarCoordinatePlane::adjustZoomAndRepaint()
121 {
122  const qreal newZoom = qMin(d->newZoomX, d->newZoomY);
123  setZoomFactors(newZoom, newZoom);
124  update();
125 }
126 
127 
128 void PolarCoordinatePlane::resizeEvent ( QResizeEvent* )
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  Q_FOREACH( AbstractDiagram* diagram, diagrams() )
151  {
152  AbstractPolarDiagram *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 
194 qreal PolarCoordinatePlane::angleUnit() const
195 {
196  Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::angleUnit",
197  "Only call angleUnit() from within paint()." );
198  return d->currentTransformation->angleUnit;
199 }
200 
201 qreal PolarCoordinatePlane::radiusUnit() const
202 {
203  Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::radiusUnit",
204  "Only call radiusUnit() from within paint()." );
205  return d->currentTransformation->radiusUnit;
206 }
207 
208 void PolarCoordinatePlane::slotLayoutChanged ( AbstractDiagram* )
209 {
210  if ( d->initialResizeEventReceived ) layoutDiagrams();
211 }
212 
214 {
215  Q_ASSERT_X ( diagram(), "PolarCoordinatePlane::setStartPosition",
216  "setStartPosition() needs a diagram to be associated to the plane." );
217  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
218  it != d->coordinateTransformations.end();
219  ++it )
220  {
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  {
259  CoordinateTransformation& trans = *it;
260  trans.zoom.xFactor = factor;
261  }
262 }
263 
265 {
266  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
267  it != d->coordinateTransformations.end();
268  ++it )
269  {
270  CoordinateTransformation& trans = *it;
271  trans.zoom.yFactor = factor;
272  }
273 }
274 
276 {
277  return d->coordinateTransformations.isEmpty()
278  ? QPointF( 0.5, 0.5 )
279  : QPointF( d->coordinateTransformations.first().zoom.xCenter, d->coordinateTransformations.first().zoom.yCenter );
280 }
281 
283 {
284  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
285  it != d->coordinateTransformations.end();
286  ++it )
287  {
288  CoordinateTransformation& trans = *it;
289  trans.zoom.xCenter = center.x();
290  trans.zoom.yCenter = center.y();
291  }
292 }
293 
294 DataDimensionsList PolarCoordinatePlane::getDataDimensionsList() const
295 {
297 
298  //FIXME(khz): do the real calculation
299 
300  return l;
301 }
302 
304  bool circular,
305  const GridAttributes& a )
306 {
307  if ( circular )
308  d->gridAttributesCircular = a;
309  else
310  d->gridAttributesSagittal = a;
311  setHasOwnGridAttributes( circular, true );
312  update();
314 }
315 
317  bool circular )
318 {
319  setHasOwnGridAttributes( circular, false );
320  update();
321 }
322 
324  bool circular ) const
325 {
326  if ( hasOwnGridAttributes( circular ) ) {
327  if ( circular )
328  return d->gridAttributesCircular;
329  else
330  return d->gridAttributesSagittal;
331  } else {
332  return globalGridAttributes();
333  }
334 }
335 
336 QRectF KChart::PolarCoordinatePlane::Private::contentsRect( const KChart::PolarCoordinatePlane* plane )
337 {
339  QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) );
340  QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop;
341  const qreal offset = temp.y();
342  referencePointAtTop.setX( referencePointAtTop.x() - offset );
343  contentsRect.setTopLeft( referencePointAtTop );
344  contentsRect.setBottomRight( referencePointAtTop + QPointF( 2.0 * offset, 2.0 * offset) );
345  return contentsRect;
346 }
347 
348 void KChart::PolarCoordinatePlane::setHasOwnGridAttributes(
349  bool circular, bool on )
350 {
351  if ( circular )
352  d->hasOwnGridAttributesCircular = on;
353  else
354  d->hasOwnGridAttributesSagittal = on;
356 }
357 
359  bool circular ) const
360 {
361  return
362  ( circular )
363  ? d->hasOwnGridAttributesCircular
364  : d->hasOwnGridAttributesSagittal;
365 }
AbstractDiagram defines the interface for diagram classes.
void paint(PaintContext *paintContext) override
void layoutDiagrams() override
Distribute the available space among the diagrams and axes.
QRect contentsRect() const const
int height() const const
void setGridAttributes(bool circular, const GridAttributes &)
Set the attributes to be used for grid lines drawn in circular direction (or in sagittal direction...
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(QPainter *) override
reimpl
void setZoomFactorX(qreal factor) override
Sets the zoom factor in horizontal direction, that is applied to all coordinate transformations.
int size() const const
const QRect & geometry() const const
void layoutChanged(KChart::AbstractDiagram *)
Diagrams are supposed to emit this signal, when the layout of one of their element changes...
void propertiesChanged()
Emitted upon change of a property of the Diagram.
qreal x() const const
qreal y() const const
Stores information about painting diagrams.
void setZoomCenter(const QPointF &center) override
Set the point (in value coordinates) to be used as the center point in zoom operations.
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane.
ZoomParameters stores the center and the factor of zooming internally.
void resetGridAttributes(bool circular)
Reset the attributes to be used for grid lines drawn in circular direction (or in sagittal direction...
const QPointF translate(const QPointF &diagramPoint) const override
Translate the given point in value space coordinates to a position in pixel space.
void setZoomFactorY(qreal factor) override
Sets the zoom factor in vertical direction, that is applied to all coordinate transformations.
QRect rect() const const
virtual void addDiagram(AbstractDiagram *diagram)
Adds a diagram to this coordinate plane.
void setTopLeft(const QPointF &position)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
qreal startPosition() const
Retrieve the rotation of the coordinate plane.
bool hasOwnGridAttributes(bool circular) const
int width() const const
void setZoomFactors(qreal factorX, qreal factorY) override
Sets both zoom factors in one go.
A set of attributes controlling the appearance of grids.
void setX(qreal x)
const GridAttributes gridAttributes(bool circular) const
Base class for diagrams based on a polar coordinate system.
A chart with one or more diagrams.
Definition: KChartChart.h:84
Global namespace.
QueuedConnection
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setBottomRight(const QPointF &position)
void setStartPosition(qreal degrees)
Specify the rotation of the coordinate plane.
PolarDiagram defines a common polar diagram.
Q_EMITQ_EMIT
void addDiagram(AbstractDiagram *diagram) override
Adds a diagram to this coordinate plane.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Sep 26 2021 22:37:21 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.