KChart

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

KDE's Doxygen guidelines are available online.