KChart

KChartRadarGrid.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 "KChartRadarGrid.h"
21 
22 #include "KChartPaintContext.h"
23 #include "KChartRadarDiagram.h"
24 #include "KChartPieDiagram.h"
25 #include "KChartPrintingParameters.h"
26 #include "KChartMath_p.h"
27 
28 #include <QPainter>
29 
30 
31 using namespace KChart;
32 
33 
34 DataDimensionsList RadarGrid::calculateGrid(
35  const DataDimensionsList& rawDataDimensions ) const
36 {
37  qDebug("Calling PolarGrid::calculateGrid()");
39 
40  //FIXME(khz): do the real calculation
41 
42  l = rawDataDimensions;
43 
44  return l;
45 }
46 
47 static qreal fitFontSizeToGeometry( const QString& text, const QFont& font, const QRectF& geometry, const TextAttributes& ta )
48 {
49  QFont f = font;
50  const qreal origResult = f.pointSizeF();
51  qreal result = origResult;
52  const QSizeF mySize = geometry.size();
53  if ( mySize.isNull() )
54  return result;
55 
56  const QString t = text;
57  QFontMetrics fm( f );
58  while ( true )
59  {
60  const QSizeF textSize = rotatedRect( fm.boundingRect( t ), ta.rotation() ).normalized().size();
61 
62  if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() )
63  return result;
64 
65  result -= 0.5;
66  if ( result <= 0.0 )
67  return origResult;
68  f.setPointSizeF( result );
69  fm = QFontMetrics( f );
70  }
71 }
72 
73 QPointF scaleToRealPosition( const QPointF& origin, const QRectF& sourceRect, const QRectF& destRect, const AbstractCoordinatePlane& plane )
74 {
75  QPointF result = plane.translate( origin );
76  result -= sourceRect.topLeft();
77  result.setX( result.x() / sourceRect.width() * destRect.width() );
78  result.setY( result.y() / sourceRect.height() * destRect.height() );
79  result += destRect.topLeft();
80  return result;
81 }
82 
83 QPointF scaleToRect( const QPointF& origin, const QRectF& sourceRect, const QRectF& destRect )
84 {
85  QPointF result( origin );
86  result -= sourceRect.topLeft();
87  result.setX( result.x() / sourceRect.width() * destRect.width() );
88  result.setY( result.y() / sourceRect.height() * destRect.height() );
89  result += destRect.topLeft();
90  return result;
91 }
92 
94 {
95  const QBrush backupBrush( context->painter()->brush() );
96  context->painter()->setBrush( QBrush() );
97  RadarCoordinatePlane* plane = dynamic_cast< RadarCoordinatePlane* >( context->coordinatePlane() );
98  Q_ASSERT( plane );
99  Q_ASSERT( plane->diagram() );
100  QPair< QPointF, QPointF > boundaries = plane->diagram()->dataBoundaries();
101  Q_ASSERT_X ( plane, "PolarGrid::drawGrid",
102  "Bad function call: PaintContext::coodinatePlane() NOT a polar plane." );
103 
104  const GridAttributes gridAttrsCircular( plane->gridAttributes( true ) );
105  const GridAttributes gridAttrsSagittal( plane->gridAttributes( false ) );
106 
107  //qDebug() << "OK:";
108  if ( !gridAttrsCircular.isGridVisible() && !gridAttrsSagittal.isGridVisible() ) return;
109  //qDebug() << "A";
110 
111  // FIXME: we paint the rulers to the settings of the first diagram for now:
112  AbstractPolarDiagram* dgr = dynamic_cast<AbstractPolarDiagram*> (plane->diagrams().first() );
113  Q_ASSERT ( dgr ); // only polar diagrams are allowed here
114 
115 
116  // Do not draw a grid for pie diagrams
117  if ( dynamic_cast<PieDiagram*> (plane->diagrams().first() ) ) return;
118 
119 
120  context->painter()->setPen ( PrintingParameters::scalePen( QColor ( Qt::lightGray ) ) );
121  const qreal min = dgr->dataBoundaries().first.y();
122  QPointF origin = plane->translate( QPointF( min, 0 ) ) + context->rectangle().topLeft();
123  //qDebug() << "origin" << origin;
124 
125  const qreal r = qAbs( min ) + dgr->dataBoundaries().second.y(); // use the full extents
126 
127  // distance between two axis lines
128  const qreal step = ( r - qAbs( min ) ) / ( dgr->numberOfGridRings() );
129 
130  // calculate the height needed for text to be displayed at the bottom and top of the chart
131  QPointF topLeft = context->rectangle().topLeft();
132  Q_ASSERT( plane->diagram()->model() );
133  TextAttributes ta = plane->textAttributes();
134  const int numberOfSpokes = ( int ) ( 360 / plane->angleUnit() );
135  const qreal stepWidth = boundaries.second.y() / ( dgr->numberOfGridRings() );
136  QRectF destRect = context->rectangle();
137  if (ta.isVisible() )
138  {
139  QAbstractItemModel* model = plane->diagram()->model();
140  QRectF fontRect = context->rectangle();
141  fontRect.setSize( QSizeF( fontRect.width(), step / 2.0 ) );
142  const qreal labelFontSize = fitFontSizeToGeometry( QString::fromLatin1( "TestXYWQgqy" ), ta.font(), fontRect, ta );
143  QFont labelFont = ta.font();
144  context->painter()->setPen( ta.pen() );
145  labelFont.setPointSizeF( labelFontSize );
146  const QFontMetricsF metric( labelFont );
147  const qreal labelHeight = metric.height();
148  QPointF offset;
149  destRect.setY( destRect.y() + 2 * labelHeight );
150  destRect.setHeight( destRect.height() - 4 * labelHeight );
151  offset.setY( labelHeight );
152  offset.setX( 0 );
153  topLeft += offset;
154  origin += offset;
155  origin = scaleToRealPosition( QPointF( min, 0 ), context->rectangle(), destRect, *plane );
156 
157  const qreal aWidth = metric.boundingRect( QString::fromLatin1( "A" ) ).width();
158  const QLineF startLine( origin, scaleToRealPosition( QPointF( r - qAbs( min ), 0 ), context->rectangle(), destRect, *plane ) );
159  for ( int i = 0; i < model->rowCount(); ++i )
160  {
161  const QLineF currentLine( origin, scaleToRealPosition( QPointF( r - qAbs( min ), i ), context->rectangle(), destRect, *plane ) );
162  const int angle = ( int ) startLine.angleTo( currentLine ) % 360;
163  const qreal angleTest = qAbs( angle - 180 );
164  const QString data = model->headerData( i, Qt::Vertical ).toString();
165  const qreal xOffset = metric.boundingRect( data ).width() / 2.0;
166  if ( angleTest < 5.0 )
167  context->painter()->drawText( currentLine.pointAt( 1 ) + QPointF( -xOffset, labelHeight + qAbs( min ) ) , data );
168  else if ( qAbs( angleTest - 180 ) < 5.0 )
169  context->painter()->drawText( currentLine.pointAt( 1 ) - QPointF( xOffset, labelHeight + qAbs( min ) ) , data );
170  else if ( angle < 175 && angle > 5 )
171  context->painter()->drawText( currentLine.pointAt( 1 ) - QPointF( xOffset * 2 + qAbs( min ) + aWidth, -labelHeight/ 2.0 + qAbs( min ) ) , data );
172  else if ( angle < 355 && angle > 185 )
173  context->painter()->drawText( currentLine.pointAt( 1 ) + QPointF( qAbs( min ) + aWidth, labelHeight/ 2.0 + qAbs( min ) ) , data );
174 
175  }
176  }
177  context->painter()->setPen ( PrintingParameters::scalePen( QColor ( Qt::lightGray ) ) );
178  if ( plane->globalGridAttributes().isGridVisible() )
179  {
180  for ( int j = 1; j < dgr->numberOfGridRings() + 1; ++j )
181  {
182  QPointF oldPoint( scaleToRealPosition( QPointF( j * step - qAbs( min ), numberOfSpokes - 1 ), context->rectangle(), destRect, *plane ) );
183  for ( int i = 0; i < numberOfSpokes ; ++i ) {
184  const QPointF newPoint = scaleToRealPosition( QPointF( j * step - qAbs( min ), i ), context->rectangle(), destRect, *plane );
185  context->painter()->drawLine( oldPoint, newPoint );
186  oldPoint = newPoint;
187 
188  context->painter()->drawLine( origin, newPoint );
189  }
190  }
191  context->painter()->setPen( ta.pen() );
192  qreal fontSize = 0;
193  for ( int i = 0; i < dgr->numberOfGridRings() + 1; ++i )
194  {
195  const QString text = QString::number( i * stepWidth );
196  const QPointF translatedPoint = scaleToRealPosition( QPointF( i * step - qAbs( min ), 0 ), context->rectangle(), destRect, *plane );
197  const QFontMetrics metric( ta.font()/*QFont( "Arial", 10 )*/ );
198  const qreal textLength = metric.boundingRect( text ).width();
199  const qreal textHeight = metric.height() / 2.0;
200  QPointF textOffset( textLength, -textHeight );
201  textOffset = scaleToRect( textOffset, context->rectangle(), destRect );
202  QPointF _topLeft = topLeft;
203  _topLeft.setY( translatedPoint.y() );
204  QRectF boundary( _topLeft, ( translatedPoint + QPointF( 0, step / 2.0 ) ) );
205  const qreal calcFontSize = fitFontSizeToGeometry( text, ta.font(), boundary, ta );
206  if ( fontSize != calcFontSize )
207  {
208  QFont paintFont( ta.font() );
209  paintFont.setPointSizeF( calcFontSize );
210  ta.setFont( paintFont );
211  ta.setFontSize( calcFontSize );
212  const qreal textHeight2 = QFontMetricsF( paintFont ).height() / 2.0;
213  textOffset.setY( - textHeight2 );
214  textOffset = scaleToRect( textOffset, context->rectangle(), destRect );
215  context->painter()->setFont( paintFont );
216  fontSize = calcFontSize;
217  }
218  context->painter()->drawText( translatedPoint + destRect.topLeft() - textOffset, text );
219 
220  }
221  }
222  plane->setTextAttributes( ta );
223  context->painter()->setPen ( PrintingParameters::scalePen( QColor ( Qt::lightGray ) ) );
224  context->painter()->setBrush( backupBrush );
225 }
virtual int rowCount(const QModelIndex &parent) const const =0
virtual const QPointF translate(const QPointF &diagramPoint) const =0
Translate the given point in value space coordinates to a position in pixel space.
qreal y() const const
qreal pointSizeF() const const
QSizeF size() const const
const QPair< QPointF, QPointF > dataBoundaries() const
Return the bottom left and top right data point, that the diagram will display (unless the grid adjus...
qreal angleTo(const QLineF &line) const const
void drawLine(const QLineF &line)
void setHeight(qreal height)
const TextAttributes textAttributes() const
QRect boundingRect(QChar ch) const const
void setFont(const QFont &font)
QString number(int n, int base)
qreal x() const const
qreal y() const const
Stores information about painting diagrams.
void setPen(const QColor &color)
QRectF boundingRect(const QString &text) const const
QPointF topLeft() const const
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane.
void setBrush(const QBrush &brush)
void drawText(const QPointF &position, const QString &text)
const QPointF translate(const QPointF &diagramPoint) const override
Translate the given point in value space coordinates to a position in pixel space.
T & first()
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const const
void drawGrid(PaintContext *context) override
Doing the actual drawing.
const QBrush & brush() const const
void setPointSizeF(qreal pointSize)
int width() const const
qreal width() const const
void setTextAttributes(const TextAttributes &attr)
Set the attributes to be used for axis captions.
A set of attributes controlling the appearance of grids.
void setX(qreal x)
void setY(qreal y)
QPointF pointAt(qreal t) const const
const GridAttributes gridAttributes(bool circular) const
A set of text attributes.
Base class for diagrams based on a polar coordinate system.
QString fromLatin1(const char *str, int size)
qreal height() const const
void setSize(const QSizeF &size)
Vertical
void setY(qreal y)
QAbstractItemModel * model() const const
bool isNull() const const
qreal height() const const
lightGray
Global namespace.
QString toString() const const
qreal width() const const
qreal height() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Wed Jan 20 2021 22:39:31 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.