KChart

KChartPolarCoordinatePlane.cpp
1 /*
2  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3  *
4  * This file is part of the KD Chart library.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "KChartPolarCoordinatePlane.h"
21 #include "KChartPolarCoordinatePlane_p.h"
22 
23 #include "KChartPainterSaver_p.h"
24 #include "KChartChart.h"
25 #include "KChartPaintContext.h"
26 #include "KChartAbstractDiagram.h"
27 #include "KChartAbstractPolarDiagram.h"
28 #include "KChartPolarDiagram.h"
29 #include "KChartMath_p.h"
30 
31 #include <QFont>
32 #include <QList>
33 #include <QtDebug>
34 #include <QPainter>
35 #include <QTimer>
36 
37 
38 using namespace KChart;
39 
40 #define d d_func()
41 
42 PolarCoordinatePlane::PolarCoordinatePlane ( Chart* parent )
43  : AbstractCoordinatePlane ( new Private(), parent )
44 {
45  // this bloc left empty intentionally
46 }
47 
48 PolarCoordinatePlane::~PolarCoordinatePlane()
49 {
50  // this bloc left empty intentionally
51 }
52 
53 void PolarCoordinatePlane::init()
54 {
55  // this bloc left empty intentionally
56 }
57 
59 {
60  Q_ASSERT_X ( dynamic_cast<AbstractPolarDiagram*> ( diagram ),
61  "PolarCoordinatePlane::addDiagram", "Only polar"
62  "diagrams can be added to a polar coordinate plane!" );
64  connect ( diagram, SIGNAL (layoutChanged(AbstractDiagram*)),
65  SLOT (slotLayoutChanged(AbstractDiagram*)) );
66 
67 }
68 
70 {
71  AbstractDiagramList diags = diagrams();
72  if ( d->coordinateTransformations.size() != diags.size() ) {
73  // diagrams have not been set up yet
74  return;
75  }
76  // need at least one so d->currentTransformation can be a valid pointer
77  Q_ASSERT( !d->coordinateTransformations.isEmpty() );
78 
79  PaintContext ctx;
80  ctx.setPainter ( painter );
81  ctx.setCoordinatePlane ( this );
82  ctx.setRectangle ( geometry() /*d->contentRect*/ );
83 
84  // 1. ask (only!) PolarDiagrams if they need additional space for data labels / data comments
85 
86  const qreal oldZoomX = zoomFactorX();
87  const qreal oldZoomY = zoomFactorY();
88  d->newZoomX = oldZoomX;
89  d->newZoomY = oldZoomY;
90  for ( int i = 0; i < diags.size(); i++ ) {
91  d->currentTransformation = & ( d->coordinateTransformations[i] );
92  qreal zoomX;
93  qreal zoomY;
94  PolarDiagram* polarDia = dynamic_cast<PolarDiagram*> ( diags[i] );
95  if ( polarDia ) {
96  polarDia->paint( &ctx, true, zoomX, zoomY );
97  d->newZoomX = qMin( d->newZoomX, zoomX );
98  d->newZoomY = qMin( d->newZoomY, zoomY );
99  }
100  }
101 
102  if ( d->newZoomX != oldZoomX || d->newZoomY != oldZoomY ) {
103  //qDebug() << "new zoom:" << d->newZoomY << " old zoom:" << oldZoomY;
104  d->currentTransformation = nullptr; // not painting anymore until we get called again
105  QMetaObject::invokeMethod( this, "adjustZoomAndRepaint", Qt::QueuedConnection );
106  return;
107  }
108 
109  // 2. there was room enough for the labels, so start drawing
110 
111  // paint the coordinate system rulers:
112  d->currentTransformation = &d->coordinateTransformations.first();
113  d->grid->drawGrid( &ctx );
114 
115  // paint the diagrams which will re-use their DataValueTextInfoList(s) filled in step 1:
116  for ( int i = 0; i < diags.size(); i++ ) {
117  d->currentTransformation = & ( d->coordinateTransformations[i] );
118  PainterSaver painterSaver( painter );
119  PolarDiagram* polarDia = dynamic_cast<PolarDiagram*>( diags[i] );
120  if ( polarDia ) {
121  qreal dummy1, dummy2;
122  polarDia->paint( &ctx, false, dummy1, dummy2 );
123  } else {
124  diags[i]->paint( &ctx );
125  }
126  }
127  d->currentTransformation = nullptr;
128 }
129 
130 
131 void PolarCoordinatePlane::adjustZoomAndRepaint()
132 {
133  const qreal newZoom = qMin(d->newZoomX, d->newZoomY);
134  setZoomFactors(newZoom, newZoom);
135  update();
136 }
137 
138 
139 void PolarCoordinatePlane::resizeEvent ( QResizeEvent* )
140 {
141  d->initialResizeEventReceived = true;
142  layoutDiagrams();
143 }
144 
146 {
147  // the rectangle the diagrams cover in the *plane*:
148  // (Why -3? We save 1px on each side for the antialiased drawing, and
149  // respect the way QPainter calculates the width of a painted rect (the
150  // size is the rectangle size plus the pen width). This way, most clipping
151  // for regular pens should be avoided. When pens with a penWidth or larger
152  // than 1 are used, this may not b sufficient.
153  const QRect rect( areaGeometry() );
154  d->contentRect = QRectF ( 1, 1, rect.width() - 3, rect.height() - 3 );
155 
156  const ZoomParameters zoom = d->coordinateTransformations.isEmpty() ? ZoomParameters()
157  : d->coordinateTransformations.front().zoom;
158  // FIXME distribute space according to options:
159  const qreal oldStartPosition = startPosition();
160  d->coordinateTransformations.clear();
161  Q_FOREACH( AbstractDiagram* diagram, diagrams() )
162  {
163  AbstractPolarDiagram *polarDiagram = dynamic_cast<AbstractPolarDiagram*>( diagram );
164  Q_ASSERT( polarDiagram );
165  QPair<QPointF, QPointF> dataBoundariesPair = polarDiagram->dataBoundaries();
166 
167  const qreal angleUnit = 360 / polarDiagram->valueTotals();
168 //qDebug() << "--------------------------------------------------------";
169  const qreal radius = qAbs( dataBoundariesPair.first.y() ) + dataBoundariesPair.second.y();
170 //qDebug() << radius <<"="<<dataBoundariesPair.second.y();
171  const qreal diagramWidth = radius * 2; // == height
172  const qreal planeWidth = d->contentRect.width();
173  const qreal planeHeight = d->contentRect.height();
174  const qreal radiusUnit = qMin( planeWidth, planeHeight ) / diagramWidth;
175 //qDebug() << radiusUnit <<"=" << "qMin( "<<planeWidth<<","<< planeHeight <<") / "<<diagramWidth;
176  QPointF coordinateOrigin = QPointF ( planeWidth / 2, planeHeight / 2 );
177  coordinateOrigin += d->contentRect.topLeft();
178 
179  CoordinateTransformation diagramTransposition;
180  diagramTransposition.originTranslation = coordinateOrigin;
181  diagramTransposition.radiusUnit = radiusUnit;
182  diagramTransposition.angleUnit = angleUnit;
183  diagramTransposition.startPosition = oldStartPosition;
184  diagramTransposition.zoom = zoom;
185  diagramTransposition.minValue = dataBoundariesPair.first.y() < 0 ? dataBoundariesPair.first.y() : 0.0;
186  d->coordinateTransformations.append( diagramTransposition );
187  }
188  update();
189 }
190 
191 const QPointF PolarCoordinatePlane::translate( const QPointF& diagramPoint ) const
192 {
193  Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::translate",
194  "Only call translate() from within paint()." );
195  return d->currentTransformation->translate ( diagramPoint );
196 }
197 
198 const QPointF PolarCoordinatePlane::translatePolar( const QPointF& diagramPoint ) const
199 {
200  Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::translate",
201  "Only call translate() from within paint()." );
202  return d->currentTransformation->translatePolar ( diagramPoint );
203 }
204 
205 qreal PolarCoordinatePlane::angleUnit() const
206 {
207  Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::angleUnit",
208  "Only call angleUnit() from within paint()." );
209  return d->currentTransformation->angleUnit;
210 }
211 
212 qreal PolarCoordinatePlane::radiusUnit() const
213 {
214  Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::radiusUnit",
215  "Only call radiusUnit() from within paint()." );
216  return d->currentTransformation->radiusUnit;
217 }
218 
219 void PolarCoordinatePlane::slotLayoutChanged ( AbstractDiagram* )
220 {
221  if ( d->initialResizeEventReceived ) layoutDiagrams();
222 }
223 
225 {
226  Q_ASSERT_X ( diagram(), "PolarCoordinatePlane::setStartPosition",
227  "setStartPosition() needs a diagram to be associated to the plane." );
228  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
229  it != d->coordinateTransformations.end();
230  ++it )
231  {
232  CoordinateTransformation& trans = *it;
233  trans.startPosition = degrees;
234  }
235 }
236 
238 {
239  return d->coordinateTransformations.isEmpty()
240  ? 0.0
241  : d->coordinateTransformations.first().startPosition;
242 }
243 
245 {
246  return d->coordinateTransformations.isEmpty()
247  ? 1.0
248  : d->coordinateTransformations.first().zoom.xFactor;
249 }
250 
252 {
253  return d->coordinateTransformations.isEmpty()
254  ? 1.0
255  : d->coordinateTransformations.first().zoom.yFactor;
256 }
257 
258 void PolarCoordinatePlane::setZoomFactors( qreal factorX, qreal factorY )
259 {
260  setZoomFactorX( factorX );
261  setZoomFactorY( factorY );
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.xFactor = factor;
272  }
273 }
274 
276 {
277  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
278  it != d->coordinateTransformations.end();
279  ++it )
280  {
281  CoordinateTransformation& trans = *it;
282  trans.zoom.yFactor = factor;
283  }
284 }
285 
287 {
288  return d->coordinateTransformations.isEmpty()
289  ? QPointF( 0.5, 0.5 )
290  : QPointF( d->coordinateTransformations.first().zoom.xCenter, d->coordinateTransformations.first().zoom.yCenter );
291 }
292 
294 {
295  for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin();
296  it != d->coordinateTransformations.end();
297  ++it )
298  {
299  CoordinateTransformation& trans = *it;
300  trans.zoom.xCenter = center.x();
301  trans.zoom.yCenter = center.y();
302  }
303 }
304 
305 DataDimensionsList PolarCoordinatePlane::getDataDimensionsList() const
306 {
308 
309  //FIXME(khz): do the real calculation
310 
311  return l;
312 }
313 
315  bool circular,
316  const GridAttributes& a )
317 {
318  if ( circular )
319  d->gridAttributesCircular = a;
320  else
321  d->gridAttributesSagittal = a;
322  setHasOwnGridAttributes( circular, true );
323  update();
324  emit propertiesChanged();
325 }
326 
328  bool circular )
329 {
330  setHasOwnGridAttributes( circular, false );
331  update();
332 }
333 
335  bool circular ) const
336 {
337  if ( hasOwnGridAttributes( circular ) ) {
338  if ( circular )
339  return d->gridAttributesCircular;
340  else
341  return d->gridAttributesSagittal;
342  } else {
343  return globalGridAttributes();
344  }
345 }
346 
347 QRectF KChart::PolarCoordinatePlane::Private::contentsRect( const KChart::PolarCoordinatePlane* plane )
348 {
350  QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) );
351  QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop;
352  const qreal offset = temp.y();
353  referencePointAtTop.setX( referencePointAtTop.x() - offset );
354  contentsRect.setTopLeft( referencePointAtTop );
355  contentsRect.setBottomRight( referencePointAtTop + QPointF( 2.0 * offset, 2.0 * offset) );
356  return contentsRect;
357 }
358 
359 void KChart::PolarCoordinatePlane::setHasOwnGridAttributes(
360  bool circular, bool on )
361 {
362  if ( circular )
363  d->hasOwnGridAttributesCircular = on;
364  else
365  d->hasOwnGridAttributesSagittal = on;
366  emit propertiesChanged();
367 }
368 
370  bool circular ) const
371 {
372  return
373  ( circular )
374  ? d->hasOwnGridAttributesCircular
375  : d->hasOwnGridAttributesSagittal;
376 }
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 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 layoutChanged(AbstractDiagram *)
Diagrams are supposed to emit this signal, when the layout of one of their element changes...
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:95
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.
void addDiagram(AbstractDiagram *diagram) override
Adds a diagram to this coordinate plane.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue Sep 29 2020 22:42:41 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.