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