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 #ifndef KCHARTCARTESIANCOORDINATEPLANE_H
21 #define KCHARTCARTESIANCOORDINATEPLANE_H
22 
23 #include "KChartAbstractCoordinatePlane.h"
24 
25 namespace KChart {
26 
27     class Chart;
28     class PaintContext;
29     class AbstractDiagram;
30     class CartesianAxis;
31     class CartesianGrid;
32 
33     /**
34       * @brief Cartesian coordinate plane
35       */
36     class KCHART_EXPORT CartesianCoordinatePlane : public AbstractCoordinatePlane
37     {
38         Q_OBJECT
39 
40         Q_DISABLE_COPY( CartesianCoordinatePlane )
41         KCHART_DECLARE_PRIVATE_DERIVED_PARENT( CartesianCoordinatePlane, Chart* )
42 
43     friend class CartesianAxis;
44     friend class CartesianGrid;
45 
46     public:
47         explicit CartesianCoordinatePlane ( Chart* parent = nullptr );
48         ~CartesianCoordinatePlane();
49 
50         void addDiagram ( AbstractDiagram* diagram ) override;
51 
52         /**
53          * If @p onOff is true, enforce that X and Y distances are scaled by the same factor.
54          * This makes the plane's height a function of its width, and hasHeightForWidth()
55          * will return true.
56          */
57         void setIsometricScaling ( bool onOff );
58 
59         bool doesIsometricScaling() const;
60 
61         const QPointF translate ( const QPointF& diagramPoint ) const override;
62 
63         /**
64          * \sa setZoomFactorX, setZoomCenter
65          */
66         qreal zoomFactorX() const override;
67         /**
68          * \sa setZoomFactorY, setZoomCenter
69          */
70         qreal zoomFactorY() const override;
71 
72         /**
73          * \sa setZoomFactorX,setZoomFactorY
74          */
75         void setZoomFactors( qreal factorX, qreal factorY ) override;
76         /**
77          * \sa zoomFactorX, setZoomCenter
78          */
79         void setZoomFactorX( qreal factor ) override;
80         /**
81          * \sa zoomFactorY, setZoomCenter
82          */
83         void setZoomFactorY( qreal factor ) override;
84 
85         /**
86          * \sa setZoomCenter, setZoomFactorX, setZoomFactorY
87          */
88         QPointF zoomCenter() const override;
89 
90         /**
91          * \sa zoomCenter, setZoomFactorX, setZoomFactorY
92          */
93         void setZoomCenter( const QPointF& center ) override;
94 
95         /**
96          * Allows to specify a fixed data-space / coordinate-space relation. If set
97          * to true then fixed bar widths are used, so you see more bars as the window
98          * is made wider.
99          *
100          * This allows to completely restrict the size of bars in a graph such that,
101          * upon resizing a window, the graphs coordinate plane will grow (add more
102          * ticks to x- and y-coordinates) rather than have the image grow.
103          */
104         void setFixedDataCoordinateSpaceRelation( bool fixed );
105         bool hasFixedDataCoordinateSpaceRelation() const;
106 
107         /**
108           * Allows to fix the lower bound of X axis to zero when diagram is in first quadrant.
109           *
110           * The default behavior is to lower x or y bound to be 0. If this behaviour is not wanted,
111           * either \a CartesianCoordinatePlane::setHorizontalRange could be used instead of letting
112           * KChart auto-adjust the ranges, or this method can be used to disable this behavior.
113           */
114         void setXAxisStartAtZero(bool fixedStart);
115         bool xAxisStartAtZero() const;
116 
117         /**
118          * \brief Set the boundaries of the visible value space displayed in horizontal direction.
119          *
120          * This is also known as the horizontal viewport.
121          *
122          * By default the horizontal range is adjusted to the range covered by the model's data,
123          * see setAutoAdjustHorizontalRangeToData for details.
124          * Calling setHorizontalRange with a valid range disables this default automatic adjusting,
125          * while on the other hand automatic adjusting will set these ranges.
126          *
127          * To disable use of this range you can either pass an empty pair by using the default
128          * constructor QPair() or you can set both values to the same which constitutes
129          * a null range.
130          *
131          * \note By default the visible data range often is larger than the
132          * range calculated from the data model (or set by setHoriz.|Vert.Range(), resp.).
133          * This is due to the built-in grid calculation feature: The visible start/end
134          * values get adjusted so that they match a main-grid line.
135          * You can turn this feature off for any of the four bounds by calling
136          * GridAttributes::setAdjustBoundsToGrid() for either the global grid-attributes
137          * or for the horizontal/vertical attrs separately.
138          *
139          * \note To set only one of the ends of the range to a fixed value while keeping
140          * the other dynamically adjusted, use std::numeric_limits< qreal >::quiet_NaN()
141          * for the dynamic value.
142          *
143          * \note If you use user defined vertical ranges together with logarithmic scale, only
144          * positive values are supported. If you set it to negative values, the result is undefined.
145          *
146          * \param range a pair of values representing the smalles and the largest
147          * horizontal value space coordinate displayed.
148          *
149          * \sa setAutoAdjustHorizontalRangeToData, setVerticalRange
150          * \sa GridAttributes::setAdjustBoundsToGrid()
151          */
152         void setHorizontalRange( const QPair<qreal, qreal> & range );
153 
154         /**
155          * \brief Set the boundaries of the visible value space displayed in vertical direction.
156          *
157          * This is also known as the vertical viewport.
158          *
159          * By default the vertical range is adjusted to the range covered by the model's data,
160          * see setAutoAdjustVerticalRangeToData for details.
161          * Calling setVerticalRange with a valid range disables this default automatic adjusting,
162          * while on the other hand automatic adjusting will set these ranges.
163          *
164          * To disable use of this range you can either pass an empty pair by using the default
165          * constructor QPair() or you can set setting both values to the same which constitutes
166          * a null range.
167          *
168          * \note By default the visible data range often is larger than the
169          * range calculated from the data model (or set by setHoriz.|Vert.Range(), resp.).
170          * This is due to the built-in grid calculation feature: The visible start/end
171          * values get adjusted so that they match a main-grid line.
172          * You can turn this feature off for any of the four bounds by calling
173          * GridAttributes::setAdjustBoundsToGrid() for either the global grid-attributes
174          * or for the horizontal/vertical attrs separately.
175          *
176          * \note To set only one of the ends of the range to a fixed value while keeping
177          * the other dynamically adjusted, use std::numeric_limits< qreal >::quiet_NaN()
178          * for the dynamic value.
179          *
180          * \note If you use user defined vertical ranges together with logarithmic scale, only
181          * positive values are supported. If you set it to negative values, the result is undefined.
182          *
183          * \param range a pair of values representing the smalles and the largest
184          * vertical value space coordinate displayed.
185          *
186          * \sa setAutoAdjustVerticalRangeToData, setHorizontalRange
187          * \sa GridAttributes::setAdjustBoundsToGrid()
188          */
189         void setVerticalRange( const QPair<qreal, qreal> & range );
190 
191         /**
192          * @return The largest and smallest visible horizontal value space
193          * value. If this is not explicitly set,or if both values are the same,
194          * the plane will use the union of the dataBoundaries of all
195          * associated diagrams.
196          * \see KChart::AbstractDiagram::dataBoundaries
197          */
198         QPair<qreal, qreal> horizontalRange() const;
199 
200         /**
201          * @return The largest and smallest visible horizontal value space
202          * value. If this is not explicitly set, or if both values are the same,
203          * the plane will use the union of the dataBoundaries of all
204          * associated diagrams.
205          * \see KChart::AbstractDiagram::dataBoundaries
206          */
207         QPair<qreal, qreal> verticalRange() const;
208 
209         /**
210          * \brief Automatically adjust horizontal range settings to the ranges covered by
211          * the model's values, when ever the data have changed, and then emit horizontalRangeAutomaticallyAdjusted.
212          *
213          * By default the horizontal range is adjusted automatically, if more than 67 percent of
214          * the available horizontal space would be empty otherwise.
215          *
216          * Range setting is adjusted if more than \c percentEmpty percent of the horizontal
217          * space covered by the coordinate plane would otherwise be empty.
218          * Automatic range adjusting can happen, when either all of the data are positive or all are negative.
219          *
220          * Set percentEmpty to 100 to disable automatic range adjusting.
221          *
222          * \param percentEmpty The maximal percentage of horizontal space that may be empty.
223          *
224          * \sa horizontalRangeAutomaticallyAdjusted
225          * \sa autoAdjustHorizontalRangeToData, adjustRangesToData
226          * \sa setHorizontalRange, setVerticalRange
227          * \sa setAutoAdjustVerticalRangeToData
228          */
229         void setAutoAdjustHorizontalRangeToData( unsigned int percentEmpty = 67 );
230 
231         /**
232          * \brief Automatically adjust vertical range settings to the ranges covered by
233          * the model's values, when ever the data have changed, and then emit verticalRangeAutomaticallyAdjusted.
234          *
235          * By default the vertical range is adjusted automatically, if more than 67 percent of
236          * the available vertical space would be empty otherwise.
237          *
238          * Range setting is adjusted if more than \c percentEmpty percent of the horizontal
239          * space covered by the coordinate plane would otherwise be empty.
240          * Automatic range adjusting can happen, when either all of the data are positive or all are negative.
241          *
242          * Set percentEmpty to 100 to disable automatic range adjusting.
243          *
244          * \param percentEmpty The maximal percentage of horizontal space that may be empty.
245          *
246          * \sa verticalRangeAutomaticallyAdjusted
247          * \sa autoAdjustVerticalRangeToData, adjustRangesToData
248          * \sa setHorizontalRange, setVerticalRange
249          * \sa setAutoAdjustHorizontalRangeToData
250          */
251         void setAutoAdjustVerticalRangeToData( unsigned int percentEmpty = 67 );
252 
253         /**
254          * \brief Returns the maximal allowed percent of the horizontal
255          * space covered by the coordinate plane that may be empty.
256          *
257          * \return A percent value indicating how much of the horizontal space may be empty.
258          * If more than this is empty, automatic range adjusting is applied.
259          * A return value of 100 indicates that no such automatic adjusting is done at all.
260          *
261          * \sa setAutoAdjustHorizontalRangeToData, adjustRangesToData
262          */
263         unsigned int autoAdjustHorizontalRangeToData() const;
264 
265         /**
266          * \brief Returns the maximal allowed percent of the vertical
267          * space covered by the coordinate plane that may be empty.
268          *
269          * \return A percent value indicating how much of the vertical space may be empty.
270          * If more than this is empty, automatic range adjusting is applied.
271          * A return value of 100 indicates that no such automatic adjusting is done at all.
272          *
273          * \sa setAutoAdjustVerticalRangeToData, adjustRangesToData
274          */
275         unsigned int autoAdjustVerticalRangeToData() const;
276 
277 
278         /**
279          * Set the attributes to be used for grid lines drawn in horizontal
280          * direction (or in vertical direction, resp.).
281          *
282          * To disable horizontal grid painting, for example, your code should like this:
283          * \code
284          * GridAttributes ga = plane->gridAttributes( Qt::Horizontal );
285          * ga.setGridVisible( false );
286          * plane-setGridAttributes( Qt::Horizontal, ga );
287          * \endcode
288          *
289          * \note setGridAttributes overwrites the global attributes that
290          * were set by AbstractCoordinatePlane::setGlobalGridAttributes.
291          * To re-activate these global attributes you can call
292          * resetGridAttributes.
293          *
294          * \sa resetGridAttributes, gridAttributes
295          * \sa setAutoAdjustGridToZoom
296          * \sa AbstractCoordinatePlane::setGlobalGridAttributes
297          * \sa hasOwnGridAttributes
298          */
299         void setGridAttributes( Qt::Orientation orientation, const GridAttributes & );
300 
301         /**
302          * Reset the attributes to be used for grid lines drawn in horizontal
303          * direction (or in vertical direction, resp.).
304          * By calling this method you specify that the global attributes set by
305          * AbstractCoordinatePlane::setGlobalGridAttributes be used.
306          *
307          * \sa setGridAttributes, gridAttributes
308          * \sa setAutoAdjustGridToZoom
309          * \sa AbstractCoordinatePlane::globalGridAttributes
310          * \sa hasOwnGridAttributes
311          */
312         void resetGridAttributes( Qt::Orientation orientation );
313 
314         /**
315          * \return The attributes used for grid lines drawn in horizontal
316          * direction (or in vertical direction, resp.).
317          *
318          * \note This function always returns a valid set of grid attributes:
319          * If no special grid attributes were set foe this orientation
320          * the global attributes are returned, as returned by
321          * AbstractCoordinatePlane::globalGridAttributes.
322          *
323          * \sa setGridAttributes
324          * \sa resetGridAttributes
325          * \sa AbstractCoordinatePlane::globalGridAttributes
326          * \sa hasOwnGridAttributes
327          */
328         const GridAttributes gridAttributes( Qt::Orientation orientation ) const;
329 
330         /**
331          * \return Returns whether the grid attributes have been set for the
332          * respective direction via setGridAttributes( orientation ).
333          *
334          * If false, the grid will use the global attributes set
335          * by AbstractCoordinatePlane::globalGridAttributes (or the default
336          * attributes, resp.)
337          *
338          * \sa setGridAttributes
339          * \sa resetGridAttributes
340          * \sa AbstractCoordinatePlane::globalGridAttributes
341          */
342         bool hasOwnGridAttributes( Qt::Orientation orientation ) const;
343 
344         /**
345          * Disable / re-enable the built-in grid adjusting feature.
346          *
347          * By default additional lines will be drawn in a Linear grid when zooming in.
348          *
349          * \sa autoAdjustGridToZoom, setGridAttributes
350          */
351         void setAutoAdjustGridToZoom( bool autoAdjust );
352 
353         /**
354          * Return the status of the built-in grid adjusting feature.
355          *
356          * \sa setAutoAdjustGridToZoom
357          */
358 #if defined(Q_COMPILER_MANGLES_RETURN_TYPE)
359         const bool autoAdjustGridToZoom() const;
360 #else
361         bool autoAdjustGridToZoom() const;
362 #endif
363 
364         AxesCalcMode axesCalcModeY() const;
365         AxesCalcMode axesCalcModeX() const;
366 
367         /** Specifies the calculation modes for all axes */
368         void setAxesCalcModes( AxesCalcMode mode );
369         /** Specifies the calculation mode for all Ordinate axes */
370         void setAxesCalcModeY( AxesCalcMode mode );
371         /** Specifies the calculation mode for all Abscissa axes */
372         void setAxesCalcModeX( AxesCalcMode mode );
373 
374         /** reimpl */
375         void paint( QPainter* ) override;
376 
377         /** reimpl */
378         AbstractCoordinatePlane* sharedAxisMasterPlane( QPainter* p = nullptr ) override;
379 
380         /**
381          * Returns the currently visible data range. Might be greater than the
382          * range of the grid.
383          */
384         QRectF visibleDataRange() const;
385 
386         /**
387          * Returns the logical area, i.e., the rectangle defined by the very top
388          * left and very bottom right coordinate.
389          */
390         QRectF logicalArea() const;
391 
392         /**
393          * Returns the (physical) area occupied by the diagram. Unless zoom is applied
394          * (which is also true when a fixed data coordinate / space relation is used),
395          * \code diagramArea() == drawingArea() \endcode .
396          * \sa setFixedDataCoordinateSpaceRelation
397          * \sa drawingArea
398          */
399         QRectF diagramArea() const;
400 
401         /**
402          * Returns the visible part of the diagram area, i.e.
403          * \code diagramArea().intersected( drawingArea() ) \endcode
404          * \sa diagramArea
405          */
406         QRectF visibleDiagramArea() const;
407 
408         /**
409          * Sets whether the horizontal range should be reversed or not, i.e.
410          * small values to the left and large values to the right (the default)
411          * or vice versa.
412          * \param reverse Whether the horizontal range should be reversed or not
413          */
414         void setHorizontalRangeReversed( bool reverse );
415 
416         /**
417          * \return Whether the horizontal range is reversed or not
418          */
419         bool isHorizontalRangeReversed() const;
420 
421         /**
422          * Sets whether the vertical range should be reversed or not, i.e.
423          * small values at the bottom and large values at the top (the default)
424          * or vice versa.
425          * \param reverse Whether the vertical range should be reversed or not
426          */
427         void setVerticalRangeReversed( bool reverse );
428 
429         /**
430          * \return Whether the vertical range is reversed or not
431          */
432         bool isVerticalRangeReversed() const;
433 
434         /**
435          * reimplemented from AbstractCoordinatePlane
436          */
437         void setGeometry( const QRect& r ) override;
438 
439         // reimplemented
440         Qt::Orientations expandingDirections() const override;
441 
442 
443     public Q_SLOTS:
444         /**
445          * \brief Adjust both, horizontal and vertical range settings to the
446          * ranges covered by the model's data values.
447          *
448          * \sa setHorizontalRange, setVerticalRange
449          * \sa adjustHorizontalRangeToData, adjustVerticalRangeToData
450          * \sa setAutoAdjustHorizontalRangeToData, setAutoAdjustVerticalRangeToData
451          */
452         void adjustRangesToData();
453 
454         /**
455          * Adjust horizontal range settings to the ranges covered by the model's data values.
456          * \sa adjustRangesToData
457          */
458         void adjustHorizontalRangeToData();
459 
460         /**
461          * Adjust vertical range settings to the ranges covered by the model's data values.
462          * \sa adjustRangesToData
463          */
464         void adjustVerticalRangeToData();
465 
466     protected:
467         QRectF getRawDataBoundingRectFromDiagrams() const;
468         QRectF adjustedToMaxEmptyInnerPercentage(
469                 const QRectF& r, unsigned int percentX, unsigned int percentY ) const;
470         virtual QRectF calculateRawDataBoundingRect() const;
471         DataDimensionsList getDataDimensionsList() const override;
472         // the whole drawing area, includes diagrams and axes, but maybe smaller
473         // than (width, height):
474         virtual QRectF drawingArea() const;
475     public:
476         const QPointF translateBack( const QPointF& screenPoint ) const;
477     protected:
478         void paintEvent ( QPaintEvent* );
479         void layoutDiagrams() override;
480         // the following three return true if the new value is different from the old
481         bool doneSetZoomFactorX( qreal factor );
482         bool doneSetZoomFactorY( qreal factor );
483         bool doneSetZoomCenter( const QPointF& center );
484 
485         void handleFixedDataCoordinateSpaceRelation( const QRectF& geometry );
486 
487         // reimplemented from QLayoutItem, via AbstractLayoutItem, AbstractArea, AbstractCoordinatePlane
488         bool hasHeightForWidth() const override;
489         int heightForWidth( int w ) const override;
490         QSize sizeHint() const override;
491 
492     protected Q_SLOTS:
493         void slotLayoutChanged( AbstractDiagram* );
494 
495     private:
496         void setHasOwnGridAttributes(
497             Qt::Orientation orientation, bool on );
498     };
499 
500 }
501 
502 #endif
503