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 "KChartPolarDiagram.h"
21 #include "KChartPolarDiagram_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
Private()31 PolarDiagram::Private::Private() :
32 rotateCircularLabels( false ),
33 closeDatasets( false )
34 {
35 }
36
~Private()37 PolarDiagram::Private::~Private() {}
38
39 #define d d_func()
40
PolarDiagram(QWidget * parent,PolarCoordinatePlane * plane)41 PolarDiagram::PolarDiagram( QWidget* parent, PolarCoordinatePlane* plane ) :
42 AbstractPolarDiagram( new Private( ), parent, plane )
43 {
44 //init();
45 }
46
~PolarDiagram()47 PolarDiagram::~PolarDiagram()
48 {
49 }
50
51
init()52 void PolarDiagram::init()
53 {
54 setShowDelimitersAtPosition( Position::Unknown, false );
55 setShowDelimitersAtPosition( Position::Center, false );
56 setShowDelimitersAtPosition( Position::NorthWest, false );
57 setShowDelimitersAtPosition( Position::North, true );
58 setShowDelimitersAtPosition( Position::NorthEast, false );
59 setShowDelimitersAtPosition( Position::West, false );
60 setShowDelimitersAtPosition( Position::East, false );
61 setShowDelimitersAtPosition( Position::SouthWest, false );
62 setShowDelimitersAtPosition( Position::South, true );
63 setShowDelimitersAtPosition( Position::SouthEast, false );
64 setShowDelimitersAtPosition( Position::Floating, false );
65
66 setShowLabelsAtPosition( Position::Unknown, false );
67 setShowLabelsAtPosition( Position::Center, false );
68 setShowLabelsAtPosition( Position::NorthWest, false );
69 setShowLabelsAtPosition( Position::North, true );
70 setShowLabelsAtPosition( Position::NorthEast, false );
71 setShowLabelsAtPosition( Position::West, false );
72 setShowLabelsAtPosition( Position::East, false );
73 setShowLabelsAtPosition( Position::SouthWest, false );
74 setShowLabelsAtPosition( Position::South, true );
75 setShowLabelsAtPosition( Position::SouthEast, false );
76 setShowLabelsAtPosition( Position::Floating, false );
77 }
78
clone() const79 PolarDiagram * PolarDiagram::clone() const
80 {
81 PolarDiagram* newDiagram = new PolarDiagram( new Private( *d ) );
82 // This needs to be copied after the fact
83 newDiagram->d->showDelimitersAtPosition = d->showDelimitersAtPosition;
84 newDiagram->d->showLabelsAtPosition = d->showLabelsAtPosition;
85 newDiagram->d->rotateCircularLabels = d->rotateCircularLabels;
86 newDiagram->d->closeDatasets = d->closeDatasets;
87 return newDiagram;
88 }
89
calculateDataBoundaries() const90 const QPair<QPointF, QPointF> PolarDiagram::calculateDataBoundaries () const
91 {
92 if ( !checkInvariants(true) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
93 const int rowCount = model()->rowCount(rootIndex());
94 const int colCount = model()->columnCount(rootIndex());
95 qreal xMin = 0.0;
96 qreal xMax = colCount;
97 qreal yMin = 0, yMax = 0;
98 for ( int iCol=0; iCol<colCount; ++iCol ) {
99 for ( int iRow=0; iRow< rowCount; ++iRow ) {
100 qreal value = model()->data( model()->index( iRow, iCol, rootIndex() ) ).toReal(); // checked
101 yMax = qMax( yMax, value );
102 yMin = qMin( yMin, value );
103 }
104 }
105 QPointF bottomLeft ( QPointF( xMin, yMin ) );
106 QPointF topRight ( QPointF( xMax, yMax ) );
107 return QPair<QPointF, QPointF> ( bottomLeft, topRight );
108 }
109
110
111
paintEvent(QPaintEvent *)112 void PolarDiagram::paintEvent ( QPaintEvent*)
113 {
114 QPainter painter ( viewport() );
115 PaintContext ctx;
116 ctx.setPainter ( &painter );
117 ctx.setRectangle( QRectF ( 0, 0, width(), height() ) );
118 paint ( &ctx );
119 }
120
resizeEvent(QResizeEvent *)121 void PolarDiagram::resizeEvent ( QResizeEvent*)
122 {
123 }
124
paintPolarMarkers(PaintContext * ctx,const QPolygonF & polygon)125 void PolarDiagram::paintPolarMarkers( PaintContext* ctx, const QPolygonF& polygon )
126 {
127 Q_UNUSED(ctx);
128 Q_UNUSED(polygon);
129 // obsolete, since we are using real markers now!
130 }
131
paint(PaintContext * ctx)132 void PolarDiagram::paint( PaintContext* ctx )
133 {
134 qreal dummy1, dummy2;
135 paint( ctx, true, dummy1, dummy2 );
136 paint( ctx, false, dummy1, dummy2 );
137 }
138
paint(PaintContext * ctx,bool calculateListAndReturnScale,qreal & newZoomX,qreal & newZoomY)139 void PolarDiagram::paint( PaintContext* ctx,
140 bool calculateListAndReturnScale,
141 qreal& newZoomX, qreal& newZoomY )
142 {
143 // note: Not having any data model assigned is no bug
144 // but we can not draw a diagram then either.
145 if ( !checkInvariants(true) )
146 return;
147 d->reverseMapper.clear();
148
149 const int rowCount = model()->rowCount( rootIndex() );
150 const int colCount = model()->columnCount( rootIndex() );
151
152 if ( calculateListAndReturnScale ) {
153 // Check if all of the data value texts / data comments fit into the available space...
154 d->labelPaintCache.clear();
155
156 for ( int iCol = 0; iCol < colCount; ++iCol ) {
157 for ( int iRow=0; iRow < rowCount; ++iRow ) {
158 QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
159 const qreal value = model()->data( index ).toReal();
160 QPointF point = coordinatePlane()->translate(
161 QPointF( value, iRow ) ) + ctx->rectangle().topLeft();
162 //qDebug() << point;
163 d->addLabel( &d->labelPaintCache, index, nullptr, PositionPoints( point ),
164 Position::Center, Position::Center, value );
165 }
166 }
167
168 newZoomX = coordinatePlane()->zoomFactorX();
169 newZoomY = coordinatePlane()->zoomFactorY();
170
171 if ( d->labelPaintCache.paintReplay.count() ) {
172 // ...and zoom out if necessary
173 const qreal oldZoomX = newZoomX;
174 const qreal oldZoomY = newZoomY;
175
176 QRectF txtRectF;
177 d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true, true, &txtRectF );
178 const QRect txtRect = txtRectF.toRect();
179 const QRect curRect = coordinatePlane()->geometry();
180 const qreal gapX = qMin( txtRect.left() - curRect.left(), curRect.right() - txtRect.right() );
181 const qreal gapY = qMin( txtRect.top() - curRect.top(), curRect.bottom() - txtRect.bottom() );
182 if ( gapX < 0.0 ) {
183 newZoomX = oldZoomX * ( 1.0 + ( gapX - 1.0 ) / curRect.width() );
184 }
185 if ( gapY < 0.0 ) {
186 newZoomY = oldZoomY * ( 1.0 + ( gapY - 1.0 ) / curRect.height() );
187 }
188 }
189 } else {
190 // Paint the data sets
191 for ( int iCol = 0; iCol < colCount; ++iCol ) {
192 //TODO(khz): As of yet PolarDiagram can not show per-segment line attributes
193 // but it draws every polyline in one go - using one color.
194 // This needs to be enhanced to allow for cell-specific settings
195 // in the same way as LineDiagram does it.
196 QBrush brush = d->datasetAttrs( iCol, KChart::DatasetBrushRole ).value<QBrush>();
197 QPolygonF polygon;
198 for ( int iRow = 0; iRow < rowCount; ++iRow ) {
199 QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
200 const qreal value = model()->data( index ).toReal();
201 QPointF point = coordinatePlane()->translate( QPointF( value, iRow ) )
202 + ctx->rectangle().topLeft();
203 polygon.append( point );
204 //qDebug() << point;
205 }
206 if ( closeDatasets() && !polygon.isEmpty() ) {
207 // close the circle by connecting the last data point to the first
208 polygon.append( polygon.first() );
209 }
210
211 PainterSaver painterSaver( ctx->painter() );
212 ctx->painter()->setRenderHint ( QPainter::Antialiasing );
213 ctx->painter()->setBrush( brush );
214 QPen p = d->datasetAttrs( iCol, KChart::DatasetPenRole ).value< QPen >();
215 if ( p.style() != Qt::NoPen )
216 {
217 ctx->painter()->setPen( PrintingParameters::scalePen( p ) );
218 ctx->painter()->drawPolyline( polygon );
219 }
220 }
221 d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true );
222 }
223 }
224
resize(const QSizeF & size)225 void PolarDiagram::resize ( const QSizeF& size )
226 {
227 AbstractPolarDiagram::resize(size);
228 }
229
230 /*virtual*/
valueTotals() const231 qreal PolarDiagram::valueTotals () const
232 {
233 return model()->rowCount(rootIndex());
234 }
235
236 /*virtual*/
numberOfValuesPerDataset() const237 qreal PolarDiagram::numberOfValuesPerDataset() const
238 {
239 return model() ? model()->rowCount(rootIndex()) : 0.0;
240 }
241
242 /*virtual*/
numberOfGridRings() const243 qreal PolarDiagram::numberOfGridRings() const
244 {
245 return 5; // FIXME
246 }
247
setZeroDegreePosition(int degrees)248 void PolarDiagram::setZeroDegreePosition( int degrees )
249 {
250 Q_UNUSED( degrees );
251 qWarning() << "Deprecated PolarDiagram::setZeroDegreePosition() called, setting ignored.";
252 }
253
zeroDegreePosition() const254 int PolarDiagram::zeroDegreePosition() const
255 {
256 qWarning() << "Deprecated PolarDiagram::zeroDegreePosition() called.";
257 return 0;
258 }
259
setRotateCircularLabels(bool rotateCircularLabels)260 void PolarDiagram::setRotateCircularLabels( bool rotateCircularLabels )
261 {
262 d->rotateCircularLabels = rotateCircularLabels;
263 }
264
rotateCircularLabels() const265 bool PolarDiagram::rotateCircularLabels() const
266 {
267 return d->rotateCircularLabels;
268 }
269
setCloseDatasets(bool closeDatasets)270 void PolarDiagram::setCloseDatasets( bool closeDatasets )
271 {
272 d->closeDatasets = closeDatasets;
273 }
274
closeDatasets() const275 bool PolarDiagram::closeDatasets() const
276 {
277 return d->closeDatasets;
278 }
279
setShowDelimitersAtPosition(Position position,bool showDelimiters)280 void PolarDiagram::setShowDelimitersAtPosition( Position position,
281 bool showDelimiters )
282 {
283 d->showDelimitersAtPosition[position.value()] = showDelimiters;
284 }
285
setShowLabelsAtPosition(Position position,bool showLabels)286 void PolarDiagram::setShowLabelsAtPosition( Position position,
287 bool showLabels )
288 {
289 d->showLabelsAtPosition[position.value()] = showLabels;
290 }
291
showDelimitersAtPosition(Position position) const292 bool PolarDiagram::showDelimitersAtPosition( Position position ) const
293 {
294 return d->showDelimitersAtPosition[position.value()];
295 }
296
showLabelsAtPosition(Position position) const297 bool PolarDiagram::showLabelsAtPosition( Position position ) const
298 {
299 return d->showLabelsAtPosition[position.value()];
300 }
301
302
303
304