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
27using namespace KChart;
28
29#define d d_func()
30
31PolarCoordinatePlane::PolarCoordinatePlane ( Chart* parent )
32 : AbstractCoordinatePlane ( new Private(), parent )
33{
34 // this block left empty intentionally
35}
36
37PolarCoordinatePlane::~PolarCoordinatePlane()
38{
39 // this block left empty intentionally
40}
41
42void 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{
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
120void PolarCoordinatePlane::adjustZoomAndRepaint()
121{
122 const qreal newZoom = qMin(d->newZoomX, d->newZoomY);
123 setZoomFactors(newZoom, newZoom);
124 update();
125}
126
127
128void PolarCoordinatePlane::resizeEvent ( QResizeEvent* )
129{
130 d->initialResizeEventReceived = true;
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 ds = diagrams();
151 for ( AbstractDiagram* diagram : ds ) {
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
180const 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
187const 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
194qreal 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
201qreal 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
208void 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
247void 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
294DataDimensionsList 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();
313 Q_EMIT propertiesChanged();
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
336QRectF KChart::PolarCoordinatePlane::Private::contentsRect( const KChart::PolarCoordinatePlane* plane )
337{
338 QRectF contentsRect;
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
348void KChart::PolarCoordinatePlane::setHasOwnGridAttributes(
349 bool circular, bool on )
350{
351 if ( circular )
352 d->hasOwnGridAttributesCircular = on;
353 else
354 d->hasOwnGridAttributesSagittal = on;
355 Q_EMIT propertiesChanged();
356}
357
359 bool circular ) const
360{
361 return
362 ( circular )
363 ? d->hasOwnGridAttributesCircular
364 : d->hasOwnGridAttributesSagittal;
365}
QRect areaGeometry() const override
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
virtual void addDiagram(AbstractDiagram *diagram)
Adds a diagram to this coordinate plane.
QRect geometry() const override
pure virtual in QLayoutItem
void update()
Calling update() on the plane triggers the global KChart::Chart::update()
AbstractDiagram defines the interface for diagram classes.
const QPair< QPointF, QPointF > dataBoundaries() const
Return the bottom left and top right data point, that the diagram will display (unless the grid adjus...
Base class for diagrams based on a polar coordinate system.
A set of attributes controlling the appearance of grids.
Stores information about painting diagrams.
void setZoomFactorY(qreal factor) override
Sets the zoom factor in vertical direction, that is applied to all coordinate transformations.
bool hasOwnGridAttributes(bool circular) const
void layoutDiagrams() override
Distribute the available space among the diagrams and axes.
qreal startPosition() const
Retrieve the rotation of the coordinate plane.
const GridAttributes gridAttributes(bool circular) const
void resetGridAttributes(bool circular)
Reset the attributes to be used for grid lines drawn in circular direction (or in sagittal direction,...
void addDiagram(AbstractDiagram *diagram) override
Adds a diagram to this coordinate plane.
void setZoomCenter(const QPointF &center) override
Set the point (in value coordinates) to be used as the center point in zoom operations.
void setZoomFactorX(qreal factor) override
Sets the zoom factor in horizontal direction, that is applied to all coordinate transformations.
void paint(QPainter *) override
reimpl
void setStartPosition(qreal degrees)
Specify the rotation of the coordinate plane.
void setGridAttributes(bool circular, const GridAttributes &)
Set 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 setZoomFactors(qreal factorX, qreal factorY) override
Sets both zoom factors in one go.
PolarDiagram defines a common polar diagram.
void paint(PaintContext *paintContext) override
\reimpl
ZoomParameters stores the center and the factor of zooming internally.
qsizetype size() const const
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setX(qreal x)
qreal x() const const
qreal y() const const
int height() const const
int width() const const
void setBottomRight(const QPointF &position)
void setTopLeft(const QPointF &position)
QueuedConnection
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 4 2024 12:04:17 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.