KChart

KChartRadarDiagram.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 "KChartRadarDiagram.h"
10 #include "KChartRadarDiagram_p.h"
11 
12 #include "KChartPaintContext.h"
13 #include "KChartPainterSaver_p.h"
14 #include "KChartMath_p.h"
15 
16 #include <QPainter>
17 
18 using namespace KChart;
19 
20 RadarDiagram::Private::Private() :
21  closeDatasets( false ),
22  reverseData( false ),
23  fillAlpha( 0.0 )
24 {
25 }
26 
27 RadarDiagram::Private::~Private() {}
28 
29 #define d d_func()
30 
31 RadarDiagram::RadarDiagram( QWidget* parent, RadarCoordinatePlane* plane ) :
32  AbstractPolarDiagram( new Private( ), parent, plane )
33 {
34  //init();
35 }
36 
37 RadarDiagram::~RadarDiagram()
38 {
39 
40 }
41 
42 void RadarDiagram::init()
43 {
44 }
45 
46 
48 {
49  RadarDiagram* newDiagram = new RadarDiagram( new Private( *d ) );
50  // This needs to be copied after the fact
51  newDiagram->d->closeDatasets = d->closeDatasets;
52  return newDiagram;
53 }
54 
56 {
57  if ( !checkInvariants(true) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
58  const int rowCount = model()->rowCount(rootIndex());
59  const int colCount = model()->columnCount(rootIndex());
60  qreal xMin = 0.0;
61  qreal xMax = colCount;
62  qreal yMin = 0, yMax = 0;
63  for ( int iCol=0; iCol<colCount; ++iCol ) {
64  for ( int iRow=0; iRow< rowCount; ++iRow ) {
65  qreal value = model()->data( model()->index( iRow, iCol, rootIndex() ) ).toReal(); // checked
66  yMax = qMax( yMax, value );
67  yMin = qMin( yMin, value );
68  }
69  }
70  QPointF bottomLeft ( QPointF( xMin, yMin ) );
71  QPointF topRight ( QPointF( xMax, yMax ) );
72  return QPair<QPointF, QPointF> ( bottomLeft, topRight );
73 }
74 
75 
76 
77 void RadarDiagram::paintEvent ( QPaintEvent*)
78 {
79  QPainter painter ( viewport() );
80  PaintContext ctx;
81  ctx.setPainter ( &painter );
82  ctx.setRectangle( QRectF ( 0, 0, width(), height() ) );
83  paint ( &ctx );
84 }
85 
86 void RadarDiagram::paint( PaintContext* ctx )
87 {
88  qreal dummy1, dummy2;
89  paint( ctx, true, dummy1, dummy2 );
90  paint( ctx, false, dummy1, dummy2 );
91 }
92 
93 static qreal fitFontSizeToGeometry( const QString& text, const QFont& font, const QRectF& geometry, const TextAttributes& ta )
94 {
95  QFont f = font;
96  const qreal origResult = f.pointSizeF();
97  qreal result = origResult;
98  const QSizeF mySize = geometry.size();
99  if ( mySize.isNull() )
100  return result;
101 
102  const QString t = text;
103  QFontMetrics fm( f );
104  while ( true )
105  {
106  const QSizeF textSize = rotatedRect( fm.boundingRect( t ), ta.rotation() ).normalized().size();
107 
108  if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() )
109  return result;
110 
111  result -= 0.5;
112  if ( result <= 0.0 )
113  return origResult;
114  f.setPointSizeF( result );
115  fm = QFontMetrics( f );
116  }
117 }
118 
119 static QPointF scaleToRealPosition( const QPointF& origin, const QRectF& sourceRect, const QRectF& destRect, const AbstractCoordinatePlane& plane )
120 {
121  QPointF result = plane.translate( origin );
122  result -= sourceRect.topLeft();
123  result.setX( result.x() / sourceRect.width() * destRect.width() );
124  result.setY( result.y() / sourceRect.height() * destRect.height() );
125  result += destRect.topLeft();
126  return result;
127 }
128 
130 {
131  d->reverseData = val;
132 }
133 bool RadarDiagram::reverseData()
134 {
135  return d->reverseData;
136 }
137 
138 // local structure to remember the settings of a polygon inclusive the used color and pen.
139 struct Polygon {
140  QPolygonF polygon;
141  QBrush brush;
142  QPen pen;
143  Polygon(const QPolygonF &polygon, const QBrush &brush, const QPen &pen) : polygon(polygon), brush(brush), pen(pen) {}
144 };
145 
146 void RadarDiagram::paint( PaintContext* ctx,
147  bool calculateListAndReturnScale,
148  qreal& newZoomX, qreal& newZoomY )
149 {
150  // note: Not having any data model assigned is no bug
151  // but we can not draw a diagram then either.
152  if ( !checkInvariants(true) )
153  return;
154  d->reverseMapper.clear();
155 
156  const int rowCount = model()->rowCount( rootIndex() );
157  const int colCount = model()->columnCount( rootIndex() );
158 
159  int iRow, iCol;
160 
161  const qreal min = dataBoundaries().first.y();
162  const qreal r = qAbs( min ) + dataBoundaries().second.y();
163  const qreal step = ( r - qAbs( min ) ) / ( numberOfGridRings() );
164 
165  RadarCoordinatePlane* plane = dynamic_cast<RadarCoordinatePlane*>(ctx->coordinatePlane());
166  TextAttributes ta = plane->textAttributes();
167  QRectF fontRect = ctx->rectangle();
168  fontRect.setSize( QSizeF( fontRect.width(), step / 2.0 ) );
169  const qreal labelFontSize = fitFontSizeToGeometry( QString::fromLatin1( "TestXYWQgqy" ), ta.font(), fontRect, ta );
170  QFont labelFont = ta.font();
171  ctx->painter()->setPen( ta.pen() );
172  labelFont.setPointSizeF( labelFontSize );
173  const QFontMetricsF metric( labelFont );
174  const qreal labelHeight = metric.height();
175  QRectF destRect = ctx->rectangle();
176  if ( ta.isVisible() )
177  {
178  destRect.setY( destRect.y() + 2 * labelHeight );
179  destRect.setHeight( destRect.height() - 4 * labelHeight );
180  }
181 
182  if ( calculateListAndReturnScale ) {
183  ctx->painter()->save();
184  // Check if all of the data value texts / data comments will fit
185  // into the available space:
186  d->labelPaintCache.clear();
187  ctx->painter()->save();
188  for ( iCol=0; iCol < colCount; ++iCol ) {
189  for ( iRow=0; iRow < rowCount; ++iRow ) {
190  QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
191  const qreal value = model()->data( index ).toReal();
192  QPointF point = scaleToRealPosition( QPointF( value, iRow ), ctx->rectangle(), destRect, *ctx->coordinatePlane() );
193  d->addLabel( &d->labelPaintCache, index, nullptr, PositionPoints( point ),
194  Position::Center, Position::Center, value );
195  }
196  }
197  ctx->painter()->restore();
198  const qreal oldZoomX = coordinatePlane()->zoomFactorX();
199  const qreal oldZoomY = coordinatePlane()->zoomFactorY();
200  newZoomX = oldZoomX;
201  newZoomY = oldZoomY;
202  if ( d->labelPaintCache.paintReplay.count() ) {
203  QRectF txtRectF;
204  d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true, true, &txtRectF );
205  const QRect txtRect = txtRectF.toRect();
206  const QRect curRect = coordinatePlane()->geometry();
207  const qreal gapX = qMin( txtRect.left() - curRect.left(), curRect.right() - txtRect.right() );
208  const qreal gapY = qMin( txtRect.top() - curRect.top(), curRect.bottom() - txtRect.bottom() );
209  newZoomX = oldZoomX;
210  newZoomY = oldZoomY;
211  if ( gapX < 0.0 )
212  newZoomX *= 1.0 + (gapX-1.0) / curRect.width();
213  if ( gapY < 0.0 )
214  newZoomY *= 1.0 + (gapY-1.0) / curRect.height();
215  }
216  ctx->painter()->restore();
217 
218  } else {
219  // Iterate through data sets and create a list of polygons out of them.
220  QList<Polygon> polygons;
221  for ( iCol=0; iCol < colCount; ++iCol ) {
222  //TODO(khz): As of yet RadarDiagram can not show per-segment line attributes
223  // but it draws every polyline in one go - using one color.
224  // This needs to be enhanced to allow for cell-specific settings
225  // in the same way as LineDiagram does it.
226  QPolygonF polygon;
227  QPointF point0;
228  for ( iRow=0; iRow < rowCount; ++iRow ) {
229  QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
230  const qreal value = model()->data( index ).toReal();
231  QPointF point = scaleToRealPosition( QPointF( value, d->reverseData ? ( rowCount - iRow ) : iRow ), ctx->rectangle(), destRect, *ctx->coordinatePlane() );
232  polygon.append( point );
233  if ( ! iRow )
234  point0= point;
235  }
236  if ( closeDatasets() && rowCount )
237  polygon.append( point0 );
238 
239  QBrush brush = d->datasetAttrs( iCol, KChart::DatasetBrushRole ).value<QBrush>();
240  QPen p = d->datasetAttrs( iCol, KChart::DatasetPenRole ).value< QPen >();
241  if ( p.style() != Qt::NoPen )
242  {
243  polygons.append( Polygon( polygon, brush, PrintingParameters::scalePen( p ) ) );
244  }
245  }
246 
247  // first fill the areas with the brush-color and the defined alpha-value.
248  if (d->fillAlpha > 0.0) {
249  Q_FOREACH(const Polygon& p, polygons) {
250  PainterSaver painterSaver( ctx->painter() );
251  ctx->painter()->setRenderHint ( QPainter::Antialiasing );
252  QBrush br = p.brush;
253  QColor c = br.color();
254  c.setAlphaF(d->fillAlpha);
255  br.setColor(c);
256  ctx->painter()->setBrush( br );
257  ctx->painter()->setPen( p.pen );
258  ctx->painter()->drawPolygon( p.polygon );
259  }
260  }
261 
262  // then draw the poly-lines.
263  Q_FOREACH(const Polygon& p, polygons) {
264  PainterSaver painterSaver( ctx->painter() );
265  ctx->painter()->setRenderHint ( QPainter::Antialiasing );
266  ctx->painter()->setBrush( p.brush );
267  ctx->painter()->setPen( p.pen );
268  ctx->painter()->drawPolyline( p.polygon );
269  }
270 
271  d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true );
272  }
273 }
274 
275 void RadarDiagram::resize ( const QSizeF& size )
276 {
278 }
279 
280 /*virtual*/
282 {
283  return model()->rowCount(rootIndex());
284 }
285 
286 /*virtual*/
288 {
289  return model() ? model()->rowCount(rootIndex()) : 0.0;
290 }
291 
292 /*virtual*/
294 {
295  return 5; // FIXME
296 }
297 
298 void RadarDiagram::setCloseDatasets( bool closeDatasets )
299 {
300  d->closeDatasets = closeDatasets;
301 }
302 
303 bool RadarDiagram::closeDatasets() const
304 {
305  return d->closeDatasets;
306 }
307 
309 {
310  return d->fillAlpha;
311 }
312 
313 void RadarDiagram::setFillAlpha(qreal alphaF)
314 {
315  d->fillAlpha = alphaF;
316 }
317 
318 void RadarDiagram::resizeEvent ( QResizeEvent*)
319 {
320 }
Class only listed here to document inheritance of some KChart classes.
QRect toRect() const const
qreal fillAlpha() const
Fill the areas of the radar chart with there respective color defined via KChart::DatasetBrushRole.
void setRenderHint(QPainter::RenderHint hint, bool on)
virtual const QPointF translate(const QPointF &diagramPoint) const =0
Translate the given point in value space coordinates to a position in pixel space.
void append(const T &value)
int right() const const
qreal y() const const
qreal pointSizeF() const const
void setReverseData(bool val)
if val is true the diagram will mirror the diagram datapoints
void drawPolyline(const QPointF *points, int pointCount)
QSizeF size() const const
void save()
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
int height() const const
qreal numberOfValuesPerDataset() const override
virtual RadarDiagram * clone() const
Creates an exact copy of this diagram.
void paint(QPainter *) override
reimpl
qreal valueTotals() const override
Stores the absolute target points of a Position.
void setHeight(qreal height)
const TextAttributes textAttributes() const
const QColor & color() const const
QRect boundingRect(QChar ch) const const
qreal x() const const
qreal y() const const
void append(const T &value)
Stores information about painting diagrams.
int top() const const
void setPen(const QColor &color)
int left() const const
RadarDiagram defines a common radar diagram.
QPointF topLeft() const const
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane.
void setBrush(const QBrush &brush)
qreal numberOfGridRings() const override
void setCloseDatasets(bool closeDatasets)
Close each of the data series by connecting the last point to its respective start point...
QRect geometry() const override
pure virtual in QLayoutItem
void setPointSizeF(qreal pointSize)
void restore()
virtual void resize(const QSizeF &area)
Called by the widget&#39;s sizeEvent.
int width() const const
qreal width() const const
const QPair< QPointF, QPointF > calculateDataBoundaries() const override
QVariant data(int role) const const
void setX(qreal x)
void setY(qreal y)
int bottom() const const
A set of text attributes.
Base class for diagrams based on a polar coordinate system.
QString fromLatin1(const char *str, int size)
void setAlphaF(qreal alpha)
qreal height() const const
void setSize(const QSizeF &size)
void setY(qreal y)
qreal toReal(bool *ok) const const
bool isNull() const const
qreal height() const const
Global namespace.
void setColor(const QColor &color)
qreal width() const const
qreal height() const const
void resize(const QSizeF &area) override
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu Jul 29 2021 22:36:48 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.