1 /***************************************************************************
2 **                                                                        **
3 **  QCustomPlot, an easy to use, modern plotting widget for Qt            **
4 **  Copyright (C) 2011-2015 Emanuel Eichhammer                            **
5 **                                                                        **
6 **  This program is free software: you can redistribute it and/or modify  **
7 **  it under the terms of the GNU General Public License as published by  **
8 **  the Free Software Foundation, either version 2 of the License, or     **
9 **  (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 http://www.gnu.org/licenses/.   **
18 **                                                                        **
19 ****************************************************************************
20 **           Author: Emanuel Eichhammer                                   **
21 **  Website/Contact: http://www.qcustomplot.com/                          **
22 **             Date: 22.12.15                                             **
23 **          Version: 1.3.2                                                **
24 ****************************************************************************/
25 
26 #include "qcustomplot.h"
27 
28 
29 
30 ////////////////////////////////////////////////////////////////////////////////////////////////////
31 //////////////////// QCPPainter
32 ////////////////////////////////////////////////////////////////////////////////////////////////////
33 
34 /*! \class QCPPainter
35   \brief QPainter subclass used internally
36 
37   This QPainter subclass is used to provide some extended functionality e.g. for tweaking position
38   consistency between antialiased and non-antialiased painting. Further it provides workarounds
39   for QPainter quirks.
40 
41   \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and
42   restore. So while it is possible to pass a QCPPainter instance to a function that expects a
43   QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because
44   it will call the base class implementations of the functions actually hidden by QCPPainter).
45 */
46 
47 /*!
48   Creates a new QCPPainter instance and sets default values
49 */
QCPPainter()50 QCPPainter::QCPPainter() :
51   QPainter(),
52   mModes(pmDefault),
53   mIsAntialiasing(false)
54 {
55   // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
56   // a call to begin() will follow
57 }
58 
59 /*!
60   Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just
61   like the analogous QPainter constructor, begins painting on \a device immediately.
62 
63   Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt versions before Qt5.
64 */
QCPPainter(QPaintDevice * device)65 QCPPainter::QCPPainter(QPaintDevice *device) :
66   QPainter(device),
67   mModes(pmDefault),
68   mIsAntialiasing(false)
69 {
70 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
71   if (isActive())
72     setRenderHint(QPainter::NonCosmeticDefaultPen);
73 #endif
74 }
75 
~QCPPainter()76 QCPPainter::~QCPPainter()
77 {
78 }
79 
80 /*!
81   Sets the pen of the painter and applies certain fixes to it, depending on the mode of this
82   QCPPainter.
83 
84   \note this function hides the non-virtual base class implementation.
85 */
setPen(const QPen & pen)86 void QCPPainter::setPen(const QPen &pen)
87 {
88   QPainter::setPen(pen);
89   if (mModes.testFlag(pmNonCosmetic))
90     makeNonCosmetic();
91 }
92 
93 /*! \overload
94 
95   Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of
96   this QCPPainter.
97 
98   \note this function hides the non-virtual base class implementation.
99 */
setPen(const QColor & color)100 void QCPPainter::setPen(const QColor &color)
101 {
102   QPainter::setPen(color);
103   if (mModes.testFlag(pmNonCosmetic))
104     makeNonCosmetic();
105 }
106 
107 /*! \overload
108 
109   Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of
110   this QCPPainter.
111 
112   \note this function hides the non-virtual base class implementation.
113 */
setPen(Qt::PenStyle penStyle)114 void QCPPainter::setPen(Qt::PenStyle penStyle)
115 {
116   QPainter::setPen(penStyle);
117   if (mModes.testFlag(pmNonCosmetic))
118     makeNonCosmetic();
119 }
120 
121 /*! \overload
122 
123   Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when
124   antialiasing is disabled. Thus when antialiasing is disabled, it rounds the \a line to
125   integer coordinates and then passes it to the original drawLine.
126 
127   \note this function hides the non-virtual base class implementation.
128 */
drawLine(const QLineF & line)129 void QCPPainter::drawLine(const QLineF &line)
130 {
131   if (mIsAntialiasing || mModes.testFlag(pmVectorized))
132     QPainter::drawLine(line);
133   else
134     QPainter::drawLine(line.toLine());
135 }
136 
137 /*!
138   Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint
139   with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between
140   antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for
141   AA/Non-AA painting).
142 */
setAntialiasing(bool enabled)143 void QCPPainter::setAntialiasing(bool enabled)
144 {
145   setRenderHint(QPainter::Antialiasing, enabled);
146   if (mIsAntialiasing != enabled)
147   {
148     mIsAntialiasing = enabled;
149     if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
150     {
151       if (mIsAntialiasing)
152         translate(0.5, 0.5);
153       else
154         translate(-0.5, -0.5);
155     }
156   }
157 }
158 
159 /*!
160   Sets the mode of the painter. This controls whether the painter shall adjust its
161   fixes/workarounds optimized for certain output devices.
162 */
setModes(QCPPainter::PainterModes modes)163 void QCPPainter::setModes(QCPPainter::PainterModes modes)
164 {
165   mModes = modes;
166 }
167 
168 /*!
169   Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on \a
170   device. This is necessary to get cosmetic pen consistency across Qt versions, because since Qt5,
171   all pens are non-cosmetic by default, and in Qt4 this render hint must be set to get that
172   behaviour.
173 
174   The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts painting also sets
175   the render hint as appropriate.
176 
177   \note this function hides the non-virtual base class implementation.
178 */
begin(QPaintDevice * device)179 bool QCPPainter::begin(QPaintDevice *device)
180 {
181   bool result = QPainter::begin(device);
182 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
183   if (result)
184     setRenderHint(QPainter::NonCosmeticDefaultPen);
185 #endif
186   return result;
187 }
188 
189 /*! \overload
190 
191   Sets the mode of the painter. This controls whether the painter shall adjust its
192   fixes/workarounds optimized for certain output devices.
193 */
setMode(QCPPainter::PainterMode mode,bool enabled)194 void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled)
195 {
196   if (!enabled && mModes.testFlag(mode))
197     mModes &= ~mode;
198   else if (enabled && !mModes.testFlag(mode))
199     mModes |= mode;
200 }
201 
202 /*!
203   Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to
204   QPainter, the save/restore functions are reimplemented to also save/restore those members.
205 
206   \note this function hides the non-virtual base class implementation.
207 
208   \see restore
209 */
save()210 void QCPPainter::save()
211 {
212   mAntialiasingStack.push(mIsAntialiasing);
213   QPainter::save();
214 }
215 
216 /*!
217   Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to
218   QPainter, the save/restore functions are reimplemented to also save/restore those members.
219 
220   \note this function hides the non-virtual base class implementation.
221 
222   \see save
223 */
restore()224 void QCPPainter::restore()
225 {
226   if (!mAntialiasingStack.isEmpty())
227     mIsAntialiasing = mAntialiasingStack.pop();
228   else
229     qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
230   QPainter::restore();
231 }
232 
233 /*!
234   Changes the pen width to 1 if it currently is 0. This function is called in the \ref setPen
235   overrides when the \ref pmNonCosmetic mode is set.
236 */
makeNonCosmetic()237 void QCPPainter::makeNonCosmetic()
238 {
239   if (qFuzzyIsNull(pen().widthF()))
240   {
241     QPen p = pen();
242     p.setWidth(1);
243     QPainter::setPen(p);
244   }
245 }
246 
247 
248 ////////////////////////////////////////////////////////////////////////////////////////////////////
249 //////////////////// QCPScatterStyle
250 ////////////////////////////////////////////////////////////////////////////////////////////////////
251 
252 /*! \class QCPScatterStyle
253   \brief Represents the visual appearance of scatter points
254 
255   This class holds information about shape, color and size of scatter points. In plottables like
256   QCPGraph it is used to store how scatter points shall be drawn. For example, \ref
257   QCPGraph::setScatterStyle takes a QCPScatterStyle instance.
258 
259   A scatter style consists of a shape (\ref setShape), a line color (\ref setPen) and possibly a
260   fill (\ref setBrush), if the shape provides a fillable area. Further, the size of the shape can
261   be controlled with \ref setSize.
262 
263   \section QCPScatterStyle-defining Specifying a scatter style
264 
265   You can set all these configurations either by calling the respective functions on an instance:
266   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1
267 
268   Or you can use one of the various constructors that take different parameter combinations, making
269   it easy to specify a scatter style in a single call, like so:
270   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-2
271 
272   \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the plottable
273 
274   There are two constructors which leave the pen undefined: \ref QCPScatterStyle() and \ref
275   QCPScatterStyle(ScatterShape shape, double size). If those constructors are used, a call to \ref
276   isPenDefined will return false. It leads to scatter points that inherit the pen from the
277   plottable that uses the scatter style. Thus, if such a scatter style is passed to QCPGraph, the line
278   color of the graph (\ref QCPGraph::setPen) will be used by the scatter points. This makes
279   it very convenient to set up typical scatter settings:
280 
281   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-shortcreation
282 
283   Notice that it wasn't even necessary to explicitly call a QCPScatterStyle constructor. This works
284   because QCPScatterStyle provides a constructor that can transform a \ref ScatterShape directly
285   into a QCPScatterStyle instance (that's the \ref QCPScatterStyle(ScatterShape shape, double size)
286   constructor with a default for \a size). In those cases, C++ allows directly supplying a \ref
287   ScatterShape, where actually a QCPScatterStyle is expected.
288 
289   \section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps
290 
291   QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as scatter points.
292 
293   For custom shapes, you can provide a QPainterPath with the desired shape to the \ref
294   setCustomPath function or call the constructor that takes a painter path. The scatter shape will
295   automatically be set to \ref ssCustom.
296 
297   For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively you can use the
298   constructor that takes a QPixmap. The scatter shape will automatically be set to \ref ssPixmap.
299   Note that \ref setSize does not influence the appearance of the pixmap.
300 */
301 
302 /* start documentation of inline functions */
303 
304 /*! \fn bool QCPScatterStyle::isNone() const
305 
306   Returns whether the scatter shape is \ref ssNone.
307 
308   \see setShape
309 */
310 
311 /*! \fn bool QCPScatterStyle::isPenDefined() const
312 
313   Returns whether a pen has been defined for this scatter style.
314 
315   The pen is undefined if a constructor is called that does not carry \a pen as parameter. Those are
316   \ref QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size). If the pen is
317   left undefined, the scatter color will be inherited from the plottable that uses this scatter
318   style.
319 
320   \see setPen
321 */
322 
323 /* end documentation of inline functions */
324 
325 /*!
326   Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or brush is defined.
327 
328   Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited
329   from the plottable that uses this scatter style.
330 */
QCPScatterStyle()331 QCPScatterStyle::QCPScatterStyle() :
332   mSize(6),
333   mShape(ssNone),
334   mPen(Qt::NoPen),
335   mBrush(Qt::NoBrush),
336   mPenDefined(false)
337 {
338 }
339 
340 /*!
341   Creates a new QCPScatterStyle instance with shape set to \a shape and size to \a size. No pen or
342   brush is defined.
343 
344   Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited
345   from the plottable that uses this scatter style.
346 */
QCPScatterStyle(ScatterShape shape,double size)347 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) :
348   mSize(size),
349   mShape(shape),
350   mPen(Qt::NoPen),
351   mBrush(Qt::NoBrush),
352   mPenDefined(false)
353 {
354 }
355 
356 /*!
357   Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color,
358   and size to \a size. No brush is defined, i.e. the scatter point will not be filled.
359 */
QCPScatterStyle(ScatterShape shape,const QColor & color,double size)360 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :
361   mSize(size),
362   mShape(shape),
363   mPen(QPen(color)),
364   mBrush(Qt::NoBrush),
365   mPenDefined(true)
366 {
367 }
368 
369 /*!
370   Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color,
371   the brush color to \a fill (with a solid pattern), and size to \a size.
372 */
QCPScatterStyle(ScatterShape shape,const QColor & color,const QColor & fill,double size)373 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
374   mSize(size),
375   mShape(shape),
376   mPen(QPen(color)),
377   mBrush(QBrush(fill)),
378   mPenDefined(true)
379 {
380 }
381 
382 /*!
383   Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set to \a pen, the
384   brush to \a brush, and size to \a size.
385 
386   \warning In some cases it might be tempting to directly use a pen style like <tt>Qt::NoPen</tt> as \a pen
387   and a color like <tt>Qt::blue</tt> as \a brush. Notice however, that the corresponding call\n
388   <tt>QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)</tt>\n
389   doesn't necessarily lead C++ to use this constructor in some cases, but might mistake
390   <tt>Qt::NoPen</tt> for a QColor and use the
391   \ref QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size)
392   constructor instead (which will lead to an unexpected look of the scatter points). To prevent
393   this, be more explicit with the parameter types. For example, use <tt>QBrush(Qt::blue)</tt>
394   instead of just <tt>Qt::blue</tt>, to clearly point out to the compiler that this constructor is
395   wanted.
396 */
QCPScatterStyle(ScatterShape shape,const QPen & pen,const QBrush & brush,double size)397 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
398   mSize(size),
399   mShape(shape),
400   mPen(pen),
401   mBrush(brush),
402   mPenDefined(pen.style() != Qt::NoPen)
403 {
404 }
405 
406 /*!
407   Creates a new QCPScatterStyle instance which will show the specified \a pixmap. The scatter shape
408   is set to \ref ssPixmap.
409 */
QCPScatterStyle(const QPixmap & pixmap)410 QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :
411   mSize(5),
412   mShape(ssPixmap),
413   mPen(Qt::NoPen),
414   mBrush(Qt::NoBrush),
415   mPixmap(pixmap),
416   mPenDefined(false)
417 {
418 }
419 
420 /*!
421   Creates a new QCPScatterStyle instance with a custom shape that is defined via \a customPath. The
422   scatter shape is set to \ref ssCustom.
423 
424   The custom shape line will be drawn with \a pen and filled with \a brush. The size has a slightly
425   different meaning than for built-in scatter points: The custom path will be drawn scaled by a
426   factor of \a size/6.0. Since the default \a size is 6, the custom path will appear at a its
427   natural size by default. To double the size of the path for example, set \a size to 12.
428 */
QCPScatterStyle(const QPainterPath & customPath,const QPen & pen,const QBrush & brush,double size)429 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
430   mSize(size),
431   mShape(ssCustom),
432   mPen(pen),
433   mBrush(brush),
434   mCustomPath(customPath),
435   mPenDefined(pen.style() != Qt::NoPen)
436 {
437 }
438 
439 /*!
440   Sets the size (pixel diameter) of the drawn scatter points to \a size.
441 
442   \see setShape
443 */
setSize(double size)444 void QCPScatterStyle::setSize(double size)
445 {
446   mSize = size;
447 }
448 
449 /*!
450   Sets the shape to \a shape.
451 
452   Note that the calls \ref setPixmap and \ref setCustomPath automatically set the shape to \ref
453   ssPixmap and \ref ssCustom, respectively.
454 
455   \see setSize
456 */
setShape(QCPScatterStyle::ScatterShape shape)457 void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape)
458 {
459   mShape = shape;
460 }
461 
462 /*!
463   Sets the pen that will be used to draw scatter points to \a pen.
464 
465   If the pen was previously undefined (see \ref isPenDefined), the pen is considered defined after
466   a call to this function, even if \a pen is <tt>Qt::NoPen</tt>.
467 
468   \see setBrush
469 */
setPen(const QPen & pen)470 void QCPScatterStyle::setPen(const QPen &pen)
471 {
472   mPenDefined = true;
473   mPen = pen;
474 }
475 
476 /*!
477   Sets the brush that will be used to fill scatter points to \a brush. Note that not all scatter
478   shapes have fillable areas. For example, \ref ssPlus does not while \ref ssCircle does.
479 
480   \see setPen
481 */
setBrush(const QBrush & brush)482 void QCPScatterStyle::setBrush(const QBrush &brush)
483 {
484   mBrush = brush;
485 }
486 
487 /*!
488   Sets the pixmap that will be drawn as scatter point to \a pixmap.
489 
490   Note that \ref setSize does not influence the appearance of the pixmap.
491 
492   The scatter shape is automatically set to \ref ssPixmap.
493 */
setPixmap(const QPixmap & pixmap)494 void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
495 {
496   setShape(ssPixmap);
497   mPixmap = pixmap;
498 }
499 
500 /*!
501   Sets the custom shape that will be drawn as scatter point to \a customPath.
502 
503   The scatter shape is automatically set to \ref ssCustom.
504 */
setCustomPath(const QPainterPath & customPath)505 void QCPScatterStyle::setCustomPath(const QPainterPath &customPath)
506 {
507   setShape(ssCustom);
508   mCustomPath = customPath;
509 }
510 
511 /*!
512   Applies the pen and the brush of this scatter style to \a painter. If this scatter style has an
513   undefined pen (\ref isPenDefined), sets the pen of \a painter to \a defaultPen instead.
514 
515   This function is used by plottables (or any class that wants to draw scatters) just before a
516   number of scatters with this style shall be drawn with the \a painter.
517 
518   \see drawShape
519 */
applyTo(QCPPainter * painter,const QPen & defaultPen) const520 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
521 {
522   painter->setPen(mPenDefined ? mPen : defaultPen);
523   painter->setBrush(mBrush);
524 }
525 
526 /*!
527   Draws the scatter shape with \a painter at position \a pos.
528 
529   This function does not modify the pen or the brush on the painter, as \ref applyTo is meant to be
530   called before scatter points are drawn with \ref drawShape.
531 
532   \see applyTo
533 */
drawShape(QCPPainter * painter,QPointF pos) const534 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
535 {
536   drawShape(painter, pos.x(), pos.y());
537 }
538 
539 /*! \overload
540   Draws the scatter shape with \a painter at position \a x and \a y.
541 */
drawShape(QCPPainter * painter,double x,double y) const542 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
543 {
544   double w = mSize/2.0;
545   switch (mShape)
546   {
547     case ssNone: break;
548     case ssDot:
549     {
550       painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
551       break;
552     }
553     case ssCross:
554     {
555       painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
556       painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
557       break;
558     }
559     case ssPlus:
560     {
561       painter->drawLine(QLineF(x-w,   y, x+w,   y));
562       painter->drawLine(QLineF(  x, y+w,   x, y-w));
563       break;
564     }
565     case ssCircle:
566     {
567       painter->drawEllipse(QPointF(x , y), w, w);
568       break;
569     }
570     case ssDisc:
571     {
572       QBrush b = painter->brush();
573       painter->setBrush(painter->pen().color());
574       painter->drawEllipse(QPointF(x , y), w, w);
575       painter->setBrush(b);
576       break;
577     }
578     case ssSquare:
579     {
580       painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
581       break;
582     }
583     case ssDiamond:
584     {
585       painter->drawLine(QLineF(x-w,   y,   x, y-w));
586       painter->drawLine(QLineF(  x, y-w, x+w,   y));
587       painter->drawLine(QLineF(x+w,   y,   x, y+w));
588       painter->drawLine(QLineF(  x, y+w, x-w,   y));
589       break;
590     }
591     case ssStar:
592     {
593       painter->drawLine(QLineF(x-w,   y, x+w,   y));
594       painter->drawLine(QLineF(  x, y+w,   x, y-w));
595       painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
596       painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
597       break;
598     }
599     case ssTriangle:
600     {
601        painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
602        painter->drawLine(QLineF(x+w, y+0.755*w,   x, y-0.977*w));
603        painter->drawLine(QLineF(  x, y-0.977*w, x-w, y+0.755*w));
604       break;
605     }
606     case ssTriangleInverted:
607     {
608        painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
609        painter->drawLine(QLineF(x+w, y-0.755*w,   x, y+0.977*w));
610        painter->drawLine(QLineF(  x, y+0.977*w, x-w, y-0.755*w));
611       break;
612     }
613     case ssCrossSquare:
614     {
615        painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
616        painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
617        painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
618       break;
619     }
620     case ssPlusSquare:
621     {
622        painter->drawLine(QLineF(x-w,   y, x+w*0.95,   y));
623        painter->drawLine(QLineF(  x, y+w,        x, y-w));
624        painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
625       break;
626     }
627     case ssCrossCircle:
628     {
629        painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
630        painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
631        painter->drawEllipse(QPointF(x, y), w, w);
632       break;
633     }
634     case ssPlusCircle:
635     {
636        painter->drawLine(QLineF(x-w,   y, x+w,   y));
637        painter->drawLine(QLineF(  x, y+w,   x, y-w));
638        painter->drawEllipse(QPointF(x, y), w, w);
639       break;
640     }
641     case ssPeace:
642     {
643        painter->drawLine(QLineF(x, y-w,         x,       y+w));
644        painter->drawLine(QLineF(x,   y, x-w*0.707, y+w*0.707));
645        painter->drawLine(QLineF(x,   y, x+w*0.707, y+w*0.707));
646        painter->drawEllipse(QPointF(x, y), w, w);
647       break;
648     }
649     case ssPixmap:
650     {
651       painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
652       break;
653     }
654     case ssCustom:
655     {
656       QTransform oldTransform = painter->transform();
657       painter->translate(x, y);
658       painter->scale(mSize/6.0, mSize/6.0);
659       painter->drawPath(mCustomPath);
660       painter->setTransform(oldTransform);
661       break;
662     }
663   }
664 }
665 
666 
667 ////////////////////////////////////////////////////////////////////////////////////////////////////
668 //////////////////// QCPLayer
669 ////////////////////////////////////////////////////////////////////////////////////////////////////
670 
671 /*! \class QCPLayer
672   \brief A layer that may contain objects, to control the rendering order
673 
674   The Layering system of QCustomPlot is the mechanism to control the rendering order of the
675   elements inside the plot.
676 
677   It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an ordered list of
678   one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer,
679   QCustomPlot::moveLayer, etc.). When replotting, QCustomPlot goes through the list of layers
680   bottom to top and successively draws the layerables of the layers.
681 
682   A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract base
683   class from which almost all visible objects derive, like axes, grids, graphs, items, etc.
684 
685   Initially, QCustomPlot has five layers: "background", "grid", "main", "axes" and "legend" (in
686   that order). The top two layers "axes" and "legend" contain the default axes and legend, so they
687   will be drawn on top. In the middle, there is the "main" layer. It is initially empty and set as
688   the current layer (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc.
689   are created on this layer by default. Then comes the "grid" layer which contains the QCPGrid
690   instances (which belong tightly to QCPAxis, see \ref QCPAxis::grid). The Axis rect background
691   shall be drawn behind everything else, thus the default QCPAxisRect instance is placed on the
692   "background" layer. Of course, the layer affiliation of the individual objects can be changed as
693   required (\ref QCPLayerable::setLayer).
694 
695   Controlling the ordering of objects is easy: Create a new layer in the position you want it to
696   be, e.g. above "main", with QCustomPlot::addLayer. Then set the current layer with
697   QCustomPlot::setCurrentLayer to that new layer and finally create the objects normally. They will
698   be placed on the new layer automatically, due to the current layer setting. Alternatively you
699   could have also ignored the current layer setting and just moved the objects with
700   QCPLayerable::setLayer to the desired layer after creating them.
701 
702   It is also possible to move whole layers. For example, If you want the grid to be shown in front
703   of all plottables/items on the "main" layer, just move it above "main" with
704   QCustomPlot::moveLayer.
705 
706   The rendering order within one layer is simply by order of creation or insertion. The item
707   created last (or added last to the layer), is drawn on top of all other objects on that layer.
708 
709   When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below
710   the deleted layer, see QCustomPlot::removeLayer.
711 */
712 
713 /* start documentation of inline functions */
714 
715 /*! \fn QList<QCPLayerable*> QCPLayer::children() const
716 
717   Returns a list of all layerables on this layer. The order corresponds to the rendering order:
718   layerables with higher indices are drawn above layerables with lower indices.
719 */
720 
721 /*! \fn int QCPLayer::index() const
722 
723   Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be
724   accessed via \ref QCustomPlot::layer.
725 
726   Layers with higher indices will be drawn above layers with lower indices.
727 */
728 
729 /* end documentation of inline functions */
730 
731 /*!
732   Creates a new QCPLayer instance.
733 
734   Normally you shouldn't directly instantiate layers, use \ref QCustomPlot::addLayer instead.
735 
736   \warning It is not checked that \a layerName is actually a unique layer name in \a parentPlot.
737   This check is only performed by \ref QCustomPlot::addLayer.
738 */
QCPLayer(QCustomPlot * parentPlot,const QString & layerName)739 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
740   QObject(parentPlot),
741   mParentPlot(parentPlot),
742   mName(layerName),
743   mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
744   mVisible(true)
745 {
746   // Note: no need to make sure layerName is unique, because layer
747   // management is done with QCustomPlot functions.
748 }
749 
~QCPLayer()750 QCPLayer::~QCPLayer()
751 {
752   // If child layerables are still on this layer, detach them, so they don't try to reach back to this
753   // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
754   // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
755   // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
756 
757   while (!mChildren.isEmpty())
758     mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
759 
760   if (mParentPlot->currentLayer() == this)
761     qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand.";
762 }
763 
764 /*!
765   Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this
766   layer will be invisible.
767 
768   This function doesn't change the visibility property of the layerables (\ref
769   QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the
770   visibility of the parent layer into account.
771 */
setVisible(bool visible)772 void QCPLayer::setVisible(bool visible)
773 {
774   mVisible = visible;
775 }
776 
777 /*! \internal
778 
779   Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
780   be prepended to the list, i.e. be drawn beneath the other layerables already in the list.
781 
782   This function does not change the \a mLayer member of \a layerable to this layer. (Use
783   QCPLayerable::setLayer to change the layer of an object, not this function.)
784 
785   \see removeChild
786 */
addChild(QCPLayerable * layerable,bool prepend)787 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
788 {
789   if (!mChildren.contains(layerable))
790   {
791     if (prepend)
792       mChildren.prepend(layerable);
793     else
794       mChildren.append(layerable);
795   } else
796     qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
797 }
798 
799 /*! \internal
800 
801   Removes the \a layerable from the list of this layer.
802 
803   This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer
804   to change the layer of an object, not this function.)
805 
806   \see addChild
807 */
removeChild(QCPLayerable * layerable)808 void QCPLayer::removeChild(QCPLayerable *layerable)
809 {
810   if (!mChildren.removeOne(layerable))
811     qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
812 }
813 
814 
815 ////////////////////////////////////////////////////////////////////////////////////////////////////
816 //////////////////// QCPLayerable
817 ////////////////////////////////////////////////////////////////////////////////////////////////////
818 
819 /*! \class QCPLayerable
820   \brief Base class for all drawable objects
821 
822   This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid
823   etc.
824 
825   Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking
826   the layers accordingly.
827 
828   For details about the layering mechanism, see the QCPLayer documentation.
829 */
830 
831 /* start documentation of inline functions */
832 
833 /*! \fn QCPLayerable *QCPLayerable::parentLayerable() const
834 
835   Returns the parent layerable of this layerable. The parent layerable is used to provide
836   visibility hierarchies in conjunction with the method \ref realVisibility. This way, layerables
837   only get drawn if their parent layerables are visible, too.
838 
839   Note that a parent layerable is not necessarily also the QObject parent for memory management.
840   Further, a layerable doesn't always have a parent layerable, so this function may return 0.
841 
842   A parent layerable is set implicitly with when placed inside layout elements and doesn't need to be
843   set manually by the user.
844 */
845 
846 /* end documentation of inline functions */
847 /* start documentation of pure virtual functions */
848 
849 /*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0
850   \internal
851 
852   This function applies the default antialiasing setting to the specified \a painter, using the
853   function \ref applyAntialiasingHint. It is the antialiasing state the painter is put in, when
854   \ref draw is called on the layerable. If the layerable has multiple entities whose antialiasing
855   setting may be specified individually, this function should set the antialiasing state of the
856   most prominent entity. In this case however, the \ref draw function usually calls the specialized
857   versions of this function before drawing each entity, effectively overriding the setting of the
858   default antialiasing hint.
859 
860   <b>First example:</b> QCPGraph has multiple entities that have an antialiasing setting: The graph
861   line, fills, scatters and error bars. Those can be configured via QCPGraph::setAntialiased,
862   QCPGraph::setAntialiasedFill, QCPGraph::setAntialiasedScatters etc. Consequently, there isn't
863   only the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's
864   antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and
865   QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw
866   calls the respective specialized applyAntialiasingHint function.
867 
868   <b>Second example:</b> QCPItemLine consists only of a line so there is only one antialiasing
869   setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by
870   all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the
871   respective layerable subclass.) Consequently it only has the normal
872   QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to
873   care about setting any antialiasing states, because the default antialiasing hint is already set
874   on the painter when the \ref draw function is called, and that's the state it wants to draw the
875   line with.
876 */
877 
878 /*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
879   \internal
880 
881   This function draws the layerable with the specified \a painter. It is only called by
882   QCustomPlot, if the layerable is visible (\ref setVisible).
883 
884   Before this function is called, the painter's antialiasing state is set via \ref
885   applyDefaultAntialiasingHint, see the documentation there. Further, the clipping rectangle was
886   set to \ref clipRect.
887 */
888 
889 /* end documentation of pure virtual functions */
890 /* start documentation of signals */
891 
892 /*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
893 
894   This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to
895   a different layer.
896 
897   \see setLayer
898 */
899 
900 /* end documentation of signals */
901 
902 /*!
903   Creates a new QCPLayerable instance.
904 
905   Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the
906   derived classes.
907 
908   If \a plot is provided, it automatically places itself on the layer named \a targetLayer. If \a
909   targetLayer is an empty string, it places itself on the current layer of the plot (see \ref
910   QCustomPlot::setCurrentLayer).
911 
912   It is possible to provide 0 as \a plot. In that case, you should assign a parent plot at a later
913   time with \ref initializeParentPlot.
914 
915   The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable
916   parents are mainly used to control visibility in a hierarchy of layerables. This means a
917   layerable is only drawn, if all its ancestor layerables are also visible. Note that \a
918   parentLayerable does not become the QObject-parent (for memory management) of this layerable, \a
919   plot does. It is not uncommon to set the QObject-parent to something else in the constructors of
920   QCPLayerable subclasses, to guarantee a working destruction hierarchy.
921 */
QCPLayerable(QCustomPlot * plot,QString targetLayer,QCPLayerable * parentLayerable)922 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
923   QObject(plot),
924   mVisible(true),
925   mParentPlot(plot),
926   mParentLayerable(parentLayerable),
927   mLayer(0),
928   mAntialiased(true)
929 {
930   if (mParentPlot)
931   {
932     if (targetLayer.isEmpty())
933       setLayer(mParentPlot->currentLayer());
934     else if (!setLayer(targetLayer))
935       qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
936   }
937 }
938 
~QCPLayerable()939 QCPLayerable::~QCPLayerable()
940 {
941   if (mLayer)
942   {
943     mLayer->removeChild(this);
944     mLayer = 0;
945   }
946 }
947 
948 /*!
949   Sets the visibility of this layerable object. If an object is not visible, it will not be drawn
950   on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not
951   possible.
952 */
setVisible(bool on)953 void QCPLayerable::setVisible(bool on)
954 {
955   mVisible = on;
956 }
957 
958 /*!
959   Sets the \a layer of this layerable object. The object will be placed on top of the other objects
960   already on \a layer.
961 
962   If \a layer is 0, this layerable will not be on any layer and thus not appear in the plot (or
963   interact/receive events).
964 
965   Returns true if the layer of this layerable was successfully changed to \a layer.
966 */
setLayer(QCPLayer * layer)967 bool QCPLayerable::setLayer(QCPLayer *layer)
968 {
969   return moveToLayer(layer, false);
970 }
971 
972 /*! \overload
973   Sets the layer of this layerable object by name
974 
975   Returns true on success, i.e. if \a layerName is a valid layer name.
976 */
setLayer(const QString & layerName)977 bool QCPLayerable::setLayer(const QString &layerName)
978 {
979   if (!mParentPlot)
980   {
981     qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
982     return false;
983   }
984   if (QCPLayer *layer = mParentPlot->layer(layerName))
985   {
986     return setLayer(layer);
987   } else
988   {
989     qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
990     return false;
991   }
992 }
993 
994 /*!
995   Sets whether this object will be drawn antialiased or not.
996 
997   Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and
998   QCustomPlot::setNotAntialiasedElements.
999 */
setAntialiased(bool enabled)1000 void QCPLayerable::setAntialiased(bool enabled)
1001 {
1002   mAntialiased = enabled;
1003 }
1004 
1005 /*!
1006   Returns whether this layerable is visible, taking the visibility of the layerable parent and the
1007   visibility of the layer this layerable is on into account. This is the method that is consulted
1008   to decide whether a layerable shall be drawn or not.
1009 
1010   If this layerable has a direct layerable parent (usually set via hierarchies implemented in
1011   subclasses, like in the case of QCPLayoutElement), this function returns true only if this
1012   layerable has its visibility set to true and the parent layerable's \ref realVisibility returns
1013   true.
1014 
1015   If this layerable doesn't have a direct layerable parent, returns the state of this layerable's
1016   visibility.
1017 */
realVisibility() const1018 bool QCPLayerable::realVisibility() const
1019 {
1020   return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable->realVisibility());
1021 }
1022 
1023 /*!
1024   This function is used to decide whether a click hits a layerable object or not.
1025 
1026   \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
1027   shortest pixel distance of this point to the object. If the object is either invisible or the
1028   distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the
1029   object is not selectable, -1.0 is returned, too.
1030 
1031   If the object is represented not by single lines but by an area like a \ref QCPItemText or the
1032   bars of a \ref QCPBars plottable, a click inside the area should also be considered a hit. In
1033   these cases this function thus returns a constant value greater zero but still below the parent
1034   plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99).
1035 
1036   Providing a constant value for area objects allows selecting line objects even when they are
1037   obscured by such area objects, by clicking close to the lines (i.e. closer than
1038   0.99*selectionTolerance).
1039 
1040   The actual setting of the selection state is not done by this function. This is handled by the
1041   parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified
1042   via the selectEvent/deselectEvent methods.
1043 
1044   \a details is an optional output parameter. Every layerable subclass may place any information
1045   in \a details. This information will be passed to \ref selectEvent when the parent QCustomPlot
1046   decides on the basis of this selectTest call, that the object was successfully selected. The
1047   subsequent call to \ref selectEvent will carry the \a details. This is useful for multi-part
1048   objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked
1049   is only done once in \ref selectTest. The result (i.e. the actually clicked part) can then be
1050   placed in \a details. So in the subsequent \ref selectEvent, the decision which part was
1051   selected doesn't have to be done a second time for a single selection operation.
1052 
1053   You may pass 0 as \a details to indicate that you are not interested in those selection details.
1054 
1055   \see selectEvent, deselectEvent, QCustomPlot::setInteractions
1056 */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const1057 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1058 {
1059   Q_UNUSED(pos)
1060   Q_UNUSED(onlySelectable)
1061   Q_UNUSED(details)
1062   return -1.0;
1063 }
1064 
1065 /*! \internal
1066 
1067   Sets the parent plot of this layerable. Use this function once to set the parent plot if you have
1068   passed 0 in the constructor. It can not be used to move a layerable from one QCustomPlot to
1069   another one.
1070 
1071   Note that, unlike when passing a non-null parent plot in the constructor, this function does not
1072   make \a parentPlot the QObject-parent of this layerable. If you want this, call
1073   QObject::setParent(\a parentPlot) in addition to this function.
1074 
1075   Further, you will probably want to set a layer (\ref setLayer) after calling this function, to
1076   make the layerable appear on the QCustomPlot.
1077 
1078   The parent plot change will be propagated to subclasses via a call to \ref parentPlotInitialized
1079   so they can react accordingly (e.g. also initialize the parent plot of child layerables, like
1080   QCPLayout does).
1081 */
initializeParentPlot(QCustomPlot * parentPlot)1082 void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot)
1083 {
1084   if (mParentPlot)
1085   {
1086     qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1087     return;
1088   }
1089 
1090   if (!parentPlot)
1091     qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1092 
1093   mParentPlot = parentPlot;
1094   parentPlotInitialized(mParentPlot);
1095 }
1096 
1097 /*! \internal
1098 
1099   Sets the parent layerable of this layerable to \a parentLayerable. Note that \a parentLayerable does not
1100   become the QObject-parent (for memory management) of this layerable.
1101 
1102   The parent layerable has influence on the return value of the \ref realVisibility method. Only
1103   layerables with a fully visible parent tree will return true for \ref realVisibility, and thus be
1104   drawn.
1105 
1106   \see realVisibility
1107 */
setParentLayerable(QCPLayerable * parentLayerable)1108 void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable)
1109 {
1110   mParentLayerable = parentLayerable;
1111 }
1112 
1113 /*! \internal
1114 
1115   Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to
1116   the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is
1117   false, the object will be appended.
1118 
1119   Returns true on success, i.e. if \a layer is a valid layer.
1120 */
moveToLayer(QCPLayer * layer,bool prepend)1121 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
1122 {
1123   if (layer && !mParentPlot)
1124   {
1125     qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1126     return false;
1127   }
1128   if (layer && layer->parentPlot() != mParentPlot)
1129   {
1130     qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1131     return false;
1132   }
1133 
1134   QCPLayer *oldLayer = mLayer;
1135   if (mLayer)
1136     mLayer->removeChild(this);
1137   mLayer = layer;
1138   if (mLayer)
1139     mLayer->addChild(this, prepend);
1140   if (mLayer != oldLayer)
1141     emit layerChanged(mLayer);
1142   return true;
1143 }
1144 
1145 /*! \internal
1146 
1147   Sets the QCPainter::setAntialiasing state on the provided \a painter, depending on the \a
1148   localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and \ref
1149   QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is
1150   controlled via \a overrideElement.
1151 */
applyAntialiasingHint(QCPPainter * painter,bool localAntialiased,QCP::AntialiasedElement overrideElement) const1152 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1153 {
1154   if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1155     painter->setAntialiasing(false);
1156   else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1157     painter->setAntialiasing(true);
1158   else
1159     painter->setAntialiasing(localAntialiased);
1160 }
1161 
1162 /*! \internal
1163 
1164   This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting
1165   of a parent plot. This is the case when 0 was passed as parent plot in the constructor, and the
1166   parent plot is set at a later time.
1167 
1168   For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any
1169   QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level
1170   element of the hierarchy gets its parent plot initialized with \ref initializeParentPlot. To
1171   propagate the parent plot to all the children of the hierarchy, the top level element then uses
1172   this function to pass the parent plot on to its child elements.
1173 
1174   The default implementation does nothing.
1175 
1176   \see initializeParentPlot
1177 */
parentPlotInitialized(QCustomPlot * parentPlot)1178 void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot)
1179 {
1180    Q_UNUSED(parentPlot)
1181 }
1182 
1183 /*! \internal
1184 
1185   Returns the selection category this layerable shall belong to. The selection category is used in
1186   conjunction with \ref QCustomPlot::setInteractions to control which objects are selectable and
1187   which aren't.
1188 
1189   Subclasses that don't fit any of the normal \ref QCP::Interaction values can use \ref
1190   QCP::iSelectOther. This is what the default implementation returns.
1191 
1192   \see QCustomPlot::setInteractions
1193 */
selectionCategory() const1194 QCP::Interaction QCPLayerable::selectionCategory() const
1195 {
1196   return QCP::iSelectOther;
1197 }
1198 
1199 /*! \internal
1200 
1201   Returns the clipping rectangle of this layerable object. By default, this is the viewport of the
1202   parent QCustomPlot. Specific subclasses may reimplement this function to provide different
1203   clipping rects.
1204 
1205   The returned clipping rect is set on the painter before the draw function of the respective
1206   object is called.
1207 */
clipRect() const1208 QRect QCPLayerable::clipRect() const
1209 {
1210   if (mParentPlot)
1211     return mParentPlot->viewport();
1212   else
1213     return QRect();
1214 }
1215 
1216 /*! \internal
1217 
1218   This event is called when the layerable shall be selected, as a consequence of a click by the
1219   user. Subclasses should react to it by setting their selection state appropriately. The default
1220   implementation does nothing.
1221 
1222   \a event is the mouse event that caused the selection. \a additive indicates, whether the user
1223   was holding the multi-select-modifier while performing the selection (see \ref
1224   QCustomPlot::setMultiSelectModifier). if \a additive is true, the selection state must be toggled
1225   (i.e. become selected when unselected and unselected when selected).
1226 
1227   Every selectEvent is preceded by a call to \ref selectTest, which has returned positively (i.e.
1228   returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot).
1229   The \a details data you output from \ref selectTest is fed back via \a details here. You may
1230   use it to transport any kind of information from the selectTest to the possibly subsequent
1231   selectEvent. Usually \a details is used to transfer which part was clicked, if it is a layerable
1232   that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need
1233   to do the calculation again to find out which part was actually clicked.
1234 
1235   \a selectionStateChanged is an output parameter. If the pointer is non-null, this function must
1236   set the value either to true or false, depending on whether the selection state of this layerable
1237   was actually changed. For layerables that only are selectable as a whole and not in parts, this
1238   is simple: if \a additive is true, \a selectionStateChanged must also be set to true, because the
1239   selection toggles. If \a additive is false, \a selectionStateChanged is only set to true, if the
1240   layerable was previously unselected and now is switched to the selected state.
1241 
1242   \see selectTest, deselectEvent
1243 */
selectEvent(QMouseEvent * event,bool additive,const QVariant & details,bool * selectionStateChanged)1244 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1245 {
1246   Q_UNUSED(event)
1247   Q_UNUSED(additive)
1248   Q_UNUSED(details)
1249   Q_UNUSED(selectionStateChanged)
1250 }
1251 
1252 /*! \internal
1253 
1254   This event is called when the layerable shall be deselected, either as consequence of a user
1255   interaction or a call to \ref QCustomPlot::deselectAll. Subclasses should react to it by
1256   unsetting their selection appropriately.
1257 
1258   just as in \ref selectEvent, the output parameter \a selectionStateChanged (if non-null), must
1259   return true or false when the selection state of this layerable has changed or not changed,
1260   respectively.
1261 
1262   \see selectTest, selectEvent
1263 */
deselectEvent(bool * selectionStateChanged)1264 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1265 {
1266   Q_UNUSED(selectionStateChanged)
1267 }
1268 
1269 
1270 ////////////////////////////////////////////////////////////////////////////////////////////////////
1271 //////////////////// QCPRange
1272 ////////////////////////////////////////////////////////////////////////////////////////////////////
1273 /*! \class QCPRange
1274   \brief Represents the range an axis is encompassing.
1275 
1276   contains a \a lower and \a upper double value and provides convenience input, output and
1277   modification functions.
1278 
1279   \see QCPAxis::setRange
1280 */
1281 
1282 /*!
1283   Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
1284   intervals would cause errors due to the 11-bit exponent of double precision numbers,
1285   corresponding to a minimum magnitude of roughly 1e-308.
1286   \see validRange, maxRange
1287 */
1288 const double QCPRange::minRange = 1e-280;
1289 
1290 /*!
1291   Maximum values (negative and positive) the range will accept in range-changing functions.
1292   Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
1293   corresponding to a maximum magnitude of roughly 1e308.
1294   Since the number of planck-volumes in the entire visible universe is only ~1e183, this should
1295   be enough.
1296   \see validRange, minRange
1297 */
1298 const double QCPRange::maxRange = 1e250;
1299 
1300 /*!
1301   Constructs a range with \a lower and \a upper set to zero.
1302 */
QCPRange()1303 QCPRange::QCPRange() :
1304   lower(0),
1305   upper(0)
1306 {
1307 }
1308 
1309 /*! \overload
1310   Constructs a range with the specified \a lower and \a upper values.
1311 */
QCPRange(double lower,double upper)1312 QCPRange::QCPRange(double lower, double upper) :
1313   lower(lower),
1314   upper(upper)
1315 {
1316   normalize();
1317 }
1318 
1319 /*!
1320   Returns the size of the range, i.e. \a upper-\a lower
1321 */
size() const1322 double QCPRange::size() const
1323 {
1324   return upper-lower;
1325 }
1326 
1327 /*!
1328   Returns the center of the range, i.e. (\a upper+\a lower)*0.5
1329 */
center() const1330 double QCPRange::center() const
1331 {
1332   return (upper+lower)*0.5;
1333 }
1334 
1335 /*!
1336   Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values
1337   are swapped.
1338 */
normalize()1339 void QCPRange::normalize()
1340 {
1341   if (lower > upper)
1342     qSwap(lower, upper);
1343 }
1344 
1345 /*!
1346   Expands this range such that \a otherRange is contained in the new range. It is assumed that both
1347   this range and \a otherRange are normalized (see \ref normalize).
1348 
1349   If \a otherRange is already inside the current range, this function does nothing.
1350 
1351   \see expanded
1352 */
expand(const QCPRange & otherRange)1353 void QCPRange::expand(const QCPRange &otherRange)
1354 {
1355   if (lower > otherRange.lower)
1356     lower = otherRange.lower;
1357   if (upper < otherRange.upper)
1358     upper = otherRange.upper;
1359 }
1360 
1361 
1362 /*!
1363   Returns an expanded range that contains this and \a otherRange. It is assumed that both this
1364   range and \a otherRange are normalized (see \ref normalize).
1365 
1366   \see expand
1367 */
expanded(const QCPRange & otherRange) const1368 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1369 {
1370   QCPRange result = *this;
1371   result.expand(otherRange);
1372   return result;
1373 }
1374 
1375 /*!
1376   Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
1377   the range won't span the positive and negative sign domain, i.e. contain zero. Further
1378   \a lower will always be numerically smaller (or equal) to \a upper.
1379 
1380   If the original range does span positive and negative sign domains or contains zero,
1381   the returned range will try to approximate the original range as good as possible.
1382   If the positive interval of the original range is wider than the negative interval, the
1383   returned range will only contain the positive interval, with lower bound set to \a rangeFac or
1384   \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval
1385   is wider than the positive interval, this time by changing the \a upper bound.
1386 */
sanitizedForLogScale() const1387 QCPRange QCPRange::sanitizedForLogScale() const
1388 {
1389   double rangeFac = 1e-3;
1390   QCPRange sanitizedRange(lower, upper);
1391   sanitizedRange.normalize();
1392   // can't have range spanning negative and positive values in log plot, so change range to fix it
1393   //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1394   if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1395   {
1396     // case lower is 0
1397     if (rangeFac < sanitizedRange.upper*rangeFac)
1398       sanitizedRange.lower = rangeFac;
1399     else
1400       sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1401   } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1402   else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1403   {
1404     // case upper is 0
1405     if (-rangeFac > sanitizedRange.lower*rangeFac)
1406       sanitizedRange.upper = -rangeFac;
1407     else
1408       sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1409   } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1410   {
1411     // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1412     if (-sanitizedRange.lower > sanitizedRange.upper)
1413     {
1414       // negative is wider, do same as in case upper is 0
1415       if (-rangeFac > sanitizedRange.lower*rangeFac)
1416         sanitizedRange.upper = -rangeFac;
1417       else
1418         sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1419     } else
1420     {
1421       // positive is wider, do same as in case lower is 0
1422       if (rangeFac < sanitizedRange.upper*rangeFac)
1423         sanitizedRange.lower = rangeFac;
1424       else
1425         sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1426     }
1427   }
1428   // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1429   return sanitizedRange;
1430 }
1431 
1432 /*!
1433   Returns a sanitized version of the range. Sanitized means for linear scales, that
1434   \a lower will always be numerically smaller (or equal) to \a upper.
1435 */
sanitizedForLinScale() const1436 QCPRange QCPRange::sanitizedForLinScale() const
1437 {
1438   QCPRange sanitizedRange(lower, upper);
1439   sanitizedRange.normalize();
1440   return sanitizedRange;
1441 }
1442 
1443 /*!
1444   Returns true when \a value lies within or exactly on the borders of the range.
1445 */
contains(double value) const1446 bool QCPRange::contains(double value) const
1447 {
1448   return value >= lower && value <= upper;
1449 }
1450 
1451 /*!
1452   Checks, whether the specified range is within valid bounds, which are defined
1453   as QCPRange::maxRange and QCPRange::minRange.
1454   A valid range means:
1455   \li range bounds within -maxRange and maxRange
1456   \li range size above minRange
1457   \li range size below maxRange
1458 */
validRange(double lower,double upper)1459 bool QCPRange::validRange(double lower, double upper)
1460 {
1461   return (lower > -maxRange &&
1462           upper < maxRange &&
1463           qAbs(lower-upper) > minRange &&
1464           qAbs(lower-upper) < maxRange &&
1465           !(lower > 0 && qIsInf(upper/lower)) &&
1466           !(upper < 0 && qIsInf(lower/upper)));
1467 }
1468 
1469 /*!
1470   \overload
1471   Checks, whether the specified range is within valid bounds, which are defined
1472   as QCPRange::maxRange and QCPRange::minRange.
1473   A valid range means:
1474   \li range bounds within -maxRange and maxRange
1475   \li range size above minRange
1476   \li range size below maxRange
1477 */
validRange(const QCPRange & range)1478 bool QCPRange::validRange(const QCPRange &range)
1479 {
1480   return (range.lower > -maxRange &&
1481           range.upper < maxRange &&
1482           qAbs(range.lower-range.upper) > minRange &&
1483           qAbs(range.lower-range.upper) < maxRange &&
1484           !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&
1485           !(range.upper < 0 && qIsInf(range.lower/range.upper)));
1486 }
1487 
1488 
1489 ////////////////////////////////////////////////////////////////////////////////////////////////////
1490 //////////////////// QCPMarginGroup
1491 ////////////////////////////////////////////////////////////////////////////////////////////////////
1492 
1493 /*! \class QCPMarginGroup
1494   \brief A margin group allows synchronization of margin sides if working with multiple layout elements.
1495 
1496   QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that
1497   they will all have the same size, based on the largest required margin in the group.
1498 
1499   \n
1500   \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup"
1501   \n
1502 
1503   In certain situations it is desirable that margins at specific sides are synchronized across
1504   layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will
1505   provide a cleaner look to the user if the left and right margins of the two axis rects are of the
1506   same size. The left axis of the top axis rect will then be at the same horizontal position as the
1507   left axis of the lower axis rect, making them appear aligned. The same applies for the right
1508   axes. This is what QCPMarginGroup makes possible.
1509 
1510   To add/remove a specific side of a layout element to/from a margin group, use the \ref
1511   QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call
1512   \ref clear, or just delete the margin group.
1513 
1514   \section QCPMarginGroup-example Example
1515 
1516   First create a margin group:
1517   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1
1518   Then set this group on the layout element sides:
1519   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2
1520   Here, we've used the first two axis rects of the plot and synchronized their left margins with
1521   each other and their right margins with each other.
1522 */
1523 
1524 /* start documentation of inline functions */
1525 
1526 /*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side) const
1527 
1528   Returns a list of all layout elements that have their margin \a side associated with this margin
1529   group.
1530 */
1531 
1532 /* end documentation of inline functions */
1533 
1534 /*!
1535   Creates a new QCPMarginGroup instance in \a parentPlot.
1536 */
QCPMarginGroup(QCustomPlot * parentPlot)1537 QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) :
1538   QObject(parentPlot),
1539   mParentPlot(parentPlot)
1540 {
1541   mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1542   mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1543   mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1544   mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1545 }
1546 
~QCPMarginGroup()1547 QCPMarginGroup::~QCPMarginGroup()
1548 {
1549   clear();
1550 }
1551 
1552 /*!
1553   Returns whether this margin group is empty. If this function returns true, no layout elements use
1554   this margin group to synchronize margin sides.
1555 */
isEmpty() const1556 bool QCPMarginGroup::isEmpty() const
1557 {
1558   QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1559   while (it.hasNext())
1560   {
1561     it.next();
1562     if (!it.value().isEmpty())
1563       return false;
1564   }
1565   return true;
1566 }
1567 
1568 /*!
1569   Clears this margin group. The synchronization of the margin sides that use this margin group is
1570   lifted and they will use their individual margin sizes again.
1571 */
clear()1572 void QCPMarginGroup::clear()
1573 {
1574   // make all children remove themselves from this margin group:
1575   QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1576   while (it.hasNext())
1577   {
1578     it.next();
1579     const QList<QCPLayoutElement*> elements = it.value();
1580     for (int i=elements.size()-1; i>=0; --i)
1581       elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1582   }
1583 }
1584 
1585 /*! \internal
1586 
1587   Returns the synchronized common margin for \a side. This is the margin value that will be used by
1588   the layout element on the respective side, if it is part of this margin group.
1589 
1590   The common margin is calculated by requesting the automatic margin (\ref
1591   QCPLayoutElement::calculateAutoMargin) of each element associated with \a side in this margin
1592   group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into
1593   account, too.)
1594 */
commonMargin(QCP::MarginSide side) const1595 int QCPMarginGroup::commonMargin(QCP::MarginSide side) const
1596 {
1597   // query all automatic margins of the layout elements in this margin group side and find maximum:
1598   int result = 0;
1599   const QList<QCPLayoutElement*> elements = mChildren.value(side);
1600   for (int i=0; i<elements.size(); ++i)
1601   {
1602     if (!elements.at(i)->autoMargins().testFlag(side))
1603       continue;
1604     int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1605     if (m > result)
1606       result = m;
1607   }
1608   return result;
1609 }
1610 
1611 /*! \internal
1612 
1613   Adds \a element to the internal list of child elements, for the margin \a side.
1614 
1615   This function does not modify the margin group property of \a element.
1616 */
addChild(QCP::MarginSide side,QCPLayoutElement * element)1617 void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element)
1618 {
1619   if (!mChildren[side].contains(element))
1620     mChildren[side].append(element);
1621   else
1622     qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1623 }
1624 
1625 /*! \internal
1626 
1627   Removes \a element from the internal list of child elements, for the margin \a side.
1628 
1629   This function does not modify the margin group property of \a element.
1630 */
removeChild(QCP::MarginSide side,QCPLayoutElement * element)1631 void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element)
1632 {
1633   if (!mChildren[side].removeOne(element))
1634     qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1635 }
1636 
1637 
1638 ////////////////////////////////////////////////////////////////////////////////////////////////////
1639 //////////////////// QCPLayoutElement
1640 ////////////////////////////////////////////////////////////////////////////////////////////////////
1641 
1642 /*! \class QCPLayoutElement
1643   \brief The abstract base class for all objects that form \ref thelayoutsystem "the layout system".
1644 
1645   This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses.
1646 
1647   A Layout element is a rectangular object which can be placed in layouts. It has an outer rect
1648   (QCPLayoutElement::outerRect) and an inner rect (\ref QCPLayoutElement::rect). The difference
1649   between outer and inner rect is called its margin. The margin can either be set to automatic or
1650   manual (\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be
1651   set explicitly with \ref setMargins and will stay fixed at that value. If it's set to automatic,
1652   the layout element subclass will control the value itself (via \ref calculateAutoMargin).
1653 
1654   Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level
1655   layout is reachable via \ref QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref
1656   QCPLayout itself derives from \ref QCPLayoutElement, layouts can be nested.
1657 
1658   Thus in QCustomPlot one can divide layout elements into two categories: The ones that are
1659   invisible by themselves, because they don't draw anything. Their only purpose is to manage the
1660   position and size of other layout elements. This category of layout elements usually use
1661   QCPLayout as base class. Then there is the category of layout elements which actually draw
1662   something. For example, QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does
1663   not necessarily mean that the latter category can't have child layout elements. QCPLegend for
1664   instance, actually derives from QCPLayoutGrid and the individual legend items are child layout
1665   elements in the grid layout.
1666 */
1667 
1668 /* start documentation of inline functions */
1669 
1670 /*! \fn QCPLayout *QCPLayoutElement::layout() const
1671 
1672   Returns the parent layout of this layout element.
1673 */
1674 
1675 /*! \fn QRect QCPLayoutElement::rect() const
1676 
1677   Returns the inner rect of this layout element. The inner rect is the outer rect (\ref
1678   setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins).
1679 
1680   In some cases, the area between outer and inner rect is left blank. In other cases the margin
1681   area is used to display peripheral graphics while the main content is in the inner rect. This is
1682   where automatic margin calculation becomes interesting because it allows the layout element to
1683   adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect
1684   draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if
1685   \ref setAutoMargins is enabled) according to the space required by the labels of the axes.
1686 */
1687 
1688 /*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event)
1689 
1690   This event is called, if the mouse was pressed while being inside the outer rect of this layout
1691   element.
1692 */
1693 
1694 /*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event)
1695 
1696   This event is called, if the mouse is moved inside the outer rect of this layout element.
1697 */
1698 
1699 /*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event)
1700 
1701   This event is called, if the mouse was previously pressed inside the outer rect of this layout
1702   element and is now released.
1703 */
1704 
1705 /*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event)
1706 
1707   This event is called, if the mouse is double-clicked inside the outer rect of this layout
1708   element.
1709 */
1710 
1711 /*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event)
1712 
1713   This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this
1714   layout element.
1715 */
1716 
1717 /* end documentation of inline functions */
1718 
1719 /*!
1720   Creates an instance of QCPLayoutElement and sets default values.
1721 */
QCPLayoutElement(QCustomPlot * parentPlot)1722 QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :
1723   QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1724   mParentLayout(0),
1725   mMinimumSize(),
1726   mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1727   mRect(0, 0, 0, 0),
1728   mOuterRect(0, 0, 0, 0),
1729   mMargins(0, 0, 0, 0),
1730   mMinimumMargins(0, 0, 0, 0),
1731   mAutoMargins(QCP::msAll)
1732 {
1733 }
1734 
~QCPLayoutElement()1735 QCPLayoutElement::~QCPLayoutElement()
1736 {
1737   setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1738   // unregister at layout:
1739   if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor
1740     mParentLayout->take(this);
1741 }
1742 
1743 /*!
1744   Sets the outer rect of this layout element. If the layout element is inside a layout, the layout
1745   sets the position and size of this layout element using this function.
1746 
1747   Calling this function externally has no effect, since the layout will overwrite any changes to
1748   the outer rect upon the next replot.
1749 
1750   The layout element will adapt its inner \ref rect by applying the margins inward to the outer rect.
1751 
1752   \see rect
1753 */
setOuterRect(const QRect & rect)1754 void QCPLayoutElement::setOuterRect(const QRect &rect)
1755 {
1756   if (mOuterRect != rect)
1757   {
1758     mOuterRect = rect;
1759     mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1760   }
1761 }
1762 
1763 /*!
1764   Sets the margins of this layout element. If \ref setAutoMargins is disabled for some or all
1765   sides, this function is used to manually set the margin on those sides. Sides that are still set
1766   to be handled automatically are ignored and may have any value in \a margins.
1767 
1768   The margin is the distance between the outer rect (controlled by the parent layout via \ref
1769   setOuterRect) and the inner \ref rect (which usually contains the main content of this layout
1770   element).
1771 
1772   \see setAutoMargins
1773 */
setMargins(const QMargins & margins)1774 void QCPLayoutElement::setMargins(const QMargins &margins)
1775 {
1776   if (mMargins != margins)
1777   {
1778     mMargins = margins;
1779     mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1780   }
1781 }
1782 
1783 /*!
1784   If \ref setAutoMargins is enabled on some or all margins, this function is used to provide
1785   minimum values for those margins.
1786 
1787   The minimum values are not enforced on margin sides that were set to be under manual control via
1788   \ref setAutoMargins.
1789 
1790   \see setAutoMargins
1791 */
setMinimumMargins(const QMargins & margins)1792 void QCPLayoutElement::setMinimumMargins(const QMargins &margins)
1793 {
1794   if (mMinimumMargins != margins)
1795   {
1796     mMinimumMargins = margins;
1797   }
1798 }
1799 
1800 /*!
1801   Sets on which sides the margin shall be calculated automatically. If a side is calculated
1802   automatically, a minimum margin value may be provided with \ref setMinimumMargins. If a side is
1803   set to be controlled manually, the value may be specified with \ref setMargins.
1804 
1805   Margin sides that are under automatic control may participate in a \ref QCPMarginGroup (see \ref
1806   setMarginGroup), to synchronize (align) it with other layout elements in the plot.
1807 
1808   \see setMinimumMargins, setMargins
1809 */
setAutoMargins(QCP::MarginSides sides)1810 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1811 {
1812   mAutoMargins = sides;
1813 }
1814 
1815 /*!
1816   Sets the minimum size for the inner \ref rect of this layout element. A parent layout tries to
1817   respect the \a size here by changing row/column sizes in the layout accordingly.
1818 
1819   If the parent layout size is not sufficient to satisfy all minimum size constraints of its child
1820   layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot
1821   propagates the layout's size constraints to the outside by setting its own minimum QWidget size
1822   accordingly, so violations of \a size should be exceptions.
1823 */
setMinimumSize(const QSize & size)1824 void QCPLayoutElement::setMinimumSize(const QSize &size)
1825 {
1826   if (mMinimumSize != size)
1827   {
1828     mMinimumSize = size;
1829     if (mParentLayout)
1830       mParentLayout->sizeConstraintsChanged();
1831   }
1832 }
1833 
1834 /*! \overload
1835 
1836   Sets the minimum size for the inner \ref rect of this layout element.
1837 */
setMinimumSize(int width,int height)1838 void QCPLayoutElement::setMinimumSize(int width, int height)
1839 {
1840   setMinimumSize(QSize(width, height));
1841 }
1842 
1843 /*!
1844   Sets the maximum size for the inner \ref rect of this layout element. A parent layout tries to
1845   respect the \a size here by changing row/column sizes in the layout accordingly.
1846 */
setMaximumSize(const QSize & size)1847 void QCPLayoutElement::setMaximumSize(const QSize &size)
1848 {
1849   if (mMaximumSize != size)
1850   {
1851     mMaximumSize = size;
1852     if (mParentLayout)
1853       mParentLayout->sizeConstraintsChanged();
1854   }
1855 }
1856 
1857 /*! \overload
1858 
1859   Sets the maximum size for the inner \ref rect of this layout element.
1860 */
setMaximumSize(int width,int height)1861 void QCPLayoutElement::setMaximumSize(int width, int height)
1862 {
1863   setMaximumSize(QSize(width, height));
1864 }
1865 
1866 /*!
1867   Sets the margin \a group of the specified margin \a sides.
1868 
1869   Margin groups allow synchronizing specified margins across layout elements, see the documentation
1870   of \ref QCPMarginGroup.
1871 
1872   To unset the margin group of \a sides, set \a group to 0.
1873 
1874   Note that margin groups only work for margin sides that are set to automatic (\ref
1875   setAutoMargins).
1876 */
setMarginGroup(QCP::MarginSides sides,QCPMarginGroup * group)1877 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
1878 {
1879   QVector<QCP::MarginSide> sideVector;
1880   if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1881   if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1882   if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1883   if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1884 
1885   for (int i=0; i<sideVector.size(); ++i)
1886   {
1887     QCP::MarginSide side = sideVector.at(i);
1888     if (marginGroup(side) != group)
1889     {
1890       QCPMarginGroup *oldGroup = marginGroup(side);
1891       if (oldGroup) // unregister at old group
1892         oldGroup->removeChild(side, this);
1893 
1894       if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
1895       {
1896         mMarginGroups.remove(side);
1897       } else // setting to a new group
1898       {
1899         mMarginGroups[side] = group;
1900         group->addChild(side, this);
1901       }
1902     }
1903   }
1904 }
1905 
1906 /*!
1907   Updates the layout element and sub-elements. This function is automatically called before every
1908   replot by the parent layout element. It is called multiple times, once for every \ref
1909   UpdatePhase. The phases are run through in the order of the enum values. For details about what
1910   happens at the different phases, see the documentation of \ref UpdatePhase.
1911 
1912   Layout elements that have child elements should call the \ref update method of their child
1913   elements, and pass the current \a phase unchanged.
1914 
1915   The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
1916   Subclasses should make sure to call the base class implementation.
1917 */
update(UpdatePhase phase)1918 void QCPLayoutElement::update(UpdatePhase phase)
1919 {
1920   if (phase == upMargins)
1921   {
1922     if (mAutoMargins != QCP::msNone)
1923     {
1924       // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
1925       QMargins newMargins = mMargins;
1926       QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
1927       for (auto side : allMarginSides)
1928       {
1929         if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
1930         {
1931           if (mMarginGroups.contains(side))
1932             QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
1933           else
1934             QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
1935           // apply minimum margin restrictions:
1936           if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
1937             QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
1938         }
1939       }
1940       setMargins(newMargins);
1941     }
1942   }
1943 }
1944 
1945 /*!
1946   Returns the minimum size this layout element (the inner \ref rect) may be compressed to.
1947 
1948   if a minimum size (\ref setMinimumSize) was not set manually, parent layouts consult this
1949   function to determine the minimum allowed size of this layout element. (A manual minimum size is
1950   considered set if it is non-zero.)
1951 */
minimumSizeHint() const1952 QSize QCPLayoutElement::minimumSizeHint() const
1953 {
1954   return mMinimumSize;
1955 }
1956 
1957 /*!
1958   Returns the maximum size this layout element (the inner \ref rect) may be expanded to.
1959 
1960   if a maximum size (\ref setMaximumSize) was not set manually, parent layouts consult this
1961   function to determine the maximum allowed size of this layout element. (A manual maximum size is
1962   considered set if it is smaller than Qt's QWIDGETSIZE_MAX.)
1963 */
maximumSizeHint() const1964 QSize QCPLayoutElement::maximumSizeHint() const
1965 {
1966   return mMaximumSize;
1967 }
1968 
1969 /*!
1970   Returns a list of all child elements in this layout element. If \a recursive is true, all
1971   sub-child elements are included in the list, too.
1972 
1973   \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
1974   empty cells which yield 0 at the respective index.)
1975 */
elements(bool recursive) const1976 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
1977 {
1978   Q_UNUSED(recursive)
1979   return QList<QCPLayoutElement*>();
1980 }
1981 
1982 /*!
1983   Layout elements are sensitive to events inside their outer rect. If \a pos is within the outer
1984   rect, this method returns a value corresponding to 0.99 times the parent plot's selection
1985   tolerance. However, layout elements are not selectable by default. So if \a onlySelectable is
1986   true, -1.0 is returned.
1987 
1988   See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
1989 
1990   QCPLayoutElement subclasses may reimplement this method to provide more specific selection test
1991   behaviour.
1992 */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const1993 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1994 {
1995   Q_UNUSED(details)
1996 
1997   if (onlySelectable)
1998     return -1;
1999 
2000   if (QRectF(mOuterRect).contains(pos))
2001   {
2002     if (mParentPlot)
2003       return mParentPlot->selectionTolerance()*0.99;
2004     else
2005     {
2006       qDebug() << Q_FUNC_INFO << "parent plot not defined";
2007       return -1;
2008     }
2009   } else
2010     return -1;
2011 }
2012 
2013 /*! \internal
2014 
2015   propagates the parent plot initialization to all child elements, by calling \ref
2016   QCPLayerable::initializeParentPlot on them.
2017 */
parentPlotInitialized(QCustomPlot * parentPlot)2018 void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
2019 {
2020   for (auto* el : elements(false))
2021   {
2022     if (!el->parentPlot())
2023       el->initializeParentPlot(parentPlot);
2024   }
2025 }
2026 
2027 /*! \internal
2028 
2029   Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a
2030   side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the
2031   returned value will not be smaller than the specified minimum margin.
2032 
2033   The default implementation just returns the respective manual margin (\ref setMargins) or the
2034   minimum margin, whichever is larger.
2035 */
calculateAutoMargin(QCP::MarginSide side)2036 int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side)
2037 {
2038   return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side));
2039 }
2040 
2041 ////////////////////////////////////////////////////////////////////////////////////////////////////
2042 //////////////////// QCPLayout
2043 ////////////////////////////////////////////////////////////////////////////////////////////////////
2044 
2045 /*! \class QCPLayout
2046   \brief The abstract base class for layouts
2047 
2048   This is an abstract base class for layout elements whose main purpose is to define the position
2049   and size of other child layout elements. In most cases, layouts don't draw anything themselves
2050   (but there are exceptions to this, e.g. QCPLegend).
2051 
2052   QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts.
2053 
2054   QCPLayout introduces a common interface for accessing and manipulating the child elements. Those
2055   functions are most notably \ref elementCount, \ref elementAt, \ref takeAt, \ref take, \ref
2056   simplify, \ref removeAt, \ref remove and \ref clear. Individual subclasses may add more functions
2057   to this interface which are more specialized to the form of the layout. For example, \ref
2058   QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid
2059   more conveniently.
2060 
2061   Since this is an abstract base class, you can't instantiate it directly. Rather use one of its
2062   subclasses like QCPLayoutGrid or QCPLayoutInset.
2063 
2064   For a general introduction to the layout system, see the dedicated documentation page \ref
2065   thelayoutsystem "The Layout System".
2066 */
2067 
2068 /* start documentation of pure virtual functions */
2069 
2070 /*! \fn virtual int QCPLayout::elementCount() const = 0
2071 
2072   Returns the number of elements/cells in the layout.
2073 
2074   \see elements, elementAt
2075 */
2076 
2077 /*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
2078 
2079   Returns the element in the cell with the given \a index. If \a index is invalid, returns 0.
2080 
2081   Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g.
2082   QCPLayoutGrid), so this function may return 0 in those cases. You may use this function to check
2083   whether a cell is empty or not.
2084 
2085   \see elements, elementCount, takeAt
2086 */
2087 
2088 /*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0
2089 
2090   Removes the element with the given \a index from the layout and returns it.
2091 
2092   If the \a index is invalid or the cell with that index is empty, returns 0.
2093 
2094   Note that some layouts don't remove the respective cell right away but leave an empty cell after
2095   successful removal of the layout element. To collapse empty cells, use \ref simplify.
2096 
2097   \see elementAt, take
2098 */
2099 
2100 /*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0
2101 
2102   Removes the specified \a element from the layout and returns true on success.
2103 
2104   If the \a element isn't in this layout, returns false.
2105 
2106   Note that some layouts don't remove the respective cell right away but leave an empty cell after
2107   successful removal of the layout element. To collapse empty cells, use \ref simplify.
2108 
2109   \see takeAt
2110 */
2111 
2112 /* end documentation of pure virtual functions */
2113 
2114 /*!
2115   Creates an instance of QCPLayout and sets default values. Note that since QCPLayout
2116   is an abstract base class, it can't be instantiated directly.
2117 */
QCPLayout()2118 QCPLayout::QCPLayout()
2119 {
2120 }
2121 
2122 /*!
2123   First calls the QCPLayoutElement::update base class implementation to update the margins on this
2124   layout.
2125 
2126   Then calls \ref updateLayout which subclasses reimplement to reposition and resize their cells.
2127 
2128   Finally, \ref update is called on all child elements.
2129 */
update(UpdatePhase phase)2130 void QCPLayout::update(UpdatePhase phase)
2131 {
2132   QCPLayoutElement::update(phase);
2133 
2134   // set child element rects according to layout:
2135   if (phase == upLayout)
2136     updateLayout();
2137 
2138   // propagate update call to child elements:
2139   const int elCount = elementCount();
2140   for (int i=0; i<elCount; ++i)
2141   {
2142     if (QCPLayoutElement *el = elementAt(i))
2143       el->update(phase);
2144   }
2145 }
2146 
2147 /* inherits documentation from base class */
elements(bool recursive) const2148 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2149 {
2150   const int c = elementCount();
2151   QList<QCPLayoutElement*> result;
2152 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2153   result.reserve(c);
2154 #endif
2155   for (int i=0; i<c; ++i)
2156     result.append(elementAt(i));
2157   if (recursive)
2158   {
2159     for (int i=0; i<c; ++i)
2160     {
2161       if (result.at(i))
2162         result << result.at(i)->elements(recursive);
2163     }
2164   }
2165   return result;
2166 }
2167 
2168 /*!
2169   Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the
2170   default implementation does nothing.
2171 
2172   Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit
2173   simplification while QCPLayoutGrid does.
2174 */
simplify()2175 void QCPLayout::simplify()
2176 {
2177 }
2178 
2179 /*!
2180   Removes and deletes the element at the provided \a index. Returns true on success. If \a index is
2181   invalid or points to an empty cell, returns false.
2182 
2183   This function internally uses \ref takeAt to remove the element from the layout and then deletes
2184   the returned element. Note that some layouts don't remove the respective cell right away but leave an
2185   empty cell after successful removal of the layout element. To collapse empty cells, use \ref
2186   simplify.
2187 
2188   \see remove, takeAt
2189 */
removeAt(int index)2190 bool QCPLayout::removeAt(int index)
2191 {
2192   if (QCPLayoutElement *el = takeAt(index))
2193   {
2194     delete el;
2195     return true;
2196   } else
2197     return false;
2198 }
2199 
2200 /*!
2201   Removes and deletes the provided \a element. Returns true on success. If \a element is not in the
2202   layout, returns false.
2203 
2204   This function internally uses \ref takeAt to remove the element from the layout and then deletes
2205   the element. Note that some layouts don't remove the respective cell right away but leave an
2206   empty cell after successful removal of the layout element. To collapse empty cells, use \ref
2207   simplify.
2208 
2209   \see removeAt, take
2210 */
remove(QCPLayoutElement * element)2211 bool QCPLayout::remove(QCPLayoutElement *element)
2212 {
2213   if (take(element))
2214   {
2215     delete element;
2216     return true;
2217   } else
2218     return false;
2219 }
2220 
2221 /*!
2222   Removes and deletes all layout elements in this layout. Finally calls \ref simplify to make sure
2223   all empty cells are collapsed.
2224 
2225   \see remove, removeAt
2226 */
clear()2227 void QCPLayout::clear()
2228 {
2229   for (int i=elementCount()-1; i>=0; --i)
2230   {
2231     if (elementAt(i))
2232       removeAt(i);
2233   }
2234   simplify();
2235 }
2236 
2237 /*!
2238   Subclasses call this method to report changed (minimum/maximum) size constraints.
2239 
2240   If the parent of this layout is again a QCPLayout, forwards the call to the parent's \ref
2241   sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \ref QCustomPlot::plotLayout of
2242   QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout,
2243   it may update itself and resize cells accordingly.
2244 */
sizeConstraintsChanged() const2245 void QCPLayout::sizeConstraintsChanged() const
2246 {
2247   if (QWidget *w = qobject_cast<QWidget*>(parent()))
2248     w->updateGeometry();
2249   else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2250     l->sizeConstraintsChanged();
2251 }
2252 
2253 /*! \internal
2254 
2255   Subclasses reimplement this method to update the position and sizes of the child elements/cells
2256   via calling their \ref QCPLayoutElement::setOuterRect. The default implementation does nothing.
2257 
2258   The geometry used as a reference is the inner \ref rect of this layout. Child elements should stay
2259   within that rect.
2260 
2261   \ref getSectionSizes may help with the reimplementation of this function.
2262 
2263   \see update
2264 */
updateLayout()2265 void QCPLayout::updateLayout()
2266 {
2267 }
2268 
2269 
2270 /*! \internal
2271 
2272   Associates \a el with this layout. This is done by setting the \ref QCPLayoutElement::layout, the
2273   \ref QCPLayerable::parentLayerable and the QObject parent to this layout.
2274 
2275   Further, if \a el didn't previously have a parent plot, calls \ref
2276   QCPLayerable::initializeParentPlot on \a el to set the paret plot.
2277 
2278   This method is used by subclass specific methods that add elements to the layout. Note that this
2279   method only changes properties in \a el. The removal from the old layout and the insertion into
2280   the new layout must be done additionally.
2281 */
adoptElement(QCPLayoutElement * el)2282 void QCPLayout::adoptElement(QCPLayoutElement *el)
2283 {
2284   if (el)
2285   {
2286     el->mParentLayout = this;
2287     el->setParentLayerable(this);
2288     el->setParent(this);
2289     if (!el->parentPlot())
2290       el->initializeParentPlot(mParentPlot);
2291   } else
2292     qDebug() << Q_FUNC_INFO << "Null element passed";
2293 }
2294 
2295 /*! \internal
2296 
2297   Disassociates \a el from this layout. This is done by setting the \ref QCPLayoutElement::layout
2298   and the \ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent
2299   QCustomPlot.
2300 
2301   This method is used by subclass specific methods that remove elements from the layout (e.g. \ref
2302   take or \ref takeAt). Note that this method only changes properties in \a el. The removal from
2303   the old layout must be done additionally.
2304 */
releaseElement(QCPLayoutElement * el)2305 void QCPLayout::releaseElement(QCPLayoutElement *el)
2306 {
2307   if (el)
2308   {
2309     el->mParentLayout = 0;
2310     el->setParentLayerable(0);
2311     el->setParent(mParentPlot);
2312     // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2313   } else
2314     qDebug() << Q_FUNC_INFO << "Null element passed";
2315 }
2316 
2317 /*! \internal
2318 
2319   This is a helper function for the implementation of \ref updateLayout in subclasses.
2320 
2321   It calculates the sizes of one-dimensional sections with provided constraints on maximum section
2322   sizes, minimum section sizes, relative stretch factors and the final total size of all sections.
2323 
2324   The QVector entries refer to the sections. Thus all QVectors must have the same size.
2325 
2326   \a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size
2327   imposed, set all vector values to Qt's QWIDGETSIZE_MAX.
2328 
2329   \a minSizes gives the minimum allowed size of each section. If there shall be no minimum size
2330   imposed, set all vector values to zero. If the \a minSizes entries add up to a value greater than
2331   \a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words,
2332   not exceeding the allowed total size is taken to be more important than not going below minimum
2333   section sizes.)
2334 
2335   \a stretchFactors give the relative proportions of the sections to each other. If all sections
2336   shall be scaled equally, set all values equal. If the first section shall be double the size of
2337   each individual other section, set the first number of \a stretchFactors to double the value of
2338   the other individual values (e.g. {2, 1, 1, 1}).
2339 
2340   \a totalSize is the value that the final section sizes will add up to. Due to rounding, the
2341   actual sum may differ slightly. If you want the section sizes to sum up to exactly that value,
2342   you could distribute the remaining difference on the sections.
2343 
2344   The return value is a QVector containing the section sizes.
2345 */
getSectionSizes(QVector<int> maxSizes,QVector<int> minSizes,QVector<double> stretchFactors,int totalSize) const2346 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2347 {
2348   if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2349   {
2350     qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2351     return QVector<int>();
2352   }
2353   if (stretchFactors.isEmpty())
2354     return QVector<int>();
2355   int sectionCount = stretchFactors.size();
2356   QVector<double> sectionSizes(sectionCount);
2357   // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2358   int minSizeSum = 0;
2359   for (int i=0; i<sectionCount; ++i)
2360     minSizeSum += minSizes.at(i);
2361   if (totalSize < minSizeSum)
2362   {
2363     // new stretch factors are minimum sizes and minimum sizes are set to zero:
2364     for (int i=0; i<sectionCount; ++i)
2365     {
2366       stretchFactors[i] = minSizes.at(i);
2367       minSizes[i] = 0;
2368     }
2369   }
2370 
2371   QList<int> minimumLockedSections;
2372   QList<int> unfinishedSections;
2373   for (int i=0; i<sectionCount; ++i)
2374     unfinishedSections.append(i);
2375   double freeSize = totalSize;
2376 
2377   int outerIterations = 0;
2378   while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2379   {
2380     ++outerIterations;
2381     int innerIterations = 0;
2382     while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2383     {
2384       ++innerIterations;
2385       // find section that hits its maximum next:
2386       int nextId = -1;
2387       double nextMax = 1e12;
2388       for (int i=0; i<unfinishedSections.size(); ++i)
2389       {
2390         int secId = unfinishedSections.at(i);
2391         double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2392         if (hitsMaxAt < nextMax)
2393         {
2394           nextMax = hitsMaxAt;
2395           nextId = secId;
2396         }
2397       }
2398       // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section
2399       // actually hits its maximum, without exceeding the total size when we add up all sections)
2400       double stretchFactorSum = 0;
2401       for (int i=0; i<unfinishedSections.size(); ++i)
2402         stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2403       double nextMaxLimit = freeSize/stretchFactorSum;
2404       if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2405       {
2406         for (int i=0; i<unfinishedSections.size(); ++i)
2407         {
2408           sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2409           freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2410         }
2411         unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2412       } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2413       {
2414         for (int i=0; i<unfinishedSections.size(); ++i)
2415           sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2416         unfinishedSections.clear();
2417       }
2418     }
2419     if (innerIterations == sectionCount*2)
2420       qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2421 
2422     // now check whether the resulting section sizes violate minimum restrictions:
2423     bool foundMinimumViolation = false;
2424     for (int i=0; i<sectionSizes.size(); ++i)
2425     {
2426       if (minimumLockedSections.contains(i))
2427         continue;
2428       if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2429       {
2430         sectionSizes[i] = minSizes.at(i); // set it to minimum
2431         foundMinimumViolation = true; // make sure we repeat the whole optimization process
2432         minimumLockedSections.append(i);
2433       }
2434     }
2435     if (foundMinimumViolation)
2436     {
2437       freeSize = totalSize;
2438       for (int i=0; i<sectionCount; ++i)
2439       {
2440         if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2441           unfinishedSections.append(i);
2442         else
2443           freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2444       }
2445       // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2446       for (int i=0; i<unfinishedSections.size(); ++i)
2447         sectionSizes[unfinishedSections.at(i)] = 0;
2448     }
2449   }
2450   if (outerIterations == sectionCount*2)
2451     qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2452 
2453   QVector<int> result(sectionCount);
2454   for (int i=0; i<sectionCount; ++i)
2455     result[i] = qRound(sectionSizes.at(i));
2456   return result;
2457 }
2458 
2459 
2460 ////////////////////////////////////////////////////////////////////////////////////////////////////
2461 //////////////////// QCPLayoutGrid
2462 ////////////////////////////////////////////////////////////////////////////////////////////////////
2463 
2464 /*! \class QCPLayoutGrid
2465   \brief A layout that arranges child elements in a grid
2466 
2467   Elements are laid out in a grid with configurable stretch factors (\ref setColumnStretchFactor,
2468   \ref setRowStretchFactor) and spacing (\ref setColumnSpacing, \ref setRowSpacing).
2469 
2470   Elements can be added to cells via \ref addElement. The grid is expanded if the specified row or
2471   column doesn't exist yet. Whether a cell contains a valid layout element can be checked with \ref
2472   hasElement, that element can be retrieved with \ref element. If rows and columns that only have
2473   empty cells shall be removed, call \ref simplify. Removal of elements is either done by just
2474   adding the element to a different layout or by using the QCPLayout interface \ref take or \ref
2475   remove.
2476 
2477   Row and column insertion can be performed with \ref insertRow and \ref insertColumn.
2478 */
2479 
2480 /*!
2481   Creates an instance of QCPLayoutGrid and sets default values.
2482 */
QCPLayoutGrid()2483 QCPLayoutGrid::QCPLayoutGrid() :
2484   mColumnSpacing(5),
2485   mRowSpacing(5)
2486 {
2487 }
2488 
~QCPLayoutGrid()2489 QCPLayoutGrid::~QCPLayoutGrid()
2490 {
2491   // clear all child layout elements. This is important because only the specific layouts know how
2492   // to handle removing elements (clear calls virtual removeAt method to do that).
2493   clear();
2494 }
2495 
2496 /*!
2497   Returns the element in the cell in \a row and \a column.
2498 
2499   Returns 0 if either the row/column is invalid or if the cell is empty. In those cases, a qDebug
2500   message is printed. To check whether a cell exists and isn't empty, use \ref hasElement.
2501 
2502   \see addElement, hasElement
2503 */
element(int row,int column) const2504 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2505 {
2506   if (row >= 0 && row < mElements.size())
2507   {
2508     if (column >= 0 && column < mElements.first().size())
2509     {
2510       if (QCPLayoutElement *result = mElements.at(row).at(column))
2511         return result;
2512       else
2513         qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2514     } else
2515       qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2516   } else
2517     qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2518   return 0;
2519 }
2520 
2521 /*!
2522   Returns the number of rows in the layout.
2523 
2524   \see columnCount
2525 */
rowCount() const2526 int QCPLayoutGrid::rowCount() const
2527 {
2528   return mElements.size();
2529 }
2530 
2531 /*!
2532   Returns the number of columns in the layout.
2533 
2534   \see rowCount
2535 */
columnCount() const2536 int QCPLayoutGrid::columnCount() const
2537 {
2538   if (mElements.size() > 0)
2539     return mElements.first().size();
2540   else
2541     return 0;
2542 }
2543 
2544 /*!
2545   Adds the \a element to cell with \a row and \a column. If \a element is already in a layout, it
2546   is first removed from there. If \a row or \a column don't exist yet, the layout is expanded
2547   accordingly.
2548 
2549   Returns true if the element was added successfully, i.e. if the cell at \a row and \a column
2550   didn't already have an element.
2551 
2552   \see element, hasElement, take, remove
2553 */
addElement(int row,int column,QCPLayoutElement * element)2554 bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
2555 {
2556   if (element)
2557   {
2558     if (!hasElement(row, column))
2559     {
2560       if (element->layout()) // remove from old layout first
2561         element->layout()->take(element);
2562       expandTo(row+1, column+1);
2563       mElements[row][column] = element;
2564       adoptElement(element);
2565       return true;
2566     } else
2567       qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2568   } else
2569     qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2570   return false;
2571 }
2572 
2573 /*!
2574   Returns whether the cell at \a row and \a column exists and contains a valid element, i.e. isn't
2575   empty.
2576 
2577   \see element
2578 */
hasElement(int row,int column)2579 bool QCPLayoutGrid::hasElement(int row, int column)
2580 {
2581   if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2582     return mElements.at(row).at(column);
2583   else
2584     return false;
2585 }
2586 
2587 /*!
2588   Sets the stretch \a factor of \a column.
2589 
2590   Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2591   their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2592   QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2593 
2594   The default stretch factor of newly created rows/columns is 1.
2595 
2596   \see setColumnStretchFactors, setRowStretchFactor
2597 */
setColumnStretchFactor(int column,double factor)2598 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2599 {
2600   if (column >= 0 && column < columnCount())
2601   {
2602     if (factor > 0)
2603       mColumnStretchFactors[column] = factor;
2604     else
2605       qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2606   } else
2607     qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2608 }
2609 
2610 /*!
2611   Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount.
2612 
2613   Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2614   their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2615   QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2616 
2617   The default stretch factor of newly created rows/columns is 1.
2618 
2619   \see setColumnStretchFactor, setRowStretchFactors
2620 */
setColumnStretchFactors(const QList<double> & factors)2621 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2622 {
2623   if (factors.size() == mColumnStretchFactors.size())
2624   {
2625     mColumnStretchFactors = factors;
2626     for (int i=0; i<mColumnStretchFactors.size(); ++i)
2627     {
2628       if (mColumnStretchFactors.at(i) <= 0)
2629       {
2630         qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2631         mColumnStretchFactors[i] = 1;
2632       }
2633     }
2634   } else
2635     qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2636 }
2637 
2638 /*!
2639   Sets the stretch \a factor of \a row.
2640 
2641   Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2642   their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2643   QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2644 
2645   The default stretch factor of newly created rows/columns is 1.
2646 
2647   \see setColumnStretchFactors, setRowStretchFactor
2648 */
setRowStretchFactor(int row,double factor)2649 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2650 {
2651   if (row >= 0 && row < rowCount())
2652   {
2653     if (factor > 0)
2654       mRowStretchFactors[row] = factor;
2655     else
2656       qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2657   } else
2658     qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2659 }
2660 
2661 /*!
2662   Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount.
2663 
2664   Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2665   their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2666   QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2667 
2668   The default stretch factor of newly created rows/columns is 1.
2669 
2670   \see setRowStretchFactor, setColumnStretchFactors
2671 */
setRowStretchFactors(const QList<double> & factors)2672 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2673 {
2674   if (factors.size() == mRowStretchFactors.size())
2675   {
2676     mRowStretchFactors = factors;
2677     for (int i=0; i<mRowStretchFactors.size(); ++i)
2678     {
2679       if (mRowStretchFactors.at(i) <= 0)
2680       {
2681         qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2682         mRowStretchFactors[i] = 1;
2683       }
2684     }
2685   } else
2686     qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2687 }
2688 
2689 /*!
2690   Sets the gap that is left blank between columns to \a pixels.
2691 
2692   \see setRowSpacing
2693 */
setColumnSpacing(int pixels)2694 void QCPLayoutGrid::setColumnSpacing(int pixels)
2695 {
2696   mColumnSpacing = pixels;
2697 }
2698 
2699 /*!
2700   Sets the gap that is left blank between rows to \a pixels.
2701 
2702   \see setColumnSpacing
2703 */
setRowSpacing(int pixels)2704 void QCPLayoutGrid::setRowSpacing(int pixels)
2705 {
2706   mRowSpacing = pixels;
2707 }
2708 
2709 /*!
2710   Expands the layout to have \a newRowCount rows and \a newColumnCount columns. So the last valid
2711   row index will be \a newRowCount-1, the last valid column index will be \a newColumnCount-1.
2712 
2713   If the current column/row count is already larger or equal to \a newColumnCount/\a newRowCount,
2714   this function does nothing in that dimension.
2715 
2716   Newly created cells are empty, new rows and columns have the stretch factor 1.
2717 
2718   Note that upon a call to \ref addElement, the layout is expanded automatically to contain the
2719   specified row and column, using this function.
2720 
2721   \see simplify
2722 */
expandTo(int newRowCount,int newColumnCount)2723 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2724 {
2725   // add rows as necessary:
2726   while (rowCount() < newRowCount)
2727   {
2728     mElements.append(QList<QCPLayoutElement*>());
2729     mRowStretchFactors.append(1);
2730   }
2731   // go through rows and expand columns as necessary:
2732   int newColCount = qMax(columnCount(), newColumnCount);
2733   for (int i=0; i<rowCount(); ++i)
2734   {
2735     while (mElements.at(i).size() < newColCount)
2736       mElements[i].append(0);
2737   }
2738   while (mColumnStretchFactors.size() < newColCount)
2739     mColumnStretchFactors.append(1);
2740 }
2741 
2742 /*!
2743   Inserts a new row with empty cells at the row index \a newIndex. Valid values for \a newIndex
2744   range from 0 (inserts a row at the top) to \a rowCount (appends a row at the bottom).
2745 
2746   \see insertColumn
2747 */
insertRow(int newIndex)2748 void QCPLayoutGrid::insertRow(int newIndex)
2749 {
2750   if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2751   {
2752     expandTo(1, 1);
2753     return;
2754   }
2755 
2756   if (newIndex < 0)
2757     newIndex = 0;
2758   if (newIndex > rowCount())
2759     newIndex = rowCount();
2760 
2761   mRowStretchFactors.insert(newIndex, 1);
2762   QList<QCPLayoutElement*> newRow;
2763   for (int col=0; col<columnCount(); ++col)
2764     newRow.append((QCPLayoutElement*)0);
2765   mElements.insert(newIndex, newRow);
2766 }
2767 
2768 /*!
2769   Inserts a new column with empty cells at the column index \a newIndex. Valid values for \a
2770   newIndex range from 0 (inserts a row at the left) to \a rowCount (appends a row at the right).
2771 
2772   \see insertRow
2773 */
insertColumn(int newIndex)2774 void QCPLayoutGrid::insertColumn(int newIndex)
2775 {
2776   if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2777   {
2778     expandTo(1, 1);
2779     return;
2780   }
2781 
2782   if (newIndex < 0)
2783     newIndex = 0;
2784   if (newIndex > columnCount())
2785     newIndex = columnCount();
2786 
2787   mColumnStretchFactors.insert(newIndex, 1);
2788   for (int row=0; row<rowCount(); ++row)
2789     mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2790 }
2791 
2792 /* inherits documentation from base class */
updateLayout()2793 void QCPLayoutGrid::updateLayout()
2794 {
2795   QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2796   getMinimumRowColSizes(&minColWidths, &minRowHeights);
2797   getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2798 
2799   int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2800   int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2801   QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2802   QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2803 
2804   // go through cells and set rects accordingly:
2805   int yOffset = mRect.top();
2806   for (int row=0; row<rowCount(); ++row)
2807   {
2808     if (row > 0)
2809       yOffset += rowHeights.at(row-1)+mRowSpacing;
2810     int xOffset = mRect.left();
2811     for (int col=0; col<columnCount(); ++col)
2812     {
2813       if (col > 0)
2814         xOffset += colWidths.at(col-1)+mColumnSpacing;
2815       if (mElements.at(row).at(col))
2816         mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2817     }
2818   }
2819 }
2820 
2821 /* inherits documentation from base class */
elementCount() const2822 int QCPLayoutGrid::elementCount() const
2823 {
2824   return rowCount()*columnCount();
2825 }
2826 
2827 /* inherits documentation from base class */
elementAt(int index) const2828 QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const
2829 {
2830   if (index >= 0 && index < elementCount())
2831     return mElements.at(index / columnCount()).at(index % columnCount());
2832   else
2833     return 0;
2834 }
2835 
2836 /* inherits documentation from base class */
takeAt(int index)2837 QCPLayoutElement *QCPLayoutGrid::takeAt(int index)
2838 {
2839   if (QCPLayoutElement *el = elementAt(index))
2840   {
2841     releaseElement(el);
2842     mElements[index / columnCount()][index % columnCount()] = 0;
2843     return el;
2844   } else
2845   {
2846     qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2847     return 0;
2848   }
2849 }
2850 
2851 /* inherits documentation from base class */
take(QCPLayoutElement * element)2852 bool QCPLayoutGrid::take(QCPLayoutElement *element)
2853 {
2854   if (element)
2855   {
2856     for (int i=0; i<elementCount(); ++i)
2857     {
2858       if (elementAt(i) == element)
2859       {
2860         takeAt(i);
2861         return true;
2862       }
2863     }
2864     qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2865   } else
2866     qDebug() << Q_FUNC_INFO << "Can't take null element";
2867   return false;
2868 }
2869 
2870 /* inherits documentation from base class */
elements(bool recursive) const2871 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
2872 {
2873   QList<QCPLayoutElement*> result;
2874   int colC = columnCount();
2875   int rowC = rowCount();
2876 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2877   result.reserve(colC*rowC);
2878 #endif
2879   for (int row=0; row<rowC; ++row)
2880   {
2881     for (int col=0; col<colC; ++col)
2882     {
2883       result.append(mElements.at(row).at(col));
2884     }
2885   }
2886   if (recursive)
2887   {
2888     int c = result.size();
2889     for (int i=0; i<c; ++i)
2890     {
2891       if (result.at(i))
2892         result << result.at(i)->elements(recursive);
2893     }
2894   }
2895   return result;
2896 }
2897 
2898 /*!
2899   Simplifies the layout by collapsing rows and columns which only contain empty cells.
2900 */
simplify()2901 void QCPLayoutGrid::simplify()
2902 {
2903   // remove rows with only empty cells:
2904   for (int row=rowCount()-1; row>=0; --row)
2905   {
2906     bool hasElements = false;
2907     for (int col=0; col<columnCount(); ++col)
2908     {
2909       if (mElements.at(row).at(col))
2910       {
2911         hasElements = true;
2912         break;
2913       }
2914     }
2915     if (!hasElements)
2916     {
2917       mRowStretchFactors.removeAt(row);
2918       mElements.removeAt(row);
2919       if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
2920         mColumnStretchFactors.clear();
2921     }
2922   }
2923 
2924   // remove columns with only empty cells:
2925   for (int col=columnCount()-1; col>=0; --col)
2926   {
2927     bool hasElements = false;
2928     for (int row=0; row<rowCount(); ++row)
2929     {
2930       if (mElements.at(row).at(col))
2931       {
2932         hasElements = true;
2933         break;
2934       }
2935     }
2936     if (!hasElements)
2937     {
2938       mColumnStretchFactors.removeAt(col);
2939       for (int row=0; row<rowCount(); ++row)
2940         mElements[row].removeAt(col);
2941     }
2942   }
2943 }
2944 
2945 /* inherits documentation from base class */
minimumSizeHint() const2946 QSize QCPLayoutGrid::minimumSizeHint() const
2947 {
2948   QVector<int> minColWidths, minRowHeights;
2949   getMinimumRowColSizes(&minColWidths, &minRowHeights);
2950   QSize result(0, 0);
2951   for (int i=0; i<minColWidths.size(); ++i)
2952     result.rwidth() += minColWidths.at(i);
2953   for (int i=0; i<minRowHeights.size(); ++i)
2954     result.rheight() += minRowHeights.at(i);
2955   result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2956   result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2957   return result;
2958 }
2959 
2960 /* inherits documentation from base class */
maximumSizeHint() const2961 QSize QCPLayoutGrid::maximumSizeHint() const
2962 {
2963   QVector<int> maxColWidths, maxRowHeights;
2964   getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2965 
2966   QSize result(0, 0);
2967   for (int i=0; i<maxColWidths.size(); ++i)
2968     result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
2969   for (int i=0; i<maxRowHeights.size(); ++i)
2970     result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
2971   result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2972   result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2973   return result;
2974 }
2975 
2976 /*! \internal
2977 
2978   Places the minimum column widths and row heights into \a minColWidths and \a minRowHeights
2979   respectively.
2980 
2981   The minimum height of a row is the largest minimum height of any element in that row. The minimum
2982   width of a column is the largest minimum width of any element in that column.
2983 
2984   This is a helper function for \ref updateLayout.
2985 
2986   \see getMaximumRowColSizes
2987 */
getMinimumRowColSizes(QVector<int> * minColWidths,QVector<int> * minRowHeights) const2988 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
2989 {
2990   *minColWidths = QVector<int>(columnCount(), 0);
2991   *minRowHeights = QVector<int>(rowCount(), 0);
2992   for (int row=0; row<rowCount(); ++row)
2993   {
2994     for (int col=0; col<columnCount(); ++col)
2995     {
2996       if (mElements.at(row).at(col))
2997       {
2998         QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
2999         QSize min = mElements.at(row).at(col)->minimumSize();
3000         QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3001         if (minColWidths->at(col) < final.width())
3002           (*minColWidths)[col] = final.width();
3003         if (minRowHeights->at(row) < final.height())
3004           (*minRowHeights)[row] = final.height();
3005       }
3006     }
3007   }
3008 }
3009 
3010 /*! \internal
3011 
3012   Places the maximum column widths and row heights into \a maxColWidths and \a maxRowHeights
3013   respectively.
3014 
3015   The maximum height of a row is the smallest maximum height of any element in that row. The
3016   maximum width of a column is the smallest maximum width of any element in that column.
3017 
3018   This is a helper function for \ref updateLayout.
3019 
3020   \see getMinimumRowColSizes
3021 */
getMaximumRowColSizes(QVector<int> * maxColWidths,QVector<int> * maxRowHeights) const3022 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3023 {
3024   *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3025   *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3026   for (int row=0; row<rowCount(); ++row)
3027   {
3028     for (int col=0; col<columnCount(); ++col)
3029     {
3030       if (mElements.at(row).at(col))
3031       {
3032         QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3033         QSize max = mElements.at(row).at(col)->maximumSize();
3034         QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3035         if (maxColWidths->at(col) > final.width())
3036           (*maxColWidths)[col] = final.width();
3037         if (maxRowHeights->at(row) > final.height())
3038           (*maxRowHeights)[row] = final.height();
3039       }
3040     }
3041   }
3042 }
3043 
3044 
3045 ////////////////////////////////////////////////////////////////////////////////////////////////////
3046 //////////////////// QCPLayoutInset
3047 ////////////////////////////////////////////////////////////////////////////////////////////////////
3048 /*! \class QCPLayoutInset
3049   \brief A layout that places child elements aligned to the border or arbitrarily positioned
3050 
3051   Elements are placed either aligned to the border or at arbitrary position in the area of the
3052   layout. Which placement applies is controlled with the \ref InsetPlacement (\ref
3053   setInsetPlacement).
3054 
3055   Elements are added via \ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or
3056   addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset
3057   placement will default to \ref ipBorderAligned and the element will be aligned according to the
3058   \a alignment parameter. The second method defaults to \ref ipFree and allows placing elements at
3059   arbitrary position and size, defined by \a rect.
3060 
3061   The alignment or rect can be set via \ref setInsetAlignment or \ref setInsetRect, respectively.
3062 
3063   This is the layout that every QCPAxisRect has as \ref QCPAxisRect::insetLayout.
3064 */
3065 
3066 /* start documentation of inline functions */
3067 
3068 /*! \fn virtual void QCPLayoutInset::simplify()
3069 
3070   The QCPInsetLayout does not need simplification since it can never have empty cells due to its
3071   linear index structure. This method does nothing.
3072 */
3073 
3074 /* end documentation of inline functions */
3075 
3076 /*!
3077   Creates an instance of QCPLayoutInset and sets default values.
3078 */
QCPLayoutInset()3079 QCPLayoutInset::QCPLayoutInset()
3080 {
3081 }
3082 
~QCPLayoutInset()3083 QCPLayoutInset::~QCPLayoutInset()
3084 {
3085   // clear all child layout elements. This is important because only the specific layouts know how
3086   // to handle removing elements (clear calls virtual removeAt method to do that).
3087   clear();
3088 }
3089 
3090 /*!
3091   Returns the placement type of the element with the specified \a index.
3092 */
insetPlacement(int index) const3093 QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const
3094 {
3095   if (elementAt(index))
3096     return mInsetPlacement.at(index);
3097   else
3098   {
3099     qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3100     return ipFree;
3101   }
3102 }
3103 
3104 /*!
3105   Returns the alignment of the element with the specified \a index. The alignment only has a
3106   meaning, if the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned.
3107 */
insetAlignment(int index) const3108 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3109 {
3110   if (elementAt(index))
3111     return mInsetAlignment.at(index);
3112   else
3113   {
3114     qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3115     return 0;
3116   }
3117 }
3118 
3119 /*!
3120   Returns the rect of the element with the specified \a index. The rect only has a
3121   meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree.
3122 */
insetRect(int index) const3123 QRectF QCPLayoutInset::insetRect(int index) const
3124 {
3125   if (elementAt(index))
3126     return mInsetRect.at(index);
3127   else
3128   {
3129     qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3130     return QRectF();
3131   }
3132 }
3133 
3134 /*!
3135   Sets the inset placement type of the element with the specified \a index to \a placement.
3136 
3137   \see InsetPlacement
3138 */
setInsetPlacement(int index,QCPLayoutInset::InsetPlacement placement)3139 void QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement)
3140 {
3141   if (elementAt(index))
3142     mInsetPlacement[index] = placement;
3143   else
3144     qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3145 }
3146 
3147 /*!
3148   If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this function
3149   is used to set the alignment of the element with the specified \a index to \a alignment.
3150 
3151   \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
3152   Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
3153   alignment flags will be ignored.
3154 */
setInsetAlignment(int index,Qt::Alignment alignment)3155 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3156 {
3157   if (elementAt(index))
3158     mInsetAlignment[index] = alignment;
3159   else
3160     qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3161 }
3162 
3163 /*!
3164   If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function is used to set the
3165   position and size of the element with the specified \a index to \a rect.
3166 
3167   \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
3168   will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
3169   corner of the layout, with 35% width and height of the parent layout.
3170 
3171   Note that the minimum and maximum sizes of the embedded element (\ref
3172   QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are enforced.
3173 */
setInsetRect(int index,const QRectF & rect)3174 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3175 {
3176   if (elementAt(index))
3177     mInsetRect[index] = rect;
3178   else
3179     qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3180 }
3181 
3182 /* inherits documentation from base class */
updateLayout()3183 void QCPLayoutInset::updateLayout()
3184 {
3185   for (int i=0; i<mElements.size(); ++i)
3186   {
3187     QRect insetRect;
3188     QSize finalMinSize, finalMaxSize;
3189     QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3190     QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3191     finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3192     finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3193     finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3194     finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3195     if (mInsetPlacement.at(i) == ipFree)
3196     {
3197       insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3198                         rect().y()+rect().height()*mInsetRect.at(i).y(),
3199                         rect().width()*mInsetRect.at(i).width(),
3200                         rect().height()*mInsetRect.at(i).height());
3201       if (insetRect.size().width() < finalMinSize.width())
3202         insetRect.setWidth(finalMinSize.width());
3203       if (insetRect.size().height() < finalMinSize.height())
3204         insetRect.setHeight(finalMinSize.height());
3205       if (insetRect.size().width() > finalMaxSize.width())
3206         insetRect.setWidth(finalMaxSize.width());
3207       if (insetRect.size().height() > finalMaxSize.height())
3208         insetRect.setHeight(finalMaxSize.height());
3209     } else if (mInsetPlacement.at(i) == ipBorderAligned)
3210     {
3211       insetRect.setSize(finalMinSize);
3212       Qt::Alignment al = mInsetAlignment.at(i);
3213       if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3214       else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3215       else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3216       if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3217       else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3218       else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3219     }
3220     mElements.at(i)->setOuterRect(insetRect);
3221   }
3222 }
3223 
3224 /* inherits documentation from base class */
elementCount() const3225 int QCPLayoutInset::elementCount() const
3226 {
3227   return mElements.size();
3228 }
3229 
3230 /* inherits documentation from base class */
elementAt(int index) const3231 QCPLayoutElement *QCPLayoutInset::elementAt(int index) const
3232 {
3233   if (index >= 0 && index < mElements.size())
3234     return mElements.at(index);
3235   else
3236     return 0;
3237 }
3238 
3239 /* inherits documentation from base class */
takeAt(int index)3240 QCPLayoutElement *QCPLayoutInset::takeAt(int index)
3241 {
3242   if (QCPLayoutElement *el = elementAt(index))
3243   {
3244     releaseElement(el);
3245     mElements.removeAt(index);
3246     mInsetPlacement.removeAt(index);
3247     mInsetAlignment.removeAt(index);
3248     mInsetRect.removeAt(index);
3249     return el;
3250   } else
3251   {
3252     qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3253     return 0;
3254   }
3255 }
3256 
3257 /* inherits documentation from base class */
take(QCPLayoutElement * element)3258 bool QCPLayoutInset::take(QCPLayoutElement *element)
3259 {
3260   if (element)
3261   {
3262     for (int i=0; i<elementCount(); ++i)
3263     {
3264       if (elementAt(i) == element)
3265       {
3266         takeAt(i);
3267         return true;
3268       }
3269     }
3270     qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3271   } else
3272     qDebug() << Q_FUNC_INFO << "Can't take null element";
3273   return false;
3274 }
3275 
3276 /*!
3277   The inset layout is sensitive to events only at areas where its (visible) child elements are
3278   sensitive. If the selectTest method of any of the child elements returns a positive number for \a
3279   pos, this method returns a value corresponding to 0.99 times the parent plot's selection
3280   tolerance. The inset layout is not selectable itself by default. So if \a onlySelectable is true,
3281   -1.0 is returned.
3282 
3283   See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
3284 */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const3285 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3286 {
3287   Q_UNUSED(details)
3288   if (onlySelectable)
3289     return -1;
3290 
3291   for (int i=0; i<mElements.size(); ++i)
3292   {
3293     // inset layout shall only return positive selectTest, if actually an inset object is at pos
3294     // else it would block the entire underlying QCPAxisRect with its surface.
3295     if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3296       return mParentPlot->selectionTolerance()*0.99;
3297   }
3298   return -1;
3299 }
3300 
3301 /*!
3302   Adds the specified \a element to the layout as an inset aligned at the border (\ref
3303   setInsetAlignment is initialized with \ref ipBorderAligned). The alignment is set to \a
3304   alignment.
3305 
3306   \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
3307   Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
3308   alignment flags will be ignored.
3309 
3310   \see addElement(QCPLayoutElement *element, const QRectF &rect)
3311 */
addElement(QCPLayoutElement * element,Qt::Alignment alignment)3312 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3313 {
3314   if (element)
3315   {
3316     if (element->layout()) // remove from old layout first
3317       element->layout()->take(element);
3318     mElements.append(element);
3319     mInsetPlacement.append(ipBorderAligned);
3320     mInsetAlignment.append(alignment);
3321     mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3322     adoptElement(element);
3323   } else
3324     qDebug() << Q_FUNC_INFO << "Can't add null element";
3325 }
3326 
3327 /*!
3328   Adds the specified \a element to the layout as an inset with free positioning/sizing (\ref
3329   setInsetAlignment is initialized with \ref ipFree). The position and size is set to \a
3330   rect.
3331 
3332   \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
3333   will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
3334   corner of the layout, with 35% width and height of the parent layout.
3335 
3336   \see addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3337 */
addElement(QCPLayoutElement * element,const QRectF & rect)3338 void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
3339 {
3340   if (element)
3341   {
3342     if (element->layout()) // remove from old layout first
3343       element->layout()->take(element);
3344     mElements.append(element);
3345     mInsetPlacement.append(ipFree);
3346     mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3347     mInsetRect.append(rect);
3348     adoptElement(element);
3349   } else
3350     qDebug() << Q_FUNC_INFO << "Can't add null element";
3351 }
3352 
3353 
3354 ////////////////////////////////////////////////////////////////////////////////////////////////////
3355 //////////////////// QCPLineEnding
3356 ////////////////////////////////////////////////////////////////////////////////////////////////////
3357 
3358 /*! \class QCPLineEnding
3359   \brief Handles the different ending decorations for line-like items
3360 
3361   \image html QCPLineEnding.png "The various ending styles currently supported"
3362 
3363   For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine
3364   has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.
3365 
3366   The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can
3367   be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of
3368   the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.
3369   For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite
3370   directions, e.g. "outward". This can be changed by \ref setInverted, which would make the
3371   respective arrow point inward.
3372 
3373   Note that due to the overloaded QCPLineEnding constructor, you may directly specify a
3374   QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g.
3375   \snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead
3376 */
3377 
3378 /*!
3379   Creates a QCPLineEnding instance with default values (style \ref esNone).
3380 */
QCPLineEnding()3381 QCPLineEnding::QCPLineEnding() :
3382   mStyle(esNone),
3383   mWidth(8),
3384   mLength(10),
3385   mInverted(false)
3386 {
3387 }
3388 
3389 /*!
3390   Creates a QCPLineEnding instance with the specified values.
3391 */
QCPLineEnding(QCPLineEnding::EndingStyle style,double width,double length,bool inverted)3392 QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
3393   mStyle(style),
3394   mWidth(width),
3395   mLength(length),
3396   mInverted(inverted)
3397 {
3398 }
3399 
3400 /*!
3401   Sets the style of the ending decoration.
3402 */
setStyle(QCPLineEnding::EndingStyle style)3403 void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)
3404 {
3405   mStyle = style;
3406 }
3407 
3408 /*!
3409   Sets the width of the ending decoration, if the style supports it. On arrows, for example, the
3410   width defines the size perpendicular to the arrow's pointing direction.
3411 
3412   \see setLength
3413 */
setWidth(double width)3414 void QCPLineEnding::setWidth(double width)
3415 {
3416   mWidth = width;
3417 }
3418 
3419 /*!
3420   Sets the length of the ending decoration, if the style supports it. On arrows, for example, the
3421   length defines the size in pointing direction.
3422 
3423   \see setWidth
3424 */
setLength(double length)3425 void QCPLineEnding::setLength(double length)
3426 {
3427   mLength = length;
3428 }
3429 
3430 /*!
3431   Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point
3432   inward when \a inverted is set to true.
3433 
3434   Note that also the \a width direction is inverted. For symmetrical ending styles like arrows or
3435   discs, this doesn't make a difference. However, asymmetric styles like \ref esHalfBar are
3436   affected by it, which can be used to control to which side the half bar points to.
3437 */
setInverted(bool inverted)3438 void QCPLineEnding::setInverted(bool inverted)
3439 {
3440   mInverted = inverted;
3441 }
3442 
3443 /*! \internal
3444 
3445   Returns the maximum pixel radius the ending decoration might cover, starting from the position
3446   the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item).
3447 
3448   This is relevant for clipping. Only omit painting of the decoration when the position where the
3449   decoration is supposed to be drawn is farther away from the clipping rect than the returned
3450   distance.
3451 */
boundingDistance() const3452 double QCPLineEnding::boundingDistance() const
3453 {
3454   switch (mStyle)
3455   {
3456     case esNone:
3457       return 0;
3458 
3459     case esFlatArrow:
3460     case esSpikeArrow:
3461     case esLineArrow:
3462     case esSkewedBar:
3463       return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3464 
3465     case esDisc:
3466     case esSquare:
3467     case esDiamond:
3468     case esBar:
3469     case esHalfBar:
3470       return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3471   }
3472   return 0;
3473 }
3474 
3475 /*!
3476   Starting from the origin of this line ending (which is style specific), returns the length
3477   covered by the line ending symbol, in backward direction.
3478 
3479   For example, the \ref esSpikeArrow has a shorter real length than a \ref esFlatArrow, even if
3480   both have the same \ref setLength value, because the spike arrow has an inward curved back, which
3481   reduces the length along its center axis (the drawing origin for arrows is at the tip).
3482 
3483   This function is used for precise, style specific placement of line endings, for example in
3484   QCPAxes.
3485 */
realLength() const3486 double QCPLineEnding::realLength() const
3487 {
3488   switch (mStyle)
3489   {
3490     case esNone:
3491     case esLineArrow:
3492     case esSkewedBar:
3493     case esBar:
3494     case esHalfBar:
3495       return 0;
3496 
3497     case esFlatArrow:
3498       return mLength;
3499 
3500     case esDisc:
3501     case esSquare:
3502     case esDiamond:
3503       return mWidth*0.5;
3504 
3505     case esSpikeArrow:
3506       return mLength*0.8;
3507   }
3508   return 0;
3509 }
3510 
3511 /*! \internal
3512 
3513   Draws the line ending with the specified \a painter at the position \a pos. The direction of the
3514   line ending is controlled with \a dir.
3515 */
draw(QCPPainter * painter,const QVector2D & pos,const QVector2D & dir) const3516 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3517 {
3518   if (mStyle == esNone)
3519     return;
3520 
3521   QVector2D lengthVec(dir.normalized());
3522   if (lengthVec.isNull())
3523     lengthVec = QVector2D(1, 0);
3524   QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3525   lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3526   widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3527 
3528   QPen penBackup = painter->pen();
3529   QBrush brushBackup = painter->brush();
3530   QPen miterPen = penBackup;
3531   miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3532   QBrush brush(painter->pen().color(), Qt::SolidPattern);
3533   switch (mStyle)
3534   {
3535     case esNone: break;
3536     case esFlatArrow:
3537     {
3538       QPointF points[3] = {pos.toPointF(),
3539                            (pos-lengthVec+widthVec).toPointF(),
3540                            (pos-lengthVec-widthVec).toPointF()
3541                           };
3542       painter->setPen(miterPen);
3543       painter->setBrush(brush);
3544       painter->drawConvexPolygon(points, 3);
3545       painter->setBrush(brushBackup);
3546       painter->setPen(penBackup);
3547       break;
3548     }
3549     case esSpikeArrow:
3550     {
3551       QPointF points[4] = {pos.toPointF(),
3552                            (pos-lengthVec+widthVec).toPointF(),
3553                            (pos-lengthVec*0.8f).toPointF(),
3554                            (pos-lengthVec-widthVec).toPointF()
3555                           };
3556       painter->setPen(miterPen);
3557       painter->setBrush(brush);
3558       painter->drawConvexPolygon(points, 4);
3559       painter->setBrush(brushBackup);
3560       painter->setPen(penBackup);
3561       break;
3562     }
3563     case esLineArrow:
3564     {
3565       QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3566                            pos.toPointF(),
3567                            (pos-lengthVec-widthVec).toPointF()
3568                           };
3569       painter->setPen(miterPen);
3570       painter->drawPolyline(points, 3);
3571       painter->setPen(penBackup);
3572       break;
3573     }
3574     case esDisc:
3575     {
3576       painter->setBrush(brush);
3577       painter->drawEllipse(pos.toPointF(),  mWidth*0.5, mWidth*0.5);
3578       painter->setBrush(brushBackup);
3579       break;
3580     }
3581     case esSquare:
3582     {
3583       QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3584       QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3585                            (pos-widthVecPerp-widthVec).toPointF(),
3586                            (pos+widthVecPerp-widthVec).toPointF(),
3587                            (pos+widthVecPerp+widthVec).toPointF()
3588                           };
3589       painter->setPen(miterPen);
3590       painter->setBrush(brush);
3591       painter->drawConvexPolygon(points, 4);
3592       painter->setBrush(brushBackup);
3593       painter->setPen(penBackup);
3594       break;
3595     }
3596     case esDiamond:
3597     {
3598       QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3599       QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3600                            (pos-widthVec).toPointF(),
3601                            (pos+widthVecPerp).toPointF(),
3602                            (pos+widthVec).toPointF()
3603                           };
3604       painter->setPen(miterPen);
3605       painter->setBrush(brush);
3606       painter->drawConvexPolygon(points, 4);
3607       painter->setBrush(brushBackup);
3608       painter->setPen(penBackup);
3609       break;
3610     }
3611     case esBar:
3612     {
3613       painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3614       break;
3615     }
3616     case esHalfBar:
3617     {
3618       painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3619       break;
3620     }
3621     case esSkewedBar:
3622     {
3623       if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3624       {
3625         // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3626         painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3627                           (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3628       } else
3629       {
3630         // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3631         painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3632                           (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3633       }
3634       break;
3635     }
3636   }
3637 }
3638 
3639 /*! \internal
3640   \overload
3641 
3642   Draws the line ending. The direction is controlled with the \a angle parameter in radians.
3643 */
draw(QCPPainter * painter,const QVector2D & pos,double angle) const3644 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3645 {
3646   draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3647 }
3648 
3649 
3650 ////////////////////////////////////////////////////////////////////////////////////////////////////
3651 //////////////////// QCPGrid
3652 ////////////////////////////////////////////////////////////////////////////////////////////////////
3653 
3654 /*! \class QCPGrid
3655   \brief Responsible for drawing the grid of a QCPAxis.
3656 
3657   This class is tightly bound to QCPAxis. Every axis owns a grid instance and uses it to draw the
3658   grid lines, sub grid lines and zero-line. You can interact with the grid of an axis via \ref
3659   QCPAxis::grid. Normally, you don't need to create an instance of QCPGrid yourself.
3660 
3661   The axis and grid drawing was split into two classes to allow them to be placed on different
3662   layers (both QCPAxis and QCPGrid inherit from QCPLayerable). Thus it is possible to have the grid
3663   in the background and the axes in the foreground, and any plottables/items in between. This
3664   described situation is the default setup, see the QCPLayer documentation.
3665 */
3666 
3667 /*!
3668   Creates a QCPGrid instance and sets default values.
3669 
3670   You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid.
3671 */
QCPGrid(QCPAxis * parentAxis)3672 QCPGrid::QCPGrid(QCPAxis *parentAxis) :
3673   QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3674   mParentAxis(parentAxis)
3675 {
3676   // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3677   setParent(parentAxis);
3678   setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3679   setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3680   setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3681   setSubGridVisible(false);
3682   setAntialiased(false);
3683   setAntialiasedSubGrid(false);
3684   setAntialiasedZeroLine(false);
3685 }
3686 
3687 /*!
3688   Sets whether grid lines at sub tick marks are drawn.
3689 
3690   \see setSubGridPen
3691 */
setSubGridVisible(bool visible)3692 void QCPGrid::setSubGridVisible(bool visible)
3693 {
3694   mSubGridVisible = visible;
3695 }
3696 
3697 /*!
3698   Sets whether sub grid lines are drawn antialiased.
3699 */
setAntialiasedSubGrid(bool enabled)3700 void QCPGrid::setAntialiasedSubGrid(bool enabled)
3701 {
3702   mAntialiasedSubGrid = enabled;
3703 }
3704 
3705 /*!
3706   Sets whether zero lines are drawn antialiased.
3707 */
setAntialiasedZeroLine(bool enabled)3708 void QCPGrid::setAntialiasedZeroLine(bool enabled)
3709 {
3710   mAntialiasedZeroLine = enabled;
3711 }
3712 
3713 /*!
3714   Sets the pen with which (major) grid lines are drawn.
3715 */
setPen(const QPen & pen)3716 void QCPGrid::setPen(const QPen &pen)
3717 {
3718   mPen = pen;
3719 }
3720 
3721 /*!
3722   Sets the pen with which sub grid lines are drawn.
3723 */
setSubGridPen(const QPen & pen)3724 void QCPGrid::setSubGridPen(const QPen &pen)
3725 {
3726   mSubGridPen = pen;
3727 }
3728 
3729 /*!
3730   Sets the pen with which zero lines are drawn.
3731 
3732   Zero lines are lines at value coordinate 0 which may be drawn with a different pen than other grid
3733   lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen.
3734 */
setZeroLinePen(const QPen & pen)3735 void QCPGrid::setZeroLinePen(const QPen &pen)
3736 {
3737   mZeroLinePen = pen;
3738 }
3739 
3740 /*! \internal
3741 
3742   A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
3743   before drawing the major grid lines.
3744 
3745   This is the antialiasing state the painter passed to the \ref draw method is in by default.
3746 
3747   This function takes into account the local setting of the antialiasing flag as well as the
3748   overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
3749   QCustomPlot::setNotAntialiasedElements.
3750 
3751   \see setAntialiased
3752 */
applyDefaultAntialiasingHint(QCPPainter * painter) const3753 void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
3754 {
3755   applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
3756 }
3757 
3758 /*! \internal
3759 
3760   Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning
3761   over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen).
3762 */
draw(QCPPainter * painter)3763 void QCPGrid::draw(QCPPainter *painter)
3764 {
3765   if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3766 
3767   if (mSubGridVisible)
3768     drawSubGridLines(painter);
3769   drawGridLines(painter);
3770 }
3771 
3772 /*! \internal
3773 
3774   Draws the main grid lines and possibly a zero line with the specified painter.
3775 
3776   This is a helper function called by \ref draw.
3777 */
drawGridLines(QCPPainter * painter) const3778 void QCPGrid::drawGridLines(QCPPainter *painter) const
3779 {
3780   if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3781 
3782   int lowTick = mParentAxis->mLowestVisibleTick;
3783   int highTick = mParentAxis->mHighestVisibleTick;
3784   double t; // helper variable, result of coordinate-to-pixel transforms
3785   if (mParentAxis->orientation() == Qt::Horizontal)
3786   {
3787     // draw zeroline:
3788     int zeroLineIndex = -1;
3789     if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3790     {
3791       applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3792       painter->setPen(mZeroLinePen);
3793       double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3794       for (int i=lowTick; i <= highTick; ++i)
3795       {
3796         if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3797         {
3798           zeroLineIndex = i;
3799           t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3800           painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3801           break;
3802         }
3803       }
3804     }
3805     // draw grid lines:
3806     applyDefaultAntialiasingHint(painter);
3807     painter->setPen(mPen);
3808     for (int i=lowTick; i <= highTick; ++i)
3809     {
3810       if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3811       t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3812       painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3813     }
3814   } else
3815   {
3816     // draw zeroline:
3817     int zeroLineIndex = -1;
3818     if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3819     {
3820       applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3821       painter->setPen(mZeroLinePen);
3822       double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3823       for (int i=lowTick; i <= highTick; ++i)
3824       {
3825         if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3826         {
3827           zeroLineIndex = i;
3828           t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3829           painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3830           break;
3831         }
3832       }
3833     }
3834     // draw grid lines:
3835     applyDefaultAntialiasingHint(painter);
3836     painter->setPen(mPen);
3837     for (int i=lowTick; i <= highTick; ++i)
3838     {
3839       if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3840       t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3841       painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3842     }
3843   }
3844 }
3845 
3846 /*! \internal
3847 
3848   Draws the sub grid lines with the specified painter.
3849 
3850   This is a helper function called by \ref draw.
3851 */
drawSubGridLines(QCPPainter * painter) const3852 void QCPGrid::drawSubGridLines(QCPPainter *painter) const
3853 {
3854   if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3855 
3856   applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
3857   double t; // helper variable, result of coordinate-to-pixel transforms
3858   painter->setPen(mSubGridPen);
3859   if (mParentAxis->orientation() == Qt::Horizontal)
3860   {
3861     for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3862     {
3863       t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x
3864       painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3865     }
3866   } else
3867   {
3868     for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3869     {
3870       t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y
3871       painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3872     }
3873   }
3874 }
3875 
3876 
3877 ////////////////////////////////////////////////////////////////////////////////////////////////////
3878 //////////////////// QCPAxis
3879 ////////////////////////////////////////////////////////////////////////////////////////////////////
3880 
3881 /*! \class QCPAxis
3882   \brief Manages a single axis inside a QCustomPlot.
3883 
3884   Usually doesn't need to be instantiated externally. Access %QCustomPlot's default four axes via
3885   QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and
3886   QCustomPlot::yAxis2 (right).
3887 
3888   Axes are always part of an axis rect, see QCPAxisRect.
3889   \image html AxisNamesOverview.png
3890   <center>Naming convention of axis parts</center>
3891   \n
3892 
3893   \image html AxisRectSpacingOverview.png
3894   <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed gray line
3895   on the left represents the QCustomPlot widget border.</center>
3896 
3897 */
3898 
3899 /* start of documentation of inline functions */
3900 
3901 /*! \fn Qt::Orientation QCPAxis::orientation() const
3902 
3903   Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced
3904   from the axis type (left, top, right or bottom).
3905 
3906   \see orientation(AxisType type)
3907 */
3908 
3909 /*! \fn QCPGrid *QCPAxis::grid() const
3910 
3911   Returns the \ref QCPGrid instance belonging to this axis. Access it to set details about the way the
3912   grid is displayed.
3913 */
3914 
3915 /*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type)
3916 
3917   Returns the orientation of the specified axis type
3918 
3919   \see orientation()
3920 */
3921 
3922 /* end of documentation of inline functions */
3923 /* start of documentation of signals */
3924 
3925 /*! \fn void QCPAxis::ticksRequest()
3926 
3927   This signal is emitted when \ref setAutoTicks is false and the axis is about to generate tick
3928   labels for a replot.
3929 
3930   Modifying the tick positions can be done with \ref setTickVector. If you also want to control the
3931   tick labels, set \ref setAutoTickLabels to false and also provide the labels with \ref
3932   setTickVectorLabels.
3933 
3934   If you only want static ticks you probably don't need this signal, since you can just set the
3935   tick vector (and possibly tick label vector) once. However, if you want to provide ticks (and
3936   maybe labels) dynamically, e.g. depending on the current axis range, connect a slot to this
3937   signal and set the vector/vectors there.
3938 */
3939 
3940 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange)
3941 
3942   This signal is emitted when the range of this axis has changed. You can connect it to the \ref
3943   setRange slot of another axis to communicate the new range to the other axis, in order for it to
3944   be synchronized.
3945 
3946   You may also manipulate/correct the range with \ref setRange in a slot connected to this signal.
3947   This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper
3948   range shouldn't go beyond certain values. For example, the following slot would limit the x axis
3949   to only positive ranges:
3950   \code
3951   if (newRange.lower < 0)
3952     plot->xAxis->setRange(0, newRange.size());
3953   \endcode
3954 */
3955 
3956 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange)
3957   \overload
3958 
3959   Additionally to the new range, this signal also provides the previous range held by the axis as
3960   \a oldRange.
3961 */
3962 
3963 /*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);
3964 
3965   This signal is emitted when the scale type changes, by calls to \ref setScaleType
3966 */
3967 
3968 /*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
3969 
3970   This signal is emitted when the selection state of this axis has changed, either by user interaction
3971   or by a direct call to \ref setSelectedParts.
3972 */
3973 
3974 /*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);
3975 
3976   This signal is emitted when the selectability changes, by calls to \ref setSelectableParts
3977 */
3978 
3979 /* end of documentation of signals */
3980 
3981 /*!
3982   Constructs an Axis instance of Type \a type for the axis rect \a parent.
3983 
3984   Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create
3985   them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however,
3986   create them manually and then inject them also via \ref QCPAxisRect::addAxis.
3987 */
QCPAxis(QCPAxisRect * parent,AxisType type)3988 QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) :
3989   QCPLayerable(parent->parentPlot(), QString(), parent),
3990   // axis base:
3991   mAxisType(type),
3992   mAxisRect(parent),
3993   mPadding(5),
3994   mOrientation(orientation(type)),
3995   mSelectableParts(spAxis | spTickLabels | spAxisLabel),
3996   mSelectedParts(spNone),
3997   mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
3998   mSelectedBasePen(QPen(Qt::blue, 2)),
3999   // axis label:
4000   mLabel(),
4001   mLabelFont(mParentPlot->font()),
4002   mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4003   mLabelColor(Qt::black),
4004   mSelectedLabelColor(Qt::blue),
4005   // tick labels:
4006   mTickLabels(true),
4007   mAutoTickLabels(true),
4008   mTickLabelType(ltNumber),
4009   mTickLabelFont(mParentPlot->font()),
4010   mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4011   mTickLabelColor(Qt::black),
4012   mSelectedTickLabelColor(Qt::blue),
4013   mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
4014   mDateTimeSpec(Qt::LocalTime),
4015   mNumberPrecision(6),
4016   mNumberFormatChar('g'),
4017   mNumberBeautifulPowers(true),
4018   // ticks and subticks:
4019   mTicks(true),
4020   mTickStep(1),
4021   mSubTickCount(4),
4022   mAutoTickCount(6),
4023   mAutoTicks(true),
4024   mAutoTickStep(true),
4025   mAutoSubTicks(true),
4026   mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4027   mSelectedTickPen(QPen(Qt::blue, 2)),
4028   mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4029   mSelectedSubTickPen(QPen(Qt::blue, 2)),
4030   // scale and range:
4031   mRange(0, 5),
4032   mRangeReversed(false),
4033   mScaleType(stLinear),
4034   mScaleLogBase(10),
4035   mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4036   // internal members:
4037   mGrid(new QCPGrid(this)),
4038   mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4039   mLowestVisibleTick(0),
4040   mHighestVisibleTick(-1),
4041   mCachedMarginValid(false),
4042   mCachedMargin(0)
4043 {
4044   setParent(parent);
4045   mGrid->setVisible(false);
4046   setAntialiased(false);
4047   setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again
4048 
4049   if (type == atTop)
4050   {
4051     setTickLabelPadding(3);
4052     setLabelPadding(6);
4053   } else if (type == atRight)
4054   {
4055     setTickLabelPadding(7);
4056     setLabelPadding(12);
4057   } else if (type == atBottom)
4058   {
4059     setTickLabelPadding(3);
4060     setLabelPadding(3);
4061   } else if (type == atLeft)
4062   {
4063     setTickLabelPadding(5);
4064     setLabelPadding(10);
4065   }
4066 }
4067 
~QCPAxis()4068 QCPAxis::~QCPAxis()
4069 {
4070   delete mAxisPainter;
4071   delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
4072 }
4073 
4074 /* No documentation as it is a property getter */
tickLabelPadding() const4075 int QCPAxis::tickLabelPadding() const
4076 {
4077   return mAxisPainter->tickLabelPadding;
4078 }
4079 
4080 /* No documentation as it is a property getter */
tickLabelRotation() const4081 double QCPAxis::tickLabelRotation() const
4082 {
4083   return mAxisPainter->tickLabelRotation;
4084 }
4085 
4086 /* No documentation as it is a property getter */
tickLabelSide() const4087 QCPAxis::LabelSide QCPAxis::tickLabelSide() const
4088 {
4089   return mAxisPainter->tickLabelSide;
4090 }
4091 
4092 /* No documentation as it is a property getter */
numberFormat() const4093 QString QCPAxis::numberFormat() const
4094 {
4095   QString result;
4096   result.append(mNumberFormatChar);
4097   if (mNumberBeautifulPowers)
4098   {
4099     result.append(QLatin1Char('b'));
4100     if (mAxisPainter->numberMultiplyCross)
4101       result.append(QLatin1Char('c'));
4102   }
4103   return result;
4104 }
4105 
4106 /* No documentation as it is a property getter */
tickLengthIn() const4107 int QCPAxis::tickLengthIn() const
4108 {
4109   return mAxisPainter->tickLengthIn;
4110 }
4111 
4112 /* No documentation as it is a property getter */
tickLengthOut() const4113 int QCPAxis::tickLengthOut() const
4114 {
4115   return mAxisPainter->tickLengthOut;
4116 }
4117 
4118 /* No documentation as it is a property getter */
subTickLengthIn() const4119 int QCPAxis::subTickLengthIn() const
4120 {
4121   return mAxisPainter->subTickLengthIn;
4122 }
4123 
4124 /* No documentation as it is a property getter */
subTickLengthOut() const4125 int QCPAxis::subTickLengthOut() const
4126 {
4127   return mAxisPainter->subTickLengthOut;
4128 }
4129 
4130 /* No documentation as it is a property getter */
labelPadding() const4131 int QCPAxis::labelPadding() const
4132 {
4133   return mAxisPainter->labelPadding;
4134 }
4135 
4136 /* No documentation as it is a property getter */
offset() const4137 int QCPAxis::offset() const
4138 {
4139   return mAxisPainter->offset;
4140 }
4141 
4142 /* No documentation as it is a property getter */
lowerEnding() const4143 QCPLineEnding QCPAxis::lowerEnding() const
4144 {
4145   return mAxisPainter->lowerEnding;
4146 }
4147 
4148 /* No documentation as it is a property getter */
upperEnding() const4149 QCPLineEnding QCPAxis::upperEnding() const
4150 {
4151   return mAxisPainter->upperEnding;
4152 }
4153 
4154 /*!
4155   Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
4156   stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
4157   scaling, major tick marks appear at all powers of the logarithm base. Properties like tick step
4158   (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but less major
4159   ticks, consider choosing a logarithm base of 100, 1000 or even higher.
4160 
4161   If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat) uses the 'b' option
4162   (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4163   [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4164   part). To only display the decimal power, set the number precision to zero with
4165   \ref setNumberPrecision.
4166 */
setScaleType(QCPAxis::ScaleType type)4167 void QCPAxis::setScaleType(QCPAxis::ScaleType type)
4168 {
4169   if (mScaleType != type)
4170   {
4171     mScaleType = type;
4172     if (mScaleType == stLogarithmic)
4173       setRange(mRange.sanitizedForLogScale());
4174     mCachedMarginValid = false;
4175     emit scaleTypeChanged(mScaleType);
4176   }
4177 }
4178 
4179 /*!
4180   If \ref setScaleType is set to \ref stLogarithmic, \a base will be the logarithm base of the
4181   scaling. In logarithmic axis scaling, major tick marks appear at all powers of \a base.
4182 
4183   Properties like tick step (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but
4184   less major ticks, consider choosing \a base 100, 1000 or even higher.
4185 */
setScaleLogBase(double base)4186 void QCPAxis::setScaleLogBase(double base)
4187 {
4188   if (base > 1)
4189   {
4190     mScaleLogBase = base;
4191     mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4192     mCachedMarginValid = false;
4193   } else
4194     qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4195 }
4196 
4197 /*!
4198   Sets the range of the axis.
4199 
4200   This slot may be connected with the \ref rangeChanged signal of another axis so this axis
4201   is always synchronized with the other axis range, when it changes.
4202 
4203   To invert the direction of an axis, use \ref setRangeReversed.
4204 */
setRange(const QCPRange & range)4205 void QCPAxis::setRange(const QCPRange &range)
4206 {
4207   if (range.lower == mRange.lower && range.upper == mRange.upper)
4208     return;
4209 
4210   if (!QCPRange::validRange(range)) return;
4211   QCPRange oldRange = mRange;
4212   if (mScaleType == stLogarithmic)
4213   {
4214     mRange = range.sanitizedForLogScale();
4215   } else
4216   {
4217     mRange = range.sanitizedForLinScale();
4218   }
4219   mCachedMarginValid = false;
4220   emit rangeChanged(mRange);
4221   emit rangeChanged(mRange, oldRange);
4222 }
4223 
4224 /*!
4225   Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
4226   (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
4227 
4228   However, even when \a selectable is set to a value not allowing the selection of a specific part,
4229   it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
4230   directly.
4231 
4232   \see SelectablePart, setSelectedParts
4233 */
setSelectableParts(const SelectableParts & selectable)4234 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4235 {
4236   if (mSelectableParts != selectable)
4237   {
4238     mSelectableParts = selectable;
4239     emit selectableChanged(mSelectableParts);
4240   }
4241 }
4242 
4243 /*!
4244   Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
4245   is selected, it uses a different pen/font.
4246 
4247   The entire selection mechanism for axes is handled automatically when \ref
4248   QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
4249   wish to change the selection state manually.
4250 
4251   This function can change the selection state of a part, independent of the \ref setSelectableParts setting.
4252 
4253   emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
4254 
4255   \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
4256   setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
4257 */
setSelectedParts(const SelectableParts & selected)4258 void QCPAxis::setSelectedParts(const SelectableParts &selected)
4259 {
4260   if (mSelectedParts != selected)
4261   {
4262     mSelectedParts = selected;
4263     emit selectionChanged(mSelectedParts);
4264   }
4265 }
4266 
4267 /*!
4268   \overload
4269 
4270   Sets the lower and upper bound of the axis range.
4271 
4272   To invert the direction of an axis, use \ref setRangeReversed.
4273 
4274   There is also a slot to set a range, see \ref setRange(const QCPRange &range).
4275 */
setRange(double lower,double upper)4276 void QCPAxis::setRange(double lower, double upper)
4277 {
4278   if (lower == mRange.lower && upper == mRange.upper)
4279     return;
4280 
4281   if (!QCPRange::validRange(lower, upper)) return;
4282   QCPRange oldRange = mRange;
4283   mRange.lower = lower;
4284   mRange.upper = upper;
4285   if (mScaleType == stLogarithmic)
4286   {
4287     mRange = mRange.sanitizedForLogScale();
4288   } else
4289   {
4290     mRange = mRange.sanitizedForLinScale();
4291   }
4292   mCachedMarginValid = false;
4293   emit rangeChanged(mRange);
4294   emit rangeChanged(mRange, oldRange);
4295 }
4296 
4297 /*!
4298   \overload
4299 
4300   Sets the range of the axis.
4301 
4302   The \a position coordinate indicates together with the \a alignment parameter, where the new
4303   range will be positioned. \a size defines the size of the new axis range. \a alignment may be
4304   Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,
4305   or center of the range to be aligned with \a position. Any other values of \a alignment will
4306   default to Qt::AlignCenter.
4307 */
setRange(double position,double size,Qt::AlignmentFlag alignment)4308 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4309 {
4310   if (alignment == Qt::AlignLeft)
4311     setRange(position, position+size);
4312   else if (alignment == Qt::AlignRight)
4313     setRange(position-size, position);
4314   else // alignment == Qt::AlignCenter
4315     setRange(position-size/2.0, position+size/2.0);
4316 }
4317 
4318 /*!
4319   Sets the lower bound of the axis range. The upper bound is not changed.
4320   \see setRange
4321 */
setRangeLower(double lower)4322 void QCPAxis::setRangeLower(double lower)
4323 {
4324   if (mRange.lower == lower)
4325     return;
4326 
4327   QCPRange oldRange = mRange;
4328   mRange.lower = lower;
4329   if (mScaleType == stLogarithmic)
4330   {
4331     mRange = mRange.sanitizedForLogScale();
4332   } else
4333   {
4334     mRange = mRange.sanitizedForLinScale();
4335   }
4336   mCachedMarginValid = false;
4337   emit rangeChanged(mRange);
4338   emit rangeChanged(mRange, oldRange);
4339 }
4340 
4341 /*!
4342   Sets the upper bound of the axis range. The lower bound is not changed.
4343   \see setRange
4344 */
setRangeUpper(double upper)4345 void QCPAxis::setRangeUpper(double upper)
4346 {
4347   if (mRange.upper == upper)
4348     return;
4349 
4350   QCPRange oldRange = mRange;
4351   mRange.upper = upper;
4352   if (mScaleType == stLogarithmic)
4353   {
4354     mRange = mRange.sanitizedForLogScale();
4355   } else
4356   {
4357     mRange = mRange.sanitizedForLinScale();
4358   }
4359   mCachedMarginValid = false;
4360   emit rangeChanged(mRange);
4361   emit rangeChanged(mRange, oldRange);
4362 }
4363 
4364 /*!
4365   Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
4366   axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
4367   direction of increasing values is inverted.
4368 
4369   Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part
4370   of the \ref setRange interface will still reference the mathematically smaller number than the \a
4371   upper part.
4372 */
setRangeReversed(bool reversed)4373 void QCPAxis::setRangeReversed(bool reversed)
4374 {
4375   if (mRangeReversed != reversed)
4376   {
4377     mRangeReversed = reversed;
4378     mCachedMarginValid = false;
4379   }
4380 }
4381 
4382 /*!
4383   Sets whether the tick positions should be calculated automatically (either from an automatically
4384   generated tick step or a tick step provided manually via \ref setTickStep, see \ref setAutoTickStep).
4385 
4386   If \a on is set to false, you must provide the tick positions manually via \ref setTickVector.
4387   For these manual ticks you may let QCPAxis generate the appropriate labels automatically by
4388   leaving \ref setAutoTickLabels set to true. If you also wish to control the displayed labels
4389   manually, set \ref setAutoTickLabels to false and provide the label strings with \ref
4390   setTickVectorLabels.
4391 
4392   If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
4393   vectors in a slot connected to the \ref ticksRequest signal.
4394 
4395   \see setAutoTickLabels, setAutoSubTicks, setAutoTickCount, setAutoTickStep
4396 */
setAutoTicks(bool on)4397 void QCPAxis::setAutoTicks(bool on)
4398 {
4399   if (mAutoTicks != on)
4400   {
4401     mAutoTicks = on;
4402     mCachedMarginValid = false;
4403   }
4404 }
4405 
4406 /*!
4407   When \ref setAutoTickStep is true, \a approximateCount determines how many ticks should be
4408   generated in the visible range, approximately.
4409 
4410   It's not guaranteed that this number of ticks is met exactly, but approximately within a
4411   tolerance of about two.
4412 
4413   Only values greater than zero are accepted as \a approximateCount.
4414 
4415   \see setAutoTickStep, setAutoTicks, setAutoSubTicks
4416 */
setAutoTickCount(int approximateCount)4417 void QCPAxis::setAutoTickCount(int approximateCount)
4418 {
4419   if (mAutoTickCount != approximateCount)
4420   {
4421     if (approximateCount > 0)
4422     {
4423       mAutoTickCount = approximateCount;
4424       mCachedMarginValid = false;
4425     } else
4426       qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4427   }
4428 }
4429 
4430 /*!
4431   Sets whether the tick labels are generated automatically. Depending on the tick label type (\ref
4432   ltNumber or \ref ltDateTime), the labels will either show the coordinate as floating point
4433   number (\ref setNumberFormat), or a date/time formatted according to \ref setDateTimeFormat.
4434 
4435   If \a on is set to false, you should provide the tick labels via \ref setTickVectorLabels. This
4436   is usually used in a combination with \ref setAutoTicks set to false for complete control over
4437   tick positions and labels, e.g. when the ticks should be at multiples of pi and show "2pi", "3pi"
4438   etc. as tick labels.
4439 
4440   If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
4441   vectors in a slot connected to the \ref ticksRequest signal.
4442 
4443   \see setAutoTicks
4444 */
setAutoTickLabels(bool on)4445 void QCPAxis::setAutoTickLabels(bool on)
4446 {
4447   if (mAutoTickLabels != on)
4448   {
4449     mAutoTickLabels = on;
4450     mCachedMarginValid = false;
4451   }
4452 }
4453 
4454 /*!
4455   Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated
4456   automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human
4457   readable plots.
4458 
4459   The number of ticks the algorithm aims for within the visible range can be specified with \ref
4460   setAutoTickCount.
4461 
4462   If \a on is set to false, you may set the tick step manually with \ref setTickStep.
4463 
4464   \see setAutoTicks, setAutoSubTicks, setAutoTickCount
4465 */
setAutoTickStep(bool on)4466 void QCPAxis::setAutoTickStep(bool on)
4467 {
4468   if (mAutoTickStep != on)
4469   {
4470     mAutoTickStep = on;
4471     mCachedMarginValid = false;
4472   }
4473 }
4474 
4475 /*!
4476   Sets whether the number of sub ticks in one tick interval is determined automatically. This
4477   works, as long as the tick step mantissa is a multiple of 0.5. When \ref setAutoTickStep is
4478   enabled, this is always the case.
4479 
4480   When \a on is set to false, you may set the sub tick count with \ref setSubTickCount manually.
4481 
4482   \see setAutoTickCount, setAutoTicks, setAutoTickStep
4483 */
setAutoSubTicks(bool on)4484 void QCPAxis::setAutoSubTicks(bool on)
4485 {
4486   if (mAutoSubTicks != on)
4487   {
4488     mAutoSubTicks = on;
4489     mCachedMarginValid = false;
4490   }
4491 }
4492 
4493 /*!
4494   Sets whether tick marks are displayed.
4495 
4496   Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve
4497   that, see \ref setTickLabels.
4498 */
setTicks(bool show)4499 void QCPAxis::setTicks(bool show)
4500 {
4501   if (mTicks != show)
4502   {
4503     mTicks = show;
4504     mCachedMarginValid = false;
4505   }
4506 }
4507 
4508 /*!
4509   Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.
4510 */
setTickLabels(bool show)4511 void QCPAxis::setTickLabels(bool show)
4512 {
4513   if (mTickLabels != show)
4514   {
4515     mTickLabels = show;
4516     mCachedMarginValid = false;
4517   }
4518 }
4519 
4520 /*!
4521   Sets the distance between the axis base line (including any outward ticks) and the tick labels.
4522   \see setLabelPadding, setPadding
4523 */
setTickLabelPadding(int padding)4524 void QCPAxis::setTickLabelPadding(int padding)
4525 {
4526   if (mAxisPainter->tickLabelPadding != padding)
4527   {
4528     mAxisPainter->tickLabelPadding = padding;
4529     mCachedMarginValid = false;
4530   }
4531 }
4532 
4533 /*!
4534   Sets whether the tick labels display numbers or dates/times.
4535 
4536   If \a type is set to \ref ltNumber, the format specifications of \ref setNumberFormat apply.
4537 
4538   If \a type is set to \ref ltDateTime, the format specifications of \ref setDateTimeFormat apply.
4539 
4540   In QCustomPlot, date/time coordinates are <tt>double</tt> numbers representing the seconds since
4541   1970-01-01T00:00:00 UTC. This format can be retrieved from QDateTime objects with the
4542   QDateTime::toTime_t() function. Since this only gives a resolution of one second, there is also
4543   the QDateTime::toMSecsSinceEpoch() function which returns the timespan described above in
4544   milliseconds. Divide its return value by 1000.0 to get a value with the format needed for
4545   date/time plotting, with a resolution of one millisecond.
4546 
4547   Using the toMSecsSinceEpoch function allows dates that go back to 2nd January 4713 B.C.
4548   (represented by a negative number), unlike the toTime_t function, which works with unsigned
4549   integers and thus only goes back to 1st January 1970. So both for range and accuracy, use of
4550   toMSecsSinceEpoch()/1000.0 should be preferred as key coordinate for date/time axes.
4551 
4552   \see setTickLabels
4553 */
setTickLabelType(LabelType type)4554 void QCPAxis::setTickLabelType(LabelType type)
4555 {
4556   if (mTickLabelType != type)
4557   {
4558     mTickLabelType = type;
4559     mCachedMarginValid = false;
4560   }
4561 }
4562 
4563 /*!
4564   Sets the font of the tick labels.
4565 
4566   \see setTickLabels, setTickLabelColor
4567 */
setTickLabelFont(const QFont & font)4568 void QCPAxis::setTickLabelFont(const QFont &font)
4569 {
4570   if (font != mTickLabelFont)
4571   {
4572     mTickLabelFont = font;
4573     mCachedMarginValid = false;
4574   }
4575 }
4576 
4577 /*!
4578   Sets the color of the tick labels.
4579 
4580   \see setTickLabels, setTickLabelFont
4581 */
setTickLabelColor(const QColor & color)4582 void QCPAxis::setTickLabelColor(const QColor &color)
4583 {
4584   if (color != mTickLabelColor)
4585   {
4586     mTickLabelColor = color;
4587     mCachedMarginValid = false;
4588   }
4589 }
4590 
4591 /*!
4592   Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else,
4593   the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values
4594   from -90 to 90 degrees.
4595 
4596   If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For
4597   other angles, the label is drawn with an offset such that it seems to point toward or away from
4598   the tick mark.
4599 */
setTickLabelRotation(double degrees)4600 void QCPAxis::setTickLabelRotation(double degrees)
4601 {
4602   if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4603   {
4604     mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4605     mCachedMarginValid = false;
4606   }
4607 }
4608 
4609 /*!
4610   Sets whether the tick labels (numbers) shall appear inside or outside the axis rect.
4611 
4612   The usual and default setting is \ref lsOutside. Very compact plots sometimes require tick labels
4613   to be inside the axis rect, to save space. If \a side is set to \ref lsInside, the tick labels
4614   appear on the inside are additionally clipped to the axis rect.
4615 */
setTickLabelSide(LabelSide side)4616 void QCPAxis::setTickLabelSide(LabelSide side)
4617 {
4618   mAxisPainter->tickLabelSide = side;
4619   mCachedMarginValid = false;
4620 }
4621 
4622 /*!
4623   Sets the format in which dates and times are displayed as tick labels, if \ref setTickLabelType is \ref ltDateTime.
4624   for details about the \a format string, see the documentation of QDateTime::toString().
4625 
4626   Newlines can be inserted with "\n".
4627 
4628   \see setDateTimeSpec
4629 */
setDateTimeFormat(const QString & format)4630 void QCPAxis::setDateTimeFormat(const QString &format)
4631 {
4632   if (mDateTimeFormat != format)
4633   {
4634     mDateTimeFormat = format;
4635     mCachedMarginValid = false;
4636   }
4637 }
4638 
4639 /*!
4640   Sets the time spec that is used for the date time values when \ref setTickLabelType is \ref
4641   ltDateTime.
4642 
4643   The default value of QDateTime objects (and also QCustomPlot) is <tt>Qt::LocalTime</tt>. However,
4644   if the date time values passed to QCustomPlot are given in the UTC spec, set \a
4645   timeSpec to <tt>Qt::UTC</tt> to get the correct axis labels.
4646 
4647   \see setDateTimeFormat
4648 */
setDateTimeSpec(const Qt::TimeSpec & timeSpec)4649 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4650 {
4651   mDateTimeSpec = timeSpec;
4652 }
4653 
4654 /*!
4655   Sets the number format for the numbers drawn as tick labels (if tick label type is \ref
4656   ltNumber). This \a formatCode is an extended version of the format code used e.g. by
4657   QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats"
4658   section in the detailed description of the QString class. \a formatCode is a string of one, two
4659   or three characters. The first character is identical to the normal format code used by Qt. In
4660   short, this means: 'e'/'E' scientific format, 'f' fixed format, 'g'/'G' scientific or fixed,
4661   whichever is shorter.
4662 
4663   The second and third characters are optional and specific to QCustomPlot:\n
4664   If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
4665   "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
4666   "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
4667   [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
4668   If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
4669   be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
4670   cross and 183 (0xB7) for the dot.
4671 
4672   If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a formatCode uses the 'b'
4673   option (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4674   [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4675   part). To only display the decimal power, set the number precision to zero with \ref
4676   setNumberPrecision.
4677 
4678   Examples for \a formatCode:
4679   \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
4680   normal scientific format is used
4681   \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
4682   beautifully typeset decimal powers and a dot as multiplication sign
4683   \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
4684   multiplication sign
4685   \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
4686   powers. Format code will be reduced to 'f'.
4687   \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
4688   code will not be changed.
4689 */
setNumberFormat(const QString & formatCode)4690 void QCPAxis::setNumberFormat(const QString &formatCode)
4691 {
4692   if (formatCode.isEmpty())
4693   {
4694     qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4695     return;
4696   }
4697   mCachedMarginValid = false;
4698 
4699   // interpret first char as number format char:
4700   QString allowedFormatChars(QLatin1String("eEfgG"));
4701   if (allowedFormatChars.contains(formatCode.at(0)))
4702   {
4703     mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4704   } else
4705   {
4706     qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4707     return;
4708   }
4709   if (formatCode.length() < 2)
4710   {
4711     mNumberBeautifulPowers = false;
4712     mAxisPainter->numberMultiplyCross = false;
4713     return;
4714   }
4715 
4716   // interpret second char as indicator for beautiful decimal powers:
4717   if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
4718   {
4719     mNumberBeautifulPowers = true;
4720   } else
4721   {
4722     qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4723     return;
4724   }
4725   if (formatCode.length() < 3)
4726   {
4727     mAxisPainter->numberMultiplyCross = false;
4728     return;
4729   }
4730 
4731   // interpret third char as indicator for dot or cross multiplication symbol:
4732   if (formatCode.at(2) == QLatin1Char('c'))
4733   {
4734     mAxisPainter->numberMultiplyCross = true;
4735   } else if (formatCode.at(2) == QLatin1Char('d'))
4736   {
4737     mAxisPainter->numberMultiplyCross = false;
4738   } else
4739   {
4740     qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4741     return;
4742   }
4743 }
4744 
4745 /*!
4746   Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)
4747   for details. The effect of precisions are most notably for number Formats starting with 'e', see
4748   \ref setNumberFormat
4749 
4750   If the scale type (\ref setScaleType) is \ref stLogarithmic and the number format (\ref
4751   setNumberFormat) uses the 'b' format code (beautifully typeset decimal powers), the display
4752   usually is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for logarithmic
4753   scaling (the redundant "1 [multiplication sign]" part). To only display the decimal power "10
4754   [superscript] n", set \a precision to zero.
4755 */
setNumberPrecision(int precision)4756 void QCPAxis::setNumberPrecision(int precision)
4757 {
4758   if (mNumberPrecision != precision)
4759   {
4760     mNumberPrecision = precision;
4761     mCachedMarginValid = false;
4762   }
4763 }
4764 
4765 /*!
4766   If \ref setAutoTickStep is set to false, use this function to set the tick step manually.
4767   The tick step is the interval between (major) ticks, in plot coordinates.
4768   \see setSubTickCount
4769 */
setTickStep(double step)4770 void QCPAxis::setTickStep(double step)
4771 {
4772   if (mTickStep != step)
4773   {
4774     mTickStep = step;
4775     mCachedMarginValid = false;
4776   }
4777 }
4778 
4779 /*!
4780   If you want full control over what ticks (and possibly labels) the axes show, this function is
4781   used to set the coordinates at which ticks will appear.\ref setAutoTicks must be disabled, else
4782   the provided tick vector will be overwritten with automatically generated tick coordinates upon
4783   replot. The labels of the ticks can be generated automatically when \ref setAutoTickLabels is
4784   left enabled. If it is disabled, you can set the labels manually with \ref setTickVectorLabels.
4785 
4786   \a vec is a vector containing the positions of the ticks, in plot coordinates.
4787 
4788   \warning \a vec must be sorted in ascending order, no additional checks are made to ensure this.
4789 
4790   \see setTickVectorLabels
4791 */
setTickVector(const QVector<double> & vec)4792 void QCPAxis::setTickVector(const QVector<double> &vec)
4793 {
4794   // don't check whether mTickVector != vec here, because it takes longer than we would save
4795   mTickVector = vec;
4796   mCachedMarginValid = false;
4797 }
4798 
4799 /*!
4800   If you want full control over what ticks and labels the axes show, this function is used to set a
4801   number of QStrings that will be displayed at the tick positions which you need to provide with
4802   \ref setTickVector. These two vectors should have the same size. (Note that you need to disable
4803   \ref setAutoTicks and \ref setAutoTickLabels first.)
4804 
4805   \a vec is a vector containing the labels of the ticks. The entries correspond to the respective
4806   indices in the tick vector, passed via \ref setTickVector.
4807 
4808   \see setTickVector
4809 */
setTickVectorLabels(const QVector<QString> & vec)4810 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4811 {
4812   // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4813   mTickVectorLabels = vec;
4814   mCachedMarginValid = false;
4815 }
4816 
4817 /*!
4818   Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
4819   plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
4820   zero, the tick labels and axis label will increase their distance to the axis accordingly, so
4821   they won't collide with the ticks.
4822 
4823   \see setSubTickLength, setTickLengthIn, setTickLengthOut
4824 */
setTickLength(int inside,int outside)4825 void QCPAxis::setTickLength(int inside, int outside)
4826 {
4827   setTickLengthIn(inside);
4828   setTickLengthOut(outside);
4829 }
4830 
4831 /*!
4832   Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
4833   inside the plot.
4834 
4835   \see setTickLengthOut, setTickLength, setSubTickLength
4836 */
setTickLengthIn(int inside)4837 void QCPAxis::setTickLengthIn(int inside)
4838 {
4839   if (mAxisPainter->tickLengthIn != inside)
4840   {
4841     mAxisPainter->tickLengthIn = inside;
4842   }
4843 }
4844 
4845 /*!
4846   Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach
4847   outside the plot. If \a outside is greater than zero, the tick labels and axis label will
4848   increase their distance to the axis accordingly, so they won't collide with the ticks.
4849 
4850   \see setTickLengthIn, setTickLength, setSubTickLength
4851 */
setTickLengthOut(int outside)4852 void QCPAxis::setTickLengthOut(int outside)
4853 {
4854   if (mAxisPainter->tickLengthOut != outside)
4855   {
4856     mAxisPainter->tickLengthOut = outside;
4857     mCachedMarginValid = false; // only outside tick length can change margin
4858   }
4859 }
4860 
4861 /*!
4862   Sets the number of sub ticks in one (major) tick step. A sub tick count of three for example,
4863   divides the tick intervals in four sub intervals.
4864 
4865   By default, the number of sub ticks is chosen automatically in a reasonable manner as long as the
4866   mantissa of the tick step is a multiple of 0.5. When \ref setAutoTickStep is enabled, this is
4867   always the case.
4868 
4869   If you want to disable automatic sub tick count and use this function to set the count manually,
4870   see \ref setAutoSubTicks.
4871 */
setSubTickCount(int count)4872 void QCPAxis::setSubTickCount(int count)
4873 {
4874   mSubTickCount = count;
4875 }
4876 
4877 /*!
4878   Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside
4879   the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
4880   than zero, the tick labels and axis label will increase their distance to the axis accordingly,
4881   so they won't collide with the ticks.
4882 
4883   \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
4884 */
setSubTickLength(int inside,int outside)4885 void QCPAxis::setSubTickLength(int inside, int outside)
4886 {
4887   setSubTickLengthIn(inside);
4888   setSubTickLengthOut(outside);
4889 }
4890 
4891 /*!
4892   Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
4893   the plot.
4894 
4895   \see setSubTickLengthOut, setSubTickLength, setTickLength
4896 */
setSubTickLengthIn(int inside)4897 void QCPAxis::setSubTickLengthIn(int inside)
4898 {
4899   if (mAxisPainter->subTickLengthIn != inside)
4900   {
4901     mAxisPainter->subTickLengthIn = inside;
4902   }
4903 }
4904 
4905 /*!
4906   Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach
4907   outside the plot. If \a outside is greater than zero, the tick labels will increase their
4908   distance to the axis accordingly, so they won't collide with the ticks.
4909 
4910   \see setSubTickLengthIn, setSubTickLength, setTickLength
4911 */
setSubTickLengthOut(int outside)4912 void QCPAxis::setSubTickLengthOut(int outside)
4913 {
4914   if (mAxisPainter->subTickLengthOut != outside)
4915   {
4916     mAxisPainter->subTickLengthOut = outside;
4917     mCachedMarginValid = false; // only outside tick length can change margin
4918   }
4919 }
4920 
4921 /*!
4922   Sets the pen, the axis base line is drawn with.
4923 
4924   \see setTickPen, setSubTickPen
4925 */
setBasePen(const QPen & pen)4926 void QCPAxis::setBasePen(const QPen &pen)
4927 {
4928   mBasePen = pen;
4929 }
4930 
4931 /*!
4932   Sets the pen, tick marks will be drawn with.
4933 
4934   \see setTickLength, setBasePen
4935 */
setTickPen(const QPen & pen)4936 void QCPAxis::setTickPen(const QPen &pen)
4937 {
4938   mTickPen = pen;
4939 }
4940 
4941 /*!
4942   Sets the pen, subtick marks will be drawn with.
4943 
4944   \see setSubTickCount, setSubTickLength, setBasePen
4945 */
setSubTickPen(const QPen & pen)4946 void QCPAxis::setSubTickPen(const QPen &pen)
4947 {
4948   mSubTickPen = pen;
4949 }
4950 
4951 /*!
4952   Sets the font of the axis label.
4953 
4954   \see setLabelColor
4955 */
setLabelFont(const QFont & font)4956 void QCPAxis::setLabelFont(const QFont &font)
4957 {
4958   if (mLabelFont != font)
4959   {
4960     mLabelFont = font;
4961     mCachedMarginValid = false;
4962   }
4963 }
4964 
4965 /*!
4966   Sets the color of the axis label.
4967 
4968   \see setLabelFont
4969 */
setLabelColor(const QColor & color)4970 void QCPAxis::setLabelColor(const QColor &color)
4971 {
4972   mLabelColor = color;
4973 }
4974 
4975 /*!
4976   Sets the text of the axis label that will be shown below/above or next to the axis, depending on
4977   its orientation. To disable axis labels, pass an empty string as \a str.
4978 */
setLabel(const QString & str)4979 void QCPAxis::setLabel(const QString &str)
4980 {
4981   if (mLabel != str)
4982   {
4983     mLabel = str;
4984     mCachedMarginValid = false;
4985   }
4986 }
4987 
4988 /*!
4989   Sets the distance between the tick labels and the axis label.
4990 
4991   \see setTickLabelPadding, setPadding
4992 */
setLabelPadding(int padding)4993 void QCPAxis::setLabelPadding(int padding)
4994 {
4995   if (mAxisPainter->labelPadding != padding)
4996   {
4997     mAxisPainter->labelPadding = padding;
4998     mCachedMarginValid = false;
4999   }
5000 }
5001 
5002 /*!
5003   Sets the padding of the axis.
5004 
5005   When \ref QCPAxisRect::setAutoMargins is enabled, the padding is the additional outer most space,
5006   that is left blank.
5007 
5008   The axis padding has no meaning if \ref QCPAxisRect::setAutoMargins is disabled.
5009 
5010   \see setLabelPadding, setTickLabelPadding
5011 */
setPadding(int padding)5012 void QCPAxis::setPadding(int padding)
5013 {
5014   if (mPadding != padding)
5015   {
5016     mPadding = padding;
5017     mCachedMarginValid = false;
5018   }
5019 }
5020 
5021 /*!
5022   Sets the offset the axis has to its axis rect side.
5023 
5024   If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,
5025   only the offset of the inner most axis has meaning (even if it is set to be invisible). The
5026   offset of the other, outer axes is controlled automatically, to place them at appropriate
5027   positions.
5028 */
setOffset(int offset)5029 void QCPAxis::setOffset(int offset)
5030 {
5031   mAxisPainter->offset = offset;
5032 }
5033 
5034 /*!
5035   Sets the font that is used for tick labels when they are selected.
5036 
5037   \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5038 */
setSelectedTickLabelFont(const QFont & font)5039 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
5040 {
5041   if (font != mSelectedTickLabelFont)
5042   {
5043     mSelectedTickLabelFont = font;
5044     // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5045   }
5046 }
5047 
5048 /*!
5049   Sets the font that is used for the axis label when it is selected.
5050 
5051   \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5052 */
setSelectedLabelFont(const QFont & font)5053 void QCPAxis::setSelectedLabelFont(const QFont &font)
5054 {
5055   mSelectedLabelFont = font;
5056   // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5057 }
5058 
5059 /*!
5060   Sets the color that is used for tick labels when they are selected.
5061 
5062   \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5063 */
setSelectedTickLabelColor(const QColor & color)5064 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5065 {
5066   if (color != mSelectedTickLabelColor)
5067   {
5068     mSelectedTickLabelColor = color;
5069   }
5070 }
5071 
5072 /*!
5073   Sets the color that is used for the axis label when it is selected.
5074 
5075   \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5076 */
setSelectedLabelColor(const QColor & color)5077 void QCPAxis::setSelectedLabelColor(const QColor &color)
5078 {
5079   mSelectedLabelColor = color;
5080 }
5081 
5082 /*!
5083   Sets the pen that is used to draw the axis base line when selected.
5084 
5085   \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5086 */
setSelectedBasePen(const QPen & pen)5087 void QCPAxis::setSelectedBasePen(const QPen &pen)
5088 {
5089   mSelectedBasePen = pen;
5090 }
5091 
5092 /*!
5093   Sets the pen that is used to draw the (major) ticks when selected.
5094 
5095   \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5096 */
setSelectedTickPen(const QPen & pen)5097 void QCPAxis::setSelectedTickPen(const QPen &pen)
5098 {
5099   mSelectedTickPen = pen;
5100 }
5101 
5102 /*!
5103   Sets the pen that is used to draw the subticks when selected.
5104 
5105   \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5106 */
setSelectedSubTickPen(const QPen & pen)5107 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
5108 {
5109   mSelectedSubTickPen = pen;
5110 }
5111 
5112 /*!
5113   Sets the style for the lower axis ending. See the documentation of QCPLineEnding for available
5114   styles.
5115 
5116   For horizontal axes, this method refers to the left ending, for vertical axes the bottom ending.
5117   Note that this meaning does not change when the axis range is reversed with \ref
5118   setRangeReversed.
5119 
5120   \see setUpperEnding
5121 */
setLowerEnding(const QCPLineEnding & ending)5122 void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
5123 {
5124   mAxisPainter->lowerEnding = ending;
5125 }
5126 
5127 /*!
5128   Sets the style for the upper axis ending. See the documentation of QCPLineEnding for available
5129   styles.
5130 
5131   For horizontal axes, this method refers to the right ending, for vertical axes the top ending.
5132   Note that this meaning does not change when the axis range is reversed with \ref
5133   setRangeReversed.
5134 
5135   \see setLowerEnding
5136 */
setUpperEnding(const QCPLineEnding & ending)5137 void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
5138 {
5139   mAxisPainter->upperEnding = ending;
5140 }
5141 
5142 /*!
5143   If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
5144   bounds of the range. The range is simply moved by \a diff.
5145 
5146   If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
5147   corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
5148 */
moveRange(double diff)5149 void QCPAxis::moveRange(double diff)
5150 {
5151   QCPRange oldRange = mRange;
5152   if (mScaleType == stLinear)
5153   {
5154     mRange.lower += diff;
5155     mRange.upper += diff;
5156   } else // mScaleType == stLogarithmic
5157   {
5158     mRange.lower *= diff;
5159     mRange.upper *= diff;
5160   }
5161   mCachedMarginValid = false;
5162   emit rangeChanged(mRange);
5163   emit rangeChanged(mRange, oldRange);
5164 }
5165 
5166 /*!
5167   Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
5168   factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
5169   coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
5170   around 1.0 will have moved symmetrically closer to 1.0).
5171 */
scaleRange(double factor,double center)5172 void QCPAxis::scaleRange(double factor, double center)
5173 {
5174   QCPRange oldRange = mRange;
5175   if (mScaleType == stLinear)
5176   {
5177     QCPRange newRange;
5178     newRange.lower = (mRange.lower-center)*factor + center;
5179     newRange.upper = (mRange.upper-center)*factor + center;
5180     if (QCPRange::validRange(newRange))
5181       mRange = newRange.sanitizedForLinScale();
5182   } else // mScaleType == stLogarithmic
5183   {
5184     if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5185     {
5186       QCPRange newRange;
5187       newRange.lower = qPow(mRange.lower/center, factor)*center;
5188       newRange.upper = qPow(mRange.upper/center, factor)*center;
5189       if (QCPRange::validRange(newRange))
5190         mRange = newRange.sanitizedForLogScale();
5191     } else
5192       qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5193   }
5194   mCachedMarginValid = false;
5195   emit rangeChanged(mRange);
5196   emit rangeChanged(mRange, oldRange);
5197 }
5198 
5199 /*!
5200   Scales the range of this axis to have a certain scale \a ratio to \a otherAxis. The scaling will
5201   be done around the center of the current axis range.
5202 
5203   For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs
5204   plotted with those axes will appear in a 1:1 aspect ratio, independent of the aspect ratio the
5205   axis rect has.
5206 
5207   This is an operation that changes the range of this axis once, it doesn't fix the scale ratio
5208   indefinitely. Note that calling this function in the constructor of the QCustomPlot's parent
5209   won't have the desired effect, since the widget dimensions aren't defined yet, and a resizeEvent
5210   will follow.
5211 */
setScaleRatio(const QCPAxis * otherAxis,double ratio)5212 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5213 {
5214   int otherPixelSize, ownPixelSize;
5215 
5216   if (otherAxis->orientation() == Qt::Horizontal)
5217     otherPixelSize = otherAxis->axisRect()->width();
5218   else
5219     otherPixelSize = otherAxis->axisRect()->height();
5220 
5221   if (orientation() == Qt::Horizontal)
5222     ownPixelSize = axisRect()->width();
5223   else
5224     ownPixelSize = axisRect()->height();
5225 
5226   double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5227   setRange(range().center(), newRangeSize, Qt::AlignCenter);
5228 }
5229 
5230 /*!
5231   Changes the axis range such that all plottables associated with this axis are fully visible in
5232   that dimension.
5233 
5234   \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
5235 */
rescale(bool onlyVisiblePlottables)5236 void QCPAxis::rescale(bool onlyVisiblePlottables)
5237 {
5238   QList<QCPAbstractPlottable*> p = plottables();
5239   QCPRange newRange;
5240   bool haveRange = false;
5241   for (int i=0; i<p.size(); ++i)
5242   {
5243     if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5244       continue;
5245     QCPRange plottableRange;
5246     bool currentFoundRange;
5247     QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
5248     if (mScaleType == stLogarithmic)
5249       signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
5250     if (p.at(i)->keyAxis() == this)
5251       plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5252     else
5253       plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5254     if (currentFoundRange)
5255     {
5256       if (!haveRange)
5257         newRange = plottableRange;
5258       else
5259         newRange.expand(plottableRange);
5260       haveRange = true;
5261     }
5262   }
5263   if (haveRange)
5264   {
5265     if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
5266     {
5267       double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
5268       if (mScaleType == stLinear)
5269       {
5270         newRange.lower = center-mRange.size()/2.0;
5271         newRange.upper = center+mRange.size()/2.0;
5272       } else // mScaleType == stLogarithmic
5273       {
5274         newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5275         newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5276       }
5277     }
5278     setRange(newRange);
5279   }
5280 }
5281 
5282 /*!
5283   Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
5284 */
pixelToCoord(double value) const5285 double QCPAxis::pixelToCoord(double value) const
5286 {
5287   if (orientation() == Qt::Horizontal)
5288   {
5289     if (mScaleType == stLinear)
5290     {
5291       if (!mRangeReversed)
5292         return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5293       else
5294         return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5295     } else // mScaleType == stLogarithmic
5296     {
5297       if (!mRangeReversed)
5298         return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5299       else
5300         return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5301     }
5302   } else // orientation() == Qt::Vertical
5303   {
5304     if (mScaleType == stLinear)
5305     {
5306       if (!mRangeReversed)
5307         return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5308       else
5309         return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5310     } else // mScaleType == stLogarithmic
5311     {
5312       if (!mRangeReversed)
5313         return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5314       else
5315         return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5316     }
5317   }
5318 }
5319 
5320 /*!
5321   Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
5322 */
coordToPixel(double value) const5323 double QCPAxis::coordToPixel(double value) const
5324 {
5325   if (orientation() == Qt::Horizontal)
5326   {
5327     if (mScaleType == stLinear)
5328     {
5329       if (!mRangeReversed)
5330         return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5331       else
5332         return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5333     } else // mScaleType == stLogarithmic
5334     {
5335       if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5336         return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5337       else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5338         return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5339       else
5340       {
5341         if (!mRangeReversed)
5342           return baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
5343         else
5344           return baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
5345       }
5346     }
5347   } else // orientation() == Qt::Vertical
5348   {
5349     if (mScaleType == stLinear)
5350     {
5351       if (!mRangeReversed)
5352         return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5353       else
5354         return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5355     } else // mScaleType == stLogarithmic
5356     {
5357       if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5358         return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5359       else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5360         return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5361       else
5362       {
5363         if (!mRangeReversed)
5364           return mAxisRect->bottom()-baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
5365         else
5366           return mAxisRect->bottom()-baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
5367       }
5368     }
5369   }
5370 }
5371 
5372 /*!
5373   Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
5374   is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this
5375   function does not change the current selection state of the axis.
5376 
5377   If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
5378 
5379   \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions
5380 */
getPartAt(const QPointF & pos) const5381 QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const
5382 {
5383   if (!mVisible)
5384     return spNone;
5385 
5386   if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5387     return spAxis;
5388   else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5389     return spTickLabels;
5390   else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5391     return spAxisLabel;
5392   else
5393     return spNone;
5394 }
5395 
5396 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const5397 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5398 {
5399   if (!mParentPlot) return -1;
5400   SelectablePart part = getPartAt(pos);
5401   if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5402     return -1;
5403 
5404   if (details)
5405     details->setValue(part);
5406   return mParentPlot->selectionTolerance()*0.99;
5407 }
5408 
5409 /*!
5410   Returns a list of all the plottables that have this axis as key or value axis.
5411 
5412   If you are only interested in plottables of type QCPGraph, see \ref graphs.
5413 
5414   \see graphs, items
5415 */
plottables() const5416 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5417 {
5418   QList<QCPAbstractPlottable*> result;
5419   if (!mParentPlot) return result;
5420 
5421   for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5422   {
5423     if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5424       result.append(mParentPlot->mPlottables.at(i));
5425   }
5426   return result;
5427 }
5428 
5429 /*!
5430   Returns a list of all the graphs that have this axis as key or value axis.
5431 
5432   \see plottables, items
5433 */
graphs() const5434 QList<QCPGraph*> QCPAxis::graphs() const
5435 {
5436   QList<QCPGraph*> result;
5437   if (!mParentPlot) return result;
5438 
5439   for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5440   {
5441     if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5442       result.append(mParentPlot->mGraphs.at(i));
5443   }
5444   return result;
5445 }
5446 
5447 /*!
5448   Returns a list of all the items that are associated with this axis. An item is considered
5449   associated with an axis if at least one of its positions uses the axis as key or value axis.
5450 
5451   \see plottables, graphs
5452 */
items() const5453 QList<QCPAbstractItem*> QCPAxis::items() const
5454 {
5455   QList<QCPAbstractItem*> result;
5456   if (!mParentPlot) return result;
5457 
5458   for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5459   {
5460     QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5461     for (int posId=0; posId<positions.size(); ++posId)
5462     {
5463       if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5464       {
5465         result.append(mParentPlot->mItems.at(itemId));
5466         break;
5467       }
5468     }
5469   }
5470   return result;
5471 }
5472 
5473 /*!
5474   Transforms a margin side to the logically corresponding axis type. (QCP::msLeft to
5475   QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.)
5476 */
marginSideToAxisType(QCP::MarginSide side)5477 QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side)
5478 {
5479   switch (side)
5480   {
5481     case QCP::msLeft: return atLeft;
5482     case QCP::msRight: return atRight;
5483     case QCP::msTop: return atTop;
5484     case QCP::msBottom: return atBottom;
5485     default: break;
5486   }
5487   qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5488   return atLeft;
5489 }
5490 
5491 /*!
5492   Returns the axis type that describes the opposite axis of an axis with the specified \a type.
5493 */
opposite(QCPAxis::AxisType type)5494 QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
5495 {
5496   switch (type)
5497   {
5498     case atLeft: return atRight; break;
5499     case atRight: return atLeft; break;
5500     case atBottom: return atTop; break;
5501     case atTop: return atBottom; break;
5502     default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5503   }
5504 }
5505 
5506 /*! \internal
5507 
5508   This function is called to prepare the tick vector, sub tick vector and tick label vector. If
5509   \ref setAutoTicks is set to true, appropriate tick values are determined automatically via \ref
5510   generateAutoTicks. If it's set to false, the signal ticksRequest is emitted, which can be used to
5511   provide external tick positions. Then the sub tick vectors and tick label vectors are created.
5512 */
setupTickVectors()5513 void QCPAxis::setupTickVectors()
5514 {
5515   if (!mParentPlot) return;
5516   if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5517 
5518   // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5519   if (mAutoTicks)
5520   {
5521     generateAutoTicks();
5522   } else
5523   {
5524     emit ticksRequest();
5525   }
5526 
5527   visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick);
5528   if (mTickVector.isEmpty())
5529   {
5530     mSubTickVector.clear();
5531     return;
5532   }
5533 
5534   // generate subticks between ticks:
5535   mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5536   if (mSubTickCount > 0)
5537   {
5538     double subTickStep = 0;
5539     double subTickPosition = 0;
5540     int subTickIndex = 0;
5541     bool done = false;
5542     int lowTick = mLowestVisibleTick > 0 ? mLowestVisibleTick-1 : mLowestVisibleTick;
5543     int highTick = mHighestVisibleTick < mTickVector.size()-1 ? mHighestVisibleTick+1 : mHighestVisibleTick;
5544     for (int i=lowTick+1; i<=highTick; ++i)
5545     {
5546       subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5547       for (int k=1; k<=mSubTickCount; ++k)
5548       {
5549         subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5550         if (subTickPosition < mRange.lower)
5551           continue;
5552         if (subTickPosition > mRange.upper)
5553         {
5554           done = true;
5555           break;
5556         }
5557         mSubTickVector[subTickIndex] = subTickPosition;
5558         subTickIndex++;
5559       }
5560       if (done) break;
5561     }
5562     mSubTickVector.resize(subTickIndex);
5563   }
5564 
5565   // generate tick labels according to tick positions:
5566   if (mAutoTickLabels)
5567   {
5568     int vecsize = mTickVector.size();
5569     mTickVectorLabels.resize(vecsize);
5570     if (mTickLabelType == ltNumber)
5571     {
5572       for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5573         mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
5574     } else if (mTickLabelType == ltDateTime)
5575     {
5576       for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5577       {
5578 #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) // use fromMSecsSinceEpoch function if available, to gain sub-second accuracy on tick labels (e.g. for format "hh:mm:ss:zzz")
5579         mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5580 #else
5581         mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5582 #endif
5583       }
5584     }
5585   } else // mAutoTickLabels == false
5586   {
5587     if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5588     {
5589       emit ticksRequest();
5590     }
5591     // make sure provided tick label vector has correct (minimal) length:
5592     if (mTickVectorLabels.size() < mTickVector.size())
5593       mTickVectorLabels.resize(mTickVector.size());
5594   }
5595 }
5596 
5597 /*! \internal
5598 
5599   If \ref setAutoTicks is set to true, this function is called by \ref setupTickVectors to
5600   generate reasonable tick positions (and subtick count). The algorithm tries to create
5601   approximately <tt>mAutoTickCount</tt> ticks (set via \ref setAutoTickCount).
5602 
5603   If the scale is logarithmic, \ref setAutoTickCount is ignored, and one tick is generated at every
5604   power of the current logarithm base, set via \ref setScaleLogBase.
5605 */
generateAutoTicks()5606 void QCPAxis::generateAutoTicks()
5607 {
5608   if (mScaleType == stLinear)
5609   {
5610     if (mAutoTickStep)
5611     {
5612       // Generate tick positions according to linear scaling:
5613       mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5614       double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5615       double tickStepMantissa = mTickStep/magnitudeFactor;
5616       if (tickStepMantissa < 5)
5617       {
5618         // round digit after decimal point to 0.5
5619         mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5620       } else
5621       {
5622         // round to first digit in multiples of 2
5623         mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5624       }
5625     }
5626     if (mAutoSubTicks)
5627       mSubTickCount = calculateAutoSubTickCount(mTickStep);
5628     // Generate tick positions according to mTickStep:
5629     qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision
5630     qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision
5631     int tickcount = lastStep-firstStep+1;
5632     if (tickcount < 0) tickcount = 0;
5633     mTickVector.resize(tickcount);
5634     for (int i=0; i<tickcount; ++i)
5635       mTickVector[i] = (firstStep+i)*mTickStep;
5636   } else // mScaleType == stLogarithmic
5637   {
5638     // Generate tick positions according to logbase scaling:
5639     if (mRange.lower > 0 && mRange.upper > 0) // positive range
5640     {
5641       double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5642       double currentMag = lowerMag;
5643       mTickVector.clear();
5644       mTickVector.append(currentMag);
5645       while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5646       {
5647         currentMag *= mScaleLogBase;
5648         mTickVector.append(currentMag);
5649       }
5650     } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5651     {
5652       double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5653       double currentMag = lowerMag;
5654       mTickVector.clear();
5655       mTickVector.append(currentMag);
5656       while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5657       {
5658         currentMag /= mScaleLogBase;
5659         mTickVector.append(currentMag);
5660       }
5661     } else // invalid range for logarithmic scale, because lower and upper have different sign
5662     {
5663       mTickVector.clear();
5664       qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5665     }
5666   }
5667 }
5668 
5669 /*! \internal
5670 
5671   Called by generateAutoTicks when \ref setAutoSubTicks is set to true. Depending on the \a
5672   tickStep between two major ticks on the axis, a different number of sub ticks is appropriate. For
5673   Example taking 4 sub ticks for a \a tickStep of 1 makes more sense than taking 5 sub ticks,
5674   because this corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16667. Note
5675   that a subtick count of 4 means dividing the major tick step into 5 sections.
5676 
5677   This is implemented by a hand made lookup for integer tick steps as well as fractional tick steps
5678   with a fractional part of (approximately) 0.5. If a tick step is different (i.e. has no
5679   fractional part close to 0.5), the currently set sub tick count (\ref setSubTickCount) is
5680   returned.
5681 */
calculateAutoSubTickCount(double tickStep) const5682 int QCPAxis::calculateAutoSubTickCount(double tickStep) const
5683 {
5684   int result = mSubTickCount; // default to current setting, if no proper value can be found
5685 
5686   // get mantissa of tickstep:
5687   double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5688   double tickStepMantissa = tickStep/magnitudeFactor;
5689 
5690   // separate integer and fractional part of mantissa:
5691   double epsilon = 0.01;
5692   double intPartf;
5693   int intPart;
5694   double fracPart = modf(tickStepMantissa, &intPartf);
5695   intPart = intPartf;
5696 
5697   // handle cases with (almost) integer mantissa:
5698   if (fracPart < epsilon || 1.0-fracPart < epsilon)
5699   {
5700     if (1.0-fracPart < epsilon)
5701       ++intPart;
5702     switch (intPart)
5703     {
5704       case 1: result = 4; break; // 1.0 -> 0.2 substep
5705       case 2: result = 3; break; // 2.0 -> 0.5 substep
5706       case 3: result = 2; break; // 3.0 -> 1.0 substep
5707       case 4: result = 3; break; // 4.0 -> 1.0 substep
5708       case 5: result = 4; break; // 5.0 -> 1.0 substep
5709       case 6: result = 2; break; // 6.0 -> 2.0 substep
5710       case 7: result = 6; break; // 7.0 -> 1.0 substep
5711       case 8: result = 3; break; // 8.0 -> 2.0 substep
5712       case 9: result = 2; break; // 9.0 -> 3.0 substep
5713     }
5714   } else
5715   {
5716     // handle cases with significantly fractional mantissa:
5717     if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5718     {
5719       switch (intPart)
5720       {
5721         case 1: result = 2; break; // 1.5 -> 0.5 substep
5722         case 2: result = 4; break; // 2.5 -> 0.5 substep
5723         case 3: result = 4; break; // 3.5 -> 0.7 substep
5724         case 4: result = 2; break; // 4.5 -> 1.5 substep
5725         case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5726         case 6: result = 4; break; // 6.5 -> 1.3 substep
5727         case 7: result = 2; break; // 7.5 -> 2.5 substep
5728         case 8: result = 4; break; // 8.5 -> 1.7 substep
5729         case 9: result = 4; break; // 9.5 -> 1.9 substep
5730       }
5731     }
5732     // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5733   }
5734 
5735   return result;
5736 }
5737 
5738 /* inherits documentation from base class */
selectEvent(QMouseEvent * event,bool additive,const QVariant & details,bool * selectionStateChanged)5739 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5740 {
5741   Q_UNUSED(event)
5742   SelectablePart part = details.value<SelectablePart>();
5743   if (mSelectableParts.testFlag(part))
5744   {
5745     SelectableParts selBefore = mSelectedParts;
5746     setSelectedParts(additive ? mSelectedParts^part : part);
5747     if (selectionStateChanged)
5748       *selectionStateChanged = mSelectedParts != selBefore;
5749   }
5750 }
5751 
5752 /* inherits documentation from base class */
deselectEvent(bool * selectionStateChanged)5753 void QCPAxis::deselectEvent(bool *selectionStateChanged)
5754 {
5755   SelectableParts selBefore = mSelectedParts;
5756   setSelectedParts(mSelectedParts & ~mSelectableParts);
5757   if (selectionStateChanged)
5758     *selectionStateChanged = mSelectedParts != selBefore;
5759 }
5760 
5761 /*! \internal
5762 
5763   A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
5764   before drawing axis lines.
5765 
5766   This is the antialiasing state the painter passed to the \ref draw method is in by default.
5767 
5768   This function takes into account the local setting of the antialiasing flag as well as the
5769   overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
5770   QCustomPlot::setNotAntialiasedElements.
5771 
5772   \see setAntialiased
5773 */
applyDefaultAntialiasingHint(QCPPainter * painter) const5774 void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const
5775 {
5776   applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
5777 }
5778 
5779 /*! \internal
5780 
5781   Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance.
5782 
5783 */
draw(QCPPainter * painter)5784 void QCPAxis::draw(QCPPainter *painter)
5785 {
5786   const int lowTick = mLowestVisibleTick;
5787   const int highTick = mHighestVisibleTick;
5788   QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5789   QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5790   QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5791   tickPositions.reserve(highTick-lowTick+1);
5792   tickLabels.reserve(highTick-lowTick+1);
5793   subTickPositions.reserve(mSubTickVector.size());
5794 
5795   if (mTicks)
5796   {
5797     for (int i=lowTick; i<=highTick; ++i)
5798     {
5799       tickPositions.append(coordToPixel(mTickVector.at(i)));
5800       if (mTickLabels)
5801         tickLabels.append(mTickVectorLabels.at(i));
5802     }
5803 
5804     if (mSubTickCount > 0)
5805     {
5806       const int subTickCount = mSubTickVector.size();
5807       for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5808         subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5809     }
5810   }
5811   // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5812   // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5813   mAxisPainter->type = mAxisType;
5814   mAxisPainter->basePen = getBasePen();
5815   mAxisPainter->labelFont = getLabelFont();
5816   mAxisPainter->labelColor = getLabelColor();
5817   mAxisPainter->label = mLabel;
5818   mAxisPainter->substituteExponent = mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber;
5819   mAxisPainter->tickPen = getTickPen();
5820   mAxisPainter->subTickPen = getSubTickPen();
5821   mAxisPainter->tickLabelFont = getTickLabelFont();
5822   mAxisPainter->tickLabelColor = getTickLabelColor();
5823   mAxisPainter->axisRect = mAxisRect->rect();
5824   mAxisPainter->viewportRect = mParentPlot->viewport();
5825   mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
5826   mAxisPainter->reversedEndings = mRangeReversed;
5827   mAxisPainter->tickPositions = tickPositions;
5828   mAxisPainter->tickLabels = tickLabels;
5829   mAxisPainter->subTickPositions = subTickPositions;
5830   mAxisPainter->draw(painter);
5831 }
5832 
5833 /*! \internal
5834 
5835   Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
5836   the current range. The return values are indices of the tick vector, not the positions of the
5837   ticks themselves.
5838 
5839   The actual use of this function is when an external tick vector is provided, since it might
5840   exceed far beyond the currently displayed range, and would cause unnecessary calculations e.g. of
5841   subticks.
5842 
5843   If all ticks are outside the axis range, an inverted range is returned, i.e. highIndex will be
5844   smaller than lowIndex. There is one case, where this function returns indices that are not really
5845   visible in the current axis range: When the tick spacing is larger than the axis range size and
5846   one tick is below the axis range and the next tick is already above the axis range. Because in
5847   such cases it is usually desirable to know the tick pair, to draw proper subticks.
5848 */
visibleTickBounds(int & lowIndex,int & highIndex) const5849 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5850 {
5851   bool lowFound = false;
5852   bool highFound = false;
5853   lowIndex = 0;
5854   highIndex = -1;
5855 
5856   for (int i=0; i < mTickVector.size(); ++i)
5857   {
5858     if (mTickVector.at(i) >= mRange.lower)
5859     {
5860       lowFound = true;
5861       lowIndex = i;
5862       break;
5863     }
5864   }
5865   for (int i=mTickVector.size()-1; i >= 0; --i)
5866   {
5867     if (mTickVector.at(i) <= mRange.upper)
5868     {
5869       highFound = true;
5870       highIndex = i;
5871       break;
5872     }
5873   }
5874 
5875   if (!lowFound && highFound)
5876     lowIndex = highIndex+1;
5877   else if (lowFound && !highFound)
5878     highIndex = lowIndex-1;
5879 }
5880 
5881 /*! \internal
5882 
5883   A log function with the base mScaleLogBase, used mostly for coordinate transforms in logarithmic
5884   scales with arbitrary log base. Uses the buffered mScaleLogBaseLogInv for faster calculation.
5885   This is set to <tt>1.0/qLn(mScaleLogBase)</tt> in \ref setScaleLogBase.
5886 
5887   \see basePow, setScaleLogBase, setScaleType
5888 */
baseLog(double value) const5889 double QCPAxis::baseLog(double value) const
5890 {
5891   return qLn(value)*mScaleLogBaseLogInv;
5892 }
5893 
5894 /*! \internal
5895 
5896   A power function with the base mScaleLogBase, used mostly for coordinate transforms in
5897   logarithmic scales with arbitrary log base.
5898 
5899   \see baseLog, setScaleLogBase, setScaleType
5900 */
basePow(double value) const5901 double QCPAxis::basePow(double value) const
5902 {
5903   return qPow(mScaleLogBase, value);
5904 }
5905 
5906 /*! \internal
5907 
5908   Returns the pen that is used to draw the axis base line. Depending on the selection state, this
5909   is either mSelectedBasePen or mBasePen.
5910 */
getBasePen() const5911 QPen QCPAxis::getBasePen() const
5912 {
5913   return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5914 }
5915 
5916 /*! \internal
5917 
5918   Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
5919   is either mSelectedTickPen or mTickPen.
5920 */
getTickPen() const5921 QPen QCPAxis::getTickPen() const
5922 {
5923   return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5924 }
5925 
5926 /*! \internal
5927 
5928   Returns the pen that is used to draw the subticks. Depending on the selection state, this
5929   is either mSelectedSubTickPen or mSubTickPen.
5930 */
getSubTickPen() const5931 QPen QCPAxis::getSubTickPen() const
5932 {
5933   return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
5934 }
5935 
5936 /*! \internal
5937 
5938   Returns the font that is used to draw the tick labels. Depending on the selection state, this
5939   is either mSelectedTickLabelFont or mTickLabelFont.
5940 */
getTickLabelFont() const5941 QFont QCPAxis::getTickLabelFont() const
5942 {
5943   return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
5944 }
5945 
5946 /*! \internal
5947 
5948   Returns the font that is used to draw the axis label. Depending on the selection state, this
5949   is either mSelectedLabelFont or mLabelFont.
5950 */
getLabelFont() const5951 QFont QCPAxis::getLabelFont() const
5952 {
5953   return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
5954 }
5955 
5956 /*! \internal
5957 
5958   Returns the color that is used to draw the tick labels. Depending on the selection state, this
5959   is either mSelectedTickLabelColor or mTickLabelColor.
5960 */
getTickLabelColor() const5961 QColor QCPAxis::getTickLabelColor() const
5962 {
5963   return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
5964 }
5965 
5966 /*! \internal
5967 
5968   Returns the color that is used to draw the axis label. Depending on the selection state, this
5969   is either mSelectedLabelColor or mLabelColor.
5970 */
getLabelColor() const5971 QColor QCPAxis::getLabelColor() const
5972 {
5973   return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
5974 }
5975 
5976 /*! \internal
5977 
5978   Returns the appropriate outward margin for this axis. It is needed if \ref
5979   QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis with axis type \ref
5980   atLeft will return an appropriate left margin, \ref atBottom will return an appropriate bottom
5981   margin and so forth. For the calculation, this function goes through similar steps as \ref draw,
5982   so changing one function likely requires the modification of the other one as well.
5983 
5984   The margin consists of the outward tick length, tick label padding, tick label size, label
5985   padding, label size, and padding.
5986 
5987   The margin is cached internally, so repeated calls while leaving the axis range, fonts, etc.
5988   unchanged are very fast.
5989 */
calculateMargin()5990 int QCPAxis::calculateMargin()
5991 {
5992   if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
5993     return 0;
5994 
5995   if (mCachedMarginValid)
5996     return mCachedMargin;
5997 
5998   // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
5999   int margin = 0;
6000 
6001   int lowTick, highTick;
6002   visibleTickBounds(lowTick, highTick);
6003   QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6004   QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6005   tickPositions.reserve(highTick-lowTick+1);
6006   tickLabels.reserve(highTick-lowTick+1);
6007   if (mTicks)
6008   {
6009     for (int i=lowTick; i<=highTick; ++i)
6010     {
6011       tickPositions.append(coordToPixel(mTickVector.at(i)));
6012       if (mTickLabels)
6013         tickLabels.append(mTickVectorLabels.at(i));
6014     }
6015   }
6016   // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6017   // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6018   mAxisPainter->type = mAxisType;
6019   mAxisPainter->labelFont = getLabelFont();
6020   mAxisPainter->label = mLabel;
6021   mAxisPainter->tickLabelFont = mTickLabelFont;
6022   mAxisPainter->axisRect = mAxisRect->rect();
6023   mAxisPainter->viewportRect = mParentPlot->viewport();
6024   mAxisPainter->tickPositions = tickPositions;
6025   mAxisPainter->tickLabels = tickLabels;
6026   margin += mAxisPainter->size();
6027   margin += mPadding;
6028 
6029   mCachedMargin = margin;
6030   mCachedMarginValid = true;
6031   return margin;
6032 }
6033 
6034 /* inherits documentation from base class */
selectionCategory() const6035 QCP::Interaction QCPAxis::selectionCategory() const
6036 {
6037   return QCP::iSelectAxes;
6038 }
6039 
6040 
6041 ////////////////////////////////////////////////////////////////////////////////////////////////////
6042 //////////////////// QCPAxisPainterPrivate
6043 ////////////////////////////////////////////////////////////////////////////////////////////////////
6044 
6045 /*! \class QCPAxisPainterPrivate
6046 
6047   \internal
6048   \brief (Private)
6049 
6050   This is a private class and not part of the public QCustomPlot interface.
6051 
6052   It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and
6053   axis label. It also buffers the labels to reduce replot times. The parameters are configured by
6054   directly accessing the public member variables.
6055 */
6056 
6057 /*!
6058   Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every
6059   redraw, to utilize the caching mechanisms.
6060 */
QCPAxisPainterPrivate(QCustomPlot * parentPlot)6061 QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :
6062   type(QCPAxis::atLeft),
6063   basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6064   lowerEnding(QCPLineEnding::esNone),
6065   upperEnding(QCPLineEnding::esNone),
6066   labelPadding(0),
6067   tickLabelPadding(0),
6068   tickLabelRotation(0),
6069   tickLabelSide(QCPAxis::lsOutside),
6070   substituteExponent(true),
6071   numberMultiplyCross(false),
6072   tickLengthIn(5),
6073   tickLengthOut(0),
6074   subTickLengthIn(2),
6075   subTickLengthOut(0),
6076   tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6077   subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6078   offset(0),
6079   abbreviateDecimalPowers(false),
6080   reversedEndings(false),
6081   mParentPlot(parentPlot),
6082   mLabelCache(16) // cache at most 16 (tick) labels
6083 {
6084 }
6085 
~QCPAxisPainterPrivate()6086 QCPAxisPainterPrivate::~QCPAxisPainterPrivate()
6087 {
6088 }
6089 
6090 /*! \internal
6091 
6092   Draws the axis with the specified \a painter.
6093 
6094   The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
6095   here, too.
6096 */
draw(QCPPainter * painter)6097 void QCPAxisPainterPrivate::draw(QCPPainter *painter)
6098 {
6099   QByteArray newHash = generateLabelParameterHash();
6100   if (newHash != mLabelParameterHash)
6101   {
6102     mLabelCache.clear();
6103     mLabelParameterHash = newHash;
6104   }
6105 
6106   QPoint origin;
6107   switch (type)
6108   {
6109     case QCPAxis::atLeft:   origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
6110     case QCPAxis::atRight:  origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
6111     case QCPAxis::atTop:    origin = axisRect.topLeft()    +QPoint(0, -offset); break;
6112     case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
6113   }
6114 
6115   double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6116   switch (type)
6117   {
6118     case QCPAxis::atTop: yCor = -1; break;
6119     case QCPAxis::atRight: xCor = 1; break;
6120     default: break;
6121   }
6122   int margin = 0;
6123   // draw baseline:
6124   QLineF baseLine;
6125   painter->setPen(basePen);
6126   if (QCPAxis::orientation(type) == Qt::Horizontal)
6127     baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
6128   else
6129     baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
6130   if (reversedEndings)
6131     baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6132   painter->drawLine(baseLine);
6133 
6134   // draw ticks:
6135   if (!tickPositions.isEmpty())
6136   {
6137     painter->setPen(tickPen);
6138     int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6139     if (QCPAxis::orientation(type) == Qt::Horizontal)
6140     {
6141       for (int i=0; i<tickPositions.size(); ++i)
6142         painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6143     } else
6144     {
6145       for (int i=0; i<tickPositions.size(); ++i)
6146         painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6147     }
6148   }
6149 
6150   // draw subticks:
6151   if (!subTickPositions.isEmpty())
6152   {
6153     painter->setPen(subTickPen);
6154     // direction of ticks ("inward" is right for left axis and left for right axis)
6155     int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6156     if (QCPAxis::orientation(type) == Qt::Horizontal)
6157     {
6158       for (int i=0; i<subTickPositions.size(); ++i)
6159         painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6160     } else
6161     {
6162       for (int i=0; i<subTickPositions.size(); ++i)
6163         painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6164     }
6165   }
6166   margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6167 
6168   // draw axis base endings:
6169   bool antialiasingBackup = painter->antialiasing();
6170   painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6171   painter->setBrush(QBrush(basePen.color()));
6172   QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6173   if (lowerEnding.style() != QCPLineEnding::esNone)
6174     lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6175   if (upperEnding.style() != QCPLineEnding::esNone)
6176     upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6177   painter->setAntialiasing(antialiasingBackup);
6178 
6179   // tick labels:
6180   QRect oldClipRect;
6181   if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
6182   {
6183     oldClipRect = painter->clipRegion().boundingRect();
6184     painter->setClipRect(axisRect);
6185   }
6186   QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6187   if (!tickLabels.isEmpty())
6188   {
6189     if (tickLabelSide == QCPAxis::lsOutside)
6190       margin += tickLabelPadding;
6191     painter->setFont(tickLabelFont);
6192     painter->setPen(QPen(tickLabelColor));
6193     const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6194     int distanceToAxis = margin;
6195     if (tickLabelSide == QCPAxis::lsInside)
6196       distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6197     for (int i=0; i<maxLabelIndex; ++i)
6198       placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
6199     if (tickLabelSide == QCPAxis::lsOutside)
6200       margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
6201   }
6202   if (tickLabelSide == QCPAxis::lsInside)
6203     painter->setClipRect(oldClipRect);
6204 
6205   // axis label:
6206   QRect labelBounds;
6207   if (!label.isEmpty())
6208   {
6209     margin += labelPadding;
6210     painter->setFont(labelFont);
6211     painter->setPen(QPen(labelColor));
6212     labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6213     if (type == QCPAxis::atLeft)
6214     {
6215       QTransform oldTransform = painter->transform();
6216       painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6217       painter->rotate(-90);
6218       painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6219       painter->setTransform(oldTransform);
6220     }
6221     else if (type == QCPAxis::atRight)
6222     {
6223       QTransform oldTransform = painter->transform();
6224       painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
6225       painter->rotate(90);
6226       painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6227       painter->setTransform(oldTransform);
6228     }
6229     else if (type == QCPAxis::atTop)
6230       painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6231     else if (type == QCPAxis::atBottom)
6232       painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6233   }
6234 
6235   // set selection boxes:
6236   int selectionTolerance = 0;
6237   if (mParentPlot)
6238     selectionTolerance = mParentPlot->selectionTolerance();
6239   else
6240     qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6241   int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6242   int selAxisInSize = selectionTolerance;
6243   int selTickLabelSize;
6244   int selTickLabelOffset;
6245   if (tickLabelSide == QCPAxis::lsOutside)
6246   {
6247     selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6248     selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6249   } else
6250   {
6251     selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6252     selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6253   }
6254   int selLabelSize = labelBounds.height();
6255   int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
6256   if (type == QCPAxis::atLeft)
6257   {
6258     mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
6259     mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
6260     mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
6261   } else if (type == QCPAxis::atRight)
6262   {
6263     mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
6264     mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
6265     mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
6266   } else if (type == QCPAxis::atTop)
6267   {
6268     mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
6269     mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
6270     mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
6271   } else if (type == QCPAxis::atBottom)
6272   {
6273     mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
6274     mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
6275     mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
6276   }
6277   mAxisSelectionBox = mAxisSelectionBox.normalized();
6278   mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
6279   mLabelSelectionBox = mLabelSelectionBox.normalized();
6280   // draw hitboxes for debug purposes:
6281   //painter->setBrush(Qt::NoBrush);
6282   //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6283 }
6284 
6285 /*! \internal
6286 
6287   Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
6288   direction) needed to fit the axis.
6289 */
size() const6290 int QCPAxisPainterPrivate::size() const
6291 {
6292   int result = 0;
6293 
6294   // get length of tick marks pointing outwards:
6295   if (!tickPositions.isEmpty())
6296     result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6297 
6298   // calculate size of tick labels:
6299   if (tickLabelSide == QCPAxis::lsOutside)
6300   {
6301     QSize tickLabelsSize(0, 0);
6302     if (!tickLabels.isEmpty())
6303     {
6304       for (int i=0; i<tickLabels.size(); ++i)
6305         getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6306       result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6307     result += tickLabelPadding;
6308     }
6309   }
6310 
6311   // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6312   if (!label.isEmpty())
6313   {
6314     QFontMetrics fontMetrics(labelFont);
6315     QRect bounds;
6316     bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6317     result += bounds.height() + labelPadding;
6318   }
6319 
6320   return result;
6321 }
6322 
6323 /*! \internal
6324 
6325   Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This
6326   method is called automatically in \ref draw, if any parameters have changed that invalidate the
6327   cached labels, such as font, color, etc.
6328 */
clearCache()6329 void QCPAxisPainterPrivate::clearCache()
6330 {
6331   mLabelCache.clear();
6332 }
6333 
6334 /*! \internal
6335 
6336   Returns a hash that allows uniquely identifying whether the label parameters have changed such
6337   that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the
6338   return value of this method hasn't changed since the last redraw, the respective label parameters
6339   haven't changed and cached labels may be used.
6340 */
generateLabelParameterHash() const6341 QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
6342 {
6343   QByteArray result;
6344   result.append(QByteArray::number(tickLabelRotation));
6345   result.append(QByteArray::number((int)tickLabelSide));
6346   result.append(QByteArray::number((int)substituteExponent));
6347   result.append(QByteArray::number((int)numberMultiplyCross));
6348   result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
6349   result.append(tickLabelFont.toString().toLatin1());
6350   return result;
6351 }
6352 
6353 /*! \internal
6354 
6355   Draws a single tick label with the provided \a painter, utilizing the internal label cache to
6356   significantly speed up drawing of labels that were drawn in previous calls. The tick label is
6357   always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
6358   pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
6359   for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
6360   at which the label should be drawn.
6361 
6362   In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
6363   largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
6364   drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
6365   tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
6366   holds.
6367 
6368   The label is drawn with the font and pen that are currently set on the \a painter. To draw
6369   superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
6370   getTickLabelData).
6371 */
placeTickLabel(QCPPainter * painter,double position,int distanceToAxis,const QString & text,QSize * tickLabelsSize)6372 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6373 {
6374   // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6375   if (text.isEmpty()) return;
6376   QSize finalSize;
6377   QPointF labelAnchor;
6378   switch (type)
6379   {
6380     case QCPAxis::atLeft:   labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
6381     case QCPAxis::atRight:  labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
6382     case QCPAxis::atTop:    labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
6383     case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
6384   }
6385   if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6386   {
6387     CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
6388     if (!cachedLabel)  // no cached label existed, create it
6389     {
6390       cachedLabel = new CachedLabel;
6391       TickLabelData labelData = getTickLabelData(painter->font(), text);
6392       cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
6393       cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6394       cachedLabel->pixmap.fill(Qt::transparent);
6395       QCPPainter cachePainter(&cachedLabel->pixmap);
6396       cachePainter.setPen(painter->pen());
6397       drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6398     }
6399     // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6400     bool labelClippedByBorder = false;
6401     if (tickLabelSide == QCPAxis::lsOutside)
6402     {
6403       if (QCPAxis::orientation(type) == Qt::Horizontal)
6404         labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
6405       else
6406         labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
6407     }
6408     if (!labelClippedByBorder)
6409     {
6410       painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6411       finalSize = cachedLabel->pixmap.size();
6412     }
6413     mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
6414   } else // label caching disabled, draw text directly on surface:
6415   {
6416     TickLabelData labelData = getTickLabelData(painter->font(), text);
6417     QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6418     // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6419      bool labelClippedByBorder = false;
6420     if (tickLabelSide == QCPAxis::lsOutside)
6421     {
6422       if (QCPAxis::orientation(type) == Qt::Horizontal)
6423         labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
6424       else
6425         labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
6426     }
6427     if (!labelClippedByBorder)
6428     {
6429       drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6430       finalSize = labelData.rotatedTotalBounds.size();
6431     }
6432   }
6433 
6434   // expand passed tickLabelsSize if current tick label is larger:
6435   if (finalSize.width() > tickLabelsSize->width())
6436     tickLabelsSize->setWidth(finalSize.width());
6437   if (finalSize.height() > tickLabelsSize->height())
6438     tickLabelsSize->setHeight(finalSize.height());
6439 }
6440 
6441 /*! \internal
6442 
6443   This is a \ref placeTickLabel helper function.
6444 
6445   Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
6446   y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
6447   directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
6448   QCP::phCacheLabels plotting hint is not set.
6449 */
drawTickLabel(QCPPainter * painter,double x,double y,const TickLabelData & labelData) const6450 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6451 {
6452   // backup painter settings that we're about to change:
6453   QTransform oldTransform = painter->transform();
6454   QFont oldFont = painter->font();
6455 
6456   // transform painter to position/rotation:
6457   painter->translate(x, y);
6458   if (!qFuzzyIsNull(tickLabelRotation))
6459     painter->rotate(tickLabelRotation);
6460 
6461   // draw text:
6462   if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6463   {
6464     painter->setFont(labelData.baseFont);
6465     painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6466     painter->setFont(labelData.expFont);
6467     painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip,  labelData.expPart);
6468   } else
6469   {
6470     painter->setFont(labelData.baseFont);
6471     painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6472   }
6473 
6474   // reset painter settings to what it was before:
6475   painter->setTransform(oldTransform);
6476   painter->setFont(oldFont);
6477 }
6478 
6479 /*! \internal
6480 
6481   This is a \ref placeTickLabel helper function.
6482 
6483   Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
6484   processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
6485   exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.
6486 */
getTickLabelData(const QFont & font,const QString & text) const6487 QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const
6488 {
6489   TickLabelData result;
6490 
6491   // determine whether beautiful decimal powers should be used
6492   bool useBeautifulPowers = false;
6493   int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart
6494   int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart
6495   if (substituteExponent)
6496   {
6497     ePos = text.indexOf(QLatin1Char('e'));
6498     if (ePos > 0 && text.at(ePos-1).isDigit())
6499     {
6500       eLast = ePos;
6501       while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
6502         ++eLast;
6503       if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power
6504         useBeautifulPowers = true;
6505     }
6506   }
6507 
6508   // calculate text bounding rects and do string preparation for beautiful decimal powers:
6509   result.baseFont = font;
6510   if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
6511     result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
6512   if (useBeautifulPowers)
6513   {
6514     // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6515     result.basePart = text.left(ePos);
6516     // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6517     if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6518       result.basePart = QLatin1String("10");
6519     else
6520       result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
6521     result.expPart = text.mid(ePos+1);
6522     // clip "+" and leading zeros off expPart:
6523     while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
6524       result.expPart.remove(1, 1);
6525     if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
6526       result.expPart.remove(0, 1);
6527     // prepare smaller font for exponent:
6528     result.expFont = font;
6529     if (result.expFont.pointSize() > 0)
6530       result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6531     else
6532       result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
6533     // calculate bounding rects of base part, exponent part and total one:
6534     result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6535     result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6536     result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA
6537   } else // useBeautifulPowers == false
6538   {
6539     result.basePart = text;
6540     result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6541   }
6542   result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler
6543 
6544   // calculate possibly different bounding rect after rotation:
6545   result.rotatedTotalBounds = result.totalBounds;
6546   if (!qFuzzyIsNull(tickLabelRotation))
6547   {
6548     QTransform transform;
6549     transform.rotate(tickLabelRotation);
6550     result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6551   }
6552 
6553   return result;
6554 }
6555 
6556 /*! \internal
6557 
6558   This is a \ref placeTickLabel helper function.
6559 
6560   Calculates the offset at which the top left corner of the specified tick label shall be drawn.
6561   The offset is relative to a point right next to the tick the label belongs to.
6562 
6563   This function is thus responsible for e.g. centering tick labels under ticks and positioning them
6564   appropriately when they are rotated.
6565 */
getTickLabelDrawOffset(const TickLabelData & labelData) const6566 QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const
6567 {
6568   /*
6569     calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6570     explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6571     horizontally under the corresponding tick is always on the label side that is closer to the
6572     axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6573     is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6574     will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6575     time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6576     labels.
6577   */
6578   bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6579   bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6580   double radians = tickLabelRotation/180.0*M_PI;
6581   int x=0, y=0;
6582   if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
6583   {
6584     if (doRotation)
6585     {
6586       if (tickLabelRotation > 0)
6587       {
6588         x = -qCos(radians)*labelData.totalBounds.width();
6589         y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6590       } else
6591       {
6592         x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6593         y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6594       }
6595     } else
6596     {
6597       x = -labelData.totalBounds.width();
6598       y = -labelData.totalBounds.height()/2.0;
6599     }
6600   } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
6601   {
6602     if (doRotation)
6603     {
6604       if (tickLabelRotation > 0)
6605       {
6606         x = +qSin(radians)*labelData.totalBounds.height();
6607         y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6608       } else
6609       {
6610         x = 0;
6611         y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6612       }
6613     } else
6614     {
6615       x = 0;
6616       y = -labelData.totalBounds.height()/2.0;
6617     }
6618   } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
6619   {
6620     if (doRotation)
6621     {
6622       if (tickLabelRotation > 0)
6623       {
6624         x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6625         y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6626       } else
6627       {
6628         x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6629         y = -qCos(-radians)*labelData.totalBounds.height();
6630       }
6631     } else
6632     {
6633       x = -labelData.totalBounds.width()/2.0;
6634       y = -labelData.totalBounds.height();
6635     }
6636   } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
6637   {
6638     if (doRotation)
6639     {
6640       if (tickLabelRotation > 0)
6641       {
6642         x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6643         y = 0;
6644       } else
6645       {
6646         x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6647         y = +qSin(-radians)*labelData.totalBounds.width();
6648       }
6649     } else
6650     {
6651       x = -labelData.totalBounds.width()/2.0;
6652       y = 0;
6653     }
6654   }
6655 
6656   return QPointF(x, y);
6657 }
6658 
6659 /*! \internal
6660 
6661   Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
6662   to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
6663   margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
6664   smaller width/height.
6665 */
getMaxTickLabelSize(const QFont & font,const QString & text,QSize * tickLabelsSize) const6666 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text,  QSize *tickLabelsSize) const
6667 {
6668   // note: this function must return the same tick label sizes as the placeTickLabel function.
6669   QSize finalSize;
6670   if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6671   {
6672     const CachedLabel *cachedLabel = mLabelCache.object(text);
6673     finalSize = cachedLabel->pixmap.size();
6674   } else // label caching disabled or no label with this text cached:
6675   {
6676     TickLabelData labelData = getTickLabelData(font, text);
6677     finalSize = labelData.rotatedTotalBounds.size();
6678   }
6679 
6680   // expand passed tickLabelsSize if current tick label is larger:
6681   if (finalSize.width() > tickLabelsSize->width())
6682     tickLabelsSize->setWidth(finalSize.width());
6683   if (finalSize.height() > tickLabelsSize->height())
6684     tickLabelsSize->setHeight(finalSize.height());
6685 }
6686 
6687 
6688 ////////////////////////////////////////////////////////////////////////////////////////////////////
6689 //////////////////// QCPAbstractPlottable
6690 ////////////////////////////////////////////////////////////////////////////////////////////////////
6691 
6692 /*! \class QCPAbstractPlottable
6693   \brief The abstract base class for all data representing objects in a plot.
6694 
6695   It defines a very basic interface like name, pen, brush, visibility etc. Since this class is
6696   abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself to
6697   create new ways of displaying data (see "Creating own plottables" below).
6698 
6699   All further specifics are in the subclasses, for example:
6700   \li A normal graph with possibly a line, scatter points and error bars: \ref QCPGraph
6701   (typically created with \ref QCustomPlot::addGraph)
6702   \li A parametric curve: \ref QCPCurve
6703   \li A bar chart: \ref QCPBars
6704   \li A statistical box plot: \ref QCPStatisticalBox
6705   \li A color encoded two-dimensional map: \ref QCPColorMap
6706   \li An OHLC/Candlestick chart: \ref QCPFinancial
6707 
6708   \section plottables-subclassing Creating own plottables
6709 
6710   To create an own plottable, you implement a subclass of QCPAbstractPlottable. These are the pure
6711   virtual functions, you must implement:
6712   \li \ref clearData
6713   \li \ref selectTest
6714   \li \ref draw
6715   \li \ref drawLegendIcon
6716   \li \ref getKeyRange
6717   \li \ref getValueRange
6718 
6719   See the documentation of those functions for what they need to do.
6720 
6721   For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot
6722   coordinates to pixel coordinates. This function is quite convenient, because it takes the
6723   orientation of the key and value axes into account for you (x and y are swapped when the key axis
6724   is vertical and the value axis horizontal). If you are worried about performance (i.e. you need
6725   to translate many points in a loop like QCPGraph), you can directly use \ref
6726   QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis
6727   yourself.
6728 
6729   Here are some important members you inherit from QCPAbstractPlottable:
6730   <table>
6731   <tr>
6732     <td>QCustomPlot *\b mParentPlot</td>
6733     <td>A pointer to the parent QCustomPlot instance. The parent plot is inferred from the axes that are passed in the constructor.</td>
6734   </tr><tr>
6735     <td>QString \b mName</td>
6736     <td>The name of the plottable.</td>
6737   </tr><tr>
6738     <td>QPen \b mPen</td>
6739     <td>The generic pen of the plottable. You should use this pen for the most prominent data representing lines in the plottable (e.g QCPGraph uses this pen for its graph lines and scatters)</td>
6740   </tr><tr>
6741     <td>QPen \b mSelectedPen</td>
6742     <td>The generic pen that should be used when the plottable is selected (hint: \ref mainPen gives you the right pen, depending on selection state).</td>
6743   </tr><tr>
6744     <td>QBrush \b mBrush</td>
6745     <td>The generic brush of the plottable. You should use this brush for the most prominent fillable structures in the plottable (e.g. QCPGraph uses this brush to control filling under the graph)</td>
6746   </tr><tr>
6747     <td>QBrush \b mSelectedBrush</td>
6748     <td>The generic brush that should be used when the plottable is selected (hint: \ref mainBrush gives you the right brush, depending on selection state).</td>
6749   </tr><tr>
6750     <td>QPointer<QCPAxis>\b mKeyAxis, \b mValueAxis</td>
6751     <td>The key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates to pixels in either the key or value dimension.
6752         Make sure to check whether the pointer is null before using it. If one of the axes is null, don't draw the plottable.</td>
6753   </tr><tr>
6754     <td>bool \b mSelected</td>
6755     <td>indicates whether the plottable is selected or not.</td>
6756   </tr>
6757   </table>
6758 */
6759 
6760 /* start of documentation of pure virtual functions */
6761 
6762 /*! \fn void QCPAbstractPlottable::clearData() = 0
6763   Clears all data in the plottable.
6764 */
6765 
6766 /*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0
6767   \internal
6768 
6769   called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation
6770   of this plottable inside \a rect, next to the plottable name.
6771 
6772   The passed \a painter has its cliprect set to \a rect, so painting outside of \a rect won't
6773   appear outside the legend icon border.
6774 */
6775 
6776 /*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0
6777   \internal
6778 
6779   called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
6780   set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6781   returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6782   to \ref sdNegative and all positive points will be ignored for range calculation. For no
6783   restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6784   parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6785   use the returned range (e.g. no points in data).
6786 
6787   Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6788   this function may have size zero, which wouldn't count as a valid range.
6789 
6790   \see rescaleAxes, getValueRange
6791 */
6792 
6793 /*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, SignDomain inSignDomain) const = 0
6794   \internal
6795 
6796   called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
6797   set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6798   returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6799   to \ref sdNegative and all positive points will be ignored for range calculation. For no
6800   restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6801   parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6802   use the returned range (e.g. no points in data).
6803 
6804   Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6805   this function may have size zero, which wouldn't count as a valid range.
6806 
6807   \see rescaleAxes, getKeyRange
6808 */
6809 
6810 /* end of documentation of pure virtual functions */
6811 /* start of documentation of signals */
6812 
6813 /*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
6814 
6815   This signal is emitted when the selection state of this plottable has changed, either by user
6816   interaction or by a direct call to \ref setSelected.
6817 */
6818 
6819 /*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable);
6820 
6821   This signal is emitted when the selectability of this plottable has changed.
6822 
6823   \see setSelectable
6824 */
6825 
6826 /* end of documentation of signals */
6827 
6828 /*!
6829   Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as
6830   its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance
6831   and have perpendicular orientations. If either of these restrictions is violated, a corresponding
6832   message is printed to the debug output (qDebug), the construction is not aborted, though.
6833 
6834   Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables,
6835   it can't be directly instantiated.
6836 
6837   You probably want one of the subclasses like \ref QCPGraph or \ref QCPCurve instead.
6838 */
QCPAbstractPlottable(QCPAxis * keyAxis,QCPAxis * valueAxis)6839 QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
6840   QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
6841   mName(),
6842   mAntialiasedFill(true),
6843   mAntialiasedScatters(true),
6844   mAntialiasedErrorBars(false),
6845   mPen(Qt::black),
6846   mSelectedPen(Qt::black),
6847   mBrush(Qt::NoBrush),
6848   mSelectedBrush(Qt::NoBrush),
6849   mKeyAxis(keyAxis),
6850   mValueAxis(valueAxis),
6851   mSelectable(true),
6852   mSelected(false)
6853 {
6854   if (keyAxis->parentPlot() != valueAxis->parentPlot())
6855     qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6856   if (keyAxis->orientation() == valueAxis->orientation())
6857     qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6858 }
6859 
6860 /*!
6861    The name is the textual representation of this plottable as it is displayed in the legend
6862    (\ref QCPLegend). It may contain any UTF-8 characters, including newlines.
6863 */
setName(const QString & name)6864 void QCPAbstractPlottable::setName(const QString &name)
6865 {
6866   mName = name;
6867 }
6868 
6869 /*!
6870   Sets whether fills of this plottable are drawn antialiased or not.
6871 
6872   Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6873   QCustomPlot::setNotAntialiasedElements.
6874 */
setAntialiasedFill(bool enabled)6875 void QCPAbstractPlottable::setAntialiasedFill(bool enabled)
6876 {
6877   mAntialiasedFill = enabled;
6878 }
6879 
6880 /*!
6881   Sets whether the scatter symbols of this plottable are drawn antialiased or not.
6882 
6883   Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6884   QCustomPlot::setNotAntialiasedElements.
6885 */
setAntialiasedScatters(bool enabled)6886 void QCPAbstractPlottable::setAntialiasedScatters(bool enabled)
6887 {
6888   mAntialiasedScatters = enabled;
6889 }
6890 
6891 /*!
6892   Sets whether the error bars of this plottable are drawn antialiased or not.
6893 
6894   Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6895   QCustomPlot::setNotAntialiasedElements.
6896 */
setAntialiasedErrorBars(bool enabled)6897 void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled)
6898 {
6899   mAntialiasedErrorBars = enabled;
6900 }
6901 
6902 
6903 /*!
6904   The pen is used to draw basic lines that make up the plottable representation in the
6905   plot.
6906 
6907   For example, the \ref QCPGraph subclass draws its graph lines with this pen.
6908 
6909   \see setBrush
6910 */
setPen(const QPen & pen)6911 void QCPAbstractPlottable::setPen(const QPen &pen)
6912 {
6913   mPen = pen;
6914 }
6915 
6916 /*!
6917   When the plottable is selected, this pen is used to draw basic lines instead of the normal
6918   pen set via \ref setPen.
6919 
6920   \see setSelected, setSelectable, setSelectedBrush, selectTest
6921 */
setSelectedPen(const QPen & pen)6922 void QCPAbstractPlottable::setSelectedPen(const QPen &pen)
6923 {
6924   mSelectedPen = pen;
6925 }
6926 
6927 /*!
6928   The brush is used to draw basic fills of the plottable representation in the
6929   plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
6930 
6931   For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when
6932   it's not set to Qt::NoBrush.
6933 
6934   \see setPen
6935 */
setBrush(const QBrush & brush)6936 void QCPAbstractPlottable::setBrush(const QBrush &brush)
6937 {
6938   mBrush = brush;
6939 }
6940 
6941 /*!
6942   When the plottable is selected, this brush is used to draw fills instead of the normal
6943   brush set via \ref setBrush.
6944 
6945   \see setSelected, setSelectable, setSelectedPen, selectTest
6946 */
setSelectedBrush(const QBrush & brush)6947 void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush)
6948 {
6949   mSelectedBrush = brush;
6950 }
6951 
6952 /*!
6953   The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal
6954   to the plottable's value axis. This function performs no checks to make sure this is the case.
6955   The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the
6956   y-axis (QCustomPlot::yAxis) as value axis.
6957 
6958   Normally, the key and value axes are set in the constructor of the plottable (or \ref
6959   QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
6960 
6961   \see setValueAxis
6962 */
setKeyAxis(QCPAxis * axis)6963 void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)
6964 {
6965   mKeyAxis = axis;
6966 }
6967 
6968 /*!
6969   The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is
6970   orthogonal to the plottable's key axis. This function performs no checks to make sure this is the
6971   case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and
6972   the y-axis (QCustomPlot::yAxis) as value axis.
6973 
6974   Normally, the key and value axes are set in the constructor of the plottable (or \ref
6975   QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
6976 
6977   \see setKeyAxis
6978 */
setValueAxis(QCPAxis * axis)6979 void QCPAbstractPlottable::setValueAxis(QCPAxis *axis)
6980 {
6981   mValueAxis = axis;
6982 }
6983 
6984 /*!
6985   Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
6986   (When \ref QCustomPlot::setInteractions contains iSelectPlottables.)
6987 
6988   However, even when \a selectable was set to false, it is possible to set the selection manually,
6989   by calling \ref setSelected directly.
6990 
6991   \see setSelected
6992 */
setSelectable(bool selectable)6993 void QCPAbstractPlottable::setSelectable(bool selectable)
6994 {
6995   if (mSelectable != selectable)
6996   {
6997     mSelectable = selectable;
6998     emit selectableChanged(mSelectable);
6999   }
7000 }
7001 
7002 /*!
7003   Sets whether this plottable is selected or not. When selected, it uses a different pen and brush
7004   to draw its lines and fills, see \ref setSelectedPen and \ref setSelectedBrush.
7005 
7006   The entire selection mechanism for plottables is handled automatically when \ref
7007   QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when
7008   you wish to change the selection state manually.
7009 
7010   This function can change the selection state even when \ref setSelectable was set to false.
7011 
7012   emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
7013 
7014   \see setSelectable, selectTest
7015 */
setSelected(bool selected)7016 void QCPAbstractPlottable::setSelected(bool selected)
7017 {
7018   if (mSelected != selected)
7019   {
7020     mSelected = selected;
7021     emit selectionChanged(mSelected);
7022   }
7023 }
7024 
7025 /*!
7026   Rescales the key and value axes associated with this plottable to contain all displayed data, so
7027   the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make
7028   sure not to rescale to an illegal range i.e. a range containing different signs and/or zero.
7029   Instead it will stay in the current sign domain and ignore all parts of the plottable that lie
7030   outside of that domain.
7031 
7032   \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show
7033   multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has
7034   \a onlyEnlarge set to false (the default), and all subsequent set to true.
7035 
7036   \see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes, QCPAxis::rescale
7037 */
rescaleAxes(bool onlyEnlarge) const7038 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7039 {
7040   rescaleKeyAxis(onlyEnlarge);
7041   rescaleValueAxis(onlyEnlarge);
7042 }
7043 
7044 /*!
7045   Rescales the key axis of the plottable so the whole plottable is visible.
7046 
7047   See \ref rescaleAxes for detailed behaviour.
7048 */
rescaleKeyAxis(bool onlyEnlarge) const7049 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7050 {
7051   QCPAxis *keyAxis = mKeyAxis.data();
7052   if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7053 
7054   SignDomain signDomain = sdBoth;
7055   if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7056     signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7057 
7058   bool foundRange;
7059   QCPRange newRange = getKeyRange(foundRange, signDomain);
7060   if (foundRange)
7061   {
7062     if (onlyEnlarge)
7063       newRange.expand(keyAxis->range());
7064     if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
7065     {
7066       double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
7067       if (keyAxis->scaleType() == QCPAxis::stLinear)
7068       {
7069         newRange.lower = center-keyAxis->range().size()/2.0;
7070         newRange.upper = center+keyAxis->range().size()/2.0;
7071       } else // scaleType() == stLogarithmic
7072       {
7073         newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7074         newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7075       }
7076     }
7077     keyAxis->setRange(newRange);
7078   }
7079 }
7080 
7081 /*!
7082   Rescales the value axis of the plottable so the whole plottable is visible.
7083 
7084   Returns true if the axis was actually scaled. This might not be the case if this plottable has an
7085   invalid range, e.g. because it has no data points.
7086 
7087   See \ref rescaleAxes for detailed behaviour.
7088 */
rescaleValueAxis(bool onlyEnlarge) const7089 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7090 {
7091   QCPAxis *valueAxis = mValueAxis.data();
7092   if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7093 
7094   SignDomain signDomain = sdBoth;
7095   if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7096     signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7097 
7098   bool foundRange;
7099   QCPRange newRange = getValueRange(foundRange, signDomain);
7100   if (foundRange)
7101   {
7102     if (onlyEnlarge)
7103       newRange.expand(valueAxis->range());
7104     if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
7105     {
7106       double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
7107       if (valueAxis->scaleType() == QCPAxis::stLinear)
7108       {
7109         newRange.lower = center-valueAxis->range().size()/2.0;
7110         newRange.upper = center+valueAxis->range().size()/2.0;
7111       } else // scaleType() == stLogarithmic
7112       {
7113         newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7114         newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7115       }
7116     }
7117     valueAxis->setRange(newRange);
7118   }
7119 }
7120 
7121 /*!
7122   Adds this plottable to the legend of the parent QCustomPlot (QCustomPlot::legend).
7123 
7124   Normally, a QCPPlottableLegendItem is created and inserted into the legend. If the plottable
7125   needs a more specialized representation in the legend, this function will take this into account
7126   and instead create the specialized subclass of QCPAbstractLegendItem.
7127 
7128   Returns true on success, i.e. when the legend exists and a legend item associated with this plottable isn't already in
7129   the legend.
7130 
7131   \see removeFromLegend, QCPLegend::addItem
7132 */
addToLegend()7133 bool QCPAbstractPlottable::addToLegend()
7134 {
7135   if (!mParentPlot || !mParentPlot->legend)
7136     return false;
7137 
7138   if (!mParentPlot->legend->hasItemWithPlottable(this))
7139   {
7140     mParentPlot->legend->addItem(new QCPPlottableLegendItem(mParentPlot->legend, this));
7141     return true;
7142   } else
7143     return false;
7144 }
7145 
7146 /*!
7147   Removes the plottable from the legend of the parent QCustomPlot. This means the
7148   QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable
7149   is removed.
7150 
7151   Returns true on success, i.e. if the legend exists and a legend item associated with this
7152   plottable was found and removed.
7153 
7154   \see addToLegend, QCPLegend::removeItem
7155 */
removeFromLegend() const7156 bool QCPAbstractPlottable::removeFromLegend() const
7157 {
7158   if (!mParentPlot->legend)
7159     return false;
7160 
7161   if (QCPPlottableLegendItem *lip = mParentPlot->legend->itemWithPlottable(this))
7162     return mParentPlot->legend->removeItem(lip);
7163   else
7164     return false;
7165 }
7166 
7167 /* inherits documentation from base class */
clipRect() const7168 QRect QCPAbstractPlottable::clipRect() const
7169 {
7170   if (mKeyAxis && mValueAxis)
7171     return mKeyAxis->axisRect()->rect() & mValueAxis->axisRect()->rect();
7172   else
7173     return QRect();
7174 }
7175 
7176 /* inherits documentation from base class */
selectionCategory() const7177 QCP::Interaction QCPAbstractPlottable::selectionCategory() const
7178 {
7179   return QCP::iSelectPlottables;
7180 }
7181 
7182 /*! \internal
7183 
7184   Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface,
7185   taking the orientations of the axes associated with this plottable into account (e.g. whether key
7186   represents x or y).
7187 
7188   \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y.
7189 
7190   \see pixelsToCoords, QCPAxis::coordToPixel
7191 */
coordsToPixels(double key,double value,double & x,double & y) const7192 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7193 {
7194   QCPAxis *keyAxis = mKeyAxis.data();
7195   QCPAxis *valueAxis = mValueAxis.data();
7196   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7197 
7198   if (keyAxis->orientation() == Qt::Horizontal)
7199   {
7200     x = keyAxis->coordToPixel(key);
7201     y = valueAxis->coordToPixel(value);
7202   } else
7203   {
7204     y = keyAxis->coordToPixel(key);
7205     x = valueAxis->coordToPixel(value);
7206   }
7207 }
7208 
7209 /*! \internal
7210   \overload
7211 
7212   Returns the input as pixel coordinates in a QPointF.
7213 */
coordsToPixels(double key,double value) const7214 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7215 {
7216   QCPAxis *keyAxis = mKeyAxis.data();
7217   QCPAxis *valueAxis = mValueAxis.data();
7218   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7219 
7220   if (keyAxis->orientation() == Qt::Horizontal)
7221     return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7222   else
7223     return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7224 }
7225 
7226 /*! \internal
7227 
7228   Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates,
7229   taking the orientations of the axes associated with this plottable into account (e.g. whether key
7230   represents x or y).
7231 
7232   \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value.
7233 
7234   \see coordsToPixels, QCPAxis::coordToPixel
7235 */
pixelsToCoords(double x,double y,double & key,double & value) const7236 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7237 {
7238   QCPAxis *keyAxis = mKeyAxis.data();
7239   QCPAxis *valueAxis = mValueAxis.data();
7240   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7241 
7242   if (keyAxis->orientation() == Qt::Horizontal)
7243   {
7244     key = keyAxis->pixelToCoord(x);
7245     value = valueAxis->pixelToCoord(y);
7246   } else
7247   {
7248     key = keyAxis->pixelToCoord(y);
7249     value = valueAxis->pixelToCoord(x);
7250   }
7251 }
7252 
7253 /*! \internal
7254   \overload
7255 
7256   Returns the pixel input \a pixelPos as plot coordinates \a key and \a value.
7257 */
pixelsToCoords(const QPointF & pixelPos,double & key,double & value) const7258 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7259 {
7260   pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7261 }
7262 
7263 /*! \internal
7264 
7265   Returns the pen that should be used for drawing lines of the plottable. Returns mPen when the
7266   graph is not selected and mSelectedPen when it is.
7267 */
mainPen() const7268 QPen QCPAbstractPlottable::mainPen() const
7269 {
7270   return mSelected ? mSelectedPen : mPen;
7271 }
7272 
7273 /*! \internal
7274 
7275   Returns the brush that should be used for drawing fills of the plottable. Returns mBrush when the
7276   graph is not selected and mSelectedBrush when it is.
7277 */
mainBrush() const7278 QBrush QCPAbstractPlottable::mainBrush() const
7279 {
7280   return mSelected ? mSelectedBrush : mBrush;
7281 }
7282 
7283 /*! \internal
7284 
7285   A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7286   before drawing plottable lines.
7287 
7288   This is the antialiasing state the painter passed to the \ref draw method is in by default.
7289 
7290   This function takes into account the local setting of the antialiasing flag as well as the
7291   overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7292   QCustomPlot::setNotAntialiasedElements.
7293 
7294   \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7295 */
applyDefaultAntialiasingHint(QCPPainter * painter) const7296 void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const
7297 {
7298   applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
7299 }
7300 
7301 /*! \internal
7302 
7303   A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7304   before drawing plottable fills.
7305 
7306   This function takes into account the local setting of the antialiasing flag as well as the
7307   overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7308   QCustomPlot::setNotAntialiasedElements.
7309 
7310   \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7311 */
applyFillAntialiasingHint(QCPPainter * painter) const7312 void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const
7313 {
7314   applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
7315 }
7316 
7317 /*! \internal
7318 
7319   A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7320   before drawing plottable scatter points.
7321 
7322   This function takes into account the local setting of the antialiasing flag as well as the
7323   overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7324   QCustomPlot::setNotAntialiasedElements.
7325 
7326   \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint, applyErrorBarsAntialiasingHint
7327 */
applyScattersAntialiasingHint(QCPPainter * painter) const7328 void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const
7329 {
7330   applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
7331 }
7332 
7333 /*! \internal
7334 
7335   A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7336   before drawing plottable error bars.
7337 
7338   This function takes into account the local setting of the antialiasing flag as well as the
7339   overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7340   QCustomPlot::setNotAntialiasedElements.
7341 
7342   \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyDefaultAntialiasingHint
7343 */
applyErrorBarsAntialiasingHint(QCPPainter * painter) const7344 void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(QCPPainter *painter) const
7345 {
7346   applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars);
7347 }
7348 
7349 /*! \internal
7350 
7351   Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
7352   end.
7353 
7354   This function may be used to help with the implementation of the \ref selectTest function for
7355   specific plottables.
7356 
7357   \note This function is identical to QCPAbstractItem::distSqrToLine
7358 */
distSqrToLine(const QPointF & start,const QPointF & end,const QPointF & point) const7359 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7360 {
7361   QVector2D a(start);
7362   QVector2D b(end);
7363   QVector2D p(point);
7364   QVector2D v(b-a);
7365 
7366   double vLengthSqr = v.lengthSquared();
7367   if (!qFuzzyIsNull(vLengthSqr))
7368   {
7369     double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7370     if (mu < 0)
7371       return (a-p).lengthSquared();
7372     else if (mu > 1)
7373       return (b-p).lengthSquared();
7374     else
7375       return ((a + mu*v)-p).lengthSquared();
7376   } else
7377     return (a-p).lengthSquared();
7378 }
7379 
7380 /* inherits documentation from base class */
selectEvent(QMouseEvent * event,bool additive,const QVariant & details,bool * selectionStateChanged)7381 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7382 {
7383   Q_UNUSED(event)
7384   Q_UNUSED(details)
7385   if (mSelectable)
7386   {
7387     bool selBefore = mSelected;
7388     setSelected(additive ? !mSelected : true);
7389     if (selectionStateChanged)
7390       *selectionStateChanged = mSelected != selBefore;
7391   }
7392 }
7393 
7394 /* inherits documentation from base class */
deselectEvent(bool * selectionStateChanged)7395 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7396 {
7397   if (mSelectable)
7398   {
7399     bool selBefore = mSelected;
7400     setSelected(false);
7401     if (selectionStateChanged)
7402       *selectionStateChanged = mSelected != selBefore;
7403   }
7404 }
7405 
7406 
7407 ////////////////////////////////////////////////////////////////////////////////////////////////////
7408 //////////////////// QCPItemAnchor
7409 ////////////////////////////////////////////////////////////////////////////////////////////////////
7410 
7411 /*! \class QCPItemAnchor
7412   \brief An anchor of an item to which positions can be attached to.
7413 
7414   An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't
7415   control anything on its item, but provides a way to tie other items via their positions to the
7416   anchor.
7417 
7418   For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight.
7419   Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can
7420   attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by
7421   calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the
7422   QCPItemRect. This way the start of the line will now always follow the respective anchor location
7423   on the rect item.
7424 
7425   Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an
7426   anchor to other positions.
7427 
7428   To learn how to provide anchors in your own item subclasses, see the subclassing section of the
7429   QCPAbstractItem documentation.
7430 */
7431 
7432 /* start documentation of inline functions */
7433 
7434 /*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition()
7435 
7436   Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if
7437   it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor).
7438 
7439   This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids
7440   dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with
7441   gcc compiler).
7442 */
7443 
7444 /* end documentation of inline functions */
7445 
7446 /*!
7447   Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if
7448   you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as
7449   explained in the subclassing section of the QCPAbstractItem documentation.
7450 */
QCPItemAnchor(QCustomPlot * parentPlot,QCPAbstractItem * parentItem,const QString name,int anchorId)7451 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7452   mName(name),
7453   mParentPlot(parentPlot),
7454   mParentItem(parentItem),
7455   mAnchorId(anchorId)
7456 {
7457 }
7458 
~QCPItemAnchor()7459 QCPItemAnchor::~QCPItemAnchor()
7460 {
7461   // unregister as parent at children:
7462   for (auto* child : mChildrenX.toList())
7463   {
7464     if (child->parentAnchorX() == this)
7465       child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7466   }
7467   for (auto* child : mChildrenY.toList())
7468   {
7469     if (child->parentAnchorY() == this)
7470       child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7471   }
7472 }
7473 
7474 /*!
7475   Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
7476 
7477   The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the
7478   parent item, QCPItemAnchor is just an intermediary.
7479 */
pixelPoint() const7480 QPointF QCPItemAnchor::pixelPoint() const
7481 {
7482   if (mParentItem)
7483   {
7484     if (mAnchorId > -1)
7485     {
7486       return mParentItem->anchorPixelPoint(mAnchorId);
7487     } else
7488     {
7489       qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7490       return QPointF();
7491     }
7492   } else
7493   {
7494     qDebug() << Q_FUNC_INFO << "no parent item set";
7495     return QPointF();
7496   }
7497 }
7498 
7499 /*! \internal
7500 
7501   Adds \a pos to the childX list of this anchor, which keeps track of which children use this
7502   anchor as parent anchor for the respective coordinate. This is necessary to notify the children
7503   prior to destruction of the anchor.
7504 
7505   Note that this function does not change the parent setting in \a pos.
7506 */
addChildX(QCPItemPosition * pos)7507 void QCPItemAnchor::addChildX(QCPItemPosition *pos)
7508 {
7509   if (!mChildrenX.contains(pos))
7510     mChildrenX.insert(pos);
7511   else
7512     qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7513 }
7514 
7515 /*! \internal
7516 
7517   Removes \a pos from the childX list of this anchor.
7518 
7519   Note that this function does not change the parent setting in \a pos.
7520 */
removeChildX(QCPItemPosition * pos)7521 void QCPItemAnchor::removeChildX(QCPItemPosition *pos)
7522 {
7523   if (!mChildrenX.remove(pos))
7524     qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7525 }
7526 
7527 /*! \internal
7528 
7529   Adds \a pos to the childY list of this anchor, which keeps track of which children use this
7530   anchor as parent anchor for the respective coordinate. This is necessary to notify the children
7531   prior to destruction of the anchor.
7532 
7533   Note that this function does not change the parent setting in \a pos.
7534 */
addChildY(QCPItemPosition * pos)7535 void QCPItemAnchor::addChildY(QCPItemPosition *pos)
7536 {
7537   if (!mChildrenY.contains(pos))
7538     mChildrenY.insert(pos);
7539   else
7540     qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7541 }
7542 
7543 /*! \internal
7544 
7545   Removes \a pos from the childY list of this anchor.
7546 
7547   Note that this function does not change the parent setting in \a pos.
7548 */
removeChildY(QCPItemPosition * pos)7549 void QCPItemAnchor::removeChildY(QCPItemPosition *pos)
7550 {
7551   if (!mChildrenY.remove(pos))
7552     qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7553 }
7554 
7555 
7556 ////////////////////////////////////////////////////////////////////////////////////////////////////
7557 //////////////////// QCPItemPosition
7558 ////////////////////////////////////////////////////////////////////////////////////////////////////
7559 
7560 /*! \class QCPItemPosition
7561   \brief Manages the position of an item.
7562 
7563   Every item has at least one public QCPItemPosition member pointer which provides ways to position the
7564   item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two:
7565   \a topLeft and \a bottomRight.
7566 
7567   QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type
7568   defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel
7569   coordinates, as plot coordinates of certain axes, etc. For more advanced plots it is also
7570   possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref
7571   setTypeY). This way an item could be positioned at a fixed pixel distance from the top in the Y
7572   direction, while following a plot coordinate in the X direction.
7573 
7574   A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie
7575   multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords)
7576   are considered to be absolute pixels in the reference frame of the parent anchor, where (0, 0)
7577   means directly ontop of the parent anchor. For example, You could attach the \a start position of
7578   a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting point of the line
7579   always be centered under the text label, no matter where the text is moved to. For more advanced
7580   plots, it is possible to assign different parent anchors per X/Y coordinate of the position, see
7581   \ref setParentAnchorX, \ref setParentAnchorY. This way an item could follow another item in the X
7582   direction but stay at a fixed position in the Y direction. Or even follow item A in X, and item B
7583   in Y.
7584 
7585   Note that every QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent
7586   anchor for other positions.
7587 
7588   To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPoint. This
7589   works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref
7590   setPixelPoint transforms the coordinates appropriately, to make the position appear at the specified
7591   pixel values.
7592 */
7593 
7594 /* start documentation of inline functions */
7595 
7596 /*! \fn QCPItemPosition::PositionType *QCPItemPosition::type() const
7597 
7598   Returns the current position type.
7599 
7600   If different types were set for X and Y (\ref setTypeX, \ref setTypeY), this method returns the
7601   type of the X coordinate. In that case rather use \a typeX() and \a typeY().
7602 
7603   \see setType
7604 */
7605 
7606 /*! \fn QCPItemAnchor *QCPItemPosition::parentAnchor() const
7607 
7608   Returns the current parent anchor.
7609 
7610   If different parent anchors were set for X and Y (\ref setParentAnchorX, \ref setParentAnchorY),
7611   this method returns the parent anchor of the Y coordinate. In that case rather use \a
7612   parentAnchorX() and \a parentAnchorY().
7613 
7614   \see setParentAnchor
7615 */
7616 
7617 /* end documentation of inline functions */
7618 
7619 /*!
7620   Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if
7621   you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as
7622   explained in the subclassing section of the QCPAbstractItem documentation.
7623 */
QCPItemPosition(QCustomPlot * parentPlot,QCPAbstractItem * parentItem,const QString name)7624 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7625   QCPItemAnchor(parentPlot, parentItem, name),
7626   mPositionTypeX(ptAbsolute),
7627   mPositionTypeY(ptAbsolute),
7628   mKey(0),
7629   mValue(0),
7630   mParentAnchorX(0),
7631   mParentAnchorY(0)
7632 {
7633 }
7634 
~QCPItemPosition()7635 QCPItemPosition::~QCPItemPosition()
7636 {
7637   // unregister as parent at children:
7638   // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7639   //       the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7640   for (auto* child : mChildrenX.toList())
7641   {
7642     if (child->parentAnchorX() == this)
7643       child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7644   }
7645   for (auto* child : mChildrenY.toList())
7646   {
7647     if (child->parentAnchorY() == this)
7648       child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7649   }
7650   // unregister as child in parent:
7651   if (mParentAnchorX)
7652     mParentAnchorX->removeChildX(this);
7653   if (mParentAnchorY)
7654     mParentAnchorY->removeChildY(this);
7655 }
7656 
7657 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
axisRect() const7658 QCPAxisRect *QCPItemPosition::axisRect() const
7659 {
7660   return mAxisRect.data();
7661 }
7662 
7663 /*!
7664   Sets the type of the position. The type defines how the coordinates passed to \ref setCoords
7665   should be handled and how the QCPItemPosition should behave in the plot.
7666 
7667   The possible values for \a type can be separated in two main categories:
7668 
7669   \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords
7670   and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes.
7671   By default, the QCustomPlot's x- and yAxis are used.
7672 
7673   \li The position is fixed on the QCustomPlot surface, i.e. independent of axis ranges. This
7674   corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref
7675   ptAxisRectRatio. They differ only in the way the absolute position is described, see the
7676   documentation of \ref PositionType for details. For \ref ptAxisRectRatio, note that you can specify
7677   the axis rect with \ref setAxisRect. By default this is set to the main axis rect.
7678 
7679   Note that the position type \ref ptPlotCoords is only available (and sensible) when the position
7680   has no parent anchor (\ref setParentAnchor).
7681 
7682   If the type is changed, the apparent pixel position on the plot is preserved. This means
7683   the coordinates as retrieved with coords() and set with \ref setCoords may change in the process.
7684 
7685   This method sets the type for both X and Y directions. It is also possible to set different types
7686   for X and Y, see \ref setTypeX, \ref setTypeY.
7687 */
setType(QCPItemPosition::PositionType type)7688 void QCPItemPosition::setType(QCPItemPosition::PositionType type)
7689 {
7690   setTypeX(type);
7691   setTypeY(type);
7692 }
7693 
7694 /*!
7695   This method sets the position type of the X coordinate to \a type.
7696 
7697   For a detailed description of what a position type is, see the documentation of \ref setType.
7698 
7699   \see setType, setTypeY
7700 */
setTypeX(QCPItemPosition::PositionType type)7701 void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type)
7702 {
7703   if (mPositionTypeX != type)
7704   {
7705     // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7706     // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7707     bool retainPixelPosition = true;
7708     if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7709       retainPixelPosition = false;
7710     if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7711       retainPixelPosition = false;
7712 
7713     QPointF pixel;
7714     if (retainPixelPosition)
7715       pixel = pixelPoint();
7716 
7717     mPositionTypeX = type;
7718 
7719     if (retainPixelPosition)
7720       setPixelPoint(pixel);
7721   }
7722 }
7723 
7724 /*!
7725   This method sets the position type of the Y coordinate to \a type.
7726 
7727   For a detailed description of what a position type is, see the documentation of \ref setType.
7728 
7729   \see setType, setTypeX
7730 */
setTypeY(QCPItemPosition::PositionType type)7731 void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type)
7732 {
7733   if (mPositionTypeY != type)
7734   {
7735     // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7736     // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7737     bool retainPixelPosition = true;
7738     if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7739       retainPixelPosition = false;
7740     if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7741       retainPixelPosition = false;
7742 
7743     QPointF pixel;
7744     if (retainPixelPosition)
7745       pixel = pixelPoint();
7746 
7747     mPositionTypeY = type;
7748 
7749     if (retainPixelPosition)
7750       setPixelPoint(pixel);
7751   }
7752 }
7753 
7754 /*!
7755   Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now
7756   follow any position changes of the anchor. The local coordinate system of positions with a parent
7757   anchor always is absolute pixels, with (0, 0) being exactly on top of the parent anchor. (Hence
7758   the type shouldn't be set to \ref ptPlotCoords for positions with parent anchors.)
7759 
7760   if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved
7761   during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position
7762   will be exactly on top of the parent anchor.
7763 
7764   To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0.
7765 
7766   If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is
7767   set to \ref ptAbsolute, to keep the position in a valid state.
7768 
7769   This method sets the parent anchor for both X and Y directions. It is also possible to set
7770   different parents for X and Y, see \ref setParentAnchorX, \ref setParentAnchorY.
7771 */
setParentAnchor(QCPItemAnchor * parentAnchor,bool keepPixelPosition)7772 bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7773 {
7774   bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
7775   bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
7776   return successX && successY;
7777 }
7778 
7779 /*!
7780   This method sets the parent anchor of the X coordinate to \a parentAnchor.
7781 
7782   For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
7783 
7784   \see setParentAnchor, setParentAnchorY
7785 */
setParentAnchorX(QCPItemAnchor * parentAnchor,bool keepPixelPosition)7786 bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7787 {
7788   // make sure self is not assigned as parent:
7789   if (parentAnchor == this)
7790   {
7791     qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7792     return false;
7793   }
7794   // make sure no recursive parent-child-relationships are created:
7795   QCPItemAnchor *currentParent = parentAnchor;
7796   while (currentParent)
7797   {
7798     if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7799     {
7800       // is a QCPItemPosition, might have further parent, so keep iterating
7801       if (currentParentPos == this)
7802       {
7803         qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7804         return false;
7805       }
7806       currentParent = currentParentPos->parentAnchorX();
7807     } else
7808     {
7809       // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7810       // same, to prevent a position being child of an anchor which itself depends on the position,
7811       // because they're both on the same item:
7812       if (currentParent->mParentItem == mParentItem)
7813       {
7814         qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7815         return false;
7816       }
7817       break;
7818     }
7819   }
7820 
7821   // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7822   if (!mParentAnchorX && mPositionTypeX == ptPlotCoords)
7823     setTypeX(ptAbsolute);
7824 
7825   // save pixel position:
7826   QPointF pixelP;
7827   if (keepPixelPosition)
7828     pixelP = pixelPoint();
7829   // unregister at current parent anchor:
7830   if (mParentAnchorX)
7831     mParentAnchorX->removeChildX(this);
7832   // register at new parent anchor:
7833   if (parentAnchor)
7834     parentAnchor->addChildX(this);
7835   mParentAnchorX = parentAnchor;
7836   // restore pixel position under new parent:
7837   if (keepPixelPosition)
7838     setPixelPoint(pixelP);
7839   else
7840     setCoords(0, coords().y());
7841   return true;
7842 }
7843 
7844 /*!
7845   This method sets the parent anchor of the Y coordinate to \a parentAnchor.
7846 
7847   For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
7848 
7849   \see setParentAnchor, setParentAnchorX
7850 */
setParentAnchorY(QCPItemAnchor * parentAnchor,bool keepPixelPosition)7851 bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7852 {
7853   // make sure self is not assigned as parent:
7854   if (parentAnchor == this)
7855   {
7856     qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7857     return false;
7858   }
7859   // make sure no recursive parent-child-relationships are created:
7860   QCPItemAnchor *currentParent = parentAnchor;
7861   while (currentParent)
7862   {
7863     if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7864     {
7865       // is a QCPItemPosition, might have further parent, so keep iterating
7866       if (currentParentPos == this)
7867       {
7868         qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7869         return false;
7870       }
7871       currentParent = currentParentPos->parentAnchorY();
7872     } else
7873     {
7874       // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7875       // same, to prevent a position being child of an anchor which itself depends on the position,
7876       // because they're both on the same item:
7877       if (currentParent->mParentItem == mParentItem)
7878       {
7879         qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7880         return false;
7881       }
7882       break;
7883     }
7884   }
7885 
7886   // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7887   if (!mParentAnchorY && mPositionTypeY == ptPlotCoords)
7888     setTypeY(ptAbsolute);
7889 
7890   // save pixel position:
7891   QPointF pixelP;
7892   if (keepPixelPosition)
7893     pixelP = pixelPoint();
7894   // unregister at current parent anchor:
7895   if (mParentAnchorY)
7896     mParentAnchorY->removeChildY(this);
7897   // register at new parent anchor:
7898   if (parentAnchor)
7899     parentAnchor->addChildY(this);
7900   mParentAnchorY = parentAnchor;
7901   // restore pixel position under new parent:
7902   if (keepPixelPosition)
7903     setPixelPoint(pixelP);
7904   else
7905     setCoords(coords().x(), 0);
7906   return true;
7907 }
7908 
7909 /*!
7910   Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type
7911   (\ref setType, \ref setTypeX, \ref setTypeY).
7912 
7913   For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position
7914   on the QCustomPlot surface. In that case the origin (0, 0) is in the top left corner of the
7915   QCustomPlot viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the
7916   plot coordinate system defined by the axes set by \ref setAxes. By default those are the
7917   QCustomPlot's xAxis and yAxis. See the documentation of \ref setType for other available
7918   coordinate types and their meaning.
7919 
7920   If different types were configured for X and Y (\ref setTypeX, \ref setTypeY), \a key and \a
7921   value must also be provided in the different coordinate systems. Here, the X type refers to \a
7922   key, and the Y type refers to \a value.
7923 
7924   \see setPixelPoint
7925 */
setCoords(double key,double value)7926 void QCPItemPosition::setCoords(double key, double value)
7927 {
7928   mKey = key;
7929   mValue = value;
7930 }
7931 
7932 /*! \overload
7933 
7934   Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the
7935   meaning of \a value of the \ref setCoords(double key, double value) method.
7936 */
setCoords(const QPointF & pos)7937 void QCPItemPosition::setCoords(const QPointF &pos)
7938 {
7939   setCoords(pos.x(), pos.y());
7940 }
7941 
7942 /*!
7943   Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It
7944   includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor).
7945 
7946   \see setPixelPoint
7947 */
pixelPoint() const7948 QPointF QCPItemPosition::pixelPoint() const
7949 {
7950   QPointF result;
7951 
7952   // determine X:
7953   switch (mPositionTypeX)
7954   {
7955     case ptAbsolute:
7956     {
7957       result.rx() = mKey;
7958       if (mParentAnchorX)
7959         result.rx() += mParentAnchorX->pixelPoint().x();
7960       break;
7961     }
7962     case ptViewportRatio:
7963     {
7964       result.rx() = mKey*mParentPlot->viewport().width();
7965       if (mParentAnchorX)
7966         result.rx() += mParentAnchorX->pixelPoint().x();
7967       else
7968         result.rx() += mParentPlot->viewport().left();
7969       break;
7970     }
7971     case ptAxisRectRatio:
7972     {
7973       if (mAxisRect)
7974       {
7975         result.rx() = mKey*mAxisRect->width();
7976         if (mParentAnchorX)
7977           result.rx() += mParentAnchorX->pixelPoint().x();
7978         else
7979           result.rx() += mAxisRect->left();
7980       } else
7981         qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
7982       break;
7983     }
7984     case ptPlotCoords:
7985     {
7986       if (mKeyAxis && mKeyAxis->orientation() == Qt::Horizontal)
7987         result.rx() = mKeyAxis->coordToPixel(mKey);
7988       else if (mValueAxis && mValueAxis->orientation() == Qt::Horizontal)
7989         result.rx() = mValueAxis->coordToPixel(mValue);
7990       else
7991         qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
7992       break;
7993     }
7994   }
7995 
7996   // determine Y:
7997   switch (mPositionTypeY)
7998   {
7999     case ptAbsolute:
8000     {
8001       result.ry() = mValue;
8002       if (mParentAnchorY)
8003         result.ry() += mParentAnchorY->pixelPoint().y();
8004       break;
8005     }
8006     case ptViewportRatio:
8007     {
8008       result.ry() = mValue*mParentPlot->viewport().height();
8009       if (mParentAnchorY)
8010         result.ry() += mParentAnchorY->pixelPoint().y();
8011       else
8012         result.ry() += mParentPlot->viewport().top();
8013       break;
8014     }
8015     case ptAxisRectRatio:
8016     {
8017       if (mAxisRect)
8018       {
8019         result.ry() = mValue*mAxisRect->height();
8020         if (mParentAnchorY)
8021           result.ry() += mParentAnchorY->pixelPoint().y();
8022         else
8023           result.ry() += mAxisRect->top();
8024       } else
8025         qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8026       break;
8027     }
8028     case ptPlotCoords:
8029     {
8030       if (mKeyAxis && mKeyAxis->orientation() == Qt::Vertical)
8031         result.ry() = mKeyAxis->coordToPixel(mKey);
8032       else if (mValueAxis && mValueAxis->orientation() == Qt::Vertical)
8033         result.ry() = mValueAxis->coordToPixel(mValue);
8034       else
8035         qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8036       break;
8037     }
8038   }
8039 
8040   return result;
8041 }
8042 
8043 /*!
8044   When \ref setType is \ref ptPlotCoords, this function may be used to specify the axes the
8045   coordinates set with \ref setCoords relate to. By default they are set to the initial xAxis and
8046   yAxis of the QCustomPlot.
8047 */
setAxes(QCPAxis * keyAxis,QCPAxis * valueAxis)8048 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
8049 {
8050   mKeyAxis = keyAxis;
8051   mValueAxis = valueAxis;
8052 }
8053 
8054 /*!
8055   When \ref setType is \ref ptAxisRectRatio, this function may be used to specify the axis rect the
8056   coordinates set with \ref setCoords relate to. By default this is set to the main axis rect of
8057   the QCustomPlot.
8058 */
setAxisRect(QCPAxisRect * axisRect)8059 void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect)
8060 {
8061   mAxisRect = axisRect;
8062 }
8063 
8064 /*!
8065   Sets the apparent pixel position. This works no matter what type (\ref setType) this
8066   QCPItemPosition is or what parent-child situation it is in, as coordinates are transformed
8067   appropriately, to make the position finally appear at the specified pixel values.
8068 
8069   Only if the type is \ref ptAbsolute and no parent anchor is set, this function's effect is
8070   identical to that of \ref setCoords.
8071 
8072   \see pixelPoint, setCoords
8073 */
setPixelPoint(const QPointF & pixelPoint)8074 void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
8075 {
8076   double x = pixelPoint.x();
8077   double y = pixelPoint.y();
8078 
8079   switch (mPositionTypeX)
8080   {
8081     case ptAbsolute:
8082     {
8083       if (mParentAnchorX)
8084         x -= mParentAnchorX->pixelPoint().x();
8085       break;
8086     }
8087     case ptViewportRatio:
8088     {
8089       if (mParentAnchorX)
8090         x -= mParentAnchorX->pixelPoint().x();
8091       else
8092         x -= mParentPlot->viewport().left();
8093       x /= (double)mParentPlot->viewport().width();
8094       break;
8095     }
8096     case ptAxisRectRatio:
8097     {
8098       if (mAxisRect)
8099       {
8100         if (mParentAnchorX)
8101           x -= mParentAnchorX->pixelPoint().x();
8102         else
8103           x -= mAxisRect->left();
8104         x /= (double)mAxisRect->width();
8105       } else
8106         qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
8107       break;
8108     }
8109     case ptPlotCoords:
8110     {
8111       if (mKeyAxis && mKeyAxis->orientation() == Qt::Horizontal)
8112         x = mKeyAxis->pixelToCoord(x);
8113       else if (mValueAxis && mValueAxis->orientation() == Qt::Horizontal)
8114         y = mValueAxis->pixelToCoord(x);
8115       else
8116         qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
8117       break;
8118     }
8119   }
8120 
8121   switch (mPositionTypeY)
8122   {
8123     case ptAbsolute:
8124     {
8125       if (mParentAnchorY)
8126         y -= mParentAnchorY->pixelPoint().y();
8127       break;
8128     }
8129     case ptViewportRatio:
8130     {
8131       if (mParentAnchorY)
8132         y -= mParentAnchorY->pixelPoint().y();
8133       else
8134         y -= mParentPlot->viewport().top();
8135       y /= (double)mParentPlot->viewport().height();
8136       break;
8137     }
8138     case ptAxisRectRatio:
8139     {
8140       if (mAxisRect)
8141       {
8142         if (mParentAnchorY)
8143           y -= mParentAnchorY->pixelPoint().y();
8144         else
8145           y -= mAxisRect->top();
8146         y /= (double)mAxisRect->height();
8147       } else
8148         qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8149       break;
8150     }
8151     case ptPlotCoords:
8152     {
8153       if (mKeyAxis && mKeyAxis->orientation() == Qt::Vertical)
8154         x = mKeyAxis->pixelToCoord(y);
8155       else if (mValueAxis && mValueAxis->orientation() == Qt::Vertical)
8156         y = mValueAxis->pixelToCoord(y);
8157       else
8158         qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8159       break;
8160     }
8161   }
8162 
8163   setCoords(x, y);
8164 }
8165 
8166 
8167 ////////////////////////////////////////////////////////////////////////////////////////////////////
8168 //////////////////// QCPAbstractItem
8169 ////////////////////////////////////////////////////////////////////////////////////////////////////
8170 
8171 /*! \class QCPAbstractItem
8172   \brief The abstract base class for all items in a plot.
8173 
8174   In QCustomPlot, items are supplemental graphical elements that are neither plottables
8175   (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus
8176   plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each
8177   specific item has at least one QCPItemPosition member which controls the positioning. Some items
8178   are defined by more than one coordinate and thus have two or more QCPItemPosition members (For
8179   example, QCPItemRect has \a topLeft and \a bottomRight).
8180 
8181   This abstract base class defines a very basic interface like visibility and clipping. Since this
8182   class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass
8183   yourself to create new items.
8184 
8185   The built-in items are:
8186   <table>
8187   <tr><td>QCPItemLine</td><td>A line defined by a start and an end point. May have different ending styles on each side (e.g. arrows).</td></tr>
8188   <tr><td>QCPItemStraightLine</td><td>A straight line defined by a start and a direction point. Unlike QCPItemLine, the straight line is infinitely long and has no endings.</td></tr>
8189   <tr><td>QCPItemCurve</td><td>A curve defined by start, end and two intermediate control points. May have different ending styles on each side (e.g. arrows).</td></tr>
8190   <tr><td>QCPItemRect</td><td>A rectangle</td></tr>
8191   <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>
8192   <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>
8193   <tr><td>QCPItemText</td><td>A text label</td></tr>
8194   <tr><td>QCPItemBracket</td><td>A bracket which may be used to reference/highlight certain parts in the plot.</td></tr>
8195   <tr><td>QCPItemTracer</td><td>An item that can be attached to a QCPGraph and sticks to its data points, given a key coordinate.</td></tr>
8196   </table>
8197 
8198   \section items-clipping Clipping
8199 
8200   Items are by default clipped to the main axis rect (they are only visible inside the axis rect).
8201   To make an item visible outside that axis rect, disable clipping via \ref setClipToAxisRect
8202   "setClipToAxisRect(false)".
8203 
8204   On the other hand if you want the item to be clipped to a different axis rect, specify it via
8205   \ref setClipAxisRect. This clipAxisRect property of an item is only used for clipping behaviour, and
8206   in principle is independent of the coordinate axes the item might be tied to via its position
8207   members (\ref QCPItemPosition::setAxes). However, it is common that the axis rect for clipping
8208   also contains the axes used for the item positions.
8209 
8210   \section items-using Using items
8211 
8212   First you instantiate the item you want to use and add it to the plot:
8213   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1
8214   by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just
8215   set the plot coordinates where the line should start/end:
8216   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2
8217   If we don't want the line to be positioned in plot coordinates but a different coordinate system,
8218   e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this:
8219   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3
8220   Then we can set the coordinates, this time in pixels:
8221   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4
8222   and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect:
8223   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-5
8224 
8225   For more advanced plots, it is even possible to set different types and parent anchors per X/Y
8226   coordinate of an item position, using for example \ref QCPItemPosition::setTypeX or \ref
8227   QCPItemPosition::setParentAnchorX. For details, see the documentation of \ref QCPItemPosition.
8228 
8229   \section items-subclassing Creating own items
8230 
8231   To create an own item, you implement a subclass of QCPAbstractItem. These are the pure
8232   virtual functions, you must implement:
8233   \li \ref selectTest
8234   \li \ref draw
8235 
8236   See the documentation of those functions for what they need to do.
8237 
8238   \subsection items-positioning Allowing the item to be positioned
8239 
8240   As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall
8241   have only one point as its position (as opposed to two like a rect or multiple like a polygon). You then add
8242   a public member of type QCPItemPosition like so:
8243 
8244   \code QCPItemPosition * const myPosition;\endcode
8245 
8246   the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition
8247   instance it points to, can be modified, of course).
8248   The initialization of this pointer is made easy with the \ref createPosition function. Just assign
8249   the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition
8250   takes a string which is the name of the position, typically this is identical to the variable name.
8251   For example, the constructor of QCPItemExample could look like this:
8252 
8253   \code
8254   QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :
8255     QCPAbstractItem(parentPlot),
8256     myPosition(createPosition("myPosition"))
8257   {
8258     // other constructor code
8259   }
8260   \endcode
8261 
8262   \subsection items-drawing The draw function
8263 
8264   To give your item a visual representation, reimplement the \ref draw function and use the passed
8265   QCPPainter to draw the item. You can retrieve the item position in pixel coordinates from the
8266   position member(s) via \ref QCPItemPosition::pixelPoint.
8267 
8268   To optimize performance you should calculate a bounding rect first (don't forget to take the pen
8269   width into account), check whether it intersects the \ref clipRect, and only draw the item at all
8270   if this is the case.
8271 
8272   \subsection items-selection The selectTest function
8273 
8274   Your implementation of the \ref selectTest function may use the helpers \ref distSqrToLine and
8275   \ref rectSelectTest. With these, the implementation of the selection test becomes significantly
8276   simpler for most items. See the documentation of \ref selectTest for what the function parameters
8277   mean and what the function should return.
8278 
8279   \subsection anchors Providing anchors
8280 
8281   Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public
8282   member, e.g.
8283 
8284   \code QCPItemAnchor * const bottom;\endcode
8285 
8286   and create it in the constructor with the \ref createAnchor function, assigning it a name and an
8287   anchor id (an integer enumerating all anchors on the item, you may create an own enum for this).
8288   Since anchors can be placed anywhere, relative to the item's position(s), your item needs to
8289   provide the position of every anchor with the reimplementation of the \ref anchorPixelPoint(int
8290   anchorId) function.
8291 
8292   In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel
8293   position when anything attached to the anchor needs to know the coordinates.
8294 */
8295 
8296 /* start of documentation of inline functions */
8297 
8298 /*! \fn QList<QCPItemPosition*> QCPAbstractItem::positions() const
8299 
8300   Returns all positions of the item in a list.
8301 
8302   \see anchors, position
8303 */
8304 
8305 /*! \fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const
8306 
8307   Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always
8308   also an anchor, the list will also contain the positions of this item.
8309 
8310   \see positions, anchor
8311 */
8312 
8313 /* end of documentation of inline functions */
8314 /* start documentation of pure virtual functions */
8315 
8316 /*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0
8317   \internal
8318 
8319   Draws this item with the provided \a painter.
8320 
8321   The cliprect of the provided painter is set to the rect returned by \ref clipRect before this
8322   function is called. The clipRect depends on the clipping settings defined by \ref
8323   setClipToAxisRect and \ref setClipAxisRect.
8324 */
8325 
8326 /* end documentation of pure virtual functions */
8327 /* start documentation of signals */
8328 
8329 /*! \fn void QCPAbstractItem::selectionChanged(bool selected)
8330   This signal is emitted when the selection state of this item has changed, either by user interaction
8331   or by a direct call to \ref setSelected.
8332 */
8333 
8334 /* end documentation of signals */
8335 
8336 /*!
8337   Base class constructor which initializes base class members.
8338 */
QCPAbstractItem(QCustomPlot * parentPlot)8339 QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
8340   QCPLayerable(parentPlot),
8341   mClipToAxisRect(false),
8342   mSelectable(true),
8343   mSelected(false)
8344 {
8345   QList<QCPAxisRect*> rects = parentPlot->axisRects();
8346   if (rects.size() > 0)
8347   {
8348     setClipToAxisRect(true);
8349     setClipAxisRect(rects.first());
8350   }
8351 }
8352 
~QCPAbstractItem()8353 QCPAbstractItem::~QCPAbstractItem()
8354 {
8355   // don't delete mPositions because every position is also an anchor and thus in mAnchors
8356   qDeleteAll(mAnchors);
8357 }
8358 
8359 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
clipAxisRect() const8360 QCPAxisRect *QCPAbstractItem::clipAxisRect() const
8361 {
8362   return mClipAxisRect.data();
8363 }
8364 
8365 /*!
8366   Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the
8367   entire QCustomPlot. The axis rect can be set with \ref setClipAxisRect.
8368 
8369   \see setClipAxisRect
8370 */
setClipToAxisRect(bool clip)8371 void QCPAbstractItem::setClipToAxisRect(bool clip)
8372 {
8373   mClipToAxisRect = clip;
8374   if (mClipToAxisRect)
8375     setParentLayerable(mClipAxisRect.data());
8376 }
8377 
8378 /*!
8379   Sets the clip axis rect. It defines the rect that will be used to clip the item when \ref
8380   setClipToAxisRect is set to true.
8381 
8382   \see setClipToAxisRect
8383 */
setClipAxisRect(QCPAxisRect * rect)8384 void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect)
8385 {
8386   mClipAxisRect = rect;
8387   if (mClipToAxisRect)
8388     setParentLayerable(mClipAxisRect.data());
8389 }
8390 
8391 /*!
8392   Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
8393   (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.)
8394 
8395   However, even when \a selectable was set to false, it is possible to set the selection manually,
8396   by calling \ref setSelected.
8397 
8398   \see QCustomPlot::setInteractions, setSelected
8399 */
setSelectable(bool selectable)8400 void QCPAbstractItem::setSelectable(bool selectable)
8401 {
8402   if (mSelectable != selectable)
8403   {
8404     mSelectable = selectable;
8405     emit selectableChanged(mSelectable);
8406   }
8407 }
8408 
8409 /*!
8410   Sets whether this item is selected or not. When selected, it might use a different visual
8411   appearance (e.g. pen and brush), this depends on the specific item though.
8412 
8413   The entire selection mechanism for items is handled automatically when \ref
8414   QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this
8415   function when you wish to change the selection state manually.
8416 
8417   This function can change the selection state even when \ref setSelectable was set to false.
8418 
8419   emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
8420 
8421   \see setSelectable, selectTest
8422 */
setSelected(bool selected)8423 void QCPAbstractItem::setSelected(bool selected)
8424 {
8425   if (mSelected != selected)
8426   {
8427     mSelected = selected;
8428     emit selectionChanged(mSelected);
8429   }
8430 }
8431 
8432 /*!
8433   Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by
8434   that name, returns 0.
8435 
8436   This function provides an alternative way to access item positions. Normally, you access
8437   positions direcly by their member pointers (which typically have the same variable name as \a
8438   name).
8439 
8440   \see positions, anchor
8441 */
position(const QString & name) const8442 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8443 {
8444   for (int i=0; i<mPositions.size(); ++i)
8445   {
8446     if (mPositions.at(i)->name() == name)
8447       return mPositions.at(i);
8448   }
8449   qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8450   return 0;
8451 }
8452 
8453 /*!
8454   Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by
8455   that name, returns 0.
8456 
8457   This function provides an alternative way to access item anchors. Normally, you access
8458   anchors direcly by their member pointers (which typically have the same variable name as \a
8459   name).
8460 
8461   \see anchors, position
8462 */
anchor(const QString & name) const8463 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8464 {
8465   for (int i=0; i<mAnchors.size(); ++i)
8466   {
8467     if (mAnchors.at(i)->name() == name)
8468       return mAnchors.at(i);
8469   }
8470   qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8471   return 0;
8472 }
8473 
8474 /*!
8475   Returns whether this item has an anchor with the specified \a name.
8476 
8477   Note that you can check for positions with this function, too. This is because every position is
8478   also an anchor (QCPItemPosition inherits from QCPItemAnchor).
8479 
8480   \see anchor, position
8481 */
hasAnchor(const QString & name) const8482 bool QCPAbstractItem::hasAnchor(const QString &name) const
8483 {
8484   for (int i=0; i<mAnchors.size(); ++i)
8485   {
8486     if (mAnchors.at(i)->name() == name)
8487       return true;
8488   }
8489   return false;
8490 }
8491 
8492 /*! \internal
8493 
8494   Returns the rect the visual representation of this item is clipped to. This depends on the
8495   current setting of \ref setClipToAxisRect as well as the axis rect set with \ref setClipAxisRect.
8496 
8497   If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport rect is returned.
8498 
8499   \see draw
8500 */
clipRect() const8501 QRect QCPAbstractItem::clipRect() const
8502 {
8503   if (mClipToAxisRect && mClipAxisRect)
8504     return mClipAxisRect->rect();
8505   else
8506     return mParentPlot->viewport();
8507 }
8508 
8509 /*! \internal
8510 
8511   A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
8512   before drawing item lines.
8513 
8514   This is the antialiasing state the painter passed to the \ref draw method is in by default.
8515 
8516   This function takes into account the local setting of the antialiasing flag as well as the
8517   overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
8518   QCustomPlot::setNotAntialiasedElements.
8519 
8520   \see setAntialiased
8521 */
applyDefaultAntialiasingHint(QCPPainter * painter) const8522 void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
8523 {
8524   applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
8525 }
8526 
8527 /*! \internal
8528 
8529   Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
8530   end.
8531 
8532   This function may be used to help with the implementation of the \ref selectTest function for
8533   specific items.
8534 
8535   \note This function is identical to QCPAbstractPlottable::distSqrToLine
8536 
8537   \see rectSelectTest
8538 */
distSqrToLine(const QPointF & start,const QPointF & end,const QPointF & point) const8539 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8540 {
8541   QVector2D a(start);
8542   QVector2D b(end);
8543   QVector2D p(point);
8544   QVector2D v(b-a);
8545 
8546   double vLengthSqr = v.lengthSquared();
8547   if (!qFuzzyIsNull(vLengthSqr))
8548   {
8549     double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8550     if (mu < 0)
8551       return (a-p).lengthSquared();
8552     else if (mu > 1)
8553       return (b-p).lengthSquared();
8554     else
8555       return ((a + mu*v)-p).lengthSquared();
8556   } else
8557     return (a-p).lengthSquared();
8558 }
8559 
8560 /*! \internal
8561 
8562   A convenience function which returns the selectTest value for a specified \a rect and a specified
8563   click position \a pos. \a filledRect defines whether a click inside the rect should also be
8564   considered a hit or whether only the rect border is sensitive to hits.
8565 
8566   This function may be used to help with the implementation of the \ref selectTest function for
8567   specific items.
8568 
8569   For example, if your item consists of four rects, call this function four times, once for each
8570   rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned
8571   values.
8572 
8573   \see distSqrToLine
8574 */
rectSelectTest(const QRectF & rect,const QPointF & pos,bool filledRect) const8575 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8576 {
8577   double result = -1;
8578 
8579   // distance to border:
8580   QList<QLineF> lines;
8581   lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8582         << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8583   double minDistSqr = std::numeric_limits<double>::max();
8584   for (int i=0; i<lines.size(); ++i)
8585   {
8586     double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8587     if (distSqr < minDistSqr)
8588       minDistSqr = distSqr;
8589   }
8590   result = qSqrt(minDistSqr);
8591 
8592   // filled rect, allow click inside to count as hit:
8593   if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8594   {
8595     if (rect.contains(pos))
8596       result = mParentPlot->selectionTolerance()*0.99;
8597   }
8598   return result;
8599 }
8600 
8601 /*! \internal
8602 
8603   Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in
8604   item subclasses if they want to provide anchors (QCPItemAnchor).
8605 
8606   For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor
8607   ids and returns the respective pixel points of the specified anchor.
8608 
8609   \see createAnchor
8610 */
anchorPixelPoint(int anchorId) const8611 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8612 {
8613   qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8614   return QPointF();
8615 }
8616 
8617 /*! \internal
8618 
8619   Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified
8620   \a name must be a unique string that is usually identical to the variable name of the position
8621   member (This is needed to provide the name-based \ref position access to positions).
8622 
8623   Don't delete positions created by this function manually, as the item will take care of it.
8624 
8625   Use this function in the constructor (initialization list) of the specific item subclass to
8626   create each position member. Don't create QCPItemPositions with \b new yourself, because they
8627   won't be registered with the item properly.
8628 
8629   \see createAnchor
8630 */
createPosition(const QString & name)8631 QCPItemPosition *QCPAbstractItem::createPosition(const QString &name)
8632 {
8633   if (hasAnchor(name))
8634     qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8635   QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8636   mPositions.append(newPosition);
8637   mAnchors.append(newPosition); // every position is also an anchor
8638   newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8639   newPosition->setType(QCPItemPosition::ptPlotCoords);
8640   if (mParentPlot->axisRect())
8641     newPosition->setAxisRect(mParentPlot->axisRect());
8642   newPosition->setCoords(0, 0);
8643   return newPosition;
8644 }
8645 
8646 /*! \internal
8647 
8648   Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified
8649   \a name must be a unique string that is usually identical to the variable name of the anchor
8650   member (This is needed to provide the name based \ref anchor access to anchors).
8651 
8652   The \a anchorId must be a number identifying the created anchor. It is recommended to create an
8653   enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor
8654   to identify itself when it calls QCPAbstractItem::anchorPixelPoint. That function then returns
8655   the correct pixel coordinates for the passed anchor id.
8656 
8657   Don't delete anchors created by this function manually, as the item will take care of it.
8658 
8659   Use this function in the constructor (initialization list) of the specific item subclass to
8660   create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they
8661   won't be registered with the item properly.
8662 
8663   \see createPosition
8664 */
createAnchor(const QString & name,int anchorId)8665 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8666 {
8667   if (hasAnchor(name))
8668     qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8669   QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8670   mAnchors.append(newAnchor);
8671   return newAnchor;
8672 }
8673 
8674 /* inherits documentation from base class */
selectEvent(QMouseEvent * event,bool additive,const QVariant & details,bool * selectionStateChanged)8675 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8676 {
8677   Q_UNUSED(event)
8678   Q_UNUSED(details)
8679   if (mSelectable)
8680   {
8681     bool selBefore = mSelected;
8682     setSelected(additive ? !mSelected : true);
8683     if (selectionStateChanged)
8684       *selectionStateChanged = mSelected != selBefore;
8685   }
8686 }
8687 
8688 /* inherits documentation from base class */
deselectEvent(bool * selectionStateChanged)8689 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8690 {
8691   if (mSelectable)
8692   {
8693     bool selBefore = mSelected;
8694     setSelected(false);
8695     if (selectionStateChanged)
8696       *selectionStateChanged = mSelected != selBefore;
8697   }
8698 }
8699 
8700 /* inherits documentation from base class */
selectionCategory() const8701 QCP::Interaction QCPAbstractItem::selectionCategory() const
8702 {
8703   return QCP::iSelectItems;
8704 }
8705 
8706 
8707 /*! \file */
8708 
8709 
8710 
8711 ////////////////////////////////////////////////////////////////////////////////////////////////////
8712 //////////////////// QCustomPlot
8713 ////////////////////////////////////////////////////////////////////////////////////////////////////
8714 
8715 /*! \class QCustomPlot
8716 
8717   \brief The central class of the library. This is the QWidget which displays the plot and
8718   interacts with the user.
8719 
8720   For tutorials on how to use QCustomPlot, see the website\n
8721   http://www.qcustomplot.com/
8722 */
8723 
8724 /* start of documentation of inline functions */
8725 
8726 /*! \fn QRect QCustomPlot::viewport() const
8727 
8728   Returns the viewport rect of this QCustomPlot instance. The viewport is the area the plot is
8729   drawn in, all mechanisms, e.g. margin caluclation take the viewport to be the outer border of the
8730   plot. The viewport normally is the rect() of the QCustomPlot widget, i.e. a rect with top left
8731   (0, 0) and size of the QCustomPlot widget.
8732 
8733   Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis rect is typically
8734   an area enclosed by four axes, where the graphs/plottables are drawn in. The viewport is larger
8735   and contains also the axes themselves, their tick numbers, their labels, the plot title etc.
8736 
8737   Only when saving to a file (see \ref savePng, \ref savePdf etc.) the viewport is temporarily
8738   modified to allow saving plots with sizes independent of the current widget size.
8739 */
8740 
8741 /*! \fn QCPLayoutGrid *QCustomPlot::plotLayout() const
8742 
8743   Returns the top level layout of this QCustomPlot instance. It is a \ref QCPLayoutGrid, initially containing just
8744   one cell with the main QCPAxisRect inside.
8745 */
8746 
8747 /* end of documentation of inline functions */
8748 /* start of documentation of signals */
8749 
8750 /*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)
8751 
8752   This signal is emitted when the QCustomPlot receives a mouse double click event.
8753 */
8754 
8755 /*! \fn void QCustomPlot::mousePress(QMouseEvent *event)
8756 
8757   This signal is emitted when the QCustomPlot receives a mouse press event.
8758 
8759   It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
8760   connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8761   QCPAxisRect::setRangeDragAxes.
8762 */
8763 
8764 /*! \fn void QCustomPlot::mouseMove(QMouseEvent *event)
8765 
8766   This signal is emitted when the QCustomPlot receives a mouse move event.
8767 
8768   It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
8769   connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8770   QCPAxisRect::setRangeDragAxes.
8771 
8772   \warning It is discouraged to change the drag-axes with \ref QCPAxisRect::setRangeDragAxes here,
8773   because the dragging starting point was saved the moment the mouse was pressed. Thus it only has
8774   a meaning for the range drag axes that were set at that moment. If you want to change the drag
8775   axes, consider doing this in the \ref mousePress signal instead.
8776 */
8777 
8778 /*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event)
8779 
8780   This signal is emitted when the QCustomPlot receives a mouse release event.
8781 
8782   It is emitted before QCustomPlot handles any other mechanisms like object selection. So a
8783   slot connected to this signal can still influence the behaviour e.g. with \ref setInteractions or
8784   \ref QCPAbstractPlottable::setSelectable.
8785 */
8786 
8787 /*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event)
8788 
8789   This signal is emitted when the QCustomPlot receives a mouse wheel event.
8790 
8791   It is emitted before QCustomPlot handles any other mechanisms like range zooming. So a slot
8792   connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeZoom, \ref
8793   QCPAxisRect::setRangeZoomAxes or \ref QCPAxisRect::setRangeZoomFactor.
8794 */
8795 
8796 /*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
8797 
8798   This signal is emitted when a plottable is clicked.
8799 
8800   \a event is the mouse event that caused the click and \a plottable is the plottable that received
8801   the click.
8802 
8803   \see plottableDoubleClick
8804 */
8805 
8806 /*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
8807 
8808   This signal is emitted when a plottable is double clicked.
8809 
8810   \a event is the mouse event that caused the click and \a plottable is the plottable that received
8811   the click.
8812 
8813   \see plottableClick
8814 */
8815 
8816 /*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)
8817 
8818   This signal is emitted when an item is clicked.
8819 
8820   \a event is the mouse event that caused the click and \a item is the item that received the
8821   click.
8822 
8823   \see itemDoubleClick
8824 */
8825 
8826 /*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
8827 
8828   This signal is emitted when an item is double clicked.
8829 
8830   \a event is the mouse event that caused the click and \a item is the item that received the
8831   click.
8832 
8833   \see itemClick
8834 */
8835 
8836 /*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
8837 
8838   This signal is emitted when an axis is clicked.
8839 
8840   \a event is the mouse event that caused the click, \a axis is the axis that received the click and
8841   \a part indicates the part of the axis that was clicked.
8842 
8843   \see axisDoubleClick
8844 */
8845 
8846 /*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
8847 
8848   This signal is emitted when an axis is double clicked.
8849 
8850   \a event is the mouse event that caused the click, \a axis is the axis that received the click and
8851   \a part indicates the part of the axis that was clicked.
8852 
8853   \see axisClick
8854 */
8855 
8856 /*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
8857 
8858   This signal is emitted when a legend (item) is clicked.
8859 
8860   \a event is the mouse event that caused the click, \a legend is the legend that received the
8861   click and \a item is the legend item that received the click. If only the legend and no item is
8862   clicked, \a item is 0. This happens for a click inside the legend padding or the space between
8863   two items.
8864 
8865   \see legendDoubleClick
8866 */
8867 
8868 /*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend,  QCPAbstractLegendItem *item, QMouseEvent *event)
8869 
8870   This signal is emitted when a legend (item) is double clicked.
8871 
8872   \a event is the mouse event that caused the click, \a legend is the legend that received the
8873   click and \a item is the legend item that received the click. If only the legend and no item is
8874   clicked, \a item is 0. This happens for a click inside the legend padding or the space between
8875   two items.
8876 
8877   \see legendClick
8878 */
8879 
8880 /*! \fn void QCustomPlot:: titleClick(QMouseEvent *event, QCPPlotTitle *title)
8881 
8882   This signal is emitted when a plot title is clicked.
8883 
8884   \a event is the mouse event that caused the click and \a title is the plot title that received
8885   the click.
8886 
8887   \see titleDoubleClick
8888 */
8889 
8890 /*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
8891 
8892   This signal is emitted when a plot title is double clicked.
8893 
8894   \a event is the mouse event that caused the click and \a title is the plot title that received
8895   the click.
8896 
8897   \see titleClick
8898 */
8899 
8900 /*! \fn void QCustomPlot::selectionChangedByUser()
8901 
8902   This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by
8903   clicking. It is not emitted when the selection state of an object has changed programmatically by
8904   a direct call to setSelected() on an object or by calling \ref deselectAll.
8905 
8906   In addition to this signal, selectable objects also provide individual signals, for example
8907   QCPAxis::selectionChanged or QCPAbstractPlottable::selectionChanged. Note that those signals are
8908   emitted even if the selection state is changed programmatically.
8909 
8910   See the documentation of \ref setInteractions for details about the selection mechanism.
8911 
8912   \see selectedPlottables, selectedGraphs, selectedItems, selectedAxes, selectedLegends
8913 */
8914 
8915 /*! \fn void QCustomPlot::beforeReplot()
8916 
8917   This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref
8918   replot).
8919 
8920   It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
8921   replot synchronously, it won't cause an infinite recursion.
8922 
8923   \see replot, afterReplot
8924 */
8925 
8926 /*! \fn void QCustomPlot::afterReplot()
8927 
8928   This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref
8929   replot).
8930 
8931   It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
8932   replot synchronously, it won't cause an infinite recursion.
8933 
8934   \see replot, beforeReplot
8935 */
8936 
8937 /* end of documentation of signals */
8938 /* start of documentation of public members */
8939 
8940 /*! \var QCPAxis *QCustomPlot::xAxis
8941 
8942   A pointer to the primary x Axis (bottom) of the main axis rect of the plot.
8943 
8944   QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8945   yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8946   axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8947   layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8948   QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8949   default legend is removed due to manipulation of the layout system (e.g. by removing the main
8950   axis rect), the corresponding pointers become 0.
8951 */
8952 
8953 /*! \var QCPAxis *QCustomPlot::yAxis
8954 
8955   A pointer to the primary y Axis (left) of the main axis rect of the plot.
8956 
8957   QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8958   yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8959   axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8960   layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8961   QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8962   default legend is removed due to manipulation of the layout system (e.g. by removing the main
8963   axis rect), the corresponding pointers become 0.
8964 */
8965 
8966 /*! \var QCPAxis *QCustomPlot::xAxis2
8967 
8968   A pointer to the secondary x Axis (top) of the main axis rect of the plot. Secondary axes are
8969   invisible by default. Use QCPAxis::setVisible to change this (or use \ref
8970   QCPAxisRect::setupFullAxesBox).
8971 
8972   QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8973   yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8974   axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8975   layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8976   QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8977   default legend is removed due to manipulation of the layout system (e.g. by removing the main
8978   axis rect), the corresponding pointers become 0.
8979 */
8980 
8981 /*! \var QCPAxis *QCustomPlot::yAxis2
8982 
8983   A pointer to the secondary y Axis (right) of the main axis rect of the plot. Secondary axes are
8984   invisible by default. Use QCPAxis::setVisible to change this (or use \ref
8985   QCPAxisRect::setupFullAxesBox).
8986 
8987   QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8988   yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8989   axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8990   layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8991   QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8992   default legend is removed due to manipulation of the layout system (e.g. by removing the main
8993   axis rect), the corresponding pointers become 0.
8994 */
8995 
8996 /*! \var QCPLegend *QCustomPlot::legend
8997 
8998   A pointer to the default legend of the main axis rect. The legend is invisible by default. Use
8999   QCPLegend::setVisible to change this.
9000 
9001   QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
9002   yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
9003   axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
9004   layout system\endlink to add multiple legends to the plot, use the layout system interface to
9005   access the new legend. For example, legends can be placed inside an axis rect's \ref
9006   QCPAxisRect::insetLayout "inset layout", and must then also be accessed via the inset layout. If
9007   the default legend is removed due to manipulation of the layout system (e.g. by removing the main
9008   axis rect), the corresponding pointer becomes 0.
9009 */
9010 
9011 /* end of documentation of public members */
9012 
9013 /*!
9014   Constructs a QCustomPlot and sets reasonable default values.
9015 */
QCustomPlot(QWidget * parent)9016 QCustomPlot::QCustomPlot(QWidget *parent) :
9017   QWidget(parent),
9018   xAxis(0),
9019   yAxis(0),
9020   xAxis2(0),
9021   yAxis2(0),
9022   legend(0),
9023   mPlotLayout(0),
9024   mAutoAddPlottableToLegend(true),
9025   mAntialiasedElements(QCP::aeNone),
9026   mNotAntialiasedElements(QCP::aeNone),
9027   mInteractions(0),
9028   mSelectionTolerance(8),
9029   mNoAntialiasingOnDrag(false),
9030   mBackgroundBrush(Qt::white, Qt::SolidPattern),
9031   mBackgroundScaled(true),
9032   mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9033   mCurrentLayer(0),
9034   mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9035   mMultiSelectModifier(Qt::ControlModifier),
9036   mPaintBuffer(size()),
9037   mMouseEventElement(0),
9038   mReplotting(false)
9039 {
9040   setAttribute(Qt::WA_NoMousePropagation);
9041   setAttribute(Qt::WA_OpaquePaintEvent);
9042   setMouseTracking(true);
9043   QLocale currentLocale = locale();
9044   currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9045   setLocale(currentLocale);
9046 
9047   // create initial layers:
9048   mLayers.append(new QCPLayer(this, QLatin1String("background")));
9049   mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9050   mLayers.append(new QCPLayer(this, QLatin1String("main")));
9051   mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9052   mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9053   updateLayerIndices();
9054   setCurrentLayer(QLatin1String("main"));
9055 
9056   // create initial layout, axis rect and legend:
9057   mPlotLayout = new QCPLayoutGrid;
9058   mPlotLayout->initializeParentPlot(this);
9059   mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9060   mPlotLayout->setLayer(QLatin1String("main"));
9061   QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9062   mPlotLayout->addElement(0, 0, defaultAxisRect);
9063   xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9064   yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9065   xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9066   yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9067   legend = new QCPLegend;
9068   legend->setVisible(false);
9069   defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9070   defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9071 
9072   defaultAxisRect->setLayer(QLatin1String("background"));
9073   xAxis->setLayer(QLatin1String("axes"));
9074   yAxis->setLayer(QLatin1String("axes"));
9075   xAxis2->setLayer(QLatin1String("axes"));
9076   yAxis2->setLayer(QLatin1String("axes"));
9077   xAxis->grid()->setLayer(QLatin1String("grid"));
9078   yAxis->grid()->setLayer(QLatin1String("grid"));
9079   xAxis2->grid()->setLayer(QLatin1String("grid"));
9080   yAxis2->grid()->setLayer(QLatin1String("grid"));
9081   legend->setLayer(QLatin1String("legend"));
9082 
9083   setViewport(rect()); // needs to be called after mPlotLayout has been created
9084 
9085   replot();
9086 }
9087 
~QCustomPlot()9088 QCustomPlot::~QCustomPlot()
9089 {
9090   clearPlottables();
9091   clearItems();
9092 
9093   if (mPlotLayout)
9094   {
9095     delete mPlotLayout;
9096     mPlotLayout = 0;
9097   }
9098 
9099   mCurrentLayer = 0;
9100   qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9101   mLayers.clear();
9102 }
9103 
9104 /*!
9105   Sets which elements are forcibly drawn antialiased as an \a or combination of QCP::AntialiasedElement.
9106 
9107   This overrides the antialiasing settings for whole element groups, normally controlled with the
9108   \a setAntialiasing function on the individual elements. If an element is neither specified in
9109   \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
9110   each individual element instance is used.
9111 
9112   For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be
9113   drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
9114   to.
9115 
9116   if an element in \a antialiasedElements is already set in \ref setNotAntialiasedElements, it is
9117   removed from there.
9118 
9119   \see setNotAntialiasedElements
9120 */
setAntialiasedElements(const QCP::AntialiasedElements & antialiasedElements)9121 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9122 {
9123   mAntialiasedElements = antialiasedElements;
9124 
9125   // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9126   if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9127     mNotAntialiasedElements |= ~mAntialiasedElements;
9128 }
9129 
9130 /*!
9131   Sets whether the specified \a antialiasedElement is forcibly drawn antialiased.
9132 
9133   See \ref setAntialiasedElements for details.
9134 
9135   \see setNotAntialiasedElement
9136 */
setAntialiasedElement(QCP::AntialiasedElement antialiasedElement,bool enabled)9137 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
9138 {
9139   if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9140     mAntialiasedElements &= ~antialiasedElement;
9141   else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9142     mAntialiasedElements |= antialiasedElement;
9143 
9144   // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9145   if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9146     mNotAntialiasedElements |= ~mAntialiasedElements;
9147 }
9148 
9149 /*!
9150   Sets which elements are forcibly drawn not antialiased as an \a or combination of
9151   QCP::AntialiasedElement.
9152 
9153   This overrides the antialiasing settings for whole element groups, normally controlled with the
9154   \a setAntialiasing function on the individual elements. If an element is neither specified in
9155   \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
9156   each individual element instance is used.
9157 
9158   For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be
9159   drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
9160   to.
9161 
9162   if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is
9163   removed from there.
9164 
9165   \see setAntialiasedElements
9166 */
setNotAntialiasedElements(const QCP::AntialiasedElements & notAntialiasedElements)9167 void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
9168 {
9169   mNotAntialiasedElements = notAntialiasedElements;
9170 
9171   // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9172   if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9173     mAntialiasedElements |= ~mNotAntialiasedElements;
9174 }
9175 
9176 /*!
9177   Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased.
9178 
9179   See \ref setNotAntialiasedElements for details.
9180 
9181   \see setAntialiasedElement
9182 */
setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement,bool enabled)9183 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
9184 {
9185   if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9186     mNotAntialiasedElements &= ~notAntialiasedElement;
9187   else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9188     mNotAntialiasedElements |= notAntialiasedElement;
9189 
9190   // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9191   if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9192     mAntialiasedElements |= ~mNotAntialiasedElements;
9193 }
9194 
9195 /*!
9196   If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the
9197   plottable to the legend (QCustomPlot::legend).
9198 
9199   \see addPlottable, addGraph, QCPLegend::addItem
9200 */
setAutoAddPlottableToLegend(bool on)9201 void QCustomPlot::setAutoAddPlottableToLegend(bool on)
9202 {
9203   mAutoAddPlottableToLegend = on;
9204 }
9205 
9206 /*!
9207   Sets the possible interactions of this QCustomPlot as an or-combination of \ref QCP::Interaction
9208   enums. There are the following types of interactions:
9209 
9210   <b>Axis range manipulation</b> is controlled via \ref QCP::iRangeDrag and \ref QCP::iRangeZoom. When the
9211   respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel.
9212   For details how to control which axes the user may drag/zoom and in what orientations, see \ref
9213   QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeDragAxes,
9214   \ref QCPAxisRect::setRangeZoomAxes.
9215 
9216   <b>Plottable selection</b> is controlled by \ref QCP::iSelectPlottables. If \ref QCP::iSelectPlottables is
9217   set, the user may select plottables (graphs, curves, bars,...) by clicking on them or in their
9218   vicinity (\ref setSelectionTolerance). Whether the user can actually select a plottable can
9219   further be restricted with the \ref QCPAbstractPlottable::setSelectable function on the specific
9220   plottable. To find out whether a specific plottable is selected, call
9221   QCPAbstractPlottable::selected(). To retrieve a list of all currently selected plottables, call
9222   \ref selectedPlottables. If you're only interested in QCPGraphs, you may use the convenience
9223   function \ref selectedGraphs.
9224 
9225   <b>Item selection</b> is controlled by \ref QCP::iSelectItems. If \ref QCP::iSelectItems is set, the user
9226   may select items (QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To find
9227   out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a list of
9228   all currently selected items, call \ref selectedItems.
9229 
9230   <b>Axis selection</b> is controlled with \ref QCP::iSelectAxes. If \ref QCP::iSelectAxes is set, the user
9231   may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick
9232   labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectableParts for
9233   each axis. To retrieve a list of all axes that currently contain selected parts, call \ref
9234   selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selectedParts().
9235 
9236   <b>Legend selection</b> is controlled with \ref QCP::iSelectLegend. If this is set, the user may
9237   select the legend itself or individual items by clicking on them. What parts exactly are
9238   selectable can be controlled via \ref QCPLegend::setSelectableParts. To find out whether the
9239   legend or any of its child items are selected, check the value of QCPLegend::selectedParts. To
9240   find out which child items are selected, call \ref QCPLegend::selectedItems.
9241 
9242   <b>All other selectable elements</b> The selection of all other selectable objects (e.g.
9243   QCPPlotTitle, or your own layerable subclasses) is controlled with \ref QCP::iSelectOther. If set, the
9244   user may select those objects by clicking on them. To find out which are currently selected, you
9245   need to check their selected state explicitly.
9246 
9247   If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is
9248   emitted. Each selectable object additionally emits an individual selectionChanged signal whenever
9249   their selection state has changed, i.e. not only by user interaction.
9250 
9251   To allow multiple objects to be selected by holding the selection modifier (\ref
9252   setMultiSelectModifier), set the flag \ref QCP::iMultiSelect.
9253 
9254   \note In addition to the selection mechanism presented here, QCustomPlot always emits
9255   corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and
9256   \ref plottableDoubleClick for example.
9257 
9258   \see setInteraction, setSelectionTolerance
9259 */
setInteractions(const QCP::Interactions & interactions)9260 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9261 {
9262   mInteractions = interactions;
9263 }
9264 
9265 /*!
9266   Sets the single \a interaction of this QCustomPlot to \a enabled.
9267 
9268   For details about the interaction system, see \ref setInteractions.
9269 
9270   \see setInteractions
9271 */
setInteraction(const QCP::Interaction & interaction,bool enabled)9272 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9273 {
9274   if (!enabled && mInteractions.testFlag(interaction))
9275     mInteractions &= ~interaction;
9276   else if (enabled && !mInteractions.testFlag(interaction))
9277     mInteractions |= interaction;
9278 }
9279 
9280 /*!
9281   Sets the tolerance that is used to decide whether a click selects an object (e.g. a plottable) or
9282   not.
9283 
9284   If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only regarded as a
9285   potential selection when the minimum distance between the click position and the graph line is
9286   smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks
9287   directly inside the area and ignore this selection tolerance. In other words, it only has meaning
9288   for parts of objects that are too thin to exactly hit with a click and thus need such a
9289   tolerance.
9290 
9291   \see setInteractions, QCPLayerable::selectTest
9292 */
setSelectionTolerance(int pixels)9293 void QCustomPlot::setSelectionTolerance(int pixels)
9294 {
9295   mSelectionTolerance = pixels;
9296 }
9297 
9298 /*!
9299   Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes
9300   ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves
9301   performance during dragging. Thus it creates a more responsive user experience. As soon as the
9302   user stops dragging, the last replot is done with normal antialiasing, to restore high image
9303   quality.
9304 
9305   \see setAntialiasedElements, setNotAntialiasedElements
9306 */
setNoAntialiasingOnDrag(bool enabled)9307 void QCustomPlot::setNoAntialiasingOnDrag(bool enabled)
9308 {
9309   mNoAntialiasingOnDrag = enabled;
9310 }
9311 
9312 /*!
9313   Sets the plotting hints for this QCustomPlot instance as an \a or combination of QCP::PlottingHint.
9314 
9315   \see setPlottingHint
9316 */
setPlottingHints(const QCP::PlottingHints & hints)9317 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9318 {
9319   mPlottingHints = hints;
9320 }
9321 
9322 /*!
9323   Sets the specified plotting \a hint to \a enabled.
9324 
9325   \see setPlottingHints
9326 */
setPlottingHint(QCP::PlottingHint hint,bool enabled)9327 void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)
9328 {
9329   QCP::PlottingHints newHints = mPlottingHints;
9330   if (!enabled)
9331     newHints &= ~hint;
9332   else
9333     newHints |= hint;
9334 
9335   if (newHints != mPlottingHints)
9336     setPlottingHints(newHints);
9337 }
9338 
9339 /*!
9340   Sets the keyboard modifier that will be recognized as multi-select-modifier.
9341 
9342   If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple objects
9343   by clicking on them one after the other while holding down \a modifier.
9344 
9345   By default the multi-select-modifier is set to Qt::ControlModifier.
9346 
9347   \see setInteractions
9348 */
setMultiSelectModifier(Qt::KeyboardModifier modifier)9349 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9350 {
9351   mMultiSelectModifier = modifier;
9352 }
9353 
9354 /*!
9355   Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout
9356   (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect.
9357 
9358   This function is used to allow arbitrary size exports with \ref toPixmap, \ref savePng, \ref
9359   savePdf, etc. by temporarily changing the viewport size.
9360 */
setViewport(const QRect & rect)9361 void QCustomPlot::setViewport(const QRect &rect)
9362 {
9363   mViewport = rect;
9364   if (mPlotLayout)
9365     mPlotLayout->setOuterRect(mViewport);
9366 }
9367 
9368 /*!
9369   Sets \a pm as the viewport background pixmap (see \ref setViewport). The pixmap is always drawn
9370   below all other objects in the plot.
9371 
9372   For cases where the provided pixmap doesn't have the same size as the viewport, scaling can be
9373   enabled with \ref setBackgroundScaled and the scaling mode (whether and how the aspect ratio is
9374   preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
9375   consider using the overloaded version of this function.
9376 
9377   If a background brush was set with \ref setBackground(const QBrush &brush), the viewport will
9378   first be filled with that brush, before drawing the background pixmap. This can be useful for
9379   background pixmaps with translucent areas.
9380 
9381   \see setBackgroundScaled, setBackgroundScaledMode
9382 */
setBackground(const QPixmap & pm)9383 void QCustomPlot::setBackground(const QPixmap &pm)
9384 {
9385   mBackgroundPixmap = pm;
9386   mScaledBackgroundPixmap = QPixmap();
9387 }
9388 
9389 /*!
9390   Sets the background brush of the viewport (see \ref setViewport).
9391 
9392   Before drawing everything else, the background is filled with \a brush. If a background pixmap
9393   was set with \ref setBackground(const QPixmap &pm), this brush will be used to fill the viewport
9394   before the background pixmap is drawn. This can be useful for background pixmaps with translucent
9395   areas.
9396 
9397   Set \a brush to Qt::NoBrush or Qt::Transparent to leave background transparent. This can be
9398   useful for exporting to image formats which support transparency, e.g. \ref savePng.
9399 
9400   \see setBackgroundScaled, setBackgroundScaledMode
9401 */
setBackground(const QBrush & brush)9402 void QCustomPlot::setBackground(const QBrush &brush)
9403 {
9404   mBackgroundBrush = brush;
9405 }
9406 
9407 /*! \overload
9408 
9409   Allows setting the background pixmap of the viewport, whether it shall be scaled and how it
9410   shall be scaled in one call.
9411 
9412   \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
9413 */
setBackground(const QPixmap & pm,bool scaled,Qt::AspectRatioMode mode)9414 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9415 {
9416   mBackgroundPixmap = pm;
9417   mScaledBackgroundPixmap = QPixmap();
9418   mBackgroundScaled = scaled;
9419   mBackgroundScaledMode = mode;
9420 }
9421 
9422 /*!
9423   Sets whether the viewport background pixmap shall be scaled to fit the viewport. If \a scaled is
9424   set to true, control whether and how the aspect ratio of the original pixmap is preserved with
9425   \ref setBackgroundScaledMode.
9426 
9427   Note that the scaled version of the original pixmap is buffered, so there is no performance
9428   penalty on replots. (Except when the viewport dimensions are changed continuously.)
9429 
9430   \see setBackground, setBackgroundScaledMode
9431 */
setBackgroundScaled(bool scaled)9432 void QCustomPlot::setBackgroundScaled(bool scaled)
9433 {
9434   mBackgroundScaled = scaled;
9435 }
9436 
9437 /*!
9438   If scaling of the viewport background pixmap is enabled (\ref setBackgroundScaled), use this
9439   function to define whether and how the aspect ratio of the original pixmap is preserved.
9440 
9441   \see setBackground, setBackgroundScaled
9442 */
setBackgroundScaledMode(Qt::AspectRatioMode mode)9443 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9444 {
9445   mBackgroundScaledMode = mode;
9446 }
9447 
9448 /*!
9449   Returns the plottable with \a index. If the index is invalid, returns 0.
9450 
9451   There is an overloaded version of this function with no parameter which returns the last added
9452   plottable, see QCustomPlot::plottable()
9453 
9454   \see plottableCount, addPlottable
9455 */
plottable(int index)9456 QCPAbstractPlottable *QCustomPlot::plottable(int index)
9457 {
9458   if (index >= 0 && index < mPlottables.size())
9459   {
9460     return mPlottables.at(index);
9461   } else
9462   {
9463     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9464     return 0;
9465   }
9466 }
9467 
9468 /*! \overload
9469 
9470   Returns the last plottable that was added with \ref addPlottable. If there are no plottables in
9471   the plot, returns 0.
9472 
9473   \see plottableCount, addPlottable
9474 */
plottable()9475 QCPAbstractPlottable *QCustomPlot::plottable()
9476 {
9477   if (!mPlottables.isEmpty())
9478   {
9479     return mPlottables.last();
9480   } else
9481     return 0;
9482 }
9483 
9484 /*!
9485   Adds the specified plottable to the plot and, if \ref setAutoAddPlottableToLegend is enabled, to
9486   the legend (QCustomPlot::legend). QCustomPlot takes ownership of the plottable.
9487 
9488   Returns true on success, i.e. when \a plottable isn't already in the plot and the parent plot of
9489   \a plottable is this QCustomPlot (the latter is controlled by what axes were passed in the
9490   plottable's constructor).
9491 
9492   \see plottable, plottableCount, removePlottable, clearPlottables
9493 */
addPlottable(QCPAbstractPlottable * plottable)9494 bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable)
9495 {
9496   if (mPlottables.contains(plottable))
9497   {
9498     qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9499     return false;
9500   }
9501   if (plottable->parentPlot() != this)
9502   {
9503     qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9504     return false;
9505   }
9506 
9507   mPlottables.append(plottable);
9508   // possibly add plottable to legend:
9509   if (mAutoAddPlottableToLegend)
9510     plottable->addToLegend();
9511   // special handling for QCPGraphs to maintain the simple graph interface:
9512   if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9513     mGraphs.append(graph);
9514   if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9515     plottable->setLayer(currentLayer());
9516   return true;
9517 }
9518 
9519 /*!
9520   Removes the specified plottable from the plot and, if necessary, from the legend (QCustomPlot::legend).
9521 
9522   Returns true on success.
9523 
9524   \see addPlottable, clearPlottables
9525 */
removePlottable(QCPAbstractPlottable * plottable)9526 bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)
9527 {
9528   if (!mPlottables.contains(plottable))
9529   {
9530     qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9531     return false;
9532   }
9533 
9534   // remove plottable from legend:
9535   plottable->removeFromLegend();
9536   // special handling for QCPGraphs to maintain the simple graph interface:
9537   if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9538     mGraphs.removeOne(graph);
9539   // remove plottable:
9540   delete plottable;
9541   mPlottables.removeOne(plottable);
9542   return true;
9543 }
9544 
9545 /*! \overload
9546 
9547   Removes the plottable by its \a index.
9548 */
removePlottable(int index)9549 bool QCustomPlot::removePlottable(int index)
9550 {
9551   if (index >= 0 && index < mPlottables.size())
9552     return removePlottable(mPlottables[index]);
9553   else
9554   {
9555     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9556     return false;
9557   }
9558 }
9559 
9560 /*!
9561   Removes all plottables from the plot (and the QCustomPlot::legend, if necessary).
9562 
9563   Returns the number of plottables removed.
9564 
9565   \see removePlottable
9566 */
clearPlottables()9567 int QCustomPlot::clearPlottables()
9568 {
9569   int c = mPlottables.size();
9570   for (int i=c-1; i >= 0; --i)
9571     removePlottable(mPlottables[i]);
9572   return c;
9573 }
9574 
9575 /*!
9576   Returns the number of currently existing plottables in the plot
9577 
9578   \see plottable, addPlottable
9579 */
plottableCount() const9580 int QCustomPlot::plottableCount() const
9581 {
9582   return mPlottables.size();
9583 }
9584 
9585 /*!
9586   Returns a list of the selected plottables. If no plottables are currently selected, the list is empty.
9587 
9588   There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs.
9589 
9590   \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
9591 */
selectedPlottables() const9592 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9593 {
9594   QList<QCPAbstractPlottable*> result;
9595   for (auto* plottable : mPlottables)
9596   {
9597     if (plottable->selected())
9598       result.append(plottable);
9599   }
9600   return result;
9601 }
9602 
9603 /*!
9604   Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines
9605   (like graphs) have a tolerance band around them, see \ref setSelectionTolerance. If multiple
9606   plottables come into consideration, the one closest to \a pos is returned.
9607 
9608   If \a onlySelectable is true, only plottables that are selectable
9609   (QCPAbstractPlottable::setSelectable) are considered.
9610 
9611   If there is no plottable at \a pos, the return value is 0.
9612 
9613   \see itemAt, layoutElementAt
9614 */
plottableAt(const QPointF & pos,bool onlySelectable) const9615 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9616 {
9617   QCPAbstractPlottable *resultPlottable = 0;
9618   double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9619 
9620   for (auto *plottable : mPlottables)
9621   {
9622     if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
9623       continue;
9624     if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
9625     {
9626       double currentDistance = plottable->selectTest(pos, false);
9627       if (currentDistance >= 0 && currentDistance < resultDistance)
9628       {
9629         resultPlottable = plottable;
9630         resultDistance = currentDistance;
9631       }
9632     }
9633   }
9634 
9635   return resultPlottable;
9636 }
9637 
9638 /*!
9639   Returns whether this QCustomPlot instance contains the \a plottable.
9640 
9641   \see addPlottable
9642 */
hasPlottable(QCPAbstractPlottable * plottable) const9643 bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
9644 {
9645   return mPlottables.contains(plottable);
9646 }
9647 
9648 /*!
9649   Returns the graph with \a index. If the index is invalid, returns 0.
9650 
9651   There is an overloaded version of this function with no parameter which returns the last created
9652   graph, see QCustomPlot::graph()
9653 
9654   \see graphCount, addGraph
9655 */
graph(int index) const9656 QCPGraph *QCustomPlot::graph(int index) const
9657 {
9658   if (index >= 0 && index < mGraphs.size())
9659   {
9660     return mGraphs.at(index);
9661   } else
9662   {
9663     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9664     return 0;
9665   }
9666 }
9667 
9668 /*! \overload
9669 
9670   Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot,
9671   returns 0.
9672 
9673   \see graphCount, addGraph
9674 */
graph() const9675 QCPGraph *QCustomPlot::graph() const
9676 {
9677   if (!mGraphs.isEmpty())
9678   {
9679     return mGraphs.last();
9680   } else
9681     return 0;
9682 }
9683 
9684 /*!
9685   Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the
9686   bottom (xAxis) is used as key and the left (yAxis) is used as value axis. If specified, \a
9687   keyAxis and \a valueAxis must reside in this QCustomPlot.
9688 
9689   \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically
9690   "y") for the graph.
9691 
9692   Returns a pointer to the newly created graph, or 0 if adding the graph failed.
9693 
9694   \see graph, graphCount, removeGraph, clearGraphs
9695 */
addGraph(QCPAxis * keyAxis,QCPAxis * valueAxis)9696 QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
9697 {
9698   if (!keyAxis) keyAxis = xAxis;
9699   if (!valueAxis) valueAxis = yAxis;
9700   if (!keyAxis || !valueAxis)
9701   {
9702     qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9703     return 0;
9704   }
9705   if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9706   {
9707     qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9708     return 0;
9709   }
9710 
9711   QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9712   if (addPlottable(newGraph))
9713   {
9714     newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
9715     return newGraph;
9716   } else
9717   {
9718     delete newGraph;
9719     return 0;
9720   }
9721 }
9722 
9723 /*!
9724   Removes the specified \a graph from the plot and, if necessary, from the QCustomPlot::legend. If
9725   any other graphs in the plot have a channel fill set towards the removed graph, the channel fill
9726   property of those graphs is reset to zero (no channel fill).
9727 
9728   Returns true on success.
9729 
9730   \see clearGraphs
9731 */
removeGraph(QCPGraph * graph)9732 bool QCustomPlot::removeGraph(QCPGraph *graph)
9733 {
9734   return removePlottable(graph);
9735 }
9736 
9737 /*! \overload
9738 
9739   Removes the graph by its \a index.
9740 */
removeGraph(int index)9741 bool QCustomPlot::removeGraph(int index)
9742 {
9743   if (index >= 0 && index < mGraphs.size())
9744     return removeGraph(mGraphs[index]);
9745   else
9746     return false;
9747 }
9748 
9749 /*!
9750   Removes all graphs from the plot (and the QCustomPlot::legend, if necessary).
9751 
9752   Returns the number of graphs removed.
9753 
9754   \see removeGraph
9755 */
clearGraphs()9756 int QCustomPlot::clearGraphs()
9757 {
9758   int c = mGraphs.size();
9759   for (int i=c-1; i >= 0; --i)
9760     removeGraph(mGraphs[i]);
9761   return c;
9762 }
9763 
9764 /*!
9765   Returns the number of currently existing graphs in the plot
9766 
9767   \see graph, addGraph
9768 */
graphCount() const9769 int QCustomPlot::graphCount() const
9770 {
9771   return mGraphs.size();
9772 }
9773 
9774 /*!
9775   Returns a list of the selected graphs. If no graphs are currently selected, the list is empty.
9776 
9777   If you are not only interested in selected graphs but other plottables like QCPCurve, QCPBars,
9778   etc., use \ref selectedPlottables.
9779 
9780   \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
9781 */
selectedGraphs() const9782 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9783 {
9784   QList<QCPGraph*> result;
9785   for (auto* graph : mGraphs)
9786   {
9787     if (graph->selected())
9788       result.append(graph);
9789   }
9790   return result;
9791 }
9792 
9793 /*!
9794   Returns the item with \a index. If the index is invalid, returns 0.
9795 
9796   There is an overloaded version of this function with no parameter which returns the last added
9797   item, see QCustomPlot::item()
9798 
9799   \see itemCount, addItem
9800 */
item(int index) const9801 QCPAbstractItem *QCustomPlot::item(int index) const
9802 {
9803   if (index >= 0 && index < mItems.size())
9804   {
9805     return mItems.at(index);
9806   } else
9807   {
9808     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9809     return 0;
9810   }
9811 }
9812 
9813 /*! \overload
9814 
9815   Returns the last item, that was added with \ref addItem. If there are no items in the plot,
9816   returns 0.
9817 
9818   \see itemCount, addItem
9819 */
item() const9820 QCPAbstractItem *QCustomPlot::item() const
9821 {
9822   if (!mItems.isEmpty())
9823   {
9824     return mItems.last();
9825   } else
9826     return 0;
9827 }
9828 
9829 /*!
9830   Adds the specified item to the plot. QCustomPlot takes ownership of the item.
9831 
9832   Returns true on success, i.e. when \a item wasn't already in the plot and the parent plot of \a
9833   item is this QCustomPlot.
9834 
9835   \see item, itemCount, removeItem, clearItems
9836 */
addItem(QCPAbstractItem * item)9837 bool QCustomPlot::addItem(QCPAbstractItem *item)
9838 {
9839   if (!mItems.contains(item) && item->parentPlot() == this)
9840   {
9841     mItems.append(item);
9842     return true;
9843   } else
9844   {
9845     qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9846     return false;
9847   }
9848 }
9849 
9850 /*!
9851   Removes the specified item from the plot.
9852 
9853   Returns true on success.
9854 
9855   \see addItem, clearItems
9856 */
removeItem(QCPAbstractItem * item)9857 bool QCustomPlot::removeItem(QCPAbstractItem *item)
9858 {
9859   if (mItems.contains(item))
9860   {
9861     delete item;
9862     mItems.removeOne(item);
9863     return true;
9864   } else
9865   {
9866     qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9867     return false;
9868   }
9869 }
9870 
9871 /*! \overload
9872 
9873   Removes the item by its \a index.
9874 */
removeItem(int index)9875 bool QCustomPlot::removeItem(int index)
9876 {
9877   if (index >= 0 && index < mItems.size())
9878     return removeItem(mItems[index]);
9879   else
9880   {
9881     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9882     return false;
9883   }
9884 }
9885 
9886 /*!
9887   Removes all items from the plot.
9888 
9889   Returns the number of items removed.
9890 
9891   \see removeItem
9892 */
clearItems()9893 int QCustomPlot::clearItems()
9894 {
9895   int c = mItems.size();
9896   for (int i=c-1; i >= 0; --i)
9897     removeItem(mItems[i]);
9898   return c;
9899 }
9900 
9901 /*!
9902   Returns the number of currently existing items in the plot
9903 
9904   \see item, addItem
9905 */
itemCount() const9906 int QCustomPlot::itemCount() const
9907 {
9908   return mItems.size();
9909 }
9910 
9911 /*!
9912   Returns a list of the selected items. If no items are currently selected, the list is empty.
9913 
9914   \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected
9915 */
selectedItems() const9916 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9917 {
9918   QList<QCPAbstractItem*> result;
9919   for (auto* item : mItems)
9920   {
9921     if (item->selected())
9922       result.append(item);
9923   }
9924   return result;
9925 }
9926 
9927 /*!
9928   Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref
9929   QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
9930   setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is
9931   returned.
9932 
9933   If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
9934   considered.
9935 
9936   If there is no item at \a pos, the return value is 0.
9937 
9938   \see plottableAt, layoutElementAt
9939 */
itemAt(const QPointF & pos,bool onlySelectable) const9940 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9941 {
9942   QCPAbstractItem *resultItem = 0;
9943   double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9944 
9945   for (auto* item : mItems)
9946   {
9947     if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
9948       continue;
9949     if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9950     {
9951       double currentDistance = item->selectTest(pos, false);
9952       if (currentDistance >= 0 && currentDistance < resultDistance)
9953       {
9954         resultItem = item;
9955         resultDistance = currentDistance;
9956       }
9957     }
9958   }
9959 
9960   return resultItem;
9961 }
9962 
9963 /*!
9964   Returns whether this QCustomPlot contains the \a item.
9965 
9966   \see addItem
9967 */
hasItem(QCPAbstractItem * item) const9968 bool QCustomPlot::hasItem(QCPAbstractItem *item) const
9969 {
9970   return mItems.contains(item);
9971 }
9972 
9973 /*!
9974   Returns the layer with the specified \a name. If there is no layer with the specified name, 0 is
9975   returned.
9976 
9977   Layer names are case-sensitive.
9978 
9979   \see addLayer, moveLayer, removeLayer
9980 */
layer(const QString & name) const9981 QCPLayer *QCustomPlot::layer(const QString &name) const
9982 {
9983   for (auto* layer : mLayers)
9984   {
9985     if (layer->name() == name)
9986       return layer;
9987   }
9988   return 0;
9989 }
9990 
9991 /*! \overload
9992 
9993   Returns the layer by \a index. If the index is invalid, 0 is returned.
9994 
9995   \see addLayer, moveLayer, removeLayer
9996 */
layer(int index) const9997 QCPLayer *QCustomPlot::layer(int index) const
9998 {
9999   if (index >= 0 && index < mLayers.size())
10000   {
10001     return mLayers.at(index);
10002   } else
10003   {
10004     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10005     return 0;
10006   }
10007 }
10008 
10009 /*!
10010   Returns the layer that is set as current layer (see \ref setCurrentLayer).
10011 */
currentLayer() const10012 QCPLayer *QCustomPlot::currentLayer() const
10013 {
10014   return mCurrentLayer;
10015 }
10016 
10017 /*!
10018   Sets the layer with the specified \a name to be the current layer. All layerables (\ref
10019   QCPLayerable), e.g. plottables and items, are created on the current layer.
10020 
10021   Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot.
10022 
10023   Layer names are case-sensitive.
10024 
10025   \see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer
10026 */
setCurrentLayer(const QString & name)10027 bool QCustomPlot::setCurrentLayer(const QString &name)
10028 {
10029   if (QCPLayer *newCurrentLayer = layer(name))
10030   {
10031     return setCurrentLayer(newCurrentLayer);
10032   } else
10033   {
10034     qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10035     return false;
10036   }
10037 }
10038 
10039 /*! \overload
10040 
10041   Sets the provided \a layer to be the current layer.
10042 
10043   Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot.
10044 
10045   \see addLayer, moveLayer, removeLayer
10046 */
setCurrentLayer(QCPLayer * layer)10047 bool QCustomPlot::setCurrentLayer(QCPLayer *layer)
10048 {
10049   if (!mLayers.contains(layer))
10050   {
10051     qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10052     return false;
10053   }
10054 
10055   mCurrentLayer = layer;
10056   return true;
10057 }
10058 
10059 /*!
10060   Returns the number of currently existing layers in the plot
10061 
10062   \see layer, addLayer
10063 */
layerCount() const10064 int QCustomPlot::layerCount() const
10065 {
10066   return mLayers.size();
10067 }
10068 
10069 /*!
10070   Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which
10071   must be unique. Depending on \a insertMode, it is positioned either below or above \a otherLayer.
10072 
10073   Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a
10074   valid layer inside this QCustomPlot.
10075 
10076   If \a otherLayer is 0, the highest layer in the QCustomPlot will be used.
10077 
10078   For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer.
10079 
10080   \see layer, moveLayer, removeLayer
10081 */
addLayer(const QString & name,QCPLayer * otherLayer,QCustomPlot::LayerInsertMode insertMode)10082 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10083 {
10084   if (!otherLayer)
10085     otherLayer = mLayers.last();
10086   if (!mLayers.contains(otherLayer))
10087   {
10088     qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10089     return false;
10090   }
10091   if (layer(name))
10092   {
10093     qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10094     return false;
10095   }
10096 
10097   QCPLayer *newLayer = new QCPLayer(this, name);
10098   mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10099   updateLayerIndices();
10100   return true;
10101 }
10102 
10103 /*!
10104   Removes the specified \a layer and returns true on success.
10105 
10106   All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below
10107   \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both
10108   cases, the total rendering order of all layerables in the QCustomPlot is preserved.
10109 
10110   If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom
10111   layer) becomes the new current layer.
10112 
10113   It is not possible to remove the last layer of the plot.
10114 
10115   \see layer, addLayer, moveLayer
10116 */
removeLayer(QCPLayer * layer)10117 bool QCustomPlot::removeLayer(QCPLayer *layer)
10118 {
10119   if (!mLayers.contains(layer))
10120   {
10121     qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10122     return false;
10123   }
10124   if (mLayers.size() < 2)
10125   {
10126     qDebug() << Q_FUNC_INFO << "can't remove last layer";
10127     return false;
10128   }
10129 
10130   // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10131   int removedIndex = layer->index();
10132   bool isFirstLayer = removedIndex==0;
10133   QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10134   QList<QCPLayerable*> children = layer->children();
10135   if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10136   {
10137     for (int i=children.size()-1; i>=0; --i)
10138       children.at(i)->moveToLayer(targetLayer, true);
10139   } else  // append normally
10140   {
10141     for (int i=0; i<children.size(); ++i)
10142       children.at(i)->moveToLayer(targetLayer, false);
10143   }
10144   // if removed layer is current layer, change current layer to layer below/above:
10145   if (layer == mCurrentLayer)
10146     setCurrentLayer(targetLayer);
10147   // remove layer:
10148   delete layer;
10149   mLayers.removeOne(layer);
10150   updateLayerIndices();
10151   return true;
10152 }
10153 
10154 /*!
10155   Moves the specified \a layer either above or below \a otherLayer. Whether it's placed above or
10156   below is controlled with \a insertMode.
10157 
10158   Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the
10159   QCustomPlot.
10160 
10161   \see layer, addLayer, moveLayer
10162 */
moveLayer(QCPLayer * layer,QCPLayer * otherLayer,QCustomPlot::LayerInsertMode insertMode)10163 bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10164 {
10165   if (!mLayers.contains(layer))
10166   {
10167     qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10168     return false;
10169   }
10170   if (!mLayers.contains(otherLayer))
10171   {
10172     qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10173     return false;
10174   }
10175 
10176   if (layer->index() > otherLayer->index())
10177     mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10178   else if (layer->index() < otherLayer->index())
10179     mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));
10180 
10181   updateLayerIndices();
10182   return true;
10183 }
10184 
10185 /*!
10186   Returns the number of axis rects in the plot.
10187 
10188   All axis rects can be accessed via QCustomPlot::axisRect().
10189 
10190   Initially, only one axis rect exists in the plot.
10191 
10192   \see axisRect, axisRects
10193 */
axisRectCount() const10194 int QCustomPlot::axisRectCount() const
10195 {
10196   return axisRects().size();
10197 }
10198 
10199 /*!
10200   Returns the axis rect with \a index.
10201 
10202   Initially, only one axis rect (with index 0) exists in the plot. If multiple axis rects were
10203   added, all of them may be accessed with this function in a linear fashion (even when they are
10204   nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout).
10205 
10206   \see axisRectCount, axisRects
10207 */
axisRect(int index) const10208 QCPAxisRect *QCustomPlot::axisRect(int index) const
10209 {
10210   const QList<QCPAxisRect*> rectList = axisRects();
10211   if (index >= 0 && index < rectList.size())
10212   {
10213     return rectList.at(index);
10214   } else
10215   {
10216     qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10217     return 0;
10218   }
10219 }
10220 
10221 /*!
10222   Returns all axis rects in the plot.
10223 
10224   \see axisRectCount, axisRect
10225 */
axisRects() const10226 QList<QCPAxisRect*> QCustomPlot::axisRects() const
10227 {
10228   QList<QCPAxisRect*> result;
10229   QStack<QCPLayoutElement*> elementStack;
10230   if (mPlotLayout)
10231     elementStack.push(mPlotLayout);
10232 
10233   while (!elementStack.isEmpty())
10234   {
10235     for (auto* element : elementStack.pop()->elements(false))
10236     {
10237       if (element)
10238       {
10239         elementStack.push(element);
10240         if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10241           result.append(ar);
10242       }
10243     }
10244   }
10245 
10246   return result;
10247 }
10248 
10249 /*!
10250   Returns the layout element at pixel position \a pos. If there is no element at that position,
10251   returns 0.
10252 
10253   Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on
10254   any of its parent elements is set to false, it will not be considered.
10255 
10256   \see itemAt, plottableAt
10257 */
layoutElementAt(const QPointF & pos) const10258 QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
10259 {
10260   QCPLayoutElement *currentElement = mPlotLayout;
10261   bool searchSubElements = true;
10262   while (searchSubElements && currentElement)
10263   {
10264     searchSubElements = false;
10265     for (auto* subElement : currentElement->elements(false))
10266     {
10267       if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10268       {
10269         currentElement = subElement;
10270         searchSubElements = true;
10271         break;
10272       }
10273     }
10274   }
10275   return currentElement;
10276 }
10277 
10278 /*!
10279   Returns the axes that currently have selected parts, i.e. whose selection state is not \ref
10280   QCPAxis::spNone.
10281 
10282   \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelectedParts,
10283   QCPAxis::setSelectableParts
10284 */
selectedAxes() const10285 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10286 {
10287   QList<QCPAxis*> result, allAxes;
10288   for (auto* rect : axisRects())
10289     allAxes << rect->axes();
10290 
10291   for (auto* axis : allAxes)
10292   {
10293     if (axis->selectedParts() != QCPAxis::spNone)
10294       result.append(axis);
10295   }
10296 
10297   return result;
10298 }
10299 
10300 /*!
10301   Returns the legends that currently have selected parts, i.e. whose selection state is not \ref
10302   QCPLegend::spNone.
10303 
10304   \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelectedParts,
10305   QCPLegend::setSelectableParts, QCPLegend::selectedItems
10306 */
selectedLegends() const10307 QList<QCPLegend*> QCustomPlot::selectedLegends() const
10308 {
10309   QList<QCPLegend*> result;
10310 
10311   QStack<QCPLayoutElement*> elementStack;
10312   if (mPlotLayout)
10313     elementStack.push(mPlotLayout);
10314 
10315   while (!elementStack.isEmpty())
10316   {
10317     for (auto* subElement : elementStack.pop()->elements(false))
10318     {
10319       if (subElement)
10320       {
10321         elementStack.push(subElement);
10322         if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10323         {
10324           if (leg->selectedParts() != QCPLegend::spNone)
10325             result.append(leg);
10326         }
10327       }
10328     }
10329   }
10330 
10331   return result;
10332 }
10333 
10334 /*!
10335   Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot.
10336 
10337   Since calling this function is not a user interaction, this does not emit the \ref
10338   selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the
10339   objects were previously selected.
10340 
10341   \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends
10342 */
deselectAll()10343 void QCustomPlot::deselectAll()
10344 {
10345   for (auto* layer : mLayers)
10346   {
10347     for (auto* layerable : layer->children())
10348       layerable->deselectEvent(0);
10349   }
10350 }
10351 
10352 /*!
10353   Causes a complete replot into the internal buffer. Finally, update() is called, to redraw the
10354   buffer on the QCustomPlot widget surface. This is the method that must be called to make changes,
10355   for example on the axis ranges or data points of graphs, visible.
10356 
10357   Under a few circumstances, QCustomPlot causes a replot by itself. Those are resize events of the
10358   QCustomPlot widget and user interactions (object selection and range dragging/zooming).
10359 
10360   Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref
10361   afterReplot is emitted. It is safe to mutually connect the replot slot with any of those two
10362   signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
10363   recursion.
10364 */
replot(QCustomPlot::RefreshPriority refreshPriority)10365 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
10366 {
10367   if (mReplotting) // incase signals loop back to replot slot
10368     return;
10369   mReplotting = true;
10370   emit beforeReplot();
10371 
10372   mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10373   QCPPainter painter;
10374   painter.begin(&mPaintBuffer);
10375   if (painter.isActive())
10376   {
10377     painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10378     if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10379       painter.fillRect(mViewport, mBackgroundBrush);
10380     draw(&painter);
10381     painter.end();
10382     if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10383       repaint();
10384     else
10385       update();
10386   } else // might happen if QCustomPlot has width or height zero
10387     qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
10388 
10389   emit afterReplot();
10390   mReplotting = false;
10391 }
10392 
10393 /*!
10394   Rescales the axes such that all plottables (like graphs) in the plot are fully visible.
10395 
10396   if \a onlyVisiblePlottables is set to true, only the plottables that have their visibility set to true
10397   (QCPLayerable::setVisible), will be used to rescale the axes.
10398 
10399   \see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale
10400 */
rescaleAxes(bool onlyVisiblePlottables)10401 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10402 {
10403   QList<QCPAxis*> allAxes;
10404   for (auto* rect : axisRects())
10405     allAxes << rect->axes();
10406 
10407   for (auto* axis : allAxes)
10408     axis->rescale(onlyVisiblePlottables);
10409 }
10410 
10411 /*!
10412   Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale
10413   of texts and lines will be derived from the specified \a width and \a height. This means, the
10414   output will look like the normal on-screen output of a QCustomPlot widget with the corresponding
10415   pixel width and height. If either \a width or \a height is zero, the exported image will have the
10416   same dimensions as the QCustomPlot widget currently has.
10417 
10418   \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF file. Cosmetic pens
10419   are pens with numerical width 0, which are always drawn as a one pixel wide line, no matter what
10420   zoom factor is set in the PDF-Viewer. For more information about cosmetic pens, see the QPainter
10421   and QPen documentation.
10422 
10423   The objects of the plot will appear in the current selection state. If you don't want any
10424   selected objects to be painted in their selected look, deselect everything with \ref deselectAll
10425   before calling this function.
10426 
10427   Returns true on success.
10428 
10429   \warning
10430   \li If you plan on editing the exported PDF file with a vector graphics editor like
10431   Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing those cosmetic lines
10432   (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks).
10433   \li If calling this function inside the constructor of the parent of the QCustomPlot widget
10434   (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10435   explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10436   function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10437   aren't defined yet inside the constructor, so you would get an image that has strange
10438   widths/heights.
10439 
10440   \a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting
10441   PDF file.
10442 
10443   \note On Android systems, this method does nothing and issues an according qDebug warning
10444   message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
10445 
10446   \see savePng, saveBmp, saveJpg, saveRastered
10447 */
savePdf(const QString & fileName,bool noCosmeticPen,int width,int height,const QString & pdfCreator,const QString & pdfTitle)10448 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10449 {
10450   bool success = false;
10451 #ifdef QT_NO_PRINTER
10452   Q_UNUSED(fileName)
10453   Q_UNUSED(noCosmeticPen)
10454   Q_UNUSED(width)
10455   Q_UNUSED(height)
10456   Q_UNUSED(pdfCreator)
10457   Q_UNUSED(pdfTitle)
10458   qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10459 #else
10460   int newWidth, newHeight;
10461   if (width == 0 || height == 0)
10462   {
10463     newWidth = this->width();
10464     newHeight = this->height();
10465   } else
10466   {
10467     newWidth = width;
10468     newHeight = height;
10469   }
10470 
10471   QPrinter printer(QPrinter::ScreenResolution);
10472   printer.setOutputFileName(fileName);
10473   printer.setOutputFormat(QPrinter::PdfFormat);
10474   printer.setColorMode(QPrinter::Color);
10475   printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10476   printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10477   QRect oldViewport = viewport();
10478   setViewport(QRect(0, 0, newWidth, newHeight));
10479 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10480   printer.setFullPage(true);
10481   printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10482 #else
10483   QPageLayout pageLayout;
10484   pageLayout.setMode(QPageLayout::FullPageMode);
10485   pageLayout.setOrientation(QPageLayout::Portrait);
10486   pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10487   pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
10488   printer.setPageLayout(pageLayout);
10489 #endif
10490   QCPPainter printpainter;
10491   if (printpainter.begin(&printer))
10492   {
10493     printpainter.setMode(QCPPainter::pmVectorized);
10494     printpainter.setMode(QCPPainter::pmNoCaching);
10495     printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10496     printpainter.setWindow(mViewport);
10497     if (mBackgroundBrush.style() != Qt::NoBrush &&
10498         mBackgroundBrush.color() != Qt::white &&
10499         mBackgroundBrush.color() != Qt::transparent &&
10500         mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10501       printpainter.fillRect(viewport(), mBackgroundBrush);
10502     draw(&printpainter);
10503     printpainter.end();
10504     success = true;
10505   }
10506   setViewport(oldViewport);
10507 #endif // QT_NO_PRINTER
10508   return success;
10509 }
10510 
10511 /*!
10512   Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width
10513   and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10514   the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10515   scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10516 
10517   For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10518   image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10519   texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10520   200*200 pixel resolution.
10521 
10522   If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10523   temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10524   QCustomPlot to place objects with sub-pixel accuracy.
10525 
10526   \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10527   (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10528   explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10529   function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10530   aren't defined yet inside the constructor, so you would get an image that has strange
10531   widths/heights.
10532 
10533   The objects of the plot will appear in the current selection state. If you don't want any selected
10534   objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10535   this function.
10536 
10537   If you want the PNG to have a transparent background, call \ref setBackground(const QBrush
10538   &brush) with no brush (Qt::NoBrush) or a transparent color (Qt::transparent), before saving.
10539 
10540   PNG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
10541   -1 to use the default setting.
10542 
10543   Returns true on success. If this function fails, most likely the PNG format isn't supported by
10544   the system, see Qt docs about QImageWriter::supportedImageFormats().
10545 
10546   \see savePdf, saveBmp, saveJpg, saveRastered
10547 */
savePng(const QString & fileName,int width,int height,double scale,int quality)10548 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10549 {
10550   return saveRastered(fileName, width, height, scale, "PNG", quality);
10551 }
10552 
10553 /*!
10554   Saves a JPG image file to \a fileName on disc. The output plot will have the dimensions \a width
10555   and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10556   the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10557   scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10558 
10559   For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10560   image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10561   texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10562   200*200 pixel resolution.
10563 
10564   If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10565   temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10566   QCustomPlot to place objects with sub-pixel accuracy.
10567 
10568   \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10569   (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10570   explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10571   function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10572   aren't defined yet inside the constructor, so you would get an image that has strange
10573   widths/heights.
10574 
10575   The objects of the plot will appear in the current selection state. If you don't want any selected
10576   objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10577   this function.
10578 
10579   JPG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
10580   -1 to use the default setting.
10581 
10582   Returns true on success. If this function fails, most likely the JPG format isn't supported by
10583   the system, see Qt docs about QImageWriter::supportedImageFormats().
10584 
10585   \see savePdf, savePng, saveBmp, saveRastered
10586 */
saveJpg(const QString & fileName,int width,int height,double scale,int quality)10587 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10588 {
10589   return saveRastered(fileName, width, height, scale, "JPG", quality);
10590 }
10591 
10592 /*!
10593   Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width
10594   and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10595   the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10596   scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10597 
10598   For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10599   image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10600   texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10601   200*200 pixel resolution.
10602 
10603   If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10604   temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10605   QCustomPlot to place objects with sub-pixel accuracy.
10606 
10607   \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10608   (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10609   explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10610   function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10611   aren't defined yet inside the constructor, so you would get an image that has strange
10612   widths/heights.
10613 
10614   The objects of the plot will appear in the current selection state. If you don't want any selected
10615   objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10616   this function.
10617 
10618   Returns true on success. If this function fails, most likely the BMP format isn't supported by
10619   the system, see Qt docs about QImageWriter::supportedImageFormats().
10620 
10621   \see savePdf, savePng, saveJpg, saveRastered
10622 */
saveBmp(const QString & fileName,int width,int height,double scale)10623 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10624 {
10625   return saveRastered(fileName, width, height, scale, "BMP");
10626 }
10627 
10628 /*! \internal
10629 
10630   Returns a minimum size hint that corresponds to the minimum size of the top level layout
10631   (\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width zero, set a minimum
10632   size (setMinimumSize) either on the whole QCustomPlot or on any layout elements inside the plot.
10633   This is especially important, when placed in a QLayout where other components try to take in as
10634   much space as possible (e.g. QMdiArea).
10635 */
minimumSizeHint() const10636 QSize QCustomPlot::minimumSizeHint() const
10637 {
10638   return mPlotLayout->minimumSizeHint();
10639 }
10640 
10641 /*! \internal
10642 
10643   Returns a size hint that is the same as \ref minimumSizeHint.
10644 
10645 */
sizeHint() const10646 QSize QCustomPlot::sizeHint() const
10647 {
10648   return mPlotLayout->minimumSizeHint();
10649 }
10650 
10651 /*! \internal
10652 
10653   Event handler for when the QCustomPlot widget needs repainting. This does not cause a \ref replot, but
10654   draws the internal buffer on the widget surface.
10655 */
paintEvent(QPaintEvent * event)10656 void QCustomPlot::paintEvent(QPaintEvent *event)
10657 {
10658   Q_UNUSED(event);
10659   QPainter painter(this);
10660   painter.drawPixmap(0, 0, mPaintBuffer);
10661 }
10662 
10663 /*! \internal
10664 
10665   Event handler for a resize of the QCustomPlot widget. Causes the internal buffer to be resized to
10666   the new size. The viewport (which becomes the outer rect of mPlotLayout) is resized
10667   appropriately. Finally a \ref replot is performed.
10668 */
resizeEvent(QResizeEvent * event)10669 void QCustomPlot::resizeEvent(QResizeEvent *event)
10670 {
10671   // resize and repaint the buffer:
10672   mPaintBuffer = QPixmap(event->size());
10673   setViewport(rect());
10674   replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10675 }
10676 
10677 /*! \internal
10678 
10679  Event handler for when a double click occurs. Emits the \ref mouseDoubleClick signal, then emits
10680  the specialized signals when certain objecs are clicked (e.g. \ref plottableDoubleClick, \ref
10681  axisDoubleClick, etc.). Finally determines the affected layout element and forwards the event to
10682  it.
10683 
10684  \see mousePressEvent, mouseReleaseEvent
10685 */
mouseDoubleClickEvent(QMouseEvent * event)10686 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
10687 {
10688   emit mouseDoubleClick(event);
10689 
10690   QVariant details;
10691   QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10692 
10693   // emit specialized object double click signals:
10694   if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10695     emit plottableDoubleClick(ap, event);
10696   else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10697     emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10698   else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10699     emit itemDoubleClick(ai, event);
10700   else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10701     emit legendDoubleClick(lg, 0, event);
10702   else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10703     emit legendDoubleClick(li->parentLegend(), li, event);
10704   else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10705     emit titleDoubleClick(event, pt);
10706 
10707   // call double click event of affected layout element:
10708   if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10709     el->mouseDoubleClickEvent(event);
10710 
10711   // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10712   if (mMouseEventElement)
10713   {
10714     mMouseEventElement->mouseReleaseEvent(event);
10715     mMouseEventElement = 0;
10716   }
10717 
10718   //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10719 }
10720 
10721 /*! \internal
10722 
10723   Event handler for when a mouse button is pressed. Emits the mousePress signal. Then determines
10724   the affected layout element and forwards the event to it.
10725 
10726   \see mouseMoveEvent, mouseReleaseEvent
10727 */
mousePressEvent(QMouseEvent * event)10728 void QCustomPlot::mousePressEvent(QMouseEvent *event)
10729 {
10730   emit mousePress(event);
10731   mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10732 
10733   // call event of affected layout element:
10734   mMouseEventElement = layoutElementAt(event->pos());
10735   if (mMouseEventElement)
10736     mMouseEventElement->mousePressEvent(event);
10737 
10738   QWidget::mousePressEvent(event);
10739 }
10740 
10741 /*! \internal
10742 
10743   Event handler for when the cursor is moved. Emits the \ref mouseMove signal.
10744 
10745   If a layout element has mouse capture focus (a mousePressEvent happened on top of the layout
10746   element before), the mouseMoveEvent is forwarded to that element.
10747 
10748   \see mousePressEvent, mouseReleaseEvent
10749 */
mouseMoveEvent(QMouseEvent * event)10750 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10751 {
10752   emit mouseMove(event);
10753 
10754   // call event of affected layout element:
10755   if (mMouseEventElement)
10756     mMouseEventElement->mouseMoveEvent(event);
10757 
10758   QWidget::mouseMoveEvent(event);
10759 }
10760 
10761 /*! \internal
10762 
10763   Event handler for when a mouse button is released. Emits the \ref mouseRelease signal.
10764 
10765   If the mouse was moved less than a certain threshold in any direction since the \ref
10766   mousePressEvent, it is considered a click which causes the selection mechanism (if activated via
10767   \ref setInteractions) to possibly change selection states accordingly. Further, specialized mouse
10768   click signals are emitted (e.g. \ref plottableClick, \ref axisClick, etc.)
10769 
10770   If a layout element has mouse capture focus (a \ref mousePressEvent happened on top of the layout
10771   element before), the \ref mouseReleaseEvent is forwarded to that element.
10772 
10773   \see mousePressEvent, mouseMoveEvent
10774 */
mouseReleaseEvent(QMouseEvent * event)10775 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10776 {
10777   emit mouseRelease(event);
10778   bool doReplot = false;
10779 
10780   if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10781   {
10782     if (event->button() == Qt::LeftButton)
10783     {
10784       // handle selection mechanism:
10785       QVariant details;
10786       QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10787       bool selectionStateChanged = false;
10788       bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10789       // deselect all other layerables if not additive selection:
10790       if (!additive)
10791       {
10792         for (auto* layer : mLayers)
10793         {
10794           for (auto* layerable : layer->children())
10795           {
10796             if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10797             {
10798               bool selChanged = false;
10799               layerable->deselectEvent(&selChanged);
10800               selectionStateChanged |= selChanged;
10801             }
10802           }
10803         }
10804       }
10805       if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10806       {
10807         // a layerable was actually clicked, call its selectEvent:
10808         bool selChanged = false;
10809         clickedLayerable->selectEvent(event, additive, details, &selChanged);
10810         selectionStateChanged |= selChanged;
10811       }
10812       if (selectionStateChanged)
10813       {
10814         doReplot = true;
10815         emit selectionChangedByUser();
10816       }
10817     }
10818 
10819     // emit specialized object click signals:
10820     QVariant details;
10821     QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); // for these signals, selectability is ignored, that's why we call this again with onlySelectable set to false
10822     if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10823       emit plottableClick(ap, event);
10824     else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10825       emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10826     else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10827       emit itemClick(ai, event);
10828     else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10829       emit legendClick(lg, 0, event);
10830     else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10831       emit legendClick(li->parentLegend(), li, event);
10832     else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10833       emit titleClick(event, pt);
10834   }
10835 
10836   // call event of affected layout element:
10837   if (mMouseEventElement)
10838   {
10839     mMouseEventElement->mouseReleaseEvent(event);
10840     mMouseEventElement = 0;
10841   }
10842 
10843   if (doReplot || noAntialiasingOnDrag())
10844     replot();
10845 
10846   QWidget::mouseReleaseEvent(event);
10847 }
10848 
10849 /*! \internal
10850 
10851   Event handler for mouse wheel events. First, the \ref mouseWheel signal is emitted. Then
10852   determines the affected layout element and forwards the event to it.
10853 
10854 */
wheelEvent(QWheelEvent * event)10855 void QCustomPlot::wheelEvent(QWheelEvent *event)
10856 {
10857   emit mouseWheel(event);
10858 
10859   // call event of affected layout element:
10860   if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10861     el->wheelEvent(event);
10862 
10863   QWidget::wheelEvent(event);
10864 }
10865 
10866 /*! \internal
10867 
10868   This is the main draw function. It draws the entire plot, including background pixmap, with the
10869   specified \a painter. Note that it does not fill the background with the background brush (as the
10870   user may specify with \ref setBackground(const QBrush &brush)), this is up to the respective
10871   functions calling this method (e.g. \ref replot, \ref toPixmap and \ref toPainter).
10872 */
draw(QCPPainter * painter)10873 void QCustomPlot::draw(QCPPainter *painter)
10874 {
10875   // run through layout phases:
10876   mPlotLayout->update(QCPLayoutElement::upPreparation);
10877   mPlotLayout->update(QCPLayoutElement::upMargins);
10878   mPlotLayout->update(QCPLayoutElement::upLayout);
10879 
10880   // draw viewport background pixmap:
10881   drawBackground(painter);
10882 
10883   // draw all layered objects (grid, axes, plottables, items, legend,...):
10884   for (auto* layer : mLayers)
10885   {
10886     for (auto* child : layer->children())
10887     {
10888       if (child->realVisibility())
10889       {
10890         painter->save();
10891         painter->setClipRect(child->clipRect().translated(0, -1));
10892         child->applyDefaultAntialiasingHint(painter);
10893         child->draw(painter);
10894         painter->restore();
10895       }
10896     }
10897   }
10898 
10899   /* Debug code to draw all layout element rects
10900   for (auto* el : findChildren<QCPLayoutElement*>())
10901   {
10902     painter->setBrush(Qt::NoBrush);
10903     painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10904     painter->drawRect(el->rect());
10905     painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10906     painter->drawRect(el->outerRect());
10907   }
10908   */
10909 }
10910 
10911 /*! \internal
10912 
10913   Draws the viewport background pixmap of the plot.
10914 
10915   If a pixmap was provided via \ref setBackground, this function buffers the scaled version
10916   depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
10917   the viewport with the provided \a painter. The scaled version is buffered in
10918   mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
10919   the axis rect has changed in a way that requires a rescale of the background pixmap (this is
10920   dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was
10921   set.
10922 
10923   Note that this function does not draw a fill with the background brush (\ref setBackground(const
10924   QBrush &brush)) beneath the pixmap.
10925 
10926   \see setBackground, setBackgroundScaled, setBackgroundScaledMode
10927 */
drawBackground(QCPPainter * painter)10928 void QCustomPlot::drawBackground(QCPPainter *painter)
10929 {
10930   // Note: background color is handled in individual replot/save functions
10931 
10932   // draw background pixmap (on top of fill, if brush specified):
10933   if (!mBackgroundPixmap.isNull())
10934   {
10935     if (mBackgroundScaled)
10936     {
10937       // check whether mScaledBackground needs to be updated:
10938       QSize scaledSize(mBackgroundPixmap.size());
10939       scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10940       if (mScaledBackgroundPixmap.size() != scaledSize)
10941         mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10942       painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10943     } else
10944     {
10945       painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10946     }
10947   }
10948 }
10949 
10950 
10951 /*! \internal
10952 
10953   This method is used by \ref QCPAxisRect::removeAxis to report removed axes to the QCustomPlot
10954   so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and yAxis2 members accordingly.
10955 */
axisRemoved(QCPAxis * axis)10956 void QCustomPlot::axisRemoved(QCPAxis *axis)
10957 {
10958   if (xAxis == axis)
10959     xAxis = 0;
10960   if (xAxis2 == axis)
10961     xAxis2 = 0;
10962   if (yAxis == axis)
10963     yAxis = 0;
10964   if (yAxis2 == axis)
10965     yAxis2 = 0;
10966 
10967   // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
10968 }
10969 
10970 /*! \internal
10971 
10972   This method is used by the QCPLegend destructor to report legend removal to the QCustomPlot so
10973   it may clear its QCustomPlot::legend member accordingly.
10974 */
legendRemoved(QCPLegend * legend)10975 void QCustomPlot::legendRemoved(QCPLegend *legend)
10976 {
10977   if (this->legend == legend)
10978     this->legend = 0;
10979 }
10980 
10981 /*! \internal
10982 
10983   Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This method is thus called
10984   after every operation that changes the layer indices, like layer removal, layer creation, layer
10985   moving.
10986 */
updateLayerIndices() const10987 void QCustomPlot::updateLayerIndices() const
10988 {
10989   for (int i=0; i<mLayers.size(); ++i)
10990     mLayers.at(i)->mIndex = i;
10991 }
10992 
10993 /*! \internal
10994 
10995   Returns the layerable at pixel position \a pos. If \a onlySelectable is set to true, only those
10996   layerables that are selectable will be considered. (Layerable subclasses communicate their
10997   selectability via the QCPLayerable::selectTest method, by returning -1.)
10998 
10999   \a selectionDetails is an output parameter that contains selection specifics of the affected
11000   layerable. This is useful if the respective layerable shall be given a subsequent
11001   QCPLayerable::selectEvent (like in \ref mouseReleaseEvent). \a selectionDetails usually contains
11002   information about which part of the layerable was hit, in multi-part layerables (e.g.
11003   QCPAxis::SelectablePart).
11004 */
layerableAt(const QPointF & pos,bool onlySelectable,QVariant * selectionDetails) const11005 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11006 {
11007   for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11008   {
11009     const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11010     double minimumDistance = selectionTolerance()*1.1;
11011     QCPLayerable *minimumDistanceLayerable = 0;
11012     for (int i=layerables.size()-1; i>=0; --i)
11013     {
11014       if (!layerables.at(i)->realVisibility())
11015         continue;
11016       QVariant details;
11017       double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11018       if (dist >= 0 && dist < minimumDistance)
11019       {
11020         minimumDistance = dist;
11021         minimumDistanceLayerable = layerables.at(i);
11022         if (selectionDetails) *selectionDetails = details;
11023       }
11024     }
11025     if (minimumDistance < selectionTolerance())
11026       return minimumDistanceLayerable;
11027   }
11028   return 0;
11029 }
11030 
11031 /*!
11032   Saves the plot to a rastered image file \a fileName in the image format \a format. The plot is
11033   sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and scale 2.0 lead
11034   to a full resolution file with width 200.) If the \a format supports compression, \a quality may
11035   be between 0 and 100 to control it.
11036 
11037   Returns true on success. If this function fails, most likely the given \a format isn't supported
11038   by the system, see Qt docs about QImageWriter::supportedImageFormats().
11039 
11040   \see saveBmp, saveJpg, savePng, savePdf
11041 */
saveRastered(const QString & fileName,int width,int height,double scale,const char * format,int quality)11042 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11043 {
11044   QPixmap buffer = toPixmap(width, height, scale);
11045   if (!buffer.isNull())
11046     return buffer.save(fileName, format, quality);
11047   else
11048     return false;
11049 }
11050 
11051 /*!
11052   Renders the plot to a pixmap and returns it.
11053 
11054   The plot is sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and
11055   scale 2.0 lead to a full resolution pixmap with width 200.)
11056 
11057   \see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf
11058 */
toPixmap(int width,int height,double scale)11059 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11060 {
11061   // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11062   int newWidth, newHeight;
11063   if (width == 0 || height == 0)
11064   {
11065     newWidth = this->width();
11066     newHeight = this->height();
11067   } else
11068   {
11069     newWidth = width;
11070     newHeight = height;
11071   }
11072   int scaledWidth = qRound(scale*newWidth);
11073   int scaledHeight = qRound(scale*newHeight);
11074 
11075   QPixmap result(scaledWidth, scaledHeight);
11076   result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11077   QCPPainter painter;
11078   painter.begin(&result);
11079   if (painter.isActive())
11080   {
11081     QRect oldViewport = viewport();
11082     setViewport(QRect(0, 0, newWidth, newHeight));
11083     painter.setMode(QCPPainter::pmNoCaching);
11084     if (!qFuzzyCompare(scale, 1.0))
11085     {
11086       if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11087         painter.setMode(QCPPainter::pmNonCosmetic);
11088       painter.scale(scale, scale);
11089     }
11090     if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
11091       painter.fillRect(mViewport, mBackgroundBrush);
11092     draw(&painter);
11093     setViewport(oldViewport);
11094     painter.end();
11095   } else // might happen if pixmap has width or height zero
11096   {
11097     qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11098     return QPixmap();
11099   }
11100   return result;
11101 }
11102 
11103 /*!
11104   Renders the plot using the passed \a painter.
11105 
11106   The plot is sized to \a width and \a height in pixels. If the \a painter's scale is not 1.0, the resulting plot will
11107   appear scaled accordingly.
11108 
11109   \note If you are restricted to using a QPainter (instead of QCPPainter), create a temporary QPicture and open a QCPPainter
11110   on it. Then call \ref toPainter with this QCPPainter. After ending the paint operation on the picture, draw it with
11111   the QPainter. This will reproduce the painter actions the QCPPainter took, with a QPainter.
11112 
11113   \see toPixmap
11114 */
toPainter(QCPPainter * painter,int width,int height)11115 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11116 {
11117   // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11118   int newWidth, newHeight;
11119   if (width == 0 || height == 0)
11120   {
11121     newWidth = this->width();
11122     newHeight = this->height();
11123   } else
11124   {
11125     newWidth = width;
11126     newHeight = height;
11127   }
11128 
11129   if (painter->isActive())
11130   {
11131     QRect oldViewport = viewport();
11132     setViewport(QRect(0, 0, newWidth, newHeight));
11133     painter->setMode(QCPPainter::pmNoCaching);
11134     if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here
11135       painter->fillRect(mViewport, mBackgroundBrush);
11136     draw(painter);
11137     setViewport(oldViewport);
11138   } else
11139     qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11140 }
11141 
11142 
11143 ////////////////////////////////////////////////////////////////////////////////////////////////////
11144 //////////////////// QCPColorGradient
11145 ////////////////////////////////////////////////////////////////////////////////////////////////////
11146 
11147 /*! \class QCPColorGradient
11148   \brief Defines a color gradient for use with e.g. \ref QCPColorMap
11149 
11150   This class describes a color gradient which can be used to encode data with color. For example,
11151   QCPColorMap and QCPColorScale have \ref QCPColorMap::setGradient "setGradient" methods which
11152   take an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color)
11153   with a \a position from 0 to 1. In between these defined color positions, the
11154   color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation.
11155 
11156   Alternatively, load one of the preset color gradients shown in the image below, with \ref
11157   loadPreset, or by directly specifying the preset in the constructor.
11158 
11159   \image html QCPColorGradient.png
11160 
11161   The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly
11162   converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref
11163   GradientPreset to all the \a setGradient methods, e.g.:
11164   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient
11165 
11166   The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the
11167   color gradient shall be applied periodically (wrapping around) to data values that lie outside
11168   the data range specified on the plottable instance can be controlled with \ref setPeriodic.
11169 */
11170 
11171 /*!
11172   Constructs a new QCPColorGradient initialized with the colors and color interpolation according
11173   to \a preset.
11174 
11175   The color level count is initialized to 350.
11176 */
QCPColorGradient(GradientPreset preset)11177 QCPColorGradient::QCPColorGradient(GradientPreset preset) :
11178   mLevelCount(350),
11179   mColorInterpolation(ciRGB),
11180   mPeriodic(false),
11181   mColorBufferInvalidated(true)
11182 {
11183   mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11184   loadPreset(preset);
11185 }
11186 
11187 /* undocumented operator */
operator ==(const QCPColorGradient & other) const11188 bool QCPColorGradient::operator==(const QCPColorGradient &other) const
11189 {
11190   return ((other.mLevelCount == this->mLevelCount) &&
11191           (other.mColorInterpolation == this->mColorInterpolation) &&
11192           (other.mPeriodic == this->mPeriodic) &&
11193           (other.mColorStops == this->mColorStops));
11194 }
11195 
11196 /*!
11197   Sets the number of discretization levels of the color gradient to \a n. The default is 350 which
11198   is typically enough to create a smooth appearance.
11199 
11200   \image html QCPColorGradient-levelcount.png
11201 */
setLevelCount(int n)11202 void QCPColorGradient::setLevelCount(int n)
11203 {
11204   if (n < 2)
11205   {
11206     qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11207     n = 2;
11208   }
11209   if (n != mLevelCount)
11210   {
11211     mLevelCount = n;
11212     mColorBufferInvalidated = true;
11213   }
11214 }
11215 
11216 /*!
11217   Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the
11218   colors are the values of the passed QMap \a colorStops. In between these color stops, the color
11219   is interpolated according to \ref setColorInterpolation.
11220 
11221   A more convenient way to create a custom gradient may be to clear all color stops with \ref
11222   clearColorStops and then adding them one by one with \ref setColorStopAt.
11223 
11224   \see clearColorStops
11225 */
setColorStops(const QMap<double,QColor> & colorStops)11226 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11227 {
11228   mColorStops = colorStops;
11229   mColorBufferInvalidated = true;
11230 }
11231 
11232 /*!
11233   Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between
11234   these color stops, the color is interpolated according to \ref setColorInterpolation.
11235 
11236   \see setColorStops, clearColorStops
11237 */
setColorStopAt(double position,const QColor & color)11238 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11239 {
11240   mColorStops.insert(position, color);
11241   mColorBufferInvalidated = true;
11242 }
11243 
11244 /*!
11245   Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be
11246   interpolated linearly in RGB or in HSV color space.
11247 
11248   For example, a sweep in RGB space from red to green will have a muddy brown intermediate color,
11249   whereas in HSV space the intermediate color is yellow.
11250 */
setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)11251 void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)
11252 {
11253   if (interpolation != mColorInterpolation)
11254   {
11255     mColorInterpolation = interpolation;
11256     mColorBufferInvalidated = true;
11257   }
11258 }
11259 
11260 /*!
11261   Sets whether data points that are outside the configured data range (e.g. \ref
11262   QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether
11263   they all have the same color, corresponding to the respective gradient boundary color.
11264 
11265   \image html QCPColorGradient-periodic.png
11266 
11267   As shown in the image above, gradients that have the same start and end color are especially
11268   suitable for a periodic gradient mapping, since they produce smooth color transitions throughout
11269   the color map. A preset that has this property is \ref gpHues.
11270 
11271   In practice, using periodic color gradients makes sense when the data corresponds to a periodic
11272   dimension, such as an angle or a phase. If this is not the case, the color encoding might become
11273   ambiguous, because multiple different data values are shown as the same color.
11274 */
setPeriodic(bool enabled)11275 void QCPColorGradient::setPeriodic(bool enabled)
11276 {
11277   mPeriodic = enabled;
11278 }
11279 
11280 /*!
11281   This method is used to quickly convert a \a data array to colors. The colors will be output in
11282   the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this
11283   function. The data range that shall be used for mapping the data value to the gradient is passed
11284   in \a range. \a logarithmic indicates whether the data values shall be mapped to colors
11285   logarithmically.
11286 
11287   if \a data actually contains 2D-data linearized via <tt>[row*columnCount + column]</tt>, you can
11288   set \a dataIndexFactor to <tt>columnCount</tt> to convert a column instead of a row of the data
11289   array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data
11290   is addressed <tt>data[i*dataIndexFactor]</tt>.
11291 */
colorize(const double * data,const QCPRange & range,QRgb * scanLine,int n,int dataIndexFactor,bool logarithmic)11292 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11293 {
11294   // If you change something here, make sure to also adapt ::color()
11295   if (!data)
11296   {
11297     qDebug() << Q_FUNC_INFO << "null pointer given as data";
11298     return;
11299   }
11300   if (!scanLine)
11301   {
11302     qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11303     return;
11304   }
11305   if (mColorBufferInvalidated)
11306     updateColorBuffer();
11307 
11308   if (!logarithmic)
11309   {
11310     const double posToIndexFactor = (mLevelCount-1)/range.size();
11311     if (mPeriodic)
11312     {
11313       for (int i=0; i<n; ++i)
11314       {
11315         int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11316         if (index < 0)
11317           index += mLevelCount;
11318         scanLine[i] = mColorBuffer.at(index);
11319       }
11320     } else
11321     {
11322       for (int i=0; i<n; ++i)
11323       {
11324         int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11325         if (index < 0)
11326           index = 0;
11327         else if (index >= mLevelCount)
11328           index = mLevelCount-1;
11329         scanLine[i] = mColorBuffer.at(index);
11330       }
11331     }
11332   } else // logarithmic == true
11333   {
11334     if (mPeriodic)
11335     {
11336       for (int i=0; i<n; ++i)
11337       {
11338         int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
11339         if (index < 0)
11340           index += mLevelCount;
11341         scanLine[i] = mColorBuffer.at(index);
11342       }
11343     } else
11344     {
11345       for (int i=0; i<n; ++i)
11346       {
11347         int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11348         if (index < 0)
11349           index = 0;
11350         else if (index >= mLevelCount)
11351           index = mLevelCount-1;
11352         scanLine[i] = mColorBuffer.at(index);
11353       }
11354     }
11355   }
11356 }
11357 
11358 /*! \internal
11359 
11360   This method is used to colorize a single data value given in \a position, to colors. The data
11361   range that shall be used for mapping the data value to the gradient is passed in \a range. \a
11362   logarithmic indicates whether the data value shall be mapped to a color logarithmically.
11363 
11364   If an entire array of data values shall be converted, rather use \ref colorize, for better
11365   performance.
11366 */
color(double position,const QCPRange & range,bool logarithmic)11367 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11368 {
11369   // If you change something here, make sure to also adapt ::colorize()
11370   if (mColorBufferInvalidated)
11371     updateColorBuffer();
11372   int index = 0;
11373   if (!logarithmic)
11374     index = (position-range.lower)*(mLevelCount-1)/range.size();
11375   else
11376     index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11377   if (mPeriodic)
11378   {
11379     index = index % mLevelCount;
11380     if (index < 0)
11381       index += mLevelCount;
11382   } else
11383   {
11384     if (index < 0)
11385       index = 0;
11386     else if (index >= mLevelCount)
11387       index = mLevelCount-1;
11388   }
11389   return mColorBuffer.at(index);
11390 }
11391 
11392 /*!
11393   Clears the current color stops and loads the specified \a preset. A preset consists of predefined
11394   color stops and the corresponding color interpolation method.
11395 
11396   The available presets are:
11397   \image html QCPColorGradient.png
11398 */
loadPreset(GradientPreset preset)11399 void QCPColorGradient::loadPreset(GradientPreset preset)
11400 {
11401   clearColorStops();
11402   switch (preset)
11403   {
11404     case gpGrayscale:
11405       setColorInterpolation(ciRGB);
11406       setColorStopAt(0, Qt::black);
11407       setColorStopAt(1, Qt::white);
11408       break;
11409     case gpHot:
11410       setColorInterpolation(ciRGB);
11411       setColorStopAt(0, QColor(50, 0, 0));
11412       setColorStopAt(0.2, QColor(180, 10, 0));
11413       setColorStopAt(0.4, QColor(245, 50, 0));
11414       setColorStopAt(0.6, QColor(255, 150, 10));
11415       setColorStopAt(0.8, QColor(255, 255, 50));
11416       setColorStopAt(1, QColor(255, 255, 255));
11417       break;
11418     case gpCold:
11419       setColorInterpolation(ciRGB);
11420       setColorStopAt(0, QColor(0, 0, 50));
11421       setColorStopAt(0.2, QColor(0, 10, 180));
11422       setColorStopAt(0.4, QColor(0, 50, 245));
11423       setColorStopAt(0.6, QColor(10, 150, 255));
11424       setColorStopAt(0.8, QColor(50, 255, 255));
11425       setColorStopAt(1, QColor(255, 255, 255));
11426       break;
11427     case gpNight:
11428       setColorInterpolation(ciHSV);
11429       setColorStopAt(0, QColor(10, 20, 30));
11430       setColorStopAt(1, QColor(250, 255, 250));
11431       break;
11432     case gpCandy:
11433       setColorInterpolation(ciHSV);
11434       setColorStopAt(0, QColor(0, 0, 255));
11435       setColorStopAt(1, QColor(255, 250, 250));
11436       break;
11437     case gpGeography:
11438       setColorInterpolation(ciRGB);
11439       setColorStopAt(0, QColor(70, 170, 210));
11440       setColorStopAt(0.20, QColor(90, 160, 180));
11441       setColorStopAt(0.25, QColor(45, 130, 175));
11442       setColorStopAt(0.30, QColor(100, 140, 125));
11443       setColorStopAt(0.5, QColor(100, 140, 100));
11444       setColorStopAt(0.6, QColor(130, 145, 120));
11445       setColorStopAt(0.7, QColor(140, 130, 120));
11446       setColorStopAt(0.9, QColor(180, 190, 190));
11447       setColorStopAt(1, QColor(210, 210, 230));
11448       break;
11449     case gpIon:
11450       setColorInterpolation(ciHSV);
11451       setColorStopAt(0, QColor(50, 10, 10));
11452       setColorStopAt(0.45, QColor(0, 0, 255));
11453       setColorStopAt(0.8, QColor(0, 255, 255));
11454       setColorStopAt(1, QColor(0, 255, 0));
11455       break;
11456     case gpThermal:
11457       setColorInterpolation(ciRGB);
11458       setColorStopAt(0, QColor(0, 0, 50));
11459       setColorStopAt(0.15, QColor(20, 0, 120));
11460       setColorStopAt(0.33, QColor(200, 30, 140));
11461       setColorStopAt(0.6, QColor(255, 100, 0));
11462       setColorStopAt(0.85, QColor(255, 255, 40));
11463       setColorStopAt(1, QColor(255, 255, 255));
11464       break;
11465     case gpPolar:
11466       setColorInterpolation(ciRGB);
11467       setColorStopAt(0, QColor(50, 255, 255));
11468       setColorStopAt(0.18, QColor(10, 70, 255));
11469       setColorStopAt(0.28, QColor(10, 10, 190));
11470       setColorStopAt(0.5, QColor(0, 0, 0));
11471       setColorStopAt(0.72, QColor(190, 10, 10));
11472       setColorStopAt(0.82, QColor(255, 70, 10));
11473       setColorStopAt(1, QColor(255, 255, 50));
11474       break;
11475     case gpSpectrum:
11476       setColorInterpolation(ciHSV);
11477       setColorStopAt(0, QColor(50, 0, 50));
11478       setColorStopAt(0.15, QColor(0, 0, 255));
11479       setColorStopAt(0.35, QColor(0, 255, 255));
11480       setColorStopAt(0.6, QColor(255, 255, 0));
11481       setColorStopAt(0.75, QColor(255, 30, 0));
11482       setColorStopAt(1, QColor(50, 0, 0));
11483       break;
11484     case gpJet:
11485       setColorInterpolation(ciRGB);
11486       setColorStopAt(0, QColor(0, 0, 100));
11487       setColorStopAt(0.15, QColor(0, 50, 255));
11488       setColorStopAt(0.35, QColor(0, 255, 255));
11489       setColorStopAt(0.65, QColor(255, 255, 0));
11490       setColorStopAt(0.85, QColor(255, 30, 0));
11491       setColorStopAt(1, QColor(100, 0, 0));
11492       break;
11493     case gpHues:
11494       setColorInterpolation(ciHSV);
11495       setColorStopAt(0, QColor(255, 0, 0));
11496       setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11497       setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11498       setColorStopAt(1, QColor(255, 0, 0));
11499       break;
11500   }
11501 }
11502 
11503 /*!
11504   Clears all color stops.
11505 
11506   \see setColorStops, setColorStopAt
11507 */
clearColorStops()11508 void QCPColorGradient::clearColorStops()
11509 {
11510   mColorStops.clear();
11511   mColorBufferInvalidated = true;
11512 }
11513 
11514 /*!
11515   Returns an inverted gradient. The inverted gradient has all properties as this \ref
11516   QCPColorGradient, but the order of the color stops is inverted.
11517 
11518   \see setColorStops, setColorStopAt
11519 */
inverted() const11520 QCPColorGradient QCPColorGradient::inverted() const
11521 {
11522   QCPColorGradient result(*this);
11523   result.clearColorStops();
11524   for (auto it = mColorStops.constBegin(); it != mColorStops.constEnd(); ++it)
11525     result.setColorStopAt(1.0-it.key(), it.value());
11526   return result;
11527 }
11528 
11529 /*! \internal
11530 
11531   Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly
11532   convert positions to colors. This is where the interpolation between color stops is calculated.
11533 */
updateColorBuffer()11534 void QCPColorGradient::updateColorBuffer()
11535 {
11536   if (mColorBuffer.size() != mLevelCount)
11537     mColorBuffer.resize(mLevelCount);
11538   if (mColorStops.size() > 1)
11539   {
11540     double indexToPosFactor = 1.0/(double)(mLevelCount-1);
11541     for (int i=0; i<mLevelCount; ++i)
11542     {
11543       double position = i*indexToPosFactor;
11544       auto it = mColorStops.lowerBound(position);
11545       if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
11546       {
11547         mColorBuffer[i] = (it-1).value().rgb();
11548       } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
11549       {
11550         mColorBuffer[i] = it.value().rgb();
11551       } else // position is in between stops (or on an intermediate stop), interpolate color
11552       {
11553         auto high = it;
11554         auto low = it-1;
11555         double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
11556         switch (mColorInterpolation)
11557         {
11558           case ciRGB:
11559           {
11560             mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
11561                                    (1-t)*low.value().green() + t*high.value().green(),
11562                                    (1-t)*low.value().blue() + t*high.value().blue());
11563             break;
11564           }
11565           case ciHSV:
11566           {
11567             QColor lowHsv = low.value().toHsv();
11568             QColor highHsv = high.value().toHsv();
11569             double hue = 0;
11570             double hueDiff = highHsv.hueF()-lowHsv.hueF();
11571             if (hueDiff > 0.5)
11572               hue = lowHsv.hueF() - t*(1.0-hueDiff);
11573             else if (hueDiff < -0.5)
11574               hue = lowHsv.hueF() + t*(1.0+hueDiff);
11575             else
11576               hue = lowHsv.hueF() + t*hueDiff;
11577             if (hue < 0) hue += 1.0;
11578             else if (hue >= 1.0) hue -= 1.0;
11579             mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
11580             break;
11581           }
11582         }
11583       }
11584     }
11585   } else if (mColorStops.size() == 1)
11586   {
11587     mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11588   } else // mColorStops is empty, fill color buffer with black
11589   {
11590     mColorBuffer.fill(qRgb(0, 0, 0));
11591   }
11592   mColorBufferInvalidated = false;
11593 }
11594 
11595 
11596 ////////////////////////////////////////////////////////////////////////////////////////////////////
11597 //////////////////// QCPAxisRect
11598 ////////////////////////////////////////////////////////////////////////////////////////////////////
11599 
11600 /*! \class QCPAxisRect
11601   \brief Holds multiple axes and arranges them in a rectangular shape.
11602 
11603   This class represents an axis rect, a rectangular area that is bounded on all sides with an
11604   arbitrary number of axes.
11605 
11606   Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
11607   layout system allows to have multiple axis rects, e.g. arranged in a grid layout
11608   (QCustomPlot::plotLayout).
11609 
11610   By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
11611   accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
11612   If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
11613   invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
11614   addAxes. To remove an axis, use \ref removeAxis.
11615 
11616   The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
11617   setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
11618   explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
11619   placed on other layers, independently of the axis rect.
11620 
11621   Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
11622   insetLayout and can be used to have other layout elements (or even other layouts with multiple
11623   elements) hovering inside the axis rect.
11624 
11625   If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
11626   behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
11627   is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
11628   via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
11629   only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
11630   QCP::iRangeZoom.
11631 
11632   \image html AxisRectSpacingOverview.png
11633   <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
11634   line on the far left indicates the viewport/widget border.</center>
11635 */
11636 
11637 /* start documentation of inline functions */
11638 
11639 /*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
11640 
11641   Returns the inset layout of this axis rect. It can be used to place other layout elements (or
11642   even layouts with multiple other elements) inside/on top of an axis rect.
11643 
11644   \see QCPLayoutInset
11645 */
11646 
11647 /*! \fn int QCPAxisRect::left() const
11648 
11649   Returns the pixel position of the left border of this axis rect. Margins are not taken into
11650   account here, so the returned value is with respect to the inner \ref rect.
11651 */
11652 
11653 /*! \fn int QCPAxisRect::right() const
11654 
11655   Returns the pixel position of the right border of this axis rect. Margins are not taken into
11656   account here, so the returned value is with respect to the inner \ref rect.
11657 */
11658 
11659 /*! \fn int QCPAxisRect::top() const
11660 
11661   Returns the pixel position of the top border of this axis rect. Margins are not taken into
11662   account here, so the returned value is with respect to the inner \ref rect.
11663 */
11664 
11665 /*! \fn int QCPAxisRect::bottom() const
11666 
11667   Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
11668   account here, so the returned value is with respect to the inner \ref rect.
11669 */
11670 
11671 /*! \fn int QCPAxisRect::width() const
11672 
11673   Returns the pixel width of this axis rect. Margins are not taken into account here, so the
11674   returned value is with respect to the inner \ref rect.
11675 */
11676 
11677 /*! \fn int QCPAxisRect::height() const
11678 
11679   Returns the pixel height of this axis rect. Margins are not taken into account here, so the
11680   returned value is with respect to the inner \ref rect.
11681 */
11682 
11683 /*! \fn QSize QCPAxisRect::size() const
11684 
11685   Returns the pixel size of this axis rect. Margins are not taken into account here, so the
11686   returned value is with respect to the inner \ref rect.
11687 */
11688 
11689 /*! \fn QPoint QCPAxisRect::topLeft() const
11690 
11691   Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
11692   so the returned value is with respect to the inner \ref rect.
11693 */
11694 
11695 /*! \fn QPoint QCPAxisRect::topRight() const
11696 
11697   Returns the top right corner of this axis rect in pixels. Margins are not taken into account
11698   here, so the returned value is with respect to the inner \ref rect.
11699 */
11700 
11701 /*! \fn QPoint QCPAxisRect::bottomLeft() const
11702 
11703   Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
11704   here, so the returned value is with respect to the inner \ref rect.
11705 */
11706 
11707 /*! \fn QPoint QCPAxisRect::bottomRight() const
11708 
11709   Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
11710   here, so the returned value is with respect to the inner \ref rect.
11711 */
11712 
11713 /*! \fn QPoint QCPAxisRect::center() const
11714 
11715   Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
11716   returned value is with respect to the inner \ref rect.
11717 */
11718 
11719 /* end documentation of inline functions */
11720 
11721 /*!
11722   Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
11723   sides, the top and right axes are set invisible initially.
11724 */
QCPAxisRect(QCustomPlot * parentPlot,bool setupDefaultAxes)11725 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
11726   QCPLayoutElement(parentPlot),
11727   mBackgroundBrush(Qt::NoBrush),
11728   mBackgroundScaled(true),
11729   mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11730   mInsetLayout(new QCPLayoutInset),
11731   mRangeDrag(Qt::Horizontal|Qt::Vertical),
11732   mRangeZoom(Qt::Horizontal|Qt::Vertical),
11733   mRangeZoomFactorHorz(0.85),
11734   mRangeZoomFactorVert(0.85),
11735   mDragging(false)
11736 {
11737   mInsetLayout->initializeParentPlot(mParentPlot);
11738   mInsetLayout->setParentLayerable(this);
11739   mInsetLayout->setParent(this);
11740 
11741   setMinimumSize(50, 50);
11742   setMinimumMargins(QMargins(15, 15, 15, 15));
11743   mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
11744   mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
11745   mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
11746   mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
11747 
11748   if (setupDefaultAxes)
11749   {
11750     QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11751     QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
11752     QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11753     QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11754     setRangeDragAxes(xAxis, yAxis);
11755     setRangeZoomAxes(xAxis, yAxis);
11756     xAxis2->setVisible(false);
11757     yAxis2->setVisible(false);
11758     xAxis->grid()->setVisible(true);
11759     yAxis->grid()->setVisible(true);
11760     xAxis2->grid()->setVisible(false);
11761     yAxis2->grid()->setVisible(false);
11762     xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11763     yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11764     xAxis2->grid()->setVisible(false);
11765     yAxis2->grid()->setVisible(false);
11766   }
11767 }
11768 
~QCPAxisRect()11769 QCPAxisRect::~QCPAxisRect()
11770 {
11771   delete mInsetLayout;
11772   mInsetLayout = 0;
11773 
11774   QList<QCPAxis*> axesList = axes();
11775   for (int i=0; i<axesList.size(); ++i)
11776     removeAxis(axesList.at(i));
11777 }
11778 
11779 /*!
11780   Returns the number of axes on the axis rect side specified with \a type.
11781 
11782   \see axis
11783 */
axisCount(QCPAxis::AxisType type) const11784 int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
11785 {
11786   return mAxes.value(type).size();
11787 }
11788 
11789 /*!
11790   Returns the axis with the given \a index on the axis rect side specified with \a type.
11791 
11792   \see axisCount, axes
11793 */
axis(QCPAxis::AxisType type,int index) const11794 QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
11795 {
11796   QList<QCPAxis*> ax(mAxes.value(type));
11797   if (index >= 0 && index < ax.size())
11798   {
11799     return ax.at(index);
11800   } else
11801   {
11802     qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11803     return 0;
11804   }
11805 }
11806 
11807 /*!
11808   Returns all axes on the axis rect sides specified with \a types.
11809 
11810   \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
11811   multiple sides.
11812 
11813   \see axis
11814 */
axes(QCPAxis::AxisTypes types) const11815 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
11816 {
11817   QList<QCPAxis*> result;
11818   if (types.testFlag(QCPAxis::atLeft))
11819     result << mAxes.value(QCPAxis::atLeft);
11820   if (types.testFlag(QCPAxis::atRight))
11821     result << mAxes.value(QCPAxis::atRight);
11822   if (types.testFlag(QCPAxis::atTop))
11823     result << mAxes.value(QCPAxis::atTop);
11824   if (types.testFlag(QCPAxis::atBottom))
11825     result << mAxes.value(QCPAxis::atBottom);
11826   return result;
11827 }
11828 
11829 /*! \overload
11830 
11831   Returns all axes of this axis rect.
11832 */
axes() const11833 QList<QCPAxis*> QCPAxisRect::axes() const
11834 {
11835   QList<QCPAxis*> result;
11836   QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11837   while (it.hasNext())
11838   {
11839     it.next();
11840     result << it.value();
11841   }
11842   return result;
11843 }
11844 
11845 /*!
11846   Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a
11847   new QCPAxis instance is created internally.
11848 
11849   You may inject QCPAxis instances (or sublasses of QCPAxis) by setting \a axis to an axis that was
11850   previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership
11851   of the axis, so you may not delete it afterwards. Further, the \a axis must have been created
11852   with this axis rect as parent and with the same axis type as specified in \a type. If this is not
11853   the case, a debug output is generated, the axis is not added, and the method returns 0.
11854 
11855   This method can not be used to move \a axis between axis rects. The same \a axis instance must
11856   not be added multiple times to the same or different axis rects.
11857 
11858   If an axis rect side already contains one or more axes, the lower and upper endings of the new
11859   axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref
11860   QCPLineEnding::esHalfBar.
11861 
11862   \see addAxes, setupFullAxesBox
11863 */
addAxis(QCPAxis::AxisType type,QCPAxis * axis)11864 QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis)
11865 {
11866   QCPAxis *newAxis = axis;
11867   if (!newAxis)
11868   {
11869     newAxis = new QCPAxis(this, type);
11870   } else // user provided existing axis instance, do some sanity checks
11871   {
11872     if (newAxis->axisType() != type)
11873     {
11874       qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
11875       return 0;
11876     }
11877     if (newAxis->axisRect() != this)
11878     {
11879       qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
11880       return 0;
11881     }
11882     if (axes().contains(newAxis))
11883     {
11884       qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
11885       return 0;
11886     }
11887   }
11888   if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
11889   {
11890     bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
11891     newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
11892     newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
11893   }
11894   mAxes[type].append(newAxis);
11895   return newAxis;
11896 }
11897 
11898 /*!
11899   Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
11900   <tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
11901 
11902   Returns a list of the added axes.
11903 
11904   \see addAxis, setupFullAxesBox
11905 */
addAxes(QCPAxis::AxisTypes types)11906 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
11907 {
11908   QList<QCPAxis*> result;
11909   if (types.testFlag(QCPAxis::atLeft))
11910     result << addAxis(QCPAxis::atLeft);
11911   if (types.testFlag(QCPAxis::atRight))
11912     result << addAxis(QCPAxis::atRight);
11913   if (types.testFlag(QCPAxis::atTop))
11914     result << addAxis(QCPAxis::atTop);
11915   if (types.testFlag(QCPAxis::atBottom))
11916     result << addAxis(QCPAxis::atBottom);
11917   return result;
11918 }
11919 
11920 /*!
11921   Removes the specified \a axis from the axis rect and deletes it.
11922 
11923   Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
11924 
11925   \see addAxis
11926 */
removeAxis(QCPAxis * axis)11927 bool QCPAxisRect::removeAxis(QCPAxis *axis)
11928 {
11929   // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
11930   QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11931   while (it.hasNext())
11932   {
11933     it.next();
11934     if (it.value().contains(axis))
11935     {
11936       mAxes[it.key()].removeOne(axis);
11937       if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)
11938         parentPlot()->axisRemoved(axis);
11939       delete axis;
11940       return true;
11941     }
11942   }
11943   qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
11944   return false;
11945 }
11946 
11947 /*!
11948   Convenience function to create an axis on each side that doesn't have any axes yet and set their
11949   visibility to true. Further, the top/right axes are assigned the following properties of the
11950   bottom/left axes:
11951 
11952   \li range (\ref QCPAxis::setRange)
11953   \li range reversed (\ref QCPAxis::setRangeReversed)
11954   \li scale type (\ref QCPAxis::setScaleType)
11955   \li scale log base  (\ref QCPAxis::setScaleLogBase)
11956   \li ticks (\ref QCPAxis::setTicks)
11957   \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
11958   \li sub tick count (\ref QCPAxis::setSubTickCount)
11959   \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
11960   \li tick step (\ref QCPAxis::setTickStep)
11961   \li auto tick step (\ref QCPAxis::setAutoTickStep)
11962   \li number format (\ref QCPAxis::setNumberFormat)
11963   \li number precision (\ref QCPAxis::setNumberPrecision)
11964   \li tick label type (\ref QCPAxis::setTickLabelType)
11965   \li date time format (\ref QCPAxis::setDateTimeFormat)
11966   \li date time spec (\ref QCPAxis::setDateTimeSpec)
11967 
11968   Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
11969 
11970   If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
11971   and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
11972 */
setupFullAxesBox(bool connectRanges)11973 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
11974 {
11975   QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
11976   if (axisCount(QCPAxis::atBottom) == 0)
11977     xAxis = addAxis(QCPAxis::atBottom);
11978   else
11979     xAxis = axis(QCPAxis::atBottom);
11980 
11981   if (axisCount(QCPAxis::atLeft) == 0)
11982     yAxis = addAxis(QCPAxis::atLeft);
11983   else
11984     yAxis = axis(QCPAxis::atLeft);
11985 
11986   if (axisCount(QCPAxis::atTop) == 0)
11987     xAxis2 = addAxis(QCPAxis::atTop);
11988   else
11989     xAxis2 = axis(QCPAxis::atTop);
11990 
11991   if (axisCount(QCPAxis::atRight) == 0)
11992     yAxis2 = addAxis(QCPAxis::atRight);
11993   else
11994     yAxis2 = axis(QCPAxis::atRight);
11995 
11996   xAxis->setVisible(true);
11997   yAxis->setVisible(true);
11998   xAxis2->setVisible(true);
11999   yAxis2->setVisible(true);
12000   xAxis2->setTickLabels(false);
12001   yAxis2->setTickLabels(false);
12002 
12003   xAxis2->setRange(xAxis->range());
12004   xAxis2->setRangeReversed(xAxis->rangeReversed());
12005   xAxis2->setScaleType(xAxis->scaleType());
12006   xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12007   xAxis2->setTicks(xAxis->ticks());
12008   xAxis2->setAutoTickCount(xAxis->autoTickCount());
12009   xAxis2->setSubTickCount(xAxis->subTickCount());
12010   xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12011   xAxis2->setTickStep(xAxis->tickStep());
12012   xAxis2->setAutoTickStep(xAxis->autoTickStep());
12013   xAxis2->setNumberFormat(xAxis->numberFormat());
12014   xAxis2->setNumberPrecision(xAxis->numberPrecision());
12015   xAxis2->setTickLabelType(xAxis->tickLabelType());
12016   xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12017   xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12018 
12019   yAxis2->setRange(yAxis->range());
12020   yAxis2->setRangeReversed(yAxis->rangeReversed());
12021   yAxis2->setScaleType(yAxis->scaleType());
12022   yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12023   yAxis2->setTicks(yAxis->ticks());
12024   yAxis2->setAutoTickCount(yAxis->autoTickCount());
12025   yAxis2->setSubTickCount(yAxis->subTickCount());
12026   yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12027   yAxis2->setTickStep(yAxis->tickStep());
12028   yAxis2->setAutoTickStep(yAxis->autoTickStep());
12029   yAxis2->setNumberFormat(yAxis->numberFormat());
12030   yAxis2->setNumberPrecision(yAxis->numberPrecision());
12031   yAxis2->setTickLabelType(yAxis->tickLabelType());
12032   yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12033   yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12034 
12035   if (connectRanges)
12036   {
12037     connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12038     connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12039   }
12040 }
12041 
12042 /*!
12043   Returns a list of all the plottables that are associated with this axis rect.
12044 
12045   A plottable is considered associated with an axis rect if its key or value axis (or both) is in
12046   this axis rect.
12047 
12048   \see graphs, items
12049 */
plottables() const12050 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12051 {
12052   // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12053   QList<QCPAbstractPlottable*> result;
12054   for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12055   {
12056     if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12057       result.append(mParentPlot->mPlottables.at(i));
12058   }
12059   return result;
12060 }
12061 
12062 /*!
12063   Returns a list of all the graphs that are associated with this axis rect.
12064 
12065   A graph is considered associated with an axis rect if its key or value axis (or both) is in
12066   this axis rect.
12067 
12068   \see plottables, items
12069 */
graphs() const12070 QList<QCPGraph*> QCPAxisRect::graphs() const
12071 {
12072   // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12073   QList<QCPGraph*> result;
12074   for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12075   {
12076     if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12077       result.append(mParentPlot->mGraphs.at(i));
12078   }
12079   return result;
12080 }
12081 
12082 /*!
12083   Returns a list of all the items that are associated with this axis rect.
12084 
12085   An item is considered associated with an axis rect if any of its positions has key or value axis
12086   set to an axis that is in this axis rect, or if any of its positions has \ref
12087   QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
12088   QCPAbstractItem::setClipAxisRect) is set to this axis rect.
12089 
12090   \see plottables, graphs
12091 */
items() const12092 QList<QCPAbstractItem *> QCPAxisRect::items() const
12093 {
12094   // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12095   //       and miss those items that have this axis rect as clipAxisRect.
12096   QList<QCPAbstractItem*> result;
12097   for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12098   {
12099     if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12100     {
12101       result.append(mParentPlot->mItems.at(itemId));
12102       continue;
12103     }
12104     QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12105     for (int posId=0; posId<positions.size(); ++posId)
12106     {
12107       if (positions.at(posId)->axisRect() == this ||
12108           positions.at(posId)->keyAxis()->axisRect() == this ||
12109           positions.at(posId)->valueAxis()->axisRect() == this)
12110       {
12111         result.append(mParentPlot->mItems.at(itemId));
12112         break;
12113       }
12114     }
12115   }
12116   return result;
12117 }
12118 
12119 /*!
12120   This method is called automatically upon replot and doesn't need to be called by users of
12121   QCPAxisRect.
12122 
12123   Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
12124   and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
12125   QCPInsetLayout::update function.
12126 */
update(UpdatePhase phase)12127 void QCPAxisRect::update(UpdatePhase phase)
12128 {
12129   QCPLayoutElement::update(phase);
12130 
12131   switch (phase)
12132   {
12133     case upPreparation:
12134     {
12135       QList<QCPAxis*> allAxes = axes();
12136       for (int i=0; i<allAxes.size(); ++i)
12137         allAxes.at(i)->setupTickVectors();
12138       break;
12139     }
12140     case upLayout:
12141     {
12142       mInsetLayout->setOuterRect(rect());
12143       break;
12144     }
12145     default: break;
12146   }
12147 
12148   // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12149   mInsetLayout->update(phase);
12150 }
12151 
12152 /* inherits documentation from base class */
elements(bool recursive) const12153 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12154 {
12155   QList<QCPLayoutElement*> result;
12156   if (mInsetLayout)
12157   {
12158     result << mInsetLayout;
12159     if (recursive)
12160       result << mInsetLayout->elements(recursive);
12161   }
12162   return result;
12163 }
12164 
12165 /* inherits documentation from base class */
applyDefaultAntialiasingHint(QCPPainter * painter) const12166 void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
12167 {
12168   painter->setAntialiasing(false);
12169 }
12170 
12171 /* inherits documentation from base class */
draw(QCPPainter * painter)12172 void QCPAxisRect::draw(QCPPainter *painter)
12173 {
12174   drawBackground(painter);
12175 }
12176 
12177 /*!
12178   Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
12179   axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
12180   backgrounds are usually drawn below everything else.
12181 
12182   For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
12183   enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
12184   is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
12185   consider using the overloaded version of this function.
12186 
12187   Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
12188   setBackground(const QBrush &brush).
12189 
12190   \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
12191 */
setBackground(const QPixmap & pm)12192 void QCPAxisRect::setBackground(const QPixmap &pm)
12193 {
12194   mBackgroundPixmap = pm;
12195   mScaledBackgroundPixmap = QPixmap();
12196 }
12197 
12198 /*! \overload
12199 
12200   Sets \a brush as the background brush. The axis rect background will be filled with this brush.
12201   Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
12202   are usually drawn below everything else.
12203 
12204   The brush will be drawn before (under) any background pixmap, which may be specified with \ref
12205   setBackground(const QPixmap &pm).
12206 
12207   To disable drawing of a background brush, set \a brush to Qt::NoBrush.
12208 
12209   \see setBackground(const QPixmap &pm)
12210 */
setBackground(const QBrush & brush)12211 void QCPAxisRect::setBackground(const QBrush &brush)
12212 {
12213   mBackgroundBrush = brush;
12214 }
12215 
12216 /*! \overload
12217 
12218   Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
12219   shall be scaled in one call.
12220 
12221   \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
12222 */
setBackground(const QPixmap & pm,bool scaled,Qt::AspectRatioMode mode)12223 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12224 {
12225   mBackgroundPixmap = pm;
12226   mScaledBackgroundPixmap = QPixmap();
12227   mBackgroundScaled = scaled;
12228   mBackgroundScaledMode = mode;
12229 }
12230 
12231 /*!
12232   Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
12233   is set to true, you may control whether and how the aspect ratio of the original pixmap is
12234   preserved with \ref setBackgroundScaledMode.
12235 
12236   Note that the scaled version of the original pixmap is buffered, so there is no performance
12237   penalty on replots. (Except when the axis rect dimensions are changed continuously.)
12238 
12239   \see setBackground, setBackgroundScaledMode
12240 */
setBackgroundScaled(bool scaled)12241 void QCPAxisRect::setBackgroundScaled(bool scaled)
12242 {
12243   mBackgroundScaled = scaled;
12244 }
12245 
12246 /*!
12247   If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
12248   define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
12249   \see setBackground, setBackgroundScaled
12250 */
setBackgroundScaledMode(Qt::AspectRatioMode mode)12251 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12252 {
12253   mBackgroundScaledMode = mode;
12254 }
12255 
12256 /*!
12257   Returns the range drag axis of the \a orientation provided.
12258 
12259   \see setRangeDragAxes
12260 */
rangeDragAxis(Qt::Orientation orientation)12261 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12262 {
12263   return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12264 }
12265 
12266 /*!
12267   Returns the range zoom axis of the \a orientation provided.
12268 
12269   \see setRangeZoomAxes
12270 */
rangeZoomAxis(Qt::Orientation orientation)12271 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12272 {
12273   return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12274 }
12275 
12276 /*!
12277   Returns the range zoom factor of the \a orientation provided.
12278 
12279   \see setRangeZoomFactor
12280 */
rangeZoomFactor(Qt::Orientation orientation)12281 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12282 {
12283   return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12284 }
12285 
12286 /*!
12287   Sets which axis orientation may be range dragged by the user with mouse interaction.
12288   What orientation corresponds to which specific axis can be set with
12289   \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
12290   default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
12291   is the left axis (yAxis).
12292 
12293   To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
12294   QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
12295   Qt::Vertical</tt> as \a orientations.
12296 
12297   In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12298   contains \ref QCP::iRangeDrag to enable the range dragging interaction.
12299 
12300   \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag
12301 */
setRangeDrag(Qt::Orientations orientations)12302 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12303 {
12304   mRangeDrag = orientations;
12305 }
12306 
12307 /*!
12308   Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
12309   corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
12310   QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
12311   axis is the left axis (yAxis).
12312 
12313   To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
12314   QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
12315   Qt::Vertical</tt> as \a orientations.
12316 
12317   In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12318   contains \ref QCP::iRangeZoom to enable the range zooming interaction.
12319 
12320   \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
12321 */
setRangeZoom(Qt::Orientations orientations)12322 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12323 {
12324   mRangeZoom = orientations;
12325 }
12326 
12327 /*!
12328   Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
12329   on the QCustomPlot widget.
12330 
12331   \see setRangeZoomAxes
12332 */
setRangeDragAxes(QCPAxis * horizontal,QCPAxis * vertical)12333 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12334 {
12335   mRangeDragHorzAxis = horizontal;
12336   mRangeDragVertAxis = vertical;
12337 }
12338 
12339 /*!
12340   Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
12341   QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
12342   are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
12343 
12344   \see setRangeDragAxes
12345 */
setRangeZoomAxes(QCPAxis * horizontal,QCPAxis * vertical)12346 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12347 {
12348   mRangeZoomHorzAxis = horizontal;
12349   mRangeZoomVertAxis = vertical;
12350 }
12351 
12352 /*!
12353   Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
12354   \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
12355   let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
12356   and which is vertical, can be set with \ref setRangeZoomAxes.
12357 
12358   When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
12359   will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
12360   same scrolling direction will zoom out.
12361 */
setRangeZoomFactor(double horizontalFactor,double verticalFactor)12362 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12363 {
12364   mRangeZoomFactorHorz = horizontalFactor;
12365   mRangeZoomFactorVert = verticalFactor;
12366 }
12367 
12368 /*! \overload
12369 
12370   Sets both the horizontal and vertical zoom \a factor.
12371 */
setRangeZoomFactor(double factor)12372 void QCPAxisRect::setRangeZoomFactor(double factor)
12373 {
12374   mRangeZoomFactorHorz = factor;
12375   mRangeZoomFactorVert = factor;
12376 }
12377 
12378 /*! \internal
12379 
12380   Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
12381   pixmap.
12382 
12383   If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
12384   according filling inside the axis rect with the provided \a painter.
12385 
12386   Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
12387   depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
12388   the axis rect with the provided \a painter. The scaled version is buffered in
12389   mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
12390   the axis rect has changed in a way that requires a rescale of the background pixmap (this is
12391   dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
12392   set.
12393 
12394   \see setBackground, setBackgroundScaled, setBackgroundScaledMode
12395 */
drawBackground(QCPPainter * painter)12396 void QCPAxisRect::drawBackground(QCPPainter *painter)
12397 {
12398   // draw background fill:
12399   if (mBackgroundBrush != Qt::NoBrush)
12400     painter->fillRect(mRect, mBackgroundBrush);
12401 
12402   // draw background pixmap (on top of fill, if brush specified):
12403   if (!mBackgroundPixmap.isNull())
12404   {
12405     if (mBackgroundScaled)
12406     {
12407       // check whether mScaledBackground needs to be updated:
12408       QSize scaledSize(mBackgroundPixmap.size());
12409       scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12410       if (mScaledBackgroundPixmap.size() != scaledSize)
12411         mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12412       painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12413     } else
12414     {
12415       painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12416     }
12417   }
12418 }
12419 
12420 /*! \internal
12421 
12422   This function makes sure multiple axes on the side specified with \a type don't collide, but are
12423   distributed according to their respective space requirement (QCPAxis::calculateMargin).
12424 
12425   It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
12426   one with index zero.
12427 
12428   This function is called by \ref calculateAutoMargin.
12429 */
updateAxesOffset(QCPAxis::AxisType type)12430 void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
12431 {
12432   const QList<QCPAxis*> axesList = mAxes.value(type);
12433   if (axesList.isEmpty())
12434     return;
12435 
12436   bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false
12437   for (int i=1; i<axesList.size(); ++i)
12438   {
12439     int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12440     if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible)
12441     {
12442       if (!isFirstVisible)
12443         offset += axesList.at(i)->tickLengthIn();
12444       isFirstVisible = false;
12445     }
12446     axesList.at(i)->setOffset(offset);
12447   }
12448 }
12449 
12450 /* inherits documentation from base class */
calculateAutoMargin(QCP::MarginSide side)12451 int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
12452 {
12453   if (!mAutoMargins.testFlag(side))
12454     qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12455 
12456   updateAxesOffset(QCPAxis::marginSideToAxisType(side));
12457 
12458   // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12459   const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12460   if (axesList.size() > 0)
12461     return axesList.last()->offset() + axesList.last()->calculateMargin();
12462   else
12463     return 0;
12464 }
12465 
12466 /*! \internal
12467 
12468   Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
12469   pressed, the range dragging interaction is initialized (the actual range manipulation happens in
12470   the \ref mouseMoveEvent).
12471 
12472   The mDragging flag is set to true and some anchor points are set that are needed to determine the
12473   distance the mouse was dragged in the mouse move/release events later.
12474 
12475   \see mouseMoveEvent, mouseReleaseEvent
12476 */
mousePressEvent(QMouseEvent * event)12477 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12478 {
12479   mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
12480   if (event->buttons() & Qt::LeftButton)
12481   {
12482     mDragging = true;
12483     // initialize antialiasing backup in case we start dragging:
12484     if (mParentPlot->noAntialiasingOnDrag())
12485     {
12486       mAADragBackup = mParentPlot->antialiasedElements();
12487       mNotAADragBackup = mParentPlot->notAntialiasedElements();
12488     }
12489     // Mouse range dragging interaction:
12490     if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12491     {
12492       if (mRangeDragHorzAxis)
12493         mDragStartHorzRange = mRangeDragHorzAxis->range();
12494       if (mRangeDragVertAxis)
12495         mDragStartVertRange = mRangeDragVertAxis->range();
12496     }
12497   }
12498 }
12499 
12500 /*! \internal
12501 
12502   Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
12503   preceding \ref mousePressEvent, the range is moved accordingly.
12504 
12505   \see mousePressEvent, mouseReleaseEvent
12506 */
mouseMoveEvent(QMouseEvent * event)12507 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12508 {
12509   // Mouse range dragging interaction:
12510   if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12511   {
12512     if (mRangeDrag.testFlag(Qt::Horizontal))
12513     {
12514       if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
12515       {
12516         if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
12517         {
12518           double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
12519           rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
12520         } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
12521         {
12522           double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
12523           rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
12524         }
12525       }
12526     }
12527     if (mRangeDrag.testFlag(Qt::Vertical))
12528     {
12529       if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
12530       {
12531         if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
12532         {
12533           double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
12534           rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
12535         } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
12536         {
12537           double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
12538           rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
12539         }
12540       }
12541     }
12542     if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
12543     {
12544       if (mParentPlot->noAntialiasingOnDrag())
12545         mParentPlot->setNotAntialiasedElements(QCP::aeAll);
12546       mParentPlot->replot();
12547     }
12548   }
12549 }
12550 
12551 /* inherits documentation from base class */
mouseReleaseEvent(QMouseEvent * event)12552 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12553 {
12554   Q_UNUSED(event)
12555   mDragging = false;
12556   if (mParentPlot->noAntialiasingOnDrag())
12557   {
12558     mParentPlot->setAntialiasedElements(mAADragBackup);
12559     mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
12560   }
12561 }
12562 
12563 /*! \internal
12564 
12565   Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
12566   ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
12567   the scaling operation is the current cursor position inside the axis rect. The scaling factor is
12568   dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
12569   zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
12570 
12571   Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
12572   wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
12573   multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
12574   exponent of the range zoom factor. This takes care of the wheel direction automatically, by
12575   inverting the factor, when the wheel step is negative (f^-1 = 1/f).
12576 */
wheelEvent(QWheelEvent * event)12577 void QCPAxisRect::wheelEvent(QWheelEvent *event)
12578 {
12579   // Mouse range zooming interaction:
12580   if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
12581   {
12582     if (mRangeZoom != 0)
12583     {
12584       double factor;
12585       double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
12586       if (mRangeZoom.testFlag(Qt::Horizontal))
12587       {
12588         factor = qPow(mRangeZoomFactorHorz, wheelSteps);
12589         if (mRangeZoomHorzAxis.data())
12590           mRangeZoomHorzAxis->scaleRange(factor, mRangeZoomHorzAxis->pixelToCoord(event->pos().x()));
12591       }
12592       if (mRangeZoom.testFlag(Qt::Vertical))
12593       {
12594         factor = qPow(mRangeZoomFactorVert, wheelSteps);
12595         if (mRangeZoomVertAxis.data())
12596           mRangeZoomVertAxis->scaleRange(factor, mRangeZoomVertAxis->pixelToCoord(event->pos().y()));
12597       }
12598       mParentPlot->replot();
12599     }
12600   }
12601 }
12602 
12603 
12604 ////////////////////////////////////////////////////////////////////////////////////////////////////
12605 //////////////////// QCPAbstractLegendItem
12606 ////////////////////////////////////////////////////////////////////////////////////////////////////
12607 
12608 /*! \class QCPAbstractLegendItem
12609   \brief The abstract base class for all entries in a QCPLegend.
12610 
12611   It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
12612   legend, the subclass \ref QCPPlottableLegendItem is more suitable.
12613 
12614   Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
12615   that's not even associated with a plottable).
12616 
12617   You must implement the following pure virtual functions:
12618   \li \ref draw (from QCPLayerable)
12619 
12620   You inherit the following members you may use:
12621   <table>
12622     <tr>
12623       <td>QCPLegend *\b mParentLegend</td>
12624       <td>A pointer to the parent QCPLegend.</td>
12625     </tr><tr>
12626       <td>QFont \b mFont</td>
12627       <td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>
12628     </tr>
12629   </table>
12630 */
12631 
12632 /* start of documentation of signals */
12633 
12634 /*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
12635 
12636   This signal is emitted when the selection state of this legend item has changed, either by user
12637   interaction or by a direct call to \ref setSelected.
12638 */
12639 
12640 /* end of documentation of signals */
12641 
12642 /*!
12643   Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
12644   cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
12645 */
QCPAbstractLegendItem(QCPLegend * parent)12646 QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
12647   QCPLayoutElement(parent->parentPlot()),
12648   mParentLegend(parent),
12649   mFont(parent->font()),
12650   mTextColor(parent->textColor()),
12651   mSelectedFont(parent->selectedFont()),
12652   mSelectedTextColor(parent->selectedTextColor()),
12653   mSelectable(true),
12654   mSelected(false)
12655 {
12656   setLayer(QLatin1String("legend"));
12657   setMargins(QMargins(8, 2, 8, 2));
12658 }
12659 
12660 /*!
12661   Sets the default font of this specific legend item to \a font.
12662 
12663   \see setTextColor, QCPLegend::setFont
12664 */
setFont(const QFont & font)12665 void QCPAbstractLegendItem::setFont(const QFont &font)
12666 {
12667   mFont = font;
12668 }
12669 
12670 /*!
12671   Sets the default text color of this specific legend item to \a color.
12672 
12673   \see setFont, QCPLegend::setTextColor
12674 */
setTextColor(const QColor & color)12675 void QCPAbstractLegendItem::setTextColor(const QColor &color)
12676 {
12677   mTextColor = color;
12678 }
12679 
12680 /*!
12681   When this legend item is selected, \a font is used to draw generic text, instead of the normal
12682   font set with \ref setFont.
12683 
12684   \see setFont, QCPLegend::setSelectedFont
12685 */
setSelectedFont(const QFont & font)12686 void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
12687 {
12688   mSelectedFont = font;
12689 }
12690 
12691 /*!
12692   When this legend item is selected, \a color is used to draw generic text, instead of the normal
12693   color set with \ref setTextColor.
12694 
12695   \see setTextColor, QCPLegend::setSelectedTextColor
12696 */
setSelectedTextColor(const QColor & color)12697 void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
12698 {
12699   mSelectedTextColor = color;
12700 }
12701 
12702 /*!
12703   Sets whether this specific legend item is selectable.
12704 
12705   \see setSelectedParts, QCustomPlot::setInteractions
12706 */
setSelectable(bool selectable)12707 void QCPAbstractLegendItem::setSelectable(bool selectable)
12708 {
12709   if (mSelectable != selectable)
12710   {
12711     mSelectable = selectable;
12712     emit selectableChanged(mSelectable);
12713   }
12714 }
12715 
12716 /*!
12717   Sets whether this specific legend item is selected.
12718 
12719   It is possible to set the selection state of this item by calling this function directly, even if
12720   setSelectable is set to false.
12721 
12722   \see setSelectableParts, QCustomPlot::setInteractions
12723 */
setSelected(bool selected)12724 void QCPAbstractLegendItem::setSelected(bool selected)
12725 {
12726   if (mSelected != selected)
12727   {
12728     mSelected = selected;
12729     emit selectionChanged(mSelected);
12730   }
12731 }
12732 
12733 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const12734 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
12735 {
12736   Q_UNUSED(details)
12737   if (!mParentPlot) return -1;
12738   if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12739     return -1;
12740 
12741   if (mRect.contains(pos.toPoint()))
12742     return mParentPlot->selectionTolerance()*0.99;
12743   else
12744     return -1;
12745 }
12746 
12747 /* inherits documentation from base class */
applyDefaultAntialiasingHint(QCPPainter * painter) const12748 void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
12749 {
12750   applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
12751 }
12752 
12753 /* inherits documentation from base class */
clipRect() const12754 QRect QCPAbstractLegendItem::clipRect() const
12755 {
12756   return mOuterRect;
12757 }
12758 
12759 /* inherits documentation from base class */
selectEvent(QMouseEvent * event,bool additive,const QVariant & details,bool * selectionStateChanged)12760 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12761 {
12762   Q_UNUSED(event)
12763   Q_UNUSED(details)
12764   if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12765   {
12766     bool selBefore = mSelected;
12767     setSelected(additive ? !mSelected : true);
12768     if (selectionStateChanged)
12769       *selectionStateChanged = mSelected != selBefore;
12770   }
12771 }
12772 
12773 /* inherits documentation from base class */
deselectEvent(bool * selectionStateChanged)12774 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12775 {
12776   if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12777   {
12778     bool selBefore = mSelected;
12779     setSelected(false);
12780     if (selectionStateChanged)
12781       *selectionStateChanged = mSelected != selBefore;
12782   }
12783 }
12784 
12785 ////////////////////////////////////////////////////////////////////////////////////////////////////
12786 //////////////////// QCPPlottableLegendItem
12787 ////////////////////////////////////////////////////////////////////////////////////////////////////
12788 
12789 /*! \class QCPPlottableLegendItem
12790   \brief A legend item representing a plottable with an icon and the plottable name.
12791 
12792   This is the standard legend item for plottables. It displays an icon of the plottable next to the
12793   plottable name. The icon is drawn by the respective plottable itself (\ref
12794   QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
12795   For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
12796   middle.
12797 
12798   Legend items of this type are always associated with one plottable (retrievable via the
12799   plottable() function and settable with the constructor). You may change the font of the plottable
12800   name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
12801   QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
12802 
12803   The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
12804   creates/removes legend items of this type in the default implementation. However, these functions
12805   may be reimplemented such that a different kind of legend item (e.g a direct subclass of
12806   QCPAbstractLegendItem) is used for that plottable.
12807 
12808   Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
12809   QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
12810   interface, QCPLegend has specialized functions for handling legend items conveniently, see the
12811   documentation of \ref QCPLegend.
12812 */
12813 
12814 /*!
12815   Creates a new legend item associated with \a plottable.
12816 
12817   Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
12818 
12819   A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
12820   QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
12821 */
QCPPlottableLegendItem(QCPLegend * parent,QCPAbstractPlottable * plottable)12822 QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
12823   QCPAbstractLegendItem(parent),
12824   mPlottable(plottable)
12825 {
12826 }
12827 
12828 /*! \internal
12829 
12830   Returns the pen that shall be used to draw the icon border, taking into account the selection
12831   state of this item.
12832 */
getIconBorderPen() const12833 QPen QCPPlottableLegendItem::getIconBorderPen() const
12834 {
12835   return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
12836 }
12837 
12838 /*! \internal
12839 
12840   Returns the text color that shall be used to draw text, taking into account the selection state
12841   of this item.
12842 */
getTextColor() const12843 QColor QCPPlottableLegendItem::getTextColor() const
12844 {
12845   return mSelected ? mSelectedTextColor : mTextColor;
12846 }
12847 
12848 /*! \internal
12849 
12850   Returns the font that shall be used to draw text, taking into account the selection state of this
12851   item.
12852 */
getFont() const12853 QFont QCPPlottableLegendItem::getFont() const
12854 {
12855   return mSelected ? mSelectedFont : mFont;
12856 }
12857 
12858 /*! \internal
12859 
12860   Draws the item with \a painter. The size and position of the drawn legend item is defined by the
12861   parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
12862   of this legend item.
12863 */
draw(QCPPainter * painter)12864 void QCPPlottableLegendItem::draw(QCPPainter *painter)
12865 {
12866   if (!mPlottable) return;
12867   painter->setFont(getFont());
12868   painter->setPen(QPen(getTextColor()));
12869   QSizeF iconSize = mParentLegend->iconSize();
12870   QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12871   QRectF iconRect(mRect.topLeft(), iconSize);
12872   int textHeight = qMax(textRect.height(), iconSize.height());  // if text has smaller height than icon, center text vertically in icon height, else align tops
12873   painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
12874   // draw icon:
12875   painter->save();
12876   painter->setClipRect(iconRect, Qt::IntersectClip);
12877   mPlottable->drawLegendIcon(painter, iconRect);
12878   painter->restore();
12879   // draw icon border:
12880   if (getIconBorderPen().style() != Qt::NoPen)
12881   {
12882     painter->setPen(getIconBorderPen());
12883     painter->setBrush(Qt::NoBrush);
12884     painter->drawRect(iconRect);
12885   }
12886 }
12887 
12888 /*! \internal
12889 
12890   Calculates and returns the size of this item. This includes the icon, the text and the padding in
12891   between.
12892 */
minimumSizeHint() const12893 QSize QCPPlottableLegendItem::minimumSizeHint() const
12894 {
12895   if (!mPlottable) return QSize();
12896   QSize result(0, 0);
12897   QRect textRect;
12898   QFontMetrics fontMetrics(getFont());
12899   QSize iconSize = mParentLegend->iconSize();
12900   textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12901   result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
12902   result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
12903   return result;
12904 }
12905 
12906 
12907 ////////////////////////////////////////////////////////////////////////////////////////////////////
12908 //////////////////// QCPLegend
12909 ////////////////////////////////////////////////////////////////////////////////////////////////////
12910 
12911 /*! \class QCPLegend
12912   \brief Manages a legend inside a QCustomPlot.
12913 
12914   A legend is a small box somewhere in the plot which lists plottables with their name and icon.
12915 
12916   Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
12917   respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
12918   QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
12919   itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
12920 
12921   The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
12922   QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
12923   placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
12924   handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
12925   other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
12926   However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
12927   only return the number of items with QCPAbstractLegendItems type).
12928 
12929   By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
12930   layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
12931   position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
12932   outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
12933 */
12934 
12935 /* start of documentation of signals */
12936 
12937 /*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
12938 
12939   This signal is emitted when the selection state of this legend has changed.
12940 
12941   \see setSelectedParts, setSelectableParts
12942 */
12943 
12944 /* end of documentation of signals */
12945 
12946 /*!
12947   Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
12948 
12949   Note that by default, QCustomPlot already contains a legend ready to be used as
12950   QCustomPlot::legend
12951 */
QCPLegend()12952 QCPLegend::QCPLegend()
12953 {
12954   setRowSpacing(0);
12955   setColumnSpacing(10);
12956   setMargins(QMargins(2, 3, 2, 2));
12957   setAntialiased(false);
12958   setIconSize(32, 18);
12959 
12960   setIconTextPadding(7);
12961 
12962   setSelectableParts(spLegendBox | spItems);
12963   setSelectedParts(spNone);
12964 
12965   setBorderPen(QPen(Qt::black));
12966   setSelectedBorderPen(QPen(Qt::blue, 2));
12967   setIconBorderPen(Qt::NoPen);
12968   setSelectedIconBorderPen(QPen(Qt::blue, 2));
12969   setBrush(Qt::white);
12970   setSelectedBrush(Qt::white);
12971   setTextColor(Qt::black);
12972   setSelectedTextColor(Qt::blue);
12973 }
12974 
~QCPLegend()12975 QCPLegend::~QCPLegend()
12976 {
12977   clearItems();
12978   if (qobject_cast<QCustomPlot*>(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot)
12979     mParentPlot->legendRemoved(this);
12980 }
12981 
12982 /* no doc for getter, see setSelectedParts */
selectedParts() const12983 QCPLegend::SelectableParts QCPLegend::selectedParts() const
12984 {
12985   // check whether any legend elements selected, if yes, add spItems to return value
12986   bool hasSelectedItems = false;
12987   for (int i=0; i<itemCount(); ++i)
12988   {
12989     if (item(i) && item(i)->selected())
12990     {
12991       hasSelectedItems = true;
12992       break;
12993     }
12994   }
12995   if (hasSelectedItems)
12996     return mSelectedParts | spItems;
12997   else
12998     return mSelectedParts & ~spItems;
12999 }
13000 
13001 /*!
13002   Sets the pen, the border of the entire legend is drawn with.
13003 */
setBorderPen(const QPen & pen)13004 void QCPLegend::setBorderPen(const QPen &pen)
13005 {
13006   mBorderPen = pen;
13007 }
13008 
13009 /*!
13010   Sets the brush of the legend background.
13011 */
setBrush(const QBrush & brush)13012 void QCPLegend::setBrush(const QBrush &brush)
13013 {
13014   mBrush = brush;
13015 }
13016 
13017 /*!
13018   Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
13019   use this font by default. However, a different font can be specified on a per-item-basis by
13020   accessing the specific legend item.
13021 
13022   This function will also set \a font on all already existing legend items.
13023 
13024   \see QCPAbstractLegendItem::setFont
13025 */
setFont(const QFont & font)13026 void QCPLegend::setFont(const QFont &font)
13027 {
13028   mFont = font;
13029   for (int i=0; i<itemCount(); ++i)
13030   {
13031     if (item(i))
13032       item(i)->setFont(mFont);
13033   }
13034 }
13035 
13036 /*!
13037   Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
13038   will use this color by default. However, a different colors can be specified on a per-item-basis
13039   by accessing the specific legend item.
13040 
13041   This function will also set \a color on all already existing legend items.
13042 
13043   \see QCPAbstractLegendItem::setTextColor
13044 */
setTextColor(const QColor & color)13045 void QCPLegend::setTextColor(const QColor &color)
13046 {
13047   mTextColor = color;
13048   for (int i=0; i<itemCount(); ++i)
13049   {
13050     if (item(i))
13051       item(i)->setTextColor(color);
13052   }
13053 }
13054 
13055 /*!
13056   Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
13057   representation of the graph) will use this size by default.
13058 */
setIconSize(const QSize & size)13059 void QCPLegend::setIconSize(const QSize &size)
13060 {
13061   mIconSize = size;
13062 }
13063 
13064 /*! \overload
13065 */
setIconSize(int width,int height)13066 void QCPLegend::setIconSize(int width, int height)
13067 {
13068   mIconSize.setWidth(width);
13069   mIconSize.setHeight(height);
13070 }
13071 
13072 /*!
13073   Sets the horizontal space in pixels between the legend icon and the text next to it.
13074   Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
13075   name of the graph) will use this space by default.
13076 */
setIconTextPadding(int padding)13077 void QCPLegend::setIconTextPadding(int padding)
13078 {
13079   mIconTextPadding = padding;
13080 }
13081 
13082 /*!
13083   Sets the pen used to draw a border around each legend icon. Legend items that draw an
13084   icon (e.g. a visual representation of the graph) will use this pen by default.
13085 
13086   If no border is wanted, set this to \a Qt::NoPen.
13087 */
setIconBorderPen(const QPen & pen)13088 void QCPLegend::setIconBorderPen(const QPen &pen)
13089 {
13090   mIconBorderPen = pen;
13091 }
13092 
13093 /*!
13094   Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
13095   (When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.)
13096 
13097   However, even when \a selectable is set to a value not allowing the selection of a specific part,
13098   it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
13099   directly.
13100 
13101   \see SelectablePart, setSelectedParts
13102 */
setSelectableParts(const SelectableParts & selectable)13103 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13104 {
13105   if (mSelectableParts != selectable)
13106   {
13107     mSelectableParts = selectable;
13108     emit selectableChanged(mSelectableParts);
13109   }
13110 }
13111 
13112 /*!
13113   Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
13114   is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
13115   doesn't contain \ref spItems, those items become deselected.
13116 
13117   The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
13118   contains iSelectLegend. You only need to call this function when you wish to change the selection
13119   state manually.
13120 
13121   This function can change the selection state of a part even when \ref setSelectableParts was set to a
13122   value that actually excludes the part.
13123 
13124   emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
13125 
13126   Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
13127   before, because there's no way to specify which exact items to newly select. Do this by calling
13128   \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
13129 
13130   \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
13131   setSelectedFont
13132 */
setSelectedParts(const SelectableParts & selected)13133 void QCPLegend::setSelectedParts(const SelectableParts &selected)
13134 {
13135   SelectableParts newSelected = selected;
13136   mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13137 
13138   if (mSelectedParts != newSelected)
13139   {
13140     if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13141     {
13142       qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13143       newSelected &= ~spItems;
13144     }
13145     if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13146     {
13147       for (int i=0; i<itemCount(); ++i)
13148       {
13149         if (item(i))
13150           item(i)->setSelected(false);
13151       }
13152     }
13153     mSelectedParts = newSelected;
13154     emit selectionChanged(mSelectedParts);
13155   }
13156 }
13157 
13158 /*!
13159   When the legend box is selected, this pen is used to draw the border instead of the normal pen
13160   set via \ref setBorderPen.
13161 
13162   \see setSelectedParts, setSelectableParts, setSelectedBrush
13163 */
setSelectedBorderPen(const QPen & pen)13164 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13165 {
13166   mSelectedBorderPen = pen;
13167 }
13168 
13169 /*!
13170   Sets the pen legend items will use to draw their icon borders, when they are selected.
13171 
13172   \see setSelectedParts, setSelectableParts, setSelectedFont
13173 */
setSelectedIconBorderPen(const QPen & pen)13174 void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
13175 {
13176   mSelectedIconBorderPen = pen;
13177 }
13178 
13179 /*!
13180   When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
13181   set via \ref setBrush.
13182 
13183   \see setSelectedParts, setSelectableParts, setSelectedBorderPen
13184 */
setSelectedBrush(const QBrush & brush)13185 void QCPLegend::setSelectedBrush(const QBrush &brush)
13186 {
13187   mSelectedBrush = brush;
13188 }
13189 
13190 /*!
13191   Sets the default font that is used by legend items when they are selected.
13192 
13193   This function will also set \a font on all already existing legend items.
13194 
13195   \see setFont, QCPAbstractLegendItem::setSelectedFont
13196 */
setSelectedFont(const QFont & font)13197 void QCPLegend::setSelectedFont(const QFont &font)
13198 {
13199   mSelectedFont = font;
13200   for (int i=0; i<itemCount(); ++i)
13201   {
13202     if (item(i))
13203       item(i)->setSelectedFont(font);
13204   }
13205 }
13206 
13207 /*!
13208   Sets the default text color that is used by legend items when they are selected.
13209 
13210   This function will also set \a color on all already existing legend items.
13211 
13212   \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
13213 */
setSelectedTextColor(const QColor & color)13214 void QCPLegend::setSelectedTextColor(const QColor &color)
13215 {
13216   mSelectedTextColor = color;
13217   for (int i=0; i<itemCount(); ++i)
13218   {
13219     if (item(i))
13220       item(i)->setSelectedTextColor(color);
13221   }
13222 }
13223 
13224 /*!
13225   Returns the item with index \a i.
13226 
13227   \see itemCount
13228 */
item(int index) const13229 QCPAbstractLegendItem *QCPLegend::item(int index) const
13230 {
13231   return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13232 }
13233 
13234 /*!
13235   Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13236   If such an item isn't in the legend, returns 0.
13237 
13238   \see hasItemWithPlottable
13239 */
itemWithPlottable(const QCPAbstractPlottable * plottable) const13240 QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
13241 {
13242   for (int i=0; i<itemCount(); ++i)
13243   {
13244     if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13245     {
13246       if (pli->plottable() == plottable)
13247         return pli;
13248     }
13249   }
13250   return 0;
13251 }
13252 
13253 /*!
13254   Returns the number of items currently in the legend.
13255   \see item
13256 */
itemCount() const13257 int QCPLegend::itemCount() const
13258 {
13259   return elementCount();
13260 }
13261 
13262 /*!
13263   Returns whether the legend contains \a itm.
13264 */
hasItem(QCPAbstractLegendItem * item) const13265 bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
13266 {
13267   for (int i=0; i<itemCount(); ++i)
13268   {
13269     if (item == this->item(i))
13270         return true;
13271   }
13272   return false;
13273 }
13274 
13275 /*!
13276   Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13277   If such an item isn't in the legend, returns false.
13278 
13279   \see itemWithPlottable
13280 */
hasItemWithPlottable(const QCPAbstractPlottable * plottable) const13281 bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
13282 {
13283   return itemWithPlottable(plottable);
13284 }
13285 
13286 /*!
13287   Adds \a item to the legend, if it's not present already.
13288 
13289   Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
13290 
13291   The legend takes ownership of the item.
13292 */
addItem(QCPAbstractLegendItem * item)13293 bool QCPLegend::addItem(QCPAbstractLegendItem *item)
13294 {
13295   if (!hasItem(item))
13296   {
13297     return addElement(rowCount(), 0, item);
13298   } else
13299     return false;
13300 }
13301 
13302 /*!
13303   Removes the item with index \a index from the legend.
13304 
13305   Returns true, if successful.
13306 
13307   \see itemCount, clearItems
13308 */
removeItem(int index)13309 bool QCPLegend::removeItem(int index)
13310 {
13311   if (QCPAbstractLegendItem *ali = item(index))
13312   {
13313     bool success = remove(ali);
13314     simplify();
13315     return success;
13316   } else
13317     return false;
13318 }
13319 
13320 /*! \overload
13321 
13322   Removes \a item from the legend.
13323 
13324   Returns true, if successful.
13325 
13326   \see clearItems
13327 */
removeItem(QCPAbstractLegendItem * item)13328 bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
13329 {
13330   bool success = remove(item);
13331   simplify();
13332   return success;
13333 }
13334 
13335 /*!
13336   Removes all items from the legend.
13337 */
clearItems()13338 void QCPLegend::clearItems()
13339 {
13340   for (int i=itemCount()-1; i>=0; --i)
13341     removeItem(i);
13342 }
13343 
13344 /*!
13345   Returns the legend items that are currently selected. If no items are selected,
13346   the list is empty.
13347 
13348   \see QCPAbstractLegendItem::setSelected, setSelectable
13349 */
selectedItems() const13350 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13351 {
13352   QList<QCPAbstractLegendItem*> result;
13353   for (int i=0; i<itemCount(); ++i)
13354   {
13355     if (QCPAbstractLegendItem *ali = item(i))
13356     {
13357       if (ali->selected())
13358         result.append(ali);
13359     }
13360   }
13361   return result;
13362 }
13363 
13364 /*! \internal
13365 
13366   A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
13367   before drawing main legend elements.
13368 
13369   This is the antialiasing state the painter passed to the \ref draw method is in by default.
13370 
13371   This function takes into account the local setting of the antialiasing flag as well as the
13372   overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
13373   QCustomPlot::setNotAntialiasedElements.
13374 
13375   \see setAntialiased
13376 */
applyDefaultAntialiasingHint(QCPPainter * painter) const13377 void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
13378 {
13379   applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
13380 }
13381 
13382 /*! \internal
13383 
13384   Returns the pen used to paint the border of the legend, taking into account the selection state
13385   of the legend box.
13386 */
getBorderPen() const13387 QPen QCPLegend::getBorderPen() const
13388 {
13389   return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
13390 }
13391 
13392 /*! \internal
13393 
13394   Returns the brush used to paint the background of the legend, taking into account the selection
13395   state of the legend box.
13396 */
getBrush() const13397 QBrush QCPLegend::getBrush() const
13398 {
13399   return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13400 }
13401 
13402 /*! \internal
13403 
13404   Draws the legend box with the provided \a painter. The individual legend items are layerables
13405   themselves, thus are drawn independently.
13406 */
draw(QCPPainter * painter)13407 void QCPLegend::draw(QCPPainter *painter)
13408 {
13409   // draw background rect:
13410   painter->setBrush(getBrush());
13411   painter->setPen(getBorderPen());
13412   painter->drawRect(mOuterRect);
13413 }
13414 
13415 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const13416 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13417 {
13418   if (!mParentPlot) return -1;
13419   if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13420     return -1;
13421 
13422   if (mOuterRect.contains(pos.toPoint()))
13423   {
13424     if (details) details->setValue(spLegendBox);
13425     return mParentPlot->selectionTolerance()*0.99;
13426   }
13427   return -1;
13428 }
13429 
13430 /* inherits documentation from base class */
selectEvent(QMouseEvent * event,bool additive,const QVariant & details,bool * selectionStateChanged)13431 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13432 {
13433   Q_UNUSED(event)
13434   mSelectedParts = selectedParts(); // in case item selection has changed
13435   if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13436   {
13437     SelectableParts selBefore = mSelectedParts;
13438     setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)
13439     if (selectionStateChanged)
13440       *selectionStateChanged = mSelectedParts != selBefore;
13441   }
13442 }
13443 
13444 /* inherits documentation from base class */
deselectEvent(bool * selectionStateChanged)13445 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13446 {
13447   mSelectedParts = selectedParts(); // in case item selection has changed
13448   if (mSelectableParts.testFlag(spLegendBox))
13449   {
13450     SelectableParts selBefore = mSelectedParts;
13451     setSelectedParts(selectedParts() & ~spLegendBox);
13452     if (selectionStateChanged)
13453       *selectionStateChanged = mSelectedParts != selBefore;
13454   }
13455 }
13456 
13457 /* inherits documentation from base class */
selectionCategory() const13458 QCP::Interaction QCPLegend::selectionCategory() const
13459 {
13460   return QCP::iSelectLegend;
13461 }
13462 
13463 /* inherits documentation from base class */
selectionCategory() const13464 QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
13465 {
13466   return QCP::iSelectLegend;
13467 }
13468 
13469 /* inherits documentation from base class */
parentPlotInitialized(QCustomPlot * parentPlot)13470 void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
13471 {
13472   Q_UNUSED(parentPlot)
13473 }
13474 
13475 
13476 ////////////////////////////////////////////////////////////////////////////////////////////////////
13477 //////////////////// QCPPlotTitle
13478 ////////////////////////////////////////////////////////////////////////////////////////////////////
13479 
13480 /*! \class QCPPlotTitle
13481   \brief A layout element displaying a plot title text
13482 
13483   The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
13484   and \ref setTextColor.
13485 
13486   A plot title can be added as follows:
13487   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpplottitle-creation
13488 
13489   Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
13490   easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
13491   signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
13492   QCustomPlot::titleDoubleClick signal.
13493 */
13494 
13495 /* start documentation of signals */
13496 
13497 /*! \fn void QCPPlotTitle::selectionChanged(bool selected)
13498 
13499   This signal is emitted when the selection state has changed to \a selected, either by user
13500   interaction or by a direct call to \ref setSelected.
13501 
13502   \see setSelected, setSelectable
13503 */
13504 
13505 /* end documentation of signals */
13506 
13507 /*!
13508   Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
13509 
13510   To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
13511 */
QCPPlotTitle(QCustomPlot * parentPlot)13512 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
13513   QCPLayoutElement(parentPlot),
13514   mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)),
13515   mTextColor(Qt::black),
13516   mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)),
13517   mSelectedTextColor(Qt::blue),
13518   mSelectable(false),
13519   mSelected(false)
13520 {
13521   if (parentPlot)
13522   {
13523     setLayer(parentPlot->currentLayer());
13524     mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
13525     mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
13526   }
13527   setMargins(QMargins(5, 5, 5, 0));
13528 }
13529 
13530 /*! \overload
13531 
13532   Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
13533 */
QCPPlotTitle(QCustomPlot * parentPlot,const QString & text)13534 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
13535   QCPLayoutElement(parentPlot),
13536   mText(text),
13537   mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
13538   mTextColor(Qt::black),
13539   mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
13540   mSelectedTextColor(Qt::blue),
13541   mSelectable(false),
13542   mSelected(false)
13543 {
13544   setLayer(QLatin1String("axes"));
13545   setMargins(QMargins(5, 5, 5, 0));
13546 }
13547 
13548 /*!
13549   Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
13550 
13551   \see setFont, setTextColor
13552 */
setText(const QString & text)13553 void QCPPlotTitle::setText(const QString &text)
13554 {
13555   mText = text;
13556 }
13557 
13558 /*!
13559   Sets the \a font of the title text.
13560 
13561   \see setTextColor, setSelectedFont
13562 */
setFont(const QFont & font)13563 void QCPPlotTitle::setFont(const QFont &font)
13564 {
13565   mFont = font;
13566 }
13567 
13568 /*!
13569   Sets the \a color of the title text.
13570 
13571   \see setFont, setSelectedTextColor
13572 */
setTextColor(const QColor & color)13573 void QCPPlotTitle::setTextColor(const QColor &color)
13574 {
13575   mTextColor = color;
13576 }
13577 
13578 /*!
13579   Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
13580 
13581   \see setFont
13582 */
setSelectedFont(const QFont & font)13583 void QCPPlotTitle::setSelectedFont(const QFont &font)
13584 {
13585   mSelectedFont = font;
13586 }
13587 
13588 /*!
13589   Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
13590 
13591   \see setTextColor
13592 */
setSelectedTextColor(const QColor & color)13593 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13594 {
13595   mSelectedTextColor = color;
13596 }
13597 
13598 /*!
13599   Sets whether the user may select this plot title to \a selectable.
13600 
13601   Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
13602   programmatically via \ref setSelected.
13603 */
setSelectable(bool selectable)13604 void QCPPlotTitle::setSelectable(bool selectable)
13605 {
13606   if (mSelectable != selectable)
13607   {
13608     mSelectable = selectable;
13609     emit selectableChanged(mSelectable);
13610   }
13611 }
13612 
13613 /*!
13614   Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
13615   selectionChanged is emitted.
13616 
13617   Note that this function can change the selection state independently of the current \ref
13618   setSelectable state.
13619 */
setSelected(bool selected)13620 void QCPPlotTitle::setSelected(bool selected)
13621 {
13622   if (mSelected != selected)
13623   {
13624     mSelected = selected;
13625     emit selectionChanged(mSelected);
13626   }
13627 }
13628 
13629 /* inherits documentation from base class */
applyDefaultAntialiasingHint(QCPPainter * painter) const13630 void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
13631 {
13632   applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
13633 }
13634 
13635 /* inherits documentation from base class */
draw(QCPPainter * painter)13636 void QCPPlotTitle::draw(QCPPainter *painter)
13637 {
13638   painter->setFont(mainFont());
13639   painter->setPen(QPen(mainTextColor()));
13640   painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13641 }
13642 
13643 /* inherits documentation from base class */
minimumSizeHint() const13644 QSize QCPPlotTitle::minimumSizeHint() const
13645 {
13646   QFontMetrics metrics(mFont);
13647   QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13648   result.rwidth() += mMargins.left() + mMargins.right();
13649   result.rheight() += mMargins.top() + mMargins.bottom();
13650   return result;
13651 }
13652 
13653 /* inherits documentation from base class */
maximumSizeHint() const13654 QSize QCPPlotTitle::maximumSizeHint() const
13655 {
13656   QFontMetrics metrics(mFont);
13657   QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13658   result.rheight() += mMargins.top() + mMargins.bottom();
13659   result.setWidth(QWIDGETSIZE_MAX);
13660   return result;
13661 }
13662 
13663 /* inherits documentation from base class */
selectEvent(QMouseEvent * event,bool additive,const QVariant & details,bool * selectionStateChanged)13664 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13665 {
13666   Q_UNUSED(event)
13667   Q_UNUSED(details)
13668   if (mSelectable)
13669   {
13670     bool selBefore = mSelected;
13671     setSelected(additive ? !mSelected : true);
13672     if (selectionStateChanged)
13673       *selectionStateChanged = mSelected != selBefore;
13674   }
13675 }
13676 
13677 /* inherits documentation from base class */
deselectEvent(bool * selectionStateChanged)13678 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
13679 {
13680   if (mSelectable)
13681   {
13682     bool selBefore = mSelected;
13683     setSelected(false);
13684     if (selectionStateChanged)
13685       *selectionStateChanged = mSelected != selBefore;
13686   }
13687 }
13688 
13689 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const13690 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13691 {
13692   Q_UNUSED(details)
13693   if (onlySelectable && !mSelectable)
13694     return -1;
13695 
13696   if (mTextBoundingRect.contains(pos.toPoint()))
13697     return mParentPlot->selectionTolerance()*0.99;
13698   else
13699     return -1;
13700 }
13701 
13702 /*! \internal
13703 
13704   Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
13705   <tt>true</tt>, else mFont is returned.
13706 */
mainFont() const13707 QFont QCPPlotTitle::mainFont() const
13708 {
13709   return mSelected ? mSelectedFont : mFont;
13710 }
13711 
13712 /*! \internal
13713 
13714   Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
13715   <tt>true</tt>, else mTextColor is returned.
13716 */
mainTextColor() const13717 QColor QCPPlotTitle::mainTextColor() const
13718 {
13719   return mSelected ? mSelectedTextColor : mTextColor;
13720 }
13721 
13722 
13723 ////////////////////////////////////////////////////////////////////////////////////////////////////
13724 //////////////////// QCPColorScale
13725 ////////////////////////////////////////////////////////////////////////////////////////////////////
13726 
13727 /*! \class QCPColorScale
13728   \brief A color scale for use with color coding data such as QCPColorMap
13729 
13730   This layout element can be placed on the plot to correlate a color gradient with data values. It
13731   is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps".
13732 
13733   \image html QCPColorScale.png
13734 
13735   The color scale can be either horizontal or vertical, as shown in the image above. The
13736   orientation and the side where the numbers appear is controlled with \ref setType.
13737 
13738   Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are
13739   connected, they share their gradient, data range and data scale type (\ref setGradient, \ref
13740   setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color
13741   scale, to make them all synchronize these properties.
13742 
13743   To have finer control over the number display and axis behaviour, you can directly access the
13744   \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if
13745   you want to change the number of automatically generated ticks, call
13746   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-autotickcount
13747 
13748   Placing a color scale next to the main axis rect works like with any other layout element:
13749   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-creation
13750   In this case we have placed it to the right of the default axis rect, so it wasn't necessary to
13751   call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color
13752   scale can be set with \ref setLabel.
13753 
13754   For optimum appearance (like in the image above), it may be desirable to line up the axis rect and
13755   the borders of the color scale. Use a \ref QCPMarginGroup to achieve this:
13756   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-margingroup
13757 
13758   Color scales are initialized with a non-zero minimum top and bottom margin (\ref
13759   setMinimumMargins), because vertical color scales are most common and the minimum top/bottom
13760   margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a
13761   horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you
13762   might want to also change the minimum margins accordingly, e.g. <tt>setMinimumMargins(QMargins(6, 0, 6, 0))</tt>.
13763 */
13764 
13765 /* start documentation of inline functions */
13766 
13767 /*! \fn QCPAxis *QCPColorScale::axis() const
13768 
13769   Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the
13770   appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its
13771   interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref
13772   setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref
13773   QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on
13774   the QCPColorScale or on its QCPAxis.
13775 
13776   If the type of the color scale is changed with \ref setType, the axis returned by this method
13777   will change, too, to either the left, right, bottom or top axis, depending on which type was set.
13778 */
13779 
13780 /* end documentation of signals */
13781 /* start documentation of signals */
13782 
13783 /*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange);
13784 
13785   This signal is emitted when the data range changes.
13786 
13787   \see setDataRange
13788 */
13789 
13790 /*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
13791 
13792   This signal is emitted when the data scale type changes.
13793 
13794   \see setDataScaleType
13795 */
13796 
13797 /*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient);
13798 
13799   This signal is emitted when the gradient changes.
13800 
13801   \see setGradient
13802 */
13803 
13804 /* end documentation of signals */
13805 
13806 /*!
13807   Constructs a new QCPColorScale.
13808 */
QCPColorScale(QCustomPlot * parentPlot)13809 QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
13810   QCPLayoutElement(parentPlot),
13811   mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
13812   mDataScaleType(QCPAxis::stLinear),
13813   mBarWidth(20),
13814   mAxisRect(new QCPColorScaleAxisRectPrivate(this))
13815 {
13816   setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used)
13817   setType(QCPAxis::atRight);
13818   setDataRange(QCPRange(0, 6));
13819 }
13820 
~QCPColorScale()13821 QCPColorScale::~QCPColorScale()
13822 {
13823   delete mAxisRect;
13824 }
13825 
13826 /* undocumented getter */
label() const13827 QString QCPColorScale::label() const
13828 {
13829   if (!mColorAxis)
13830   {
13831     qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13832     return QString();
13833   }
13834 
13835   return mColorAxis->label();
13836 }
13837 
13838 /* undocumented getter */
rangeDrag() const13839 bool QCPColorScale::rangeDrag() const
13840 {
13841   if (!mAxisRect)
13842   {
13843     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13844     return false;
13845   }
13846 
13847   return mAxisRect->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13848       mAxisRect->rangeDragAxis(QCPAxis::orientation(mType)) &&
13849       mAxisRect->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13850 }
13851 
13852 /* undocumented getter */
rangeZoom() const13853 bool QCPColorScale::rangeZoom() const
13854 {
13855   if (!mAxisRect)
13856   {
13857     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13858     return false;
13859   }
13860 
13861   return mAxisRect->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13862       mAxisRect->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13863       mAxisRect->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13864 }
13865 
13866 /*!
13867   Sets at which side of the color scale the axis is placed, and thus also its orientation.
13868 
13869   Note that after setting \a type to a different value, the axis returned by \ref axis() will
13870   be a different one. The new axis will adopt the following properties from the previous axis: The
13871   range, scale type, log base and label.
13872 */
setType(QCPAxis::AxisType type)13873 void QCPColorScale::setType(QCPAxis::AxisType type)
13874 {
13875   if (!mAxisRect)
13876   {
13877     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13878     return;
13879   }
13880   if (mType != type)
13881   {
13882     mType = type;
13883     QCPRange rangeTransfer(0, 6);
13884     double logBaseTransfer = 10;
13885     QString labelTransfer;
13886     // revert some settings on old axis:
13887     if (mColorAxis)
13888     {
13889       rangeTransfer = mColorAxis->range();
13890       labelTransfer = mColorAxis->label();
13891       logBaseTransfer = mColorAxis->scaleLogBase();
13892       mColorAxis->setLabel(QString());
13893       disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13894       disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13895     }
13896     QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
13897     for (auto atype : allAxisTypes)
13898     {
13899       mAxisRect->axis(atype)->setTicks(atype == mType);
13900       mAxisRect->axis(atype)->setTickLabels(atype== mType);
13901     }
13902     // set new mColorAxis pointer:
13903     mColorAxis = mAxisRect->axis(mType);
13904     // transfer settings to new axis:
13905     mColorAxis->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
13906     mColorAxis->setLabel(labelTransfer);
13907     mColorAxis->setScaleLogBase(logBaseTransfer); // scaleType is synchronized among axes in realtime via signals (connected in QCPColorScale ctor), so we only need to take care of log base here
13908     connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13909     connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13910     mAxisRect->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
13911                                        QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
13912   }
13913 }
13914 
13915 /*!
13916   Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
13917 
13918   It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is
13919   also equivalent to directly accessing the \ref axis and setting its range with \ref
13920   QCPAxis::setRange.
13921 
13922   \see setDataScaleType, setGradient, rescaleDataRange
13923 */
setDataRange(const QCPRange & dataRange)13924 void QCPColorScale::setDataRange(const QCPRange &dataRange)
13925 {
13926   if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
13927   {
13928     mDataRange = dataRange;
13929     if (mColorAxis)
13930       mColorAxis->setRange(mDataRange);
13931     emit dataRangeChanged(mDataRange);
13932   }
13933 }
13934 
13935 /*!
13936   Sets the scale type of the color scale, i.e. whether values are linearly associated with colors
13937   or logarithmically.
13938 
13939   It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is
13940   also equivalent to directly accessing the \ref axis and setting its scale type with \ref
13941   QCPAxis::setScaleType.
13942 
13943   \see setDataRange, setGradient
13944 */
setDataScaleType(QCPAxis::ScaleType scaleType)13945 void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
13946 {
13947   if (mDataScaleType != scaleType)
13948   {
13949     mDataScaleType = scaleType;
13950     if (mColorAxis)
13951       mColorAxis->setScaleType(mDataScaleType);
13952     if (mDataScaleType == QCPAxis::stLogarithmic)
13953       setDataRange(mDataRange.sanitizedForLogScale());
13954     emit dataScaleTypeChanged(mDataScaleType);
13955   }
13956 }
13957 
13958 /*!
13959   Sets the color gradient that will be used to represent data values.
13960 
13961   It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps.
13962 
13963   \see setDataRange, setDataScaleType
13964 */
setGradient(const QCPColorGradient & gradient)13965 void QCPColorScale::setGradient(const QCPColorGradient &gradient)
13966 {
13967   if (mGradient != gradient)
13968   {
13969     mGradient = gradient;
13970     if (mAxisRect)
13971       mAxisRect->mGradientImageInvalidated = true;
13972     emit gradientChanged(mGradient);
13973   }
13974 }
13975 
13976 /*!
13977   Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on
13978   the internal \ref axis.
13979 */
setLabel(const QString & str)13980 void QCPColorScale::setLabel(const QString &str)
13981 {
13982   if (!mColorAxis)
13983   {
13984     qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13985     return;
13986   }
13987 
13988   mColorAxis->setLabel(str);
13989 }
13990 
13991 /*!
13992   Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed
13993   will have.
13994 */
setBarWidth(int width)13995 void QCPColorScale::setBarWidth(int width)
13996 {
13997   mBarWidth = width;
13998 }
13999 
14000 /*!
14001   Sets whether the user can drag the data range (\ref setDataRange).
14002 
14003   Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref
14004   QCustomPlot::setInteractions) to allow range dragging.
14005 */
setRangeDrag(bool enabled)14006 void QCPColorScale::setRangeDrag(bool enabled)
14007 {
14008   if (!mAxisRect)
14009   {
14010     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14011     return;
14012   }
14013 
14014   if (enabled)
14015     mAxisRect->setRangeDrag(QCPAxis::orientation(mType));
14016   else
14017     mAxisRect->setRangeDrag(0);
14018 }
14019 
14020 /*!
14021   Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel.
14022 
14023   Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref
14024   QCustomPlot::setInteractions) to allow range dragging.
14025 */
setRangeZoom(bool enabled)14026 void QCPColorScale::setRangeZoom(bool enabled)
14027 {
14028   if (!mAxisRect)
14029   {
14030     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14031     return;
14032   }
14033 
14034   if (enabled)
14035     mAxisRect->setRangeZoom(QCPAxis::orientation(mType));
14036   else
14037     mAxisRect->setRangeZoom(0);
14038 }
14039 
14040 /*!
14041   Returns a list of all the color maps associated with this color scale.
14042 */
colorMaps() const14043 QList<QCPColorMap*> QCPColorScale::colorMaps() const
14044 {
14045   QList<QCPColorMap*> result;
14046   for (int i=0; i<mParentPlot->plottableCount(); ++i)
14047   {
14048     if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14049       if (cm->colorScale() == this)
14050         result.append(cm);
14051   }
14052   return result;
14053 }
14054 
14055 /*!
14056   Changes the data range such that all color maps associated with this color scale are fully mapped
14057   to the gradient in the data dimension.
14058 
14059   \see setDataRange
14060 */
rescaleDataRange(bool onlyVisibleMaps)14061 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14062 {
14063   QList<QCPColorMap*> maps = colorMaps();
14064   QCPRange newRange;
14065   bool haveRange = false;
14066   int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14067   if (mDataScaleType == QCPAxis::stLogarithmic)
14068     sign = (mDataRange.upper < 0 ? -1 : 1);
14069   for (int i=0; i<maps.size(); ++i)
14070   {
14071     if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14072       continue;
14073     QCPRange mapRange;
14074     if (maps.at(i)->colorScale() == this)
14075     {
14076       bool currentFoundRange = true;
14077       mapRange = maps.at(i)->data()->dataBounds();
14078       if (sign == 1)
14079       {
14080         if (mapRange.lower <= 0 && mapRange.upper > 0)
14081           mapRange.lower = mapRange.upper*1e-3;
14082         else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14083           currentFoundRange = false;
14084       } else if (sign == -1)
14085       {
14086         if (mapRange.upper >= 0 && mapRange.lower < 0)
14087           mapRange.upper = mapRange.lower*1e-3;
14088         else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14089           currentFoundRange = false;
14090       }
14091       if (currentFoundRange)
14092       {
14093         if (!haveRange)
14094           newRange = mapRange;
14095         else
14096           newRange.expand(mapRange);
14097         haveRange = true;
14098       }
14099     }
14100   }
14101   if (haveRange)
14102   {
14103     if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data
14104     {
14105       double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
14106       if (mDataScaleType == QCPAxis::stLinear)
14107       {
14108         newRange.lower = center-mDataRange.size()/2.0;
14109         newRange.upper = center+mDataRange.size()/2.0;
14110       } else // mScaleType == stLogarithmic
14111       {
14112         newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14113         newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14114       }
14115     }
14116     setDataRange(newRange);
14117   }
14118 }
14119 
14120 /* inherits documentation from base class */
update(UpdatePhase phase)14121 void QCPColorScale::update(UpdatePhase phase)
14122 {
14123   QCPLayoutElement::update(phase);
14124   if (!mAxisRect)
14125   {
14126     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14127     return;
14128   }
14129 
14130   mAxisRect->update(phase);
14131 
14132   switch (phase)
14133   {
14134     case upMargins:
14135     {
14136       if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)
14137       {
14138         setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect->margins().top()+mAxisRect->margins().bottom()+margins().top()+margins().bottom());
14139         setMinimumSize(0,               mBarWidth+mAxisRect->margins().top()+mAxisRect->margins().bottom()+margins().top()+margins().bottom());
14140       } else
14141       {
14142         setMaximumSize(mBarWidth+mAxisRect->margins().left()+mAxisRect->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14143         setMinimumSize(mBarWidth+mAxisRect->margins().left()+mAxisRect->margins().right()+margins().left()+margins().right(), 0);
14144       }
14145       break;
14146     }
14147     case upLayout:
14148     {
14149       mAxisRect->setOuterRect(rect());
14150       break;
14151     }
14152     default: break;
14153   }
14154 }
14155 
14156 /* inherits documentation from base class */
applyDefaultAntialiasingHint(QCPPainter * painter) const14157 void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const
14158 {
14159   painter->setAntialiasing(false);
14160 }
14161 
14162 /* inherits documentation from base class */
mousePressEvent(QMouseEvent * event)14163 void QCPColorScale::mousePressEvent(QMouseEvent *event)
14164 {
14165   if (!mAxisRect)
14166   {
14167     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14168     return;
14169   }
14170   mAxisRect->mousePressEvent(event);
14171 }
14172 
14173 /* inherits documentation from base class */
mouseMoveEvent(QMouseEvent * event)14174 void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14175 {
14176   if (!mAxisRect)
14177   {
14178     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14179     return;
14180   }
14181   mAxisRect->mouseMoveEvent(event);
14182 }
14183 
14184 /* inherits documentation from base class */
mouseReleaseEvent(QMouseEvent * event)14185 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14186 {
14187   if (!mAxisRect)
14188   {
14189     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14190     return;
14191   }
14192   mAxisRect->mouseReleaseEvent(event);
14193 }
14194 
14195 /* inherits documentation from base class */
wheelEvent(QWheelEvent * event)14196 void QCPColorScale::wheelEvent(QWheelEvent *event)
14197 {
14198   if (!mAxisRect)
14199   {
14200     qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14201     return;
14202   }
14203   mAxisRect->wheelEvent(event);
14204 }
14205 
14206 ////////////////////////////////////////////////////////////////////////////////////////////////////
14207 //////////////////// QCPColorScaleAxisRectPrivate
14208 ////////////////////////////////////////////////////////////////////////////////////////////////////
14209 
14210 /*! \class QCPColorScaleAxisRectPrivate
14211 
14212   \internal
14213   \brief An axis rect subclass for use in a QCPColorScale
14214 
14215   This is a private class and not part of the public QCustomPlot interface.
14216 
14217   It provides the axis rect functionality for the QCPColorScale class.
14218 */
14219 
14220 
14221 /*!
14222   Creates a new instance, as a child of \a parentColorScale.
14223 */
QCPColorScaleAxisRectPrivate(QCPColorScale * parentColorScale)14224 QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :
14225   QCPAxisRect(parentColorScale->parentPlot(), true),
14226   mParentColorScale(parentColorScale),
14227   mGradientImageInvalidated(true)
14228 {
14229   setParentLayerable(parentColorScale);
14230   setMinimumMargins(QMargins(0, 0, 0, 0));
14231   QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14232   for (auto type : allAxisTypes)
14233   {
14234     axis(type)->setVisible(true);
14235     axis(type)->grid()->setVisible(false);
14236     axis(type)->setPadding(0);
14237     connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14238     connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14239   }
14240 
14241   connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14242   connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14243   connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14244   connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14245   connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14246   connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14247   connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14248   connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14249 
14250   // make layer transfers of color scale transfer to axis rect and axes
14251   // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14252   connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14253   for (auto type : allAxisTypes)
14254     connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14255 }
14256 
14257 /*! \internal
14258   Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws
14259   it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation.
14260 */
draw(QCPPainter * painter)14261 void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)
14262 {
14263   if (mGradientImageInvalidated)
14264     updateGradientImage();
14265 
14266   bool mirrorHorz = false;
14267   bool mirrorVert = false;
14268   if (mParentColorScale->mColorAxis)
14269   {
14270     mirrorHorz = mParentColorScale->mColorAxis->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14271     mirrorVert = mParentColorScale->mColorAxis->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14272   }
14273 
14274   painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14275   QCPAxisRect::draw(painter);
14276 }
14277 
14278 /*! \internal
14279 
14280   Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to
14281   generate a gradient image. This gradient image will be used in the \ref draw method.
14282 */
updateGradientImage()14283 void QCPColorScaleAxisRectPrivate::updateGradientImage()
14284 {
14285   if (rect().isEmpty())
14286     return;
14287 
14288   int n = mParentColorScale->mGradient.levelCount();
14289   int w, h;
14290   QVector<double> data(n);
14291   for (int i=0; i<n; ++i)
14292     data[i] = i;
14293   if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)
14294   {
14295     w = n;
14296     h = rect().height();
14297     mGradientImage = QImage(w, h, QImage::Format_RGB32);
14298     QVector<QRgb*> pixels;
14299     for (int y=0; y<h; ++y)
14300       pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14301     mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14302     for (int y=1; y<h; ++y)
14303       memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14304   } else
14305   {
14306     w = rect().width();
14307     h = n;
14308     mGradientImage = QImage(w, h, QImage::Format_RGB32);
14309     for (int y=0; y<h; ++y)
14310     {
14311       QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14312       const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14313       for (int x=0; x<w; ++x)
14314         pixels[x] = lineColor;
14315     }
14316   }
14317   mGradientImageInvalidated = false;
14318 }
14319 
14320 /*! \internal
14321 
14322   This slot is connected to the selectionChanged signals of the four axes in the constructor. It
14323   synchronizes the selection state of the axes.
14324 */
axisSelectionChanged(QCPAxis::SelectableParts selectedParts)14325 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14326 {
14327   // axis bases of four axes shall always (de-)selected synchronously:
14328   QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14329   for (auto type : allAxisTypes)
14330   {
14331     if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14332       if (senderAxis->axisType() == type)
14333         continue;
14334 
14335     if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14336     {
14337       if (selectedParts.testFlag(QCPAxis::spAxis))
14338         axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14339       else
14340         axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14341     }
14342   }
14343 }
14344 
14345 /*! \internal
14346 
14347   This slot is connected to the selectableChanged signals of the four axes in the constructor. It
14348   synchronizes the selectability of the axes.
14349 */
axisSelectableChanged(QCPAxis::SelectableParts selectableParts)14350 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14351 {
14352   // synchronize axis base selectability:
14353   QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14354   for (auto type : allAxisTypes)
14355   {
14356     if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14357       if (senderAxis->axisType() == type)
14358         continue;
14359 
14360     if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14361     {
14362       if (selectableParts.testFlag(QCPAxis::spAxis))
14363         axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14364       else
14365         axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14366     }
14367   }
14368 }
14369 
14370 
14371 ////////////////////////////////////////////////////////////////////////////////////////////////////
14372 //////////////////// QCPData
14373 ////////////////////////////////////////////////////////////////////////////////////////////////////
14374 
14375 /*! \class QCPData
14376   \brief Holds the data of one single data point for QCPGraph.
14377 
14378   The container for storing multiple data points is \ref QCPDataMap.
14379 
14380   The stored data is:
14381   \li \a key: coordinate on the key axis of this data point
14382   \li \a value: coordinate on the value axis of this data point
14383   \li \a keyErrorMinus: negative error in the key dimension (for error bars)
14384   \li \a keyErrorPlus: positive error in the key dimension (for error bars)
14385   \li \a valueErrorMinus: negative error in the value dimension (for error bars)
14386   \li \a valueErrorPlus: positive error in the value dimension (for error bars)
14387 
14388   \see QCPDataMap
14389 */
14390 
14391 /*!
14392   Constructs a data point with key, value and all errors set to zero.
14393 */
QCPData()14394 QCPData::QCPData() :
14395   key(0),
14396   value(0),
14397   keyErrorPlus(0),
14398   keyErrorMinus(0),
14399   valueErrorPlus(0),
14400   valueErrorMinus(0)
14401 {
14402 }
14403 
14404 /*!
14405   Constructs a data point with the specified \a key and \a value. All errors are set to zero.
14406 */
QCPData(double key,double value)14407 QCPData::QCPData(double key, double value) :
14408   key(key),
14409   value(value),
14410   keyErrorPlus(0),
14411   keyErrorMinus(0),
14412   valueErrorPlus(0),
14413   valueErrorMinus(0)
14414 {
14415 }
14416 
14417 
14418 ////////////////////////////////////////////////////////////////////////////////////////////////////
14419 //////////////////// QCPGraph
14420 ////////////////////////////////////////////////////////////////////////////////////////////////////
14421 
14422 /*! \class QCPGraph
14423   \brief A plottable representing a graph in a plot.
14424 
14425   \image html QCPGraph.png
14426 
14427   Usually you create new graphs by calling QCustomPlot::addGraph. The resulting instance can be
14428   accessed via QCustomPlot::graph.
14429 
14430   To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can
14431   also access and modify the graph's data via the \ref data method, which returns a pointer to the
14432   internal \ref QCPDataMap.
14433 
14434   Graphs are used to display single-valued data. Single-valued means that there should only be one
14435   data point per unique key coordinate. In other words, the graph can't have \a loops. If you do
14436   want to plot non-single-valued curves, rather use the QCPCurve plottable.
14437 
14438   Gaps in the graph line can be created by adding data points with NaN as value
14439   (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be
14440   separated.
14441 
14442   \section appearance Changing the appearance
14443 
14444   The appearance of the graph is mainly determined by the line style, scatter style, brush and pen
14445   of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen).
14446 
14447   \subsection filling Filling under or between graphs
14448 
14449   QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to
14450   the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill,
14451   just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent.
14452 
14453   By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill
14454   between this graph and another one, call \ref setChannelFillGraph with the other graph as
14455   parameter.
14456 
14457   \see QCustomPlot::addGraph, QCustomPlot::graph
14458 */
14459 
14460 /* start of documentation of inline functions */
14461 
14462 /*! \fn QCPDataMap *QCPGraph::data() const
14463 
14464   Returns a pointer to the internal data storage of type \ref QCPDataMap. You may use it to
14465   directly manipulate the data, which may be more convenient and faster than using the regular \ref
14466   setData or \ref addData methods, in certain situations.
14467 */
14468 
14469 /* end of documentation of inline functions */
14470 
14471 /*!
14472   Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
14473   axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
14474   the same orientation. If either of these restrictions is violated, a corresponding message is
14475   printed to the debug output (qDebug), the construction is not aborted, though.
14476 
14477   The constructed QCPGraph can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
14478   then takes ownership of the graph.
14479 
14480   To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.
14481 */
QCPGraph(QCPAxis * keyAxis,QCPAxis * valueAxis)14482 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14483   QCPAbstractPlottable(keyAxis, valueAxis)
14484 {
14485   mData = new QCPDataMap;
14486 
14487   setPen(QPen(Qt::blue, 0));
14488   setErrorPen(QPen(Qt::black));
14489   setBrush(Qt::NoBrush);
14490   setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14491   setSelectedBrush(Qt::NoBrush);
14492 
14493   setLineStyle(lsLine);
14494   setErrorType(etNone);
14495   setErrorBarSize(6);
14496   setErrorBarSkipSymbol(true);
14497   setChannelFillGraph(0);
14498   setAdaptiveSampling(true);
14499 }
14500 
~QCPGraph()14501 QCPGraph::~QCPGraph()
14502 {
14503   delete mData;
14504 }
14505 
14506 /*!
14507   Replaces the current data with the provided \a data.
14508 
14509   If \a copy is set to true, data points in \a data will only be copied. if false, the graph
14510   takes ownership of the passed data and replaces the internal data pointer with it. This is
14511   significantly faster than copying for large datasets.
14512 
14513   Alternatively, you can also access and modify the graph's data via the \ref data method, which
14514   returns a pointer to the internal \ref QCPDataMap.
14515 */
setData(QCPDataMap * data,bool copy)14516 void QCPGraph::setData(QCPDataMap *data, bool copy)
14517 {
14518   if (mData == data)
14519   {
14520     qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
14521     return;
14522   }
14523   if (copy)
14524   {
14525     *mData = *data;
14526   } else
14527   {
14528     delete mData;
14529     mData = data;
14530   }
14531 }
14532 
14533 /*! \overload
14534 
14535   Replaces the current data with the provided points in \a key and \a value pairs. The provided
14536   vectors should have equal length. Else, the number of added points will be the size of the
14537   smallest vector.
14538 */
setData(const QVector<double> & key,const QVector<double> & value)14539 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
14540 {
14541   mData->clear();
14542   int n = key.size();
14543   n = qMin(n, value.size());
14544   QCPData newData;
14545   for (int i=0; i<n; ++i)
14546   {
14547     newData.key = key[i];
14548     newData.value = value[i];
14549     mData->insertMulti(newData.key, newData);
14550   }
14551 }
14552 
14553 /*!
14554   Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14555   symmetrical value error of the data points are set to the values in \a valueError.
14556   For error bars to show appropriately, see \ref setErrorType.
14557   The provided vectors should have equal length. Else, the number of added points will be the size of the
14558   smallest vector.
14559 
14560   For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14561 */
setDataValueError(const QVector<double> & key,const QVector<double> & value,const QVector<double> & valueError)14562 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
14563 {
14564   mData->clear();
14565   int n = key.size();
14566   n = qMin(n, value.size());
14567   n = qMin(n, valueError.size());
14568   QCPData newData;
14569   for (int i=0; i<n; ++i)
14570   {
14571     newData.key = key[i];
14572     newData.value = value[i];
14573     newData.valueErrorMinus = valueError[i];
14574     newData.valueErrorPlus = valueError[i];
14575     mData->insertMulti(key[i], newData);
14576   }
14577 }
14578 
14579 /*!
14580   \overload
14581   Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14582   negative value error of the data points are set to the values in \a valueErrorMinus, the positive
14583   value error to \a valueErrorPlus.
14584   For error bars to show appropriately, see \ref setErrorType.
14585   The provided vectors should have equal length. Else, the number of added points will be the size of the
14586   smallest vector.
14587 */
setDataValueError(const QVector<double> & key,const QVector<double> & value,const QVector<double> & valueErrorMinus,const QVector<double> & valueErrorPlus)14588 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14589 {
14590   mData->clear();
14591   int n = key.size();
14592   n = qMin(n, value.size());
14593   n = qMin(n, valueErrorMinus.size());
14594   n = qMin(n, valueErrorPlus.size());
14595   QCPData newData;
14596   for (int i=0; i<n; ++i)
14597   {
14598     newData.key = key[i];
14599     newData.value = value[i];
14600     newData.valueErrorMinus = valueErrorMinus[i];
14601     newData.valueErrorPlus = valueErrorPlus[i];
14602     mData->insertMulti(key[i], newData);
14603   }
14604 }
14605 
14606 /*!
14607   Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14608   symmetrical key error of the data points are set to the values in \a keyError.
14609   For error bars to show appropriately, see \ref setErrorType.
14610   The provided vectors should have equal length. Else, the number of added points will be the size of the
14611   smallest vector.
14612 
14613   For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14614 */
setDataKeyError(const QVector<double> & key,const QVector<double> & value,const QVector<double> & keyError)14615 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
14616 {
14617   mData->clear();
14618   int n = key.size();
14619   n = qMin(n, value.size());
14620   n = qMin(n, keyError.size());
14621   QCPData newData;
14622   for (int i=0; i<n; ++i)
14623   {
14624     newData.key = key[i];
14625     newData.value = value[i];
14626     newData.keyErrorMinus = keyError[i];
14627     newData.keyErrorPlus = keyError[i];
14628     mData->insertMulti(key[i], newData);
14629   }
14630 }
14631 
14632 /*!
14633   \overload
14634   Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14635   negative key error of the data points are set to the values in \a keyErrorMinus, the positive
14636   key error to \a keyErrorPlus.
14637   For error bars to show appropriately, see \ref setErrorType.
14638   The provided vectors should have equal length. Else, the number of added points will be the size of the
14639   smallest vector.
14640 */
setDataKeyError(const QVector<double> & key,const QVector<double> & value,const QVector<double> & keyErrorMinus,const QVector<double> & keyErrorPlus)14641 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
14642 {
14643   mData->clear();
14644   int n = key.size();
14645   n = qMin(n, value.size());
14646   n = qMin(n, keyErrorMinus.size());
14647   n = qMin(n, keyErrorPlus.size());
14648   QCPData newData;
14649   for (int i=0; i<n; ++i)
14650   {
14651     newData.key = key[i];
14652     newData.value = value[i];
14653     newData.keyErrorMinus = keyErrorMinus[i];
14654     newData.keyErrorPlus = keyErrorPlus[i];
14655     mData->insertMulti(key[i], newData);
14656   }
14657 }
14658 
14659 /*!
14660   Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14661   symmetrical key and value errors of the data points are set to the values in \a keyError and \a valueError.
14662   For error bars to show appropriately, see \ref setErrorType.
14663   The provided vectors should have equal length. Else, the number of added points will be the size of the
14664   smallest vector.
14665 
14666   For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14667 */
setDataBothError(const QVector<double> & key,const QVector<double> & value,const QVector<double> & keyError,const QVector<double> & valueError)14668 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
14669 {
14670   mData->clear();
14671   int n = key.size();
14672   n = qMin(n, value.size());
14673   n = qMin(n, valueError.size());
14674   n = qMin(n, keyError.size());
14675   QCPData newData;
14676   for (int i=0; i<n; ++i)
14677   {
14678     newData.key = key[i];
14679     newData.value = value[i];
14680     newData.keyErrorMinus = keyError[i];
14681     newData.keyErrorPlus = keyError[i];
14682     newData.valueErrorMinus = valueError[i];
14683     newData.valueErrorPlus = valueError[i];
14684     mData->insertMulti(key[i], newData);
14685   }
14686 }
14687 
14688 /*!
14689   \overload
14690   Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14691   negative key and value errors of the data points are set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive
14692   key and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus.
14693   For error bars to show appropriately, see \ref setErrorType.
14694   The provided vectors should have equal length. Else, the number of added points will be the size of the
14695   smallest vector.
14696 */
setDataBothError(const QVector<double> & key,const QVector<double> & value,const QVector<double> & keyErrorMinus,const QVector<double> & keyErrorPlus,const QVector<double> & valueErrorMinus,const QVector<double> & valueErrorPlus)14697 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14698 {
14699   mData->clear();
14700   int n = key.size();
14701   n = qMin(n, value.size());
14702   n = qMin(n, valueErrorMinus.size());
14703   n = qMin(n, valueErrorPlus.size());
14704   n = qMin(n, keyErrorMinus.size());
14705   n = qMin(n, keyErrorPlus.size());
14706   QCPData newData;
14707   for (int i=0; i<n; ++i)
14708   {
14709     newData.key = key[i];
14710     newData.value = value[i];
14711     newData.keyErrorMinus = keyErrorMinus[i];
14712     newData.keyErrorPlus = keyErrorPlus[i];
14713     newData.valueErrorMinus = valueErrorMinus[i];
14714     newData.valueErrorPlus = valueErrorPlus[i];
14715     mData->insertMulti(key[i], newData);
14716   }
14717 }
14718 
14719 
14720 /*!
14721   Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to
14722   \ref lsNone and \ref setScatterStyle to the desired scatter style.
14723 
14724   \see setScatterStyle
14725 */
setLineStyle(LineStyle ls)14726 void QCPGraph::setLineStyle(LineStyle ls)
14727 {
14728   mLineStyle = ls;
14729 }
14730 
14731 /*!
14732   Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points
14733   are drawn (e.g. for line-only-plots with appropriate line style).
14734 
14735   \see QCPScatterStyle, setLineStyle
14736 */
setScatterStyle(const QCPScatterStyle & style)14737 void QCPGraph::setScatterStyle(const QCPScatterStyle &style)
14738 {
14739   mScatterStyle = style;
14740 }
14741 
14742 /*!
14743   Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data
14744   point. If you set \a errorType to something other than \ref etNone, make sure to actually pass
14745   error data via the specific setData functions along with the data points (e.g. \ref
14746   setDataValueError, \ref setDataKeyError, \ref setDataBothError).
14747 
14748   \see ErrorType
14749 */
setErrorType(ErrorType errorType)14750 void QCPGraph::setErrorType(ErrorType errorType)
14751 {
14752   mErrorType = errorType;
14753 }
14754 
14755 /*!
14756   Sets the pen with which the error bars will be drawn.
14757   \see setErrorBarSize, setErrorType
14758 */
setErrorPen(const QPen & pen)14759 void QCPGraph::setErrorPen(const QPen &pen)
14760 {
14761   mErrorPen = pen;
14762 }
14763 
14764 /*!
14765   Sets the width of the handles at both ends of an error bar in pixels.
14766 */
setErrorBarSize(double size)14767 void QCPGraph::setErrorBarSize(double size)
14768 {
14769   mErrorBarSize = size;
14770 }
14771 
14772 /*!
14773   If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but
14774   leave some free space around the symbol.
14775 
14776   This feature uses the current scatter size (\ref QCPScatterStyle::setSize) to determine the size
14777   of the area to leave blank. So when drawing Pixmaps as scatter points (\ref
14778   QCPScatterStyle::ssPixmap), the scatter size must be set manually to a value corresponding to the
14779   size of the Pixmap, if the error bars should leave gaps to its boundaries.
14780 
14781   \ref setErrorType, setErrorBarSize, setScatterStyle
14782 */
setErrorBarSkipSymbol(bool enabled)14783 void QCPGraph::setErrorBarSkipSymbol(bool enabled)
14784 {
14785   mErrorBarSkipSymbol = enabled;
14786 }
14787 
14788 /*!
14789   Sets the target graph for filling the area between this graph and \a targetGraph with the current
14790   brush (\ref setBrush).
14791 
14792   When \a targetGraph is set to 0, a normal graph fill to the zero-value-line will be shown. To
14793   disable any filling, set the brush to Qt::NoBrush.
14794 
14795   \see setBrush
14796 */
setChannelFillGraph(QCPGraph * targetGraph)14797 void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
14798 {
14799   // prevent setting channel target to this graph itself:
14800   if (targetGraph == this)
14801   {
14802     qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14803     mChannelFillGraph = 0;
14804     return;
14805   }
14806   // prevent setting channel target to a graph not in the plot:
14807   if (targetGraph && targetGraph->mParentPlot != mParentPlot)
14808   {
14809     qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14810     mChannelFillGraph = 0;
14811     return;
14812   }
14813 
14814   mChannelFillGraph = targetGraph;
14815 }
14816 
14817 /*!
14818   Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive
14819   sampling technique can drastically improve the replot performance for graphs with a larger number
14820   of points (e.g. above 10,000), without notably changing the appearance of the graph.
14821 
14822   By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive
14823   sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no
14824   disadvantage in almost all cases.
14825 
14826   \image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling"
14827 
14828   As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are
14829   reproduced reliably, as well as the overall shape of the data set. The replot time reduces
14830   dramatically though. This allows QCustomPlot to display large amounts of data in realtime.
14831 
14832   \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling"
14833 
14834   Care must be taken when using high-density scatter plots in combination with adaptive sampling.
14835   The adaptive sampling algorithm treats scatter plots more carefully than line plots which still
14836   gives a significant reduction of replot times, but not quite as much as for line plots. This is
14837   because scatter plots inherently need more data points to be preserved in order to still resemble
14838   the original, non-adaptive-sampling plot. As shown above, the results still aren't quite
14839   identical, as banding occurs for the outer data points. This is in fact intentional, such that
14840   the boundaries of the data cloud stay visible to the viewer. How strong the banding appears,
14841   depends on the point density, i.e. the number of points in the plot.
14842 
14843   For some situations with scatter plots it might thus be desirable to manually turn adaptive
14844   sampling off. For example, when saving the plot to disk. This can be achieved by setting \a
14845   enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled
14846   back to true afterwards.
14847 */
setAdaptiveSampling(bool enabled)14848 void QCPGraph::setAdaptiveSampling(bool enabled)
14849 {
14850   mAdaptiveSampling = enabled;
14851 }
14852 
14853 /*!
14854   Adds the provided data points in \a dataMap to the current data.
14855 
14856   Alternatively, you can also access and modify the graph's data via the \ref data method, which
14857   returns a pointer to the internal \ref QCPDataMap.
14858 
14859   \see removeData
14860 */
addData(const QCPDataMap & dataMap)14861 void QCPGraph::addData(const QCPDataMap &dataMap)
14862 {
14863   mData->unite(dataMap);
14864 }
14865 
14866 /*! \overload
14867   Adds the provided single data point in \a data to the current data.
14868 
14869   Alternatively, you can also access and modify the graph's data via the \ref data method, which
14870   returns a pointer to the internal \ref QCPDataMap.
14871 
14872   \see removeData
14873 */
addData(const QCPData & data)14874 void QCPGraph::addData(const QCPData &data)
14875 {
14876   mData->insertMulti(data.key, data);
14877 }
14878 
14879 /*! \overload
14880   Adds the provided single data point as \a key and \a value pair to the current data.
14881 
14882   Alternatively, you can also access and modify the graph's data via the \ref data method, which
14883   returns a pointer to the internal \ref QCPDataMap.
14884 
14885   \see removeData
14886 */
addData(double key,double value)14887 void QCPGraph::addData(double key, double value)
14888 {
14889   QCPData newData;
14890   newData.key = key;
14891   newData.value = value;
14892   mData->insertMulti(newData.key, newData);
14893 }
14894 
14895 /*! \overload
14896   Adds the provided data points as \a key and \a value pairs to the current data.
14897 
14898   Alternatively, you can also access and modify the graph's data via the \ref data method, which
14899   returns a pointer to the internal \ref QCPDataMap.
14900 
14901   \see removeData
14902 */
addData(const QVector<double> & keys,const QVector<double> & values)14903 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14904 {
14905   int n = qMin(keys.size(), values.size());
14906   QCPData newData;
14907   for (int i=0; i<n; ++i)
14908   {
14909     newData.key = keys[i];
14910     newData.value = values[i];
14911     mData->insertMulti(newData.key, newData);
14912   }
14913 }
14914 
14915 /*!
14916   Removes all data points with keys smaller than \a key.
14917   \see addData, clearData
14918 */
removeDataBefore(double key)14919 void QCPGraph::removeDataBefore(double key)
14920 {
14921   auto it = mData->begin();
14922   while (it != mData->end() && it.key() < key)
14923     it = mData->erase(it);
14924 }
14925 
14926 /*!
14927   Removes all data points with keys greater than \a key.
14928   \see addData, clearData
14929 */
removeDataAfter(double key)14930 void QCPGraph::removeDataAfter(double key)
14931 {
14932   if (mData->isEmpty()) return;
14933   auto it = mData->upperBound(key);
14934   while (it != mData->end())
14935     it = mData->erase(it);
14936 }
14937 
14938 /*!
14939   Removes all data points with keys between \a fromKey and \a toKey.
14940   if \a fromKey is greater or equal to \a toKey, the function does nothing. To remove
14941   a single data point with known key, use \ref removeData(double key).
14942 
14943   \see addData, clearData
14944 */
removeData(double fromKey,double toKey)14945 void QCPGraph::removeData(double fromKey, double toKey)
14946 {
14947   if (fromKey >= toKey || mData->isEmpty()) return;
14948   auto it = mData->upperBound(fromKey);
14949   auto itEnd = mData->upperBound(toKey);
14950   while (it != itEnd)
14951     it = mData->erase(it);
14952 }
14953 
14954 /*! \overload
14955 
14956   Removes a single data point at \a key. If the position is not known with absolute precision,
14957   consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval around
14958   the suspected position, depeding on the precision with which the key is known.
14959 
14960   \see addData, clearData
14961 */
removeData(double key)14962 void QCPGraph::removeData(double key)
14963 {
14964   mData->remove(key);
14965 }
14966 
14967 /*!
14968   Removes all data points.
14969   \see removeData, removeDataAfter, removeDataBefore
14970 */
clearData()14971 void QCPGraph::clearData()
14972 {
14973   mData->clear();
14974 }
14975 
14976 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const14977 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
14978 {
14979   Q_UNUSED(details)
14980   if ((onlySelectable && !mSelectable) || mData->isEmpty())
14981     return -1;
14982   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14983 
14984   if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
14985     return pointDistance(pos);
14986   else
14987     return -1;
14988 }
14989 
14990 /*! \overload
14991 
14992   Allows to define whether error bars are taken into consideration when determining the new axis
14993   range.
14994 
14995   \see rescaleKeyAxis, rescaleValueAxis, QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
14996 */
rescaleAxes(bool onlyEnlarge,bool includeErrorBars) const14997 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
14998 {
14999   rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15000   rescaleValueAxis(onlyEnlarge, includeErrorBars);
15001 }
15002 
15003 /*! \overload
15004 
15005   Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken into consideration
15006   when determining the new axis range.
15007 
15008   \see rescaleAxes, QCPAbstractPlottable::rescaleKeyAxis
15009 */
rescaleKeyAxis(bool onlyEnlarge,bool includeErrorBars) const15010 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15011 {
15012   // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15013   // that getKeyRange is passed the includeErrorBars value.
15014   if (mData->isEmpty()) return;
15015 
15016   QCPAxis *keyAxis = mKeyAxis.data();
15017   if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15018 
15019   SignDomain signDomain = sdBoth;
15020   if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15021     signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15022 
15023   bool foundRange;
15024   QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15025 
15026   if (foundRange)
15027   {
15028     if (onlyEnlarge)
15029     {
15030       if (keyAxis->range().lower < newRange.lower)
15031         newRange.lower = keyAxis->range().lower;
15032       if (keyAxis->range().upper > newRange.upper)
15033         newRange.upper = keyAxis->range().upper;
15034     }
15035     keyAxis->setRange(newRange);
15036   }
15037 }
15038 
15039 /*! \overload
15040 
15041   Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken into consideration
15042   when determining the new axis range.
15043 
15044   \see rescaleAxes, QCPAbstractPlottable::rescaleValueAxis
15045 */
rescaleValueAxis(bool onlyEnlarge,bool includeErrorBars) const15046 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15047 {
15048   // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15049   // is that getValueRange is passed the includeErrorBars value.
15050   if (mData->isEmpty()) return;
15051 
15052   QCPAxis *valueAxis = mValueAxis.data();
15053   if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15054 
15055   SignDomain signDomain = sdBoth;
15056   if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15057     signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15058 
15059   bool foundRange;
15060   QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15061 
15062   if (foundRange)
15063   {
15064     if (onlyEnlarge)
15065     {
15066       if (valueAxis->range().lower < newRange.lower)
15067         newRange.lower = valueAxis->range().lower;
15068       if (valueAxis->range().upper > newRange.upper)
15069         newRange.upper = valueAxis->range().upper;
15070     }
15071     valueAxis->setRange(newRange);
15072   }
15073 }
15074 
15075 /* inherits documentation from base class */
draw(QCPPainter * painter)15076 void QCPGraph::draw(QCPPainter *painter)
15077 {
15078   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15079   if (mKeyAxis->range().size() <= 0 || mData->isEmpty()) return;
15080   if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15081 
15082   // allocate line and (if necessary) point vectors:
15083   QVector<QPointF> *lineData = new QVector<QPointF>;
15084   QVector<QCPData> *scatterData = 0;
15085   if (!mScatterStyle.isNone())
15086     scatterData = new QVector<QCPData>;
15087 
15088   // fill vectors with data appropriate to plot style:
15089   getPlotData(lineData, scatterData);
15090 
15091   // check data validity if flag set:
15092 #ifdef QCUSTOMPLOT_CHECK_DATA
15093   for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
15094   {
15095     if (QCP::isInvalidData(it.value().key, it.value().value) ||
15096         QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15097         QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15098       qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15099   }
15100 #endif
15101 
15102   // draw fill of graph:
15103   if (mLineStyle != lsNone)
15104     drawFill(painter, lineData);
15105 
15106   // draw line:
15107   if (mLineStyle == lsImpulse)
15108     drawImpulsePlot(painter, lineData);
15109   else if (mLineStyle != lsNone)
15110     drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15111 
15112   // draw scatters:
15113   if (scatterData)
15114     drawScatterPlot(painter, scatterData);
15115 
15116   // free allocated line and point vectors:
15117   delete lineData;
15118   if (scatterData)
15119     delete scatterData;
15120 }
15121 
15122 /* inherits documentation from base class */
drawLegendIcon(QCPPainter * painter,const QRectF & rect) const15123 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15124 {
15125   // draw fill:
15126   if (mBrush.style() != Qt::NoBrush)
15127   {
15128     applyFillAntialiasingHint(painter);
15129     painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15130   }
15131   // draw line vertically centered:
15132   if (mLineStyle != lsNone)
15133   {
15134     applyDefaultAntialiasingHint(painter);
15135     painter->setPen(mPen);
15136     painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
15137   }
15138   // draw scatter symbol:
15139   if (!mScatterStyle.isNone())
15140   {
15141     applyScattersAntialiasingHint(painter);
15142     // scale scatter pixmap if it's too large to fit in legend icon rect:
15143     if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15144     {
15145       QCPScatterStyle scaledStyle(mScatterStyle);
15146       scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15147       scaledStyle.applyTo(painter, mPen);
15148       scaledStyle.drawShape(painter, QRectF(rect).center());
15149     } else
15150     {
15151       mScatterStyle.applyTo(painter, mPen);
15152       mScatterStyle.drawShape(painter, QRectF(rect).center());
15153     }
15154   }
15155 }
15156 
15157 /*! \internal
15158 
15159   This function branches out to the line style specific "get(...)PlotData" functions, according to
15160   the line style of the graph.
15161 
15162   \a lineData will be filled with raw points that will be drawn with the according draw functions,
15163   e.g. \ref drawLinePlot and \ref drawImpulsePlot. These aren't necessarily the original data
15164   points, since for step plots for example, additional points are needed for drawing lines that
15165   make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
15166   untouched.
15167 
15168   \a scatterData will be filled with the original data points so \ref drawScatterPlot can draw the
15169   scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is
15170   \ref QCPScatterStyle::ssNone, pass 0 as \a scatterData, and this step will be skipped.
15171 
15172   \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
15173   getStepCenterPlotData, getImpulsePlotData
15174 */
getPlotData(QVector<QPointF> * lineData,QVector<QCPData> * scatterData) const15175 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15176 {
15177   switch(mLineStyle)
15178   {
15179     case lsNone: getScatterPlotData(scatterData); break;
15180     case lsLine: getLinePlotData(lineData, scatterData); break;
15181     case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15182     case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15183     case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15184     case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15185   }
15186 }
15187 
15188 /*! \internal
15189 
15190   If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone,
15191   this function serves at providing the visible data points in \a scatterData, so the \ref
15192   drawScatterPlot function can draw the scatter points accordingly.
15193 
15194   If line style is not \ref lsNone, this function is not called and the data for the scatter points
15195   are (if needed) calculated inside the corresponding other "get(...)PlotData" functions.
15196 
15197   \see drawScatterPlot
15198 */
getScatterPlotData(QVector<QCPData> * scatterData) const15199 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15200 {
15201   getPreparedData(0, scatterData);
15202 }
15203 
15204 /*! \internal
15205 
15206   Places the raw data points needed for a normal linearly connected graph in \a linePixelData.
15207 
15208   As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15209   points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15210   disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15211   scatterData, and the function will skip filling the vector.
15212 
15213   \see drawLinePlot
15214 */
getLinePlotData(QVector<QPointF> * linePixelData,QVector<QCPData> * scatterData) const15215 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15216 {
15217   QCPAxis *keyAxis = mKeyAxis.data();
15218   QCPAxis *valueAxis = mValueAxis.data();
15219   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15220   if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15221 
15222   QVector<QCPData> lineData;
15223   getPreparedData(&lineData, scatterData);
15224   linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15225   linePixelData->resize(lineData.size());
15226 
15227   // transform lineData points to pixels:
15228   if (keyAxis->orientation() == Qt::Vertical)
15229   {
15230     for (int i=0; i<lineData.size(); ++i)
15231     {
15232       (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15233       (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15234     }
15235   } else // key axis is horizontal
15236   {
15237     for (int i=0; i<lineData.size(); ++i)
15238     {
15239       (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15240       (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15241     }
15242   }
15243 }
15244 
15245 /*!
15246   \internal
15247   Places the raw data points needed for a step plot with left oriented steps in \a lineData.
15248 
15249   As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15250   points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15251   disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15252   scatterData, and the function will skip filling the vector.
15253 
15254   \see drawLinePlot
15255 */
getStepLeftPlotData(QVector<QPointF> * linePixelData,QVector<QCPData> * scatterData) const15256 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15257 {
15258   QCPAxis *keyAxis = mKeyAxis.data();
15259   QCPAxis *valueAxis = mValueAxis.data();
15260   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15261   if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15262 
15263   QVector<QCPData> lineData;
15264   getPreparedData(&lineData, scatterData);
15265   linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15266   linePixelData->resize(lineData.size()*2);
15267 
15268   // calculate steps from lineData and transform to pixel coordinates:
15269   if (keyAxis->orientation() == Qt::Vertical)
15270   {
15271     double lastValue = valueAxis->coordToPixel(lineData.first().value);
15272     double key;
15273     for (int i=0; i<lineData.size(); ++i)
15274     {
15275       key = keyAxis->coordToPixel(lineData.at(i).key);
15276       (*linePixelData)[i*2+0].setX(lastValue);
15277       (*linePixelData)[i*2+0].setY(key);
15278       lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15279       (*linePixelData)[i*2+1].setX(lastValue);
15280       (*linePixelData)[i*2+1].setY(key);
15281     }
15282   } else // key axis is horizontal
15283   {
15284     double lastValue = valueAxis->coordToPixel(lineData.first().value);
15285     double key;
15286     for (int i=0; i<lineData.size(); ++i)
15287     {
15288       key = keyAxis->coordToPixel(lineData.at(i).key);
15289       (*linePixelData)[i*2+0].setX(key);
15290       (*linePixelData)[i*2+0].setY(lastValue);
15291       lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15292       (*linePixelData)[i*2+1].setX(key);
15293       (*linePixelData)[i*2+1].setY(lastValue);
15294     }
15295   }
15296 }
15297 
15298 /*!
15299   \internal
15300   Places the raw data points needed for a step plot with right oriented steps in \a lineData.
15301 
15302   As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15303   points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15304   disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15305   scatterData, and the function will skip filling the vector.
15306 
15307   \see drawLinePlot
15308 */
getStepRightPlotData(QVector<QPointF> * linePixelData,QVector<QCPData> * scatterData) const15309 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15310 {
15311   QCPAxis *keyAxis = mKeyAxis.data();
15312   QCPAxis *valueAxis = mValueAxis.data();
15313   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15314   if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15315 
15316   QVector<QCPData> lineData;
15317   getPreparedData(&lineData, scatterData);
15318   linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15319   linePixelData->resize(lineData.size()*2);
15320 
15321   // calculate steps from lineData and transform to pixel coordinates:
15322   if (keyAxis->orientation() == Qt::Vertical)
15323   {
15324     double lastKey = keyAxis->coordToPixel(lineData.first().key);
15325     double value;
15326     for (int i=0; i<lineData.size(); ++i)
15327     {
15328       value = valueAxis->coordToPixel(lineData.at(i).value);
15329       (*linePixelData)[i*2+0].setX(value);
15330       (*linePixelData)[i*2+0].setY(lastKey);
15331       lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15332       (*linePixelData)[i*2+1].setX(value);
15333       (*linePixelData)[i*2+1].setY(lastKey);
15334     }
15335   } else // key axis is horizontal
15336   {
15337     double lastKey = keyAxis->coordToPixel(lineData.first().key);
15338     double value;
15339     for (int i=0; i<lineData.size(); ++i)
15340     {
15341       value = valueAxis->coordToPixel(lineData.at(i).value);
15342       (*linePixelData)[i*2+0].setX(lastKey);
15343       (*linePixelData)[i*2+0].setY(value);
15344       lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15345       (*linePixelData)[i*2+1].setX(lastKey);
15346       (*linePixelData)[i*2+1].setY(value);
15347     }
15348   }
15349 }
15350 
15351 /*!
15352   \internal
15353   Places the raw data points needed for a step plot with centered steps in \a lineData.
15354 
15355   As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15356   points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15357   disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15358   scatterData, and the function will skip filling the vector.
15359 
15360   \see drawLinePlot
15361 */
getStepCenterPlotData(QVector<QPointF> * linePixelData,QVector<QCPData> * scatterData) const15362 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15363 {
15364   QCPAxis *keyAxis = mKeyAxis.data();
15365   QCPAxis *valueAxis = mValueAxis.data();
15366   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15367   if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15368 
15369   QVector<QCPData> lineData;
15370   getPreparedData(&lineData, scatterData);
15371   linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15372   linePixelData->resize(lineData.size()*2);
15373   // calculate steps from lineData and transform to pixel coordinates:
15374   if (keyAxis->orientation() == Qt::Vertical)
15375   {
15376     double lastKey = keyAxis->coordToPixel(lineData.first().key);
15377     double lastValue = valueAxis->coordToPixel(lineData.first().value);
15378     double key;
15379     (*linePixelData)[0].setX(lastValue);
15380     (*linePixelData)[0].setY(lastKey);
15381     for (int i=1; i<lineData.size(); ++i)
15382     {
15383       key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15384       (*linePixelData)[i*2-1].setX(lastValue);
15385       (*linePixelData)[i*2-1].setY(key);
15386       lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15387       lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15388       (*linePixelData)[i*2+0].setX(lastValue);
15389       (*linePixelData)[i*2+0].setY(key);
15390     }
15391     (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15392     (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15393   } else // key axis is horizontal
15394   {
15395     double lastKey = keyAxis->coordToPixel(lineData.first().key);
15396     double lastValue = valueAxis->coordToPixel(lineData.first().value);
15397     double key;
15398     (*linePixelData)[0].setX(lastKey);
15399     (*linePixelData)[0].setY(lastValue);
15400     for (int i=1; i<lineData.size(); ++i)
15401     {
15402       key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15403       (*linePixelData)[i*2-1].setX(key);
15404       (*linePixelData)[i*2-1].setY(lastValue);
15405       lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15406       lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15407       (*linePixelData)[i*2+0].setX(key);
15408       (*linePixelData)[i*2+0].setY(lastValue);
15409     }
15410     (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15411     (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15412   }
15413 }
15414 
15415 /*!
15416   \internal
15417   Places the raw data points needed for an impulse plot in \a lineData.
15418 
15419   As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15420   points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15421   disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15422   scatterData, and the function will skip filling the vector.
15423 
15424   \see drawImpulsePlot
15425 */
getImpulsePlotData(QVector<QPointF> * linePixelData,QVector<QCPData> * scatterData) const15426 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15427 {
15428   QCPAxis *keyAxis = mKeyAxis.data();
15429   QCPAxis *valueAxis = mValueAxis.data();
15430   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15431   if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15432 
15433   QVector<QCPData> lineData;
15434   getPreparedData(&lineData, scatterData);
15435   linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15436 
15437   // transform lineData points to pixels:
15438   if (keyAxis->orientation() == Qt::Vertical)
15439   {
15440     double zeroPointX = valueAxis->coordToPixel(0);
15441     double key;
15442     for (int i=0; i<lineData.size(); ++i)
15443     {
15444       key = keyAxis->coordToPixel(lineData.at(i).key);
15445       (*linePixelData)[i*2+0].setX(zeroPointX);
15446       (*linePixelData)[i*2+0].setY(key);
15447       (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15448       (*linePixelData)[i*2+1].setY(key);
15449     }
15450   } else // key axis is horizontal
15451   {
15452     double zeroPointY = valueAxis->coordToPixel(0);
15453     double key;
15454     for (int i=0; i<lineData.size(); ++i)
15455     {
15456       key = keyAxis->coordToPixel(lineData.at(i).key);
15457       (*linePixelData)[i*2+0].setX(key);
15458       (*linePixelData)[i*2+0].setY(zeroPointY);
15459       (*linePixelData)[i*2+1].setX(key);
15460       (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15461     }
15462   }
15463 }
15464 
15465 /*! \internal
15466 
15467   Draws the fill of the graph with the specified brush.
15468 
15469   If the fill is a normal fill towards the zero-value-line, only the \a lineData is required (and
15470   two extra points at the zero-value-line, which are added by \ref addFillBasePoints and removed by
15471   \ref removeFillBasePoints after the fill drawing is done).
15472 
15473   If the fill is a channel fill between this QCPGraph and another QCPGraph (mChannelFillGraph), the
15474   more complex polygon is calculated with the \ref getChannelFillPolygon function.
15475 
15476   \see drawLinePlot
15477 */
drawFill(QCPPainter * painter,QVector<QPointF> * lineData) const15478 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15479 {
15480   if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15481   if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15482 
15483   applyFillAntialiasingHint(painter);
15484   if (!mChannelFillGraph)
15485   {
15486     // draw base fill under graph, fill goes all the way to the zero-value-line:
15487     addFillBasePoints(lineData);
15488     painter->setPen(Qt::NoPen);
15489     painter->setBrush(mainBrush());
15490     painter->drawPolygon(QPolygonF(*lineData));
15491     removeFillBasePoints(lineData);
15492   } else
15493   {
15494     // draw channel fill between this graph and mChannelFillGraph:
15495     painter->setPen(Qt::NoPen);
15496     painter->setBrush(mainBrush());
15497     painter->drawPolygon(getChannelFillPolygon(lineData));
15498   }
15499 }
15500 
15501 /*! \internal
15502 
15503   Draws scatter symbols at every data point passed in \a scatterData. scatter symbols are independent
15504   of the line style and are always drawn if the scatter style's shape is not \ref
15505   QCPScatterStyle::ssNone. Hence, the \a scatterData vector is outputted by all "get(...)PlotData"
15506   functions, together with the (line style dependent) line data.
15507 
15508   \see drawLinePlot, drawImpulsePlot
15509 */
drawScatterPlot(QCPPainter * painter,QVector<QCPData> * scatterData) const15510 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
15511 {
15512   QCPAxis *keyAxis = mKeyAxis.data();
15513   QCPAxis *valueAxis = mValueAxis.data();
15514   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15515 
15516   // draw error bars:
15517   if (mErrorType != etNone)
15518   {
15519     applyErrorBarsAntialiasingHint(painter);
15520     painter->setPen(mErrorPen);
15521     if (keyAxis->orientation() == Qt::Vertical)
15522     {
15523       for (int i=0; i<scatterData->size(); ++i)
15524         drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
15525     } else
15526     {
15527       for (int i=0; i<scatterData->size(); ++i)
15528         drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
15529     }
15530   }
15531 
15532   // draw scatter point symbols:
15533   applyScattersAntialiasingHint(painter);
15534   mScatterStyle.applyTo(painter, mPen);
15535   if (keyAxis->orientation() == Qt::Vertical)
15536   {
15537     for (int i=0; i<scatterData->size(); ++i)
15538       if (!qIsNaN(scatterData->at(i).value))
15539         mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
15540   } else
15541   {
15542     for (int i=0; i<scatterData->size(); ++i)
15543       if (!qIsNaN(scatterData->at(i).value))
15544         mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
15545   }
15546 }
15547 
15548 /*!  \internal
15549 
15550   Draws line graphs from the provided data. It connects all points in \a lineData, which was
15551   created by one of the "get(...)PlotData" functions for line styles that require simple line
15552   connections between the point vector they create. These are for example \ref getLinePlotData,
15553   \ref getStepLeftPlotData, \ref getStepRightPlotData and \ref getStepCenterPlotData.
15554 
15555   \see drawScatterPlot, drawImpulsePlot
15556 */
drawLinePlot(QCPPainter * painter,QVector<QPointF> * lineData) const15557 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15558 {
15559   // draw line of graph:
15560   if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15561   {
15562     applyDefaultAntialiasingHint(painter);
15563     painter->setPen(mainPen());
15564     painter->setBrush(Qt::NoBrush);
15565 
15566     /* Draws polyline in batches, currently not used:
15567     int p = 0;
15568     while (p < lineData->size())
15569     {
15570       int batch = qMin(25, lineData->size()-p);
15571       if (p != 0)
15572       {
15573         ++batch;
15574         --p; // to draw the connection lines between two batches
15575       }
15576       painter->drawPolyline(lineData->constData()+p, batch);
15577       p += batch;
15578     }
15579     */
15580 
15581     // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
15582     if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
15583         painter->pen().style() == Qt::SolidLine &&
15584         !painter->modes().testFlag(QCPPainter::pmVectorized) &&
15585         !painter->modes().testFlag(QCPPainter::pmNoCaching))
15586     {
15587       int i = 0;
15588       bool lastIsNan = false;
15589       const int lineDataSize = lineData->size();
15590       while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
15591         ++i;
15592       ++i; // because drawing works in 1 point retrospect
15593       while (i < lineDataSize)
15594       {
15595         if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15596         {
15597           if (!lastIsNan)
15598             painter->drawLine(lineData->at(i-1), lineData->at(i));
15599           else
15600             lastIsNan = false;
15601         } else
15602           lastIsNan = true;
15603         ++i;
15604       }
15605     } else
15606     {
15607       int segmentStart = 0;
15608       int i = 0;
15609       const int lineDataSize = lineData->size();
15610       while (i < lineDataSize)
15611      {
15612         if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()) || qIsInf(lineData->at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block
15613         {
15614           painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
15615           segmentStart = i+1;
15616         }
15617         ++i;
15618       }
15619       // draw last segment:
15620       painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
15621     }
15622   }
15623 }
15624 
15625 /*! \internal
15626 
15627   Draws impulses from the provided data, i.e. it connects all line pairs in \a lineData, which was
15628   created by \ref getImpulsePlotData.
15629 
15630   \see drawScatterPlot, drawLinePlot
15631 */
drawImpulsePlot(QCPPainter * painter,QVector<QPointF> * lineData) const15632 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15633 {
15634   // draw impulses:
15635   if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15636   {
15637     applyDefaultAntialiasingHint(painter);
15638     QPen pen = mainPen();
15639     pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
15640     painter->setPen(pen);
15641     painter->setBrush(Qt::NoBrush);
15642     painter->drawLines(*lineData);
15643   }
15644 }
15645 
15646 /*! \internal
15647 
15648   Returns the \a lineData and \a scatterData that need to be plotted for this graph taking into
15649   consideration the current axis ranges and, if \ref setAdaptiveSampling is enabled, local point
15650   densities.
15651 
15652   0 may be passed as \a lineData or \a scatterData to indicate that the respective dataset isn't
15653   needed. For example, if the scatter style (\ref setScatterStyle) is \ref QCPScatterStyle::ssNone, \a
15654   scatterData should be 0 to prevent unnecessary calculations.
15655 
15656   This method is used by the various "get(...)PlotData" methods to get the basic working set of data.
15657 */
getPreparedData(QVector<QCPData> * lineData,QVector<QCPData> * scatterData) const15658 void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
15659 {
15660   QCPAxis *keyAxis = mKeyAxis.data();
15661   QCPAxis *valueAxis = mValueAxis.data();
15662   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15663   // get visible data range:
15664   QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
15665   getVisibleDataBounds(lower, upper);
15666   if (lower == mData->constEnd() || upper == mData->constEnd())
15667     return;
15668 
15669   // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
15670   int maxCount = std::numeric_limits<int>::max();
15671   if (mAdaptiveSampling)
15672   {
15673     int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
15674     maxCount = 2*keyPixelSpan+2;
15675   }
15676   int dataCount = countDataInBounds(lower, upper, maxCount);
15677 
15678   if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
15679   {
15680     if (lineData)
15681     {
15682       auto it = lower;
15683       auto upperEnd = upper+1;
15684       double minValue = it.value().value;
15685       double maxValue = it.value().value;
15686       auto currentIntervalFirstPoint = it;
15687       int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15688       int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15689       double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15690       double lastIntervalEndKey = currentIntervalStartKey;
15691       double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15692       bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15693       int intervalDataCount = 1;
15694       ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15695       while (it != upperEnd)
15696       {
15697         if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
15698         {
15699           if (it.value().value < minValue)
15700             minValue = it.value().value;
15701           else if (it.value().value > maxValue)
15702             maxValue = it.value().value;
15703           ++intervalDataCount;
15704         } else // new pixel interval started
15705         {
15706           if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15707           {
15708             if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
15709               lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15710             lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15711             lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15712             if (it.key() > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point
15713               lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
15714           } else
15715             lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15716           lastIntervalEndKey = (it-1).value().key;
15717           minValue = it.value().value;
15718           maxValue = it.value().value;
15719           currentIntervalFirstPoint = it;
15720           currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15721           if (keyEpsilonVariable)
15722             keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15723           intervalDataCount = 1;
15724         }
15725         ++it;
15726       }
15727       // handle last interval:
15728       if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15729       {
15730         if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
15731           lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15732         lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15733         lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15734       } else
15735         lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15736     }
15737 
15738     if (scatterData)
15739     {
15740       double valueMaxRange = valueAxis->range().upper;
15741       double valueMinRange = valueAxis->range().lower;
15742       auto it = lower;
15743       auto upperEnd = upper+1;
15744       double minValue = it.value().value;
15745       double maxValue = it.value().value;
15746       auto minValueIt = it;
15747       auto maxValueIt = it;
15748       auto currentIntervalStart = it;
15749       int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15750       int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15751       double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15752       double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15753       bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15754       int intervalDataCount = 1;
15755       ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15756       while (it != upperEnd)
15757       {
15758         if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
15759         {
15760           if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15761           {
15762             minValue = it.value().value;
15763             minValueIt = it;
15764           } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15765           {
15766             maxValue = it.value().value;
15767             maxValueIt = it;
15768           }
15769           ++intervalDataCount;
15770         } else // new pixel started
15771         {
15772           if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15773           {
15774             // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15775             double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15776             int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15777             auto intervalIt = currentIntervalStart;
15778             int c = 0;
15779             while (intervalIt != it)
15780             {
15781               if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15782                 scatterData->append(intervalIt.value());
15783               ++c;
15784               ++intervalIt;
15785             }
15786           } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15787             scatterData->append(currentIntervalStart.value());
15788           minValue = it.value().value;
15789           maxValue = it.value().value;
15790           currentIntervalStart = it;
15791           currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15792           if (keyEpsilonVariable)
15793             keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15794           intervalDataCount = 1;
15795         }
15796         ++it;
15797       }
15798       // handle last interval:
15799       if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15800       {
15801         // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15802         double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15803         int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15804         auto intervalIt = currentIntervalStart;
15805         int c = 0;
15806         while (intervalIt != it)
15807         {
15808           if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15809             scatterData->append(intervalIt.value());
15810           ++c;
15811           ++intervalIt;
15812         }
15813       } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15814         scatterData->append(currentIntervalStart.value());
15815     }
15816   } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
15817   {
15818     QVector<QCPData> *dataVector = 0;
15819     if (lineData)
15820       dataVector = lineData;
15821     else if (scatterData)
15822       dataVector = scatterData;
15823     if (dataVector)
15824     {
15825       auto it = lower;
15826       auto upperEnd = upper+1;
15827       dataVector->reserve(dataCount+2); // +2 for possible fill end points
15828       while (it != upperEnd)
15829       {
15830         dataVector->append(it.value());
15831         ++it;
15832       }
15833     }
15834     if (lineData && scatterData)
15835       *scatterData = *dataVector;
15836   }
15837 }
15838 
15839 /*!  \internal
15840 
15841   called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
15842   point. \a x and \a y pixel positions of the data point are passed since they are already known in
15843   pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
15844   data is therefore only used for the errors, not key and value.
15845 */
drawError(QCPPainter * painter,double x,double y,const QCPData & data) const15846 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
15847 {
15848   if (qIsNaN(data.value))
15849     return;
15850   QCPAxis *keyAxis = mKeyAxis.data();
15851   QCPAxis *valueAxis = mValueAxis.data();
15852   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15853 
15854   double a, b; // positions of error bar bounds in pixels
15855   double barWidthHalf = mErrorBarSize*0.5;
15856   double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
15857 
15858   if (keyAxis->orientation() == Qt::Vertical)
15859   {
15860     // draw key error vertically and value error horizontally
15861     if (mErrorType == etKey || mErrorType == etBoth)
15862     {
15863       a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15864       b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15865       if (keyAxis->rangeReversed())
15866         qSwap(a,b);
15867       // draw spine:
15868       if (mErrorBarSkipSymbol)
15869       {
15870         if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15871           painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15872         if (y-b > skipSymbolMargin)
15873           painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15874       } else
15875         painter->drawLine(QLineF(x, a, x, b));
15876       // draw handles:
15877       painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15878       painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15879     }
15880     if (mErrorType == etValue || mErrorType == etBoth)
15881     {
15882       a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15883       b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15884       if (valueAxis->rangeReversed())
15885         qSwap(a,b);
15886       // draw spine:
15887       if (mErrorBarSkipSymbol)
15888       {
15889         if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15890           painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15891         if (b-x > skipSymbolMargin)
15892           painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15893       } else
15894         painter->drawLine(QLineF(a, y, b, y));
15895       // draw handles:
15896       painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15897       painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15898     }
15899   } else // mKeyAxis->orientation() is Qt::Horizontal
15900   {
15901     // draw value error vertically and key error horizontally
15902     if (mErrorType == etKey || mErrorType == etBoth)
15903     {
15904       a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15905       b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15906       if (keyAxis->rangeReversed())
15907         qSwap(a,b);
15908       // draw spine:
15909       if (mErrorBarSkipSymbol)
15910       {
15911         if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15912           painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15913         if (b-x > skipSymbolMargin)
15914           painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15915       } else
15916         painter->drawLine(QLineF(a, y, b, y));
15917       // draw handles:
15918       painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15919       painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15920     }
15921     if (mErrorType == etValue || mErrorType == etBoth)
15922     {
15923       a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15924       b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15925       if (valueAxis->rangeReversed())
15926         qSwap(a,b);
15927       // draw spine:
15928       if (mErrorBarSkipSymbol)
15929       {
15930         if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15931           painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15932         if (y-b > skipSymbolMargin)
15933           painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15934       } else
15935         painter->drawLine(QLineF(x, a, x, b));
15936       // draw handles:
15937       painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15938       painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15939     }
15940   }
15941 }
15942 
15943 /*!  \internal
15944 
15945   called by \ref getPreparedData to determine which data (key) range is visible at the current key
15946   axis range setting, so only that needs to be processed.
15947 
15948   \a lower returns an iterator to the lowest data point that needs to be taken into account when
15949   plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
15950   lower may still be just outside the visible range.
15951 
15952   \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
15953   just outside of the visible range.
15954 
15955   if the graph contains no data, both \a lower and \a upper point to constEnd.
15956 */
getVisibleDataBounds(QCPDataMap::const_iterator & lower,QCPDataMap::const_iterator & upper) const15957 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
15958 {
15959   if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15960   if (mData->isEmpty())
15961   {
15962     lower = mData->constEnd();
15963     upper = mData->constEnd();
15964     return;
15965   }
15966 
15967   // get visible data range as QMap iterators
15968   auto lbound = mData->lowerBound(mKeyAxis->range().lower);
15969   auto ubound = mData->upperBound(mKeyAxis->range().upper);
15970   bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
15971   bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
15972 
15973   lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
15974   upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
15975 }
15976 
15977 /*!  \internal
15978 
15979   Counts the number of data points between \a lower and \a upper (including them), up to a maximum
15980   of \a maxCount.
15981 
15982   This function is used by \ref getPreparedData to determine whether adaptive sampling shall be
15983   used (if enabled via \ref setAdaptiveSampling) or not. This is also why counting of data points
15984   only needs to be done until \a maxCount is reached, which should be set to the number of data
15985   points at which adaptive sampling sets in.
15986 */
countDataInBounds(const QCPDataMap::const_iterator & lower,const QCPDataMap::const_iterator & upper,int maxCount) const15987 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
15988 {
15989   if (upper == mData->constEnd() && lower == mData->constEnd())
15990     return 0;
15991   auto it = lower;
15992   int count = 1;
15993   while (it != upper && count < maxCount)
15994   {
15995     ++it;
15996     ++count;
15997   }
15998   return count;
15999 }
16000 
16001 /*! \internal
16002 
16003   The line data vector generated by e.g. getLinePlotData contains only the line that connects the
16004   data points. If the graph needs to be filled, two additional points need to be added at the
16005   value-zero-line in the lower and upper key positions of the graph. This function calculates these
16006   points and adds them to the end of \a lineData. Since the fill is typically drawn before the line
16007   stroke, these added points need to be removed again after the fill is done, with the
16008   removeFillBasePoints function.
16009 
16010   The expanding of \a lineData by two points will not cause unnecessary memory reallocations,
16011   because the data vector generation functions (getLinePlotData etc.) reserve two extra points when
16012   they allocate memory for \a lineData.
16013 
16014   \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16015 */
addFillBasePoints(QVector<QPointF> * lineData) const16016 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
16017 {
16018   if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16019   if (!lineData) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; }
16020   if (lineData->isEmpty()) return;
16021 
16022   // append points that close the polygon fill at the key axis:
16023   if (mKeyAxis->orientation() == Qt::Vertical)
16024   {
16025     *lineData << upperFillBasePoint(lineData->last().y());
16026     *lineData << lowerFillBasePoint(lineData->first().y());
16027   } else
16028   {
16029     *lineData << upperFillBasePoint(lineData->last().x());
16030     *lineData << lowerFillBasePoint(lineData->first().x());
16031   }
16032 }
16033 
16034 /*! \internal
16035 
16036   removes the two points from \a lineData that were added by \ref addFillBasePoints.
16037 
16038   \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16039 */
removeFillBasePoints(QVector<QPointF> * lineData) const16040 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16041 {
16042   if (!lineData) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; }
16043   if (lineData->isEmpty()) return;
16044 
16045   lineData->remove(lineData->size()-2, 2);
16046 }
16047 
16048 /*! \internal
16049 
16050   called by \ref addFillBasePoints to conveniently assign the point which closes the fill polygon
16051   on the lower side of the zero-value-line parallel to the key axis. The logarithmic axis scale
16052   case is a bit special, since the zero-value-line in pixel coordinates is in positive or negative
16053   infinity. So this case is handled separately by just closing the fill polygon on the axis which
16054   lies in the direction towards the zero value.
16055 
16056   \a lowerKey will be the the key (in pixels) of the returned point. Depending on whether the key
16057   axis is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned
16058   point, respectively.
16059 
16060   \see upperFillBasePoint, addFillBasePoints
16061 */
lowerFillBasePoint(double lowerKey) const16062 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16063 {
16064   QCPAxis *keyAxis = mKeyAxis.data();
16065   QCPAxis *valueAxis = mValueAxis.data();
16066   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16067 
16068   QPointF point;
16069   if (valueAxis->scaleType() == QCPAxis::stLinear)
16070   {
16071     if (keyAxis->axisType() == QCPAxis::atLeft)
16072     {
16073       point.setX(valueAxis->coordToPixel(0));
16074       point.setY(lowerKey);
16075     } else if (keyAxis->axisType() == QCPAxis::atRight)
16076     {
16077       point.setX(valueAxis->coordToPixel(0));
16078       point.setY(lowerKey);
16079     } else if (keyAxis->axisType() == QCPAxis::atTop)
16080     {
16081       point.setX(lowerKey);
16082       point.setY(valueAxis->coordToPixel(0));
16083     } else if (keyAxis->axisType() == QCPAxis::atBottom)
16084     {
16085       point.setX(lowerKey);
16086       point.setY(valueAxis->coordToPixel(0));
16087     }
16088   } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16089   {
16090     // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16091     // to the axis which is in the direction towards zero
16092     if (keyAxis->orientation() == Qt::Vertical)
16093     {
16094       if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16095           (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16096         point.setX(keyAxis->axisRect()->right());
16097       else
16098         point.setX(keyAxis->axisRect()->left());
16099       point.setY(lowerKey);
16100     } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16101     {
16102       point.setX(lowerKey);
16103       if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16104           (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16105         point.setY(keyAxis->axisRect()->top());
16106       else
16107         point.setY(keyAxis->axisRect()->bottom());
16108     }
16109   }
16110   return point;
16111 }
16112 
16113 /*! \internal
16114 
16115   called by \ref addFillBasePoints to conveniently assign the point which closes the fill
16116   polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis
16117   scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or
16118   negative infinity. So this case is handled separately by just closing the fill polygon on the
16119   axis which lies in the direction towards the zero value.
16120 
16121   \a upperKey will be the the key (in pixels) of the returned point. Depending on whether the key
16122   axis is horizontal or vertical, \a upperKey will end up as the x or y value of the returned
16123   point, respectively.
16124 
16125   \see lowerFillBasePoint, addFillBasePoints
16126 */
upperFillBasePoint(double upperKey) const16127 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16128 {
16129   QCPAxis *keyAxis = mKeyAxis.data();
16130   QCPAxis *valueAxis = mValueAxis.data();
16131   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16132 
16133   QPointF point;
16134   if (valueAxis->scaleType() == QCPAxis::stLinear)
16135   {
16136     if (keyAxis->axisType() == QCPAxis::atLeft)
16137     {
16138       point.setX(valueAxis->coordToPixel(0));
16139       point.setY(upperKey);
16140     } else if (keyAxis->axisType() == QCPAxis::atRight)
16141     {
16142       point.setX(valueAxis->coordToPixel(0));
16143       point.setY(upperKey);
16144     } else if (keyAxis->axisType() == QCPAxis::atTop)
16145     {
16146       point.setX(upperKey);
16147       point.setY(valueAxis->coordToPixel(0));
16148     } else if (keyAxis->axisType() == QCPAxis::atBottom)
16149     {
16150       point.setX(upperKey);
16151       point.setY(valueAxis->coordToPixel(0));
16152     }
16153   } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16154   {
16155     // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16156     // to the axis which is in the direction towards 0
16157     if (keyAxis->orientation() == Qt::Vertical)
16158     {
16159       if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16160           (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16161         point.setX(keyAxis->axisRect()->right());
16162       else
16163         point.setX(keyAxis->axisRect()->left());
16164       point.setY(upperKey);
16165     } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16166     {
16167       point.setX(upperKey);
16168       if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16169           (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16170         point.setY(keyAxis->axisRect()->top());
16171       else
16172         point.setY(keyAxis->axisRect()->bottom());
16173     }
16174   }
16175   return point;
16176 }
16177 
16178 /*! \internal
16179 
16180   Generates the polygon needed for drawing channel fills between this graph (data passed via \a
16181   lineData) and the graph specified by mChannelFillGraph (data generated by calling its \ref
16182   getPlotData function). May return an empty polygon if the key ranges have no overlap or fill
16183   target graph and this graph don't have same orientation (i.e. both key axes horizontal or both
16184   key axes vertical). For increased performance (due to implicit sharing), keep the returned
16185   QPolygonF const.
16186 */
getChannelFillPolygon(const QVector<QPointF> * lineData) const16187 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16188 {
16189   if (!mChannelFillGraph)
16190     return QPolygonF();
16191 
16192   QCPAxis *keyAxis = mKeyAxis.data();
16193   QCPAxis *valueAxis = mValueAxis.data();
16194   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16195   if (!mChannelFillGraph->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16196 
16197   if (mChannelFillGraph->mKeyAxis->orientation() != keyAxis->orientation())
16198     return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
16199 
16200   if (lineData->isEmpty()) return QPolygonF();
16201   QVector<QPointF> otherData;
16202   mChannelFillGraph->getPlotData(&otherData, 0);
16203   if (otherData.isEmpty()) return QPolygonF();
16204   QVector<QPointF> thisData;
16205   thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16206   for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector),  it squeezes internally, which ruins the performance tuning with reserve()
16207     thisData << lineData->at(i);
16208 
16209   // pointers to be able to swap them, depending which data range needs cropping:
16210   QVector<QPointF> *staticData = &thisData;
16211   QVector<QPointF> *croppedData = &otherData;
16212 
16213   // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16214   if (keyAxis->orientation() == Qt::Horizontal)
16215   {
16216     // x is key
16217     // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16218     if (staticData->first().x() > staticData->last().x())
16219     {
16220       int size = staticData->size();
16221       for (int i=0; i<size/2; ++i)
16222         qSwap((*staticData)[i], (*staticData)[size-1-i]);
16223     }
16224     if (croppedData->first().x() > croppedData->last().x())
16225     {
16226       int size = croppedData->size();
16227       for (int i=0; i<size/2; ++i)
16228         qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16229     }
16230     // crop lower bound:
16231     if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16232       qSwap(staticData, croppedData);
16233     int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16234     if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16235     croppedData->remove(0, lowBound);
16236     // set lowest point of cropped data to fit exactly key position of first static data
16237     // point via linear interpolation:
16238     if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16239     double slope;
16240     if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16241       slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16242     else
16243       slope = 0;
16244     (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16245     (*croppedData)[0].setX(staticData->first().x());
16246 
16247     // crop upper bound:
16248     if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16249       qSwap(staticData, croppedData);
16250     int highBound = findIndexAboveX(croppedData, staticData->last().x());
16251     if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16252     croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16253     // set highest point of cropped data to fit exactly key position of last static data
16254     // point via linear interpolation:
16255     if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16256     int li = croppedData->size()-1; // last index
16257     if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16258       slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16259     else
16260       slope = 0;
16261     (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16262     (*croppedData)[li].setX(staticData->last().x());
16263   } else // mKeyAxis->orientation() == Qt::Vertical
16264   {
16265     // y is key
16266     // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16267     // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16268     // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16269     if (staticData->first().y() < staticData->last().y())
16270     {
16271       int size = staticData->size();
16272       for (int i=0; i<size/2; ++i)
16273         qSwap((*staticData)[i], (*staticData)[size-1-i]);
16274     }
16275     if (croppedData->first().y() < croppedData->last().y())
16276     {
16277       int size = croppedData->size();
16278       for (int i=0; i<size/2; ++i)
16279         qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16280     }
16281     // crop lower bound:
16282     if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16283       qSwap(staticData, croppedData);
16284     int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16285     if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16286     croppedData->remove(0, lowBound);
16287     // set lowest point of cropped data to fit exactly key position of first static data
16288     // point via linear interpolation:
16289     if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16290     double slope;
16291     if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16292       slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16293     else
16294       slope = 0;
16295     (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16296     (*croppedData)[0].setY(staticData->first().y());
16297 
16298     // crop upper bound:
16299     if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16300       qSwap(staticData, croppedData);
16301     int highBound = findIndexBelowY(croppedData, staticData->last().y());
16302     if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16303     croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16304     // set highest point of cropped data to fit exactly key position of last static data
16305     // point via linear interpolation:
16306     if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16307     int li = croppedData->size()-1; // last index
16308     if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16309       slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16310     else
16311       slope = 0;
16312     (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16313     (*croppedData)[li].setY(staticData->last().y());
16314   }
16315 
16316   // return joined:
16317   for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16318     thisData << otherData.at(i);
16319   return QPolygonF(thisData);
16320 }
16321 
16322 /*! \internal
16323 
16324   Finds the smallest index of \a data, whose points x value is just above \a x. Assumes x values in
16325   \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
16326 
16327   Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16328 */
findIndexAboveX(const QVector<QPointF> * data,double x) const16329 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16330 {
16331   for (int i=data->size()-1; i>=0; --i)
16332   {
16333     if (data->at(i).x() < x)
16334     {
16335       if (i<data->size()-1)
16336         return i+1;
16337       else
16338         return data->size()-1;
16339     }
16340   }
16341   return -1;
16342 }
16343 
16344 /*! \internal
16345 
16346   Finds the highest index of \a data, whose points x value is just below \a x. Assumes x values in
16347   \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
16348 
16349   Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16350 */
findIndexBelowX(const QVector<QPointF> * data,double x) const16351 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16352 {
16353   for (int i=0; i<data->size(); ++i)
16354   {
16355     if (data->at(i).x() > x)
16356     {
16357       if (i>0)
16358         return i-1;
16359       else
16360         return 0;
16361     }
16362   }
16363   return -1;
16364 }
16365 
16366 /*! \internal
16367 
16368   Finds the smallest index of \a data, whose points y value is just above \a y. Assumes y values in
16369   \a data points are ordered descending, as is the case when plotting with vertical key axis.
16370 
16371   Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16372 */
findIndexAboveY(const QVector<QPointF> * data,double y) const16373 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16374 {
16375   for (int i=0; i<data->size(); ++i)
16376   {
16377     if (data->at(i).y() < y)
16378     {
16379       if (i>0)
16380         return i-1;
16381       else
16382         return 0;
16383     }
16384   }
16385   return -1;
16386 }
16387 
16388 /*! \internal
16389 
16390   Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a
16391   pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in
16392   \ref selectTest.
16393 
16394   If either the graph has no data or if the line style is \ref lsNone and the scatter style's shape
16395   is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns -1.0.
16396 */
pointDistance(const QPointF & pixelPoint) const16397 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16398 {
16399   if (mData->isEmpty())
16400     return -1.0;
16401   if (mLineStyle == lsNone && mScatterStyle.isNone())
16402     return -1.0;
16403 
16404   // calculate minimum distances to graph representation:
16405   if (mLineStyle == lsNone)
16406   {
16407     // no line displayed, only calculate distance to scatter points:
16408     QVector<QCPData> scatterData;
16409     getScatterPlotData(&scatterData);
16410     if (scatterData.size() > 0)
16411     {
16412       double minDistSqr = std::numeric_limits<double>::max();
16413       for (int i=0; i<scatterData.size(); ++i)
16414       {
16415         double currentDistSqr = QVector2D(coordsToPixels(scatterData.at(i).key, scatterData.at(i).value)-pixelPoint).lengthSquared();
16416         if (currentDistSqr < minDistSqr)
16417           minDistSqr = currentDistSqr;
16418       }
16419       return qSqrt(minDistSqr);
16420     } else // no data available in view to calculate distance to
16421       return -1.0;
16422   } else
16423   {
16424     // line displayed, calculate distance to line segments:
16425     QVector<QPointF> lineData;
16426     getPlotData(&lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16427     if (lineData.size() > 1) // at least one line segment, compare distance to line segments
16428     {
16429       double minDistSqr = std::numeric_limits<double>::max();
16430       if (mLineStyle == lsImpulse)
16431       {
16432         // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16433         for (int i=0; i<lineData.size()-1; i+=2) // iterate pairs
16434         {
16435           double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16436           if (currentDistSqr < minDistSqr)
16437             minDistSqr = currentDistSqr;
16438         }
16439       } else
16440       {
16441         // all other line plots (line and step) connect points directly:
16442         for (int i=0; i<lineData.size()-1; ++i)
16443         {
16444           double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16445           if (currentDistSqr < minDistSqr)
16446             minDistSqr = currentDistSqr;
16447         }
16448       }
16449       return qSqrt(minDistSqr);
16450     } else if (lineData.size() > 0) // only single data point, calculate distance to that point
16451     {
16452       return QVector2D(lineData.at(0)-pixelPoint).length();
16453     } else // no data available in view to calculate distance to
16454       return -1.0;
16455   }
16456 }
16457 
16458 /*! \internal
16459 
16460   Finds the highest index of \a data, whose points y value is just below \a y. Assumes y values in
16461   \a data points are ordered descending, as is the case when plotting with vertical key axis (since
16462   keys are ordered ascending).
16463 
16464   Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16465 */
findIndexBelowY(const QVector<QPointF> * data,double y) const16466 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16467 {
16468   for (int i=data->size()-1; i>=0; --i)
16469   {
16470     if (data->at(i).y() > y)
16471     {
16472       if (i<data->size()-1)
16473         return i+1;
16474       else
16475         return data->size()-1;
16476     }
16477   }
16478   return -1;
16479 }
16480 
16481 /* inherits documentation from base class */
getKeyRange(bool & foundRange,SignDomain inSignDomain) const16482 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16483 {
16484   // just call the specialized version which takes an additional argument whether error bars
16485   // should also be taken into consideration for range calculation. We set this to true here.
16486   return getKeyRange(foundRange, inSignDomain, true);
16487 }
16488 
16489 /* inherits documentation from base class */
getValueRange(bool & foundRange,SignDomain inSignDomain) const16490 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16491 {
16492   // just call the specialized version which takes an additional argument whether error bars
16493   // should also be taken into consideration for range calculation. We set this to true here.
16494   return getValueRange(foundRange, inSignDomain, true);
16495 }
16496 
16497 /*! \overload
16498 
16499   Allows to specify whether the error bars should be included in the range calculation.
16500 
16501   \see getKeyRange(bool &foundRange, SignDomain inSignDomain)
16502 */
getKeyRange(bool & foundRange,SignDomain inSignDomain,bool includeErrors) const16503 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16504 {
16505   QCPRange range;
16506   bool haveLower = false;
16507   bool haveUpper = false;
16508 
16509   double current, currentErrorMinus, currentErrorPlus;
16510 
16511   if (inSignDomain == sdBoth) // range may be anywhere
16512   {
16513     for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
16514     {
16515       if (!qIsNaN(it.value().value))
16516       {
16517         current = it.value().key;
16518         currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16519         currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16520         if (current-currentErrorMinus < range.lower || !haveLower)
16521         {
16522           range.lower = current-currentErrorMinus;
16523           haveLower = true;
16524         }
16525         if (current+currentErrorPlus > range.upper || !haveUpper)
16526         {
16527           range.upper = current+currentErrorPlus;
16528           haveUpper = true;
16529         }
16530       }
16531     }
16532   } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16533   {
16534     for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
16535     {
16536       if (!qIsNaN(it.value().value))
16537       {
16538         current = it.value().key;
16539         currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16540         currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16541         if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16542         {
16543           range.lower = current-currentErrorMinus;
16544           haveLower = true;
16545         }
16546         if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16547         {
16548           range.upper = current+currentErrorPlus;
16549           haveUpper = true;
16550         }
16551         if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16552         {
16553           if ((current < range.lower || !haveLower) && current < 0)
16554           {
16555             range.lower = current;
16556             haveLower = true;
16557           }
16558           if ((current > range.upper || !haveUpper) && current < 0)
16559           {
16560             range.upper = current;
16561             haveUpper = true;
16562           }
16563         }
16564       }
16565     }
16566   } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16567   {
16568     for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
16569     {
16570       if (!qIsNaN(it.value().value))
16571       {
16572         current = it.value().key;
16573         currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16574         currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16575         if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16576         {
16577           range.lower = current-currentErrorMinus;
16578           haveLower = true;
16579         }
16580         if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16581         {
16582           range.upper = current+currentErrorPlus;
16583           haveUpper = true;
16584         }
16585         if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16586         {
16587           if ((current < range.lower || !haveLower) && current > 0)
16588           {
16589             range.lower = current;
16590             haveLower = true;
16591           }
16592           if ((current > range.upper || !haveUpper) && current > 0)
16593           {
16594             range.upper = current;
16595             haveUpper = true;
16596           }
16597         }
16598       }
16599     }
16600   }
16601 
16602   foundRange = haveLower && haveUpper;
16603   return range;
16604 }
16605 
16606 /*! \overload
16607 
16608   Allows to specify whether the error bars should be included in the range calculation.
16609 
16610   \see getValueRange(bool &foundRange, SignDomain inSignDomain)
16611 */
getValueRange(bool & foundRange,SignDomain inSignDomain,bool includeErrors) const16612 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16613 {
16614   QCPRange range;
16615   bool haveLower = false;
16616   bool haveUpper = false;
16617 
16618   double current, currentErrorMinus, currentErrorPlus;
16619 
16620   if (inSignDomain == sdBoth) // range may be anywhere
16621   {
16622     for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
16623     {
16624       current = it.value().value;
16625       if (!qIsNaN(current))
16626       {
16627         currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16628         currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16629         if (current-currentErrorMinus < range.lower || !haveLower)
16630         {
16631           range.lower = current-currentErrorMinus;
16632           haveLower = true;
16633         }
16634         if (current+currentErrorPlus > range.upper || !haveUpper)
16635         {
16636           range.upper = current+currentErrorPlus;
16637           haveUpper = true;
16638         }
16639       }
16640     }
16641   } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16642   {
16643     for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
16644     {
16645       current = it.value().value;
16646       if (!qIsNaN(current))
16647       {
16648         currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16649         currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16650         if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16651         {
16652           range.lower = current-currentErrorMinus;
16653           haveLower = true;
16654         }
16655         if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16656         {
16657           range.upper = current+currentErrorPlus;
16658           haveUpper = true;
16659         }
16660         if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16661         {
16662           if ((current < range.lower || !haveLower) && current < 0)
16663           {
16664             range.lower = current;
16665             haveLower = true;
16666           }
16667           if ((current > range.upper || !haveUpper) && current < 0)
16668           {
16669             range.upper = current;
16670             haveUpper = true;
16671           }
16672         }
16673       }
16674     }
16675   } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16676   {
16677     for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
16678     {
16679       current = it.value().value;
16680       if (!qIsNaN(current))
16681       {
16682         currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16683         currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16684         if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16685         {
16686           range.lower = current-currentErrorMinus;
16687           haveLower = true;
16688         }
16689         if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16690         {
16691           range.upper = current+currentErrorPlus;
16692           haveUpper = true;
16693         }
16694         if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16695         {
16696           if ((current < range.lower || !haveLower) && current > 0)
16697           {
16698             range.lower = current;
16699             haveLower = true;
16700           }
16701           if ((current > range.upper || !haveUpper) && current > 0)
16702           {
16703             range.upper = current;
16704             haveUpper = true;
16705           }
16706         }
16707       }
16708     }
16709   }
16710 
16711   foundRange = haveLower && haveUpper;
16712   return range;
16713 }
16714 
16715 
16716 ////////////////////////////////////////////////////////////////////////////////////////////////////
16717 //////////////////// QCPCurveData
16718 ////////////////////////////////////////////////////////////////////////////////////////////////////
16719 
16720 /*! \class QCPCurveData
16721   \brief Holds the data of one single data point for QCPCurve.
16722 
16723   The container for storing multiple data points is \ref QCPCurveDataMap.
16724 
16725   The stored data is:
16726   \li \a t: the free parameter of the curve at this curve point (cp. the mathematical vector <em>(x(t), y(t))</em>)
16727   \li \a key: coordinate on the key axis of this curve point
16728   \li \a value: coordinate on the value axis of this curve point
16729 
16730   \see QCPCurveDataMap
16731 */
16732 
16733 /*!
16734   Constructs a curve data point with t, key and value set to zero.
16735 */
QCPCurveData()16736 QCPCurveData::QCPCurveData() :
16737   t(0),
16738   key(0),
16739   value(0)
16740 {
16741 }
16742 
16743 /*!
16744   Constructs a curve data point with the specified \a t, \a key and \a value.
16745 */
QCPCurveData(double t,double key,double value)16746 QCPCurveData::QCPCurveData(double t, double key, double value) :
16747   t(t),
16748   key(key),
16749   value(value)
16750 {
16751 }
16752 
16753 
16754 ////////////////////////////////////////////////////////////////////////////////////////////////////
16755 //////////////////// QCPCurve
16756 ////////////////////////////////////////////////////////////////////////////////////////////////////
16757 
16758 /*! \class QCPCurve
16759   \brief A plottable representing a parametric curve in a plot.
16760 
16761   \image html QCPCurve.png
16762 
16763   Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate,
16764   so their visual representation can have \a loops. This is realized by introducing a third
16765   coordinate \a t, which defines the order of the points described by the other two coordinates \a
16766   x and \a y.
16767 
16768   To plot data, assign it with the \ref setData or \ref addData functions.
16769 
16770   Gaps in the curve can be created by adding data points with NaN as key and value
16771   (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be
16772   separated.
16773 
16774   \section appearance Changing the appearance
16775 
16776   The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush).
16777   \section usage Usage
16778 
16779   Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So
16780   the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
16781 
16782   Usually, you first create an instance and add it to the customPlot:
16783   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1
16784   and then modify the properties of the newly created plottable, e.g.:
16785   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2
16786 */
16787 
16788 /*!
16789   Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
16790   axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
16791   the same orientation. If either of these restrictions is violated, a corresponding message is
16792   printed to the debug output (qDebug), the construction is not aborted, though.
16793 
16794   The constructed QCPCurve can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
16795   then takes ownership of the graph.
16796 */
QCPCurve(QCPAxis * keyAxis,QCPAxis * valueAxis)16797 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
16798   QCPAbstractPlottable(keyAxis, valueAxis)
16799 {
16800   mData = new QCPCurveDataMap;
16801   mPen.setColor(Qt::blue);
16802   mPen.setStyle(Qt::SolidLine);
16803   mBrush.setColor(Qt::blue);
16804   mBrush.setStyle(Qt::NoBrush);
16805   mSelectedPen = mPen;
16806   mSelectedPen.setWidthF(2.5);
16807   mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
16808   mSelectedBrush = mBrush;
16809 
16810   setScatterStyle(QCPScatterStyle());
16811   setLineStyle(lsLine);
16812 }
16813 
~QCPCurve()16814 QCPCurve::~QCPCurve()
16815 {
16816   delete mData;
16817 }
16818 
16819 /*!
16820   Replaces the current data with the provided \a data.
16821 
16822   If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
16823   takes ownership of the passed data and replaces the internal data pointer with it. This is
16824   significantly faster than copying for large datasets.
16825 */
setData(QCPCurveDataMap * data,bool copy)16826 void QCPCurve::setData(QCPCurveDataMap *data, bool copy)
16827 {
16828   if (mData == data)
16829   {
16830     qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
16831     return;
16832   }
16833   if (copy)
16834   {
16835     *mData = *data;
16836   } else
16837   {
16838     delete mData;
16839     mData = data;
16840   }
16841 }
16842 
16843 /*! \overload
16844 
16845   Replaces the current data with the provided points in \a t, \a key and \a value tuples. The
16846   provided vectors should have equal length. Else, the number of added points will be the size of
16847   the smallest vector.
16848 */
setData(const QVector<double> & t,const QVector<double> & key,const QVector<double> & value)16849 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
16850 {
16851   mData->clear();
16852   int n = t.size();
16853   n = qMin(n, key.size());
16854   n = qMin(n, value.size());
16855   QCPCurveData newData;
16856   for (int i=0; i<n; ++i)
16857   {
16858     newData.t = t[i];
16859     newData.key = key[i];
16860     newData.value = value[i];
16861     mData->insertMulti(newData.t, newData);
16862   }
16863 }
16864 
16865 /*! \overload
16866 
16867   Replaces the current data with the provided \a key and \a value pairs. The t parameter
16868   of each data point will be set to the integer index of the respective key/value pair.
16869 */
setData(const QVector<double> & key,const QVector<double> & value)16870 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
16871 {
16872   mData->clear();
16873   int n = key.size();
16874   n = qMin(n, value.size());
16875   QCPCurveData newData;
16876   for (int i=0; i<n; ++i)
16877   {
16878     newData.t = i; // no t vector given, so we assign t the index of the key/value pair
16879     newData.key = key[i];
16880     newData.value = value[i];
16881     mData->insertMulti(newData.t, newData);
16882   }
16883 }
16884 
16885 /*!
16886   Sets the visual appearance of single data points in the plot. If set to \ref
16887   QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate
16888   line style).
16889 
16890   \see QCPScatterStyle, setLineStyle
16891 */
setScatterStyle(const QCPScatterStyle & style)16892 void QCPCurve::setScatterStyle(const QCPScatterStyle &style)
16893 {
16894   mScatterStyle = style;
16895 }
16896 
16897 /*!
16898   Sets how the single data points are connected in the plot or how they are represented visually
16899   apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref
16900   setScatterStyle to the desired scatter style.
16901 
16902   \see setScatterStyle
16903 */
setLineStyle(QCPCurve::LineStyle style)16904 void QCPCurve::setLineStyle(QCPCurve::LineStyle style)
16905 {
16906   mLineStyle = style;
16907 }
16908 
16909 /*!
16910   Adds the provided data points in \a dataMap to the current data.
16911   \see removeData
16912 */
addData(const QCPCurveDataMap & dataMap)16913 void QCPCurve::addData(const QCPCurveDataMap &dataMap)
16914 {
16915   mData->unite(dataMap);
16916 }
16917 
16918 /*! \overload
16919   Adds the provided single data point in \a data to the current data.
16920   \see removeData
16921 */
addData(const QCPCurveData & data)16922 void QCPCurve::addData(const QCPCurveData &data)
16923 {
16924   mData->insertMulti(data.t, data);
16925 }
16926 
16927 /*! \overload
16928   Adds the provided single data point as \a t, \a key and \a value tuple to the current data
16929   \see removeData
16930 */
addData(double t,double key,double value)16931 void QCPCurve::addData(double t, double key, double value)
16932 {
16933   QCPCurveData newData;
16934   newData.t = t;
16935   newData.key = key;
16936   newData.value = value;
16937   mData->insertMulti(newData.t, newData);
16938 }
16939 
16940 /*! \overload
16941 
16942   Adds the provided single data point as \a key and \a value pair to the current data The t
16943   parameter of the data point is set to the t of the last data point plus 1. If there is no last
16944   data point, t will be set to 0.
16945 
16946   \see removeData
16947 */
addData(double key,double value)16948 void QCPCurve::addData(double key, double value)
16949 {
16950   QCPCurveData newData;
16951   if (!mData->isEmpty())
16952     newData.t = (mData->constEnd()-1).key()+1;
16953   else
16954     newData.t = 0;
16955   newData.key = key;
16956   newData.value = value;
16957   mData->insertMulti(newData.t, newData);
16958 }
16959 
16960 /*! \overload
16961   Adds the provided data points as \a t, \a key and \a value tuples to the current data.
16962   \see removeData
16963 */
addData(const QVector<double> & ts,const QVector<double> & keys,const QVector<double> & values)16964 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
16965 {
16966   int n = ts.size();
16967   n = qMin(n, keys.size());
16968   n = qMin(n, values.size());
16969   QCPCurveData newData;
16970   for (int i=0; i<n; ++i)
16971   {
16972     newData.t = ts[i];
16973     newData.key = keys[i];
16974     newData.value = values[i];
16975     mData->insertMulti(newData.t, newData);
16976   }
16977 }
16978 
16979 /*!
16980   Removes all data points with curve parameter t smaller than \a t.
16981   \see addData, clearData
16982 */
removeDataBefore(double t)16983 void QCPCurve::removeDataBefore(double t)
16984 {
16985   auto it = mData->begin();
16986   while (it != mData->end() && it.key() < t)
16987     it = mData->erase(it);
16988 }
16989 
16990 /*!
16991   Removes all data points with curve parameter t greater than \a t.
16992   \see addData, clearData
16993 */
removeDataAfter(double t)16994 void QCPCurve::removeDataAfter(double t)
16995 {
16996   if (mData->isEmpty()) return;
16997   auto it = mData->upperBound(t);
16998   while (it != mData->end())
16999     it = mData->erase(it);
17000 }
17001 
17002 /*!
17003   Removes all data points with curve parameter t between \a fromt and \a tot. if \a fromt is
17004   greater or equal to \a tot, the function does nothing. To remove a single data point with known
17005   t, use \ref removeData(double t).
17006 
17007   \see addData, clearData
17008 */
removeData(double fromt,double tot)17009 void QCPCurve::removeData(double fromt, double tot)
17010 {
17011   if (fromt >= tot || mData->isEmpty()) return;
17012   auto it = mData->upperBound(fromt);
17013   auto itEnd = mData->upperBound(tot);
17014   while (it != itEnd)
17015     it = mData->erase(it);
17016 }
17017 
17018 /*! \overload
17019 
17020   Removes a single data point at curve parameter \a t. If the position is not known with absolute
17021   precision, consider using \ref removeData(double fromt, double tot) with a small fuzziness
17022   interval around the suspected position, depeding on the precision with which the curve parameter
17023   is known.
17024 
17025   \see addData, clearData
17026 */
removeData(double t)17027 void QCPCurve::removeData(double t)
17028 {
17029   mData->remove(t);
17030 }
17031 
17032 /*!
17033   Removes all data points.
17034   \see removeData, removeDataAfter, removeDataBefore
17035 */
clearData()17036 void QCPCurve::clearData()
17037 {
17038   mData->clear();
17039 }
17040 
17041 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const17042 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17043 {
17044   Q_UNUSED(details)
17045   if ((onlySelectable && !mSelectable) || mData->isEmpty())
17046     return -1;
17047   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17048 
17049   if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
17050     return pointDistance(pos);
17051   else
17052     return -1;
17053 }
17054 
17055 /* inherits documentation from base class */
draw(QCPPainter * painter)17056 void QCPCurve::draw(QCPPainter *painter)
17057 {
17058   if (mData->isEmpty()) return;
17059 
17060   // allocate line vector:
17061   QVector<QPointF> *lineData = new QVector<QPointF>;
17062 
17063   // fill with curve data:
17064   getCurveData(lineData);
17065 
17066   // check data validity if flag set:
17067 #ifdef QCUSTOMPLOT_CHECK_DATA
17068   for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
17069   {
17070     if (QCP::isInvalidData(it.value().t) ||
17071         QCP::isInvalidData(it.value().key, it.value().value))
17072       qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17073   }
17074 #endif
17075 
17076   // draw curve fill:
17077   if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17078   {
17079     applyFillAntialiasingHint(painter);
17080     painter->setPen(Qt::NoPen);
17081     painter->setBrush(mainBrush());
17082     painter->drawPolygon(QPolygonF(*lineData));
17083   }
17084 
17085   // draw curve line:
17086   if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17087   {
17088     applyDefaultAntialiasingHint(painter);
17089     painter->setPen(mainPen());
17090     painter->setBrush(Qt::NoBrush);
17091     // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17092     if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17093         painter->pen().style() == Qt::SolidLine &&
17094         !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17095         !painter->modes().testFlag(QCPPainter::pmNoCaching))
17096     {
17097       int i = 0;
17098       bool lastIsNan = false;
17099       const int lineDataSize = lineData->size();
17100       while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
17101         ++i;
17102       ++i; // because drawing works in 1 point retrospect
17103       while (i < lineDataSize)
17104       {
17105         if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17106         {
17107           if (!lastIsNan)
17108             painter->drawLine(lineData->at(i-1), lineData->at(i));
17109           else
17110             lastIsNan = false;
17111         } else
17112           lastIsNan = true;
17113         ++i;
17114       }
17115     } else
17116     {
17117       int segmentStart = 0;
17118       int i = 0;
17119       const int lineDataSize = lineData->size();
17120       while (i < lineDataSize)
17121       {
17122         if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17123         {
17124           painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
17125           segmentStart = i+1;
17126         }
17127         ++i;
17128       }
17129       // draw last segment:
17130       painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
17131     }
17132   }
17133 
17134   // draw scatters:
17135   if (!mScatterStyle.isNone())
17136     drawScatterPlot(painter, lineData);
17137 
17138   // free allocated line data:
17139   delete lineData;
17140 }
17141 
17142 /* inherits documentation from base class */
drawLegendIcon(QCPPainter * painter,const QRectF & rect) const17143 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17144 {
17145   // draw fill:
17146   if (mBrush.style() != Qt::NoBrush)
17147   {
17148     applyFillAntialiasingHint(painter);
17149     painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17150   }
17151   // draw line vertically centered:
17152   if (mLineStyle != lsNone)
17153   {
17154     applyDefaultAntialiasingHint(painter);
17155     painter->setPen(mPen);
17156     painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
17157   }
17158   // draw scatter symbol:
17159   if (!mScatterStyle.isNone())
17160   {
17161     applyScattersAntialiasingHint(painter);
17162     // scale scatter pixmap if it's too large to fit in legend icon rect:
17163     if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17164     {
17165       QCPScatterStyle scaledStyle(mScatterStyle);
17166       scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17167       scaledStyle.applyTo(painter, mPen);
17168       scaledStyle.drawShape(painter, QRectF(rect).center());
17169     } else
17170     {
17171       mScatterStyle.applyTo(painter, mPen);
17172       mScatterStyle.drawShape(painter, QRectF(rect).center());
17173     }
17174   }
17175 }
17176 
17177 /*! \internal
17178 
17179   Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
17180   the line style and are always drawn if scatter shape is not \ref QCPScatterStyle::ssNone.
17181 */
drawScatterPlot(QCPPainter * painter,const QVector<QPointF> * pointData) const17182 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17183 {
17184   // draw scatter point symbols:
17185   applyScattersAntialiasingHint(painter);
17186   mScatterStyle.applyTo(painter, mPen);
17187   for (int i=0; i<pointData->size(); ++i)
17188     if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17189       mScatterStyle.drawShape(painter,  pointData->at(i));
17190 }
17191 
17192 /*! \internal
17193 
17194   called by QCPCurve::draw to generate a point vector (in pixel coordinates) which represents the
17195   line of the curve.
17196 
17197   Line segments that aren't visible in the current axis rect are handled in an optimized way. They
17198   are projected onto a rectangle slightly larger than the visible axis rect and simplified
17199   regarding point count. The algorithm makes sure to preserve appearance of lines and fills inside
17200   the visible axis rect by generating new temporary points on the outer rect if necessary.
17201 
17202   Methods that are also involved in the algorithm are: \ref getRegion, \ref getOptimizedPoint, \ref
17203   getOptimizedCornerPoints \ref mayTraverse, \ref getTraverse, \ref getTraverseCornerPoints.
17204 */
getCurveData(QVector<QPointF> * lineData) const17205 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17206 {
17207   QCPAxis *keyAxis = mKeyAxis.data();
17208   QCPAxis *valueAxis = mValueAxis.data();
17209   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17210 
17211   // add margins to rect to compensate for stroke width
17212   double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety
17213   if (!mScatterStyle.isNone())
17214     strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17215   double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17216   double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17217   double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17218   double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17219   int currentRegion;
17220   auto it = mData->constBegin();
17221   auto prevIt = mData->constEnd()-1;
17222   int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom);
17223   QVector<QPointF> trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right)
17224   while (it != mData->constEnd())
17225   {
17226     currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17227     if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
17228     {
17229       if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
17230       {
17231         QPointF crossA, crossB;
17232         if (prevRegion == 5) // we're coming from R, so add this point optimized
17233         {
17234           lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom));
17235           // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point
17236           *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17237         } else if (mayTraverse(prevRegion, currentRegion) &&
17238                    getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB))
17239         {
17240           // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
17241           QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
17242           getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints);
17243           if (it != mData->constBegin())
17244           {
17245             *lineData << beforeTraverseCornerPoints;
17246             lineData->append(crossA);
17247             lineData->append(crossB);
17248             *lineData << afterTraverseCornerPoints;
17249           } else
17250           {
17251             lineData->append(crossB);
17252             *lineData << afterTraverseCornerPoints;
17253             trailingPoints << beforeTraverseCornerPoints << crossA ;
17254           }
17255         } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s)
17256         {
17257           *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17258         }
17259       } else // segment does end in R, so we add previous point optimized and this point at original position
17260       {
17261         if (it == mData->constBegin()) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end
17262           trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17263         else
17264           lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom));
17265         lineData->append(coordsToPixels(it.value().key, it.value().value));
17266       }
17267     } else // region didn't change
17268     {
17269       if (currentRegion == 5) // still in R, keep adding original points
17270       {
17271         lineData->append(coordsToPixels(it.value().key, it.value().value));
17272       } else // still outside R, no need to add anything
17273       {
17274         // see how this is not doing anything? That's the main optimization...
17275       }
17276     }
17277     prevIt = it;
17278     prevRegion = currentRegion;
17279     ++it;
17280   }
17281   *lineData << trailingPoints;
17282 }
17283 
17284 /*! \internal
17285 
17286   This function is part of the curve optimization algorithm of \ref getCurveData.
17287 
17288   It returns the region of the given point (\a x, \a y) with respect to a rectangle defined by \a
17289   rectLeft, \a rectTop, \a rectRight, and \a rectBottom.
17290 
17291   The regions are enumerated from top to bottom and left to right:
17292 
17293   <table style="width:10em; text-align:center">
17294     <tr><td>1</td><td>4</td><td>7</td></tr>
17295     <tr><td>2</td><td style="border:1px solid black">5</td><td>8</td></tr>
17296     <tr><td>3</td><td>6</td><td>9</td></tr>
17297   </table>
17298 
17299   With the rectangle being region 5, and the outer regions extending infinitely outwards. In the
17300   curve optimization algorithm, region 5 is considered to be the visible portion of the plot.
17301 */
getRegion(double x,double y,double rectLeft,double rectTop,double rectRight,double rectBottom) const17302 int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17303 {
17304   if (x < rectLeft) // region 123
17305   {
17306     if (y > rectTop)
17307       return 1;
17308     else if (y < rectBottom)
17309       return 3;
17310     else
17311       return 2;
17312   } else if (x > rectRight) // region 789
17313   {
17314     if (y > rectTop)
17315       return 7;
17316     else if (y < rectBottom)
17317       return 9;
17318     else
17319       return 8;
17320   } else // region 456
17321   {
17322     if (y > rectTop)
17323       return 4;
17324     else if (y < rectBottom)
17325       return 6;
17326     else
17327       return 5;
17328   }
17329 }
17330 
17331 /*! \internal
17332 
17333   This function is part of the curve optimization algorithm of \ref getCurveData.
17334 
17335   This method is used in case the current segment passes from inside the visible rect (region 5,
17336   see \ref getRegion) to any of the outer regions (\a otherRegion). The current segment is given by
17337   the line connecting (\a key, \a value) with (\a otherKey, \a otherValue).
17338 
17339   It returns the intersection point of the segment with the border of region 5.
17340 
17341   For this function it doesn't matter whether (\a key, \a value) is the point inside region 5 or
17342   whether it's (\a otherKey, \a otherValue), i.e. whether the segment is coming from region 5 or
17343   leaving it. It is important though that \a otherRegion correctly identifies the other region not
17344   equal to 5.
17345 */
getOptimizedPoint(int otherRegion,double otherKey,double otherValue,double key,double value,double rectLeft,double rectTop,double rectRight,double rectBottom) const17346 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17347 {
17348   double intersectKey = rectLeft; // initial value is just fail-safe
17349   double intersectValue = rectTop; // initial value is just fail-safe
17350   switch (otherRegion)
17351   {
17352     case 1: // top and left edge
17353     {
17354       intersectValue = rectTop;
17355       intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17356       if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17357       {
17358         intersectKey = rectLeft;
17359         intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17360       }
17361       break;
17362     }
17363     case 2: // left edge
17364     {
17365       intersectKey = rectLeft;
17366       intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17367       break;
17368     }
17369     case 3: // bottom and left edge
17370     {
17371       intersectValue = rectBottom;
17372       intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17373       if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17374       {
17375         intersectKey = rectLeft;
17376         intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17377       }
17378       break;
17379     }
17380     case 4: // top edge
17381     {
17382       intersectValue = rectTop;
17383       intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17384       break;
17385     }
17386     case 5:
17387     {
17388       break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
17389     }
17390     case 6: // bottom edge
17391     {
17392       intersectValue = rectBottom;
17393       intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17394       break;
17395     }
17396     case 7: // top and right edge
17397     {
17398       intersectValue = rectTop;
17399       intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17400       if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17401       {
17402         intersectKey = rectRight;
17403         intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17404       }
17405       break;
17406     }
17407     case 8: // right edge
17408     {
17409       intersectKey = rectRight;
17410       intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17411       break;
17412     }
17413     case 9: // bottom and right edge
17414     {
17415       intersectValue = rectBottom;
17416       intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17417       if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17418       {
17419         intersectKey = rectRight;
17420         intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17421       }
17422       break;
17423     }
17424   }
17425   return coordsToPixels(intersectKey, intersectValue);
17426 }
17427 
17428 /*! \internal
17429 
17430   This function is part of the curve optimization algorithm of \ref getCurveData.
17431 
17432   In situations where a single segment skips over multiple regions it might become necessary to add
17433   extra points at the corners of region 5 (see \ref getRegion) such that the optimized segment
17434   doesn't unintentionally cut through the visible area of the axis rect and create plot artifacts.
17435   This method provides these points that must be added, assuming the original segment doesn't
17436   start, end, or traverse region 5. (Corner points where region 5 is traversed are calculated by
17437   \ref getTraverseCornerPoints.)
17438 
17439   For example, consider a segment which directly goes from region 4 to 2 but originally is far out
17440   to the top left such that it doesn't cross region 5. Naively optimizing these points by
17441   projecting them on the top and left borders of region 5 will create a segment that surely crosses
17442   5, creating a visual artifact in the plot. This method prevents this by providing extra points at
17443   the top left corner, making the optimized curve correctly pass from region 4 to 1 to 2 without
17444   traversing 5.
17445 */
getOptimizedCornerPoints(int prevRegion,int currentRegion,double prevKey,double prevValue,double key,double value,double rectLeft,double rectTop,double rectRight,double rectBottom) const17446 QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17447 {
17448   QVector<QPointF> result;
17449   switch (prevRegion)
17450   {
17451     case 1:
17452     {
17453       switch (currentRegion)
17454       {
17455         case 2: { result << coordsToPixels(rectLeft, rectTop); break; }
17456         case 4: { result << coordsToPixels(rectLeft, rectTop); break; }
17457         case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; }
17458         case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; }
17459         case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17460         case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17461         case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17462           if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17463           { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17464           else
17465           { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17466           break;
17467         }
17468       }
17469       break;
17470     }
17471     case 2:
17472     {
17473       switch (currentRegion)
17474       {
17475         case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17476         case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17477         case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17478         case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17479         case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17480         case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17481       }
17482       break;
17483     }
17484     case 3:
17485     {
17486       switch (currentRegion)
17487       {
17488         case 2: { result << coordsToPixels(rectLeft, rectBottom); break; }
17489         case 6: { result << coordsToPixels(rectLeft, rectBottom); break; }
17490         case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; }
17491         case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; }
17492         case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17493         case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17494         case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17495           if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17496           { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17497           else
17498           { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17499           break;
17500         }
17501       }
17502       break;
17503     }
17504     case 4:
17505     {
17506       switch (currentRegion)
17507       {
17508         case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17509         case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17510         case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17511         case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17512         case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17513         case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17514       }
17515       break;
17516     }
17517     case 5:
17518     {
17519       switch (currentRegion)
17520       {
17521         case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17522         case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17523         case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17524         case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17525       }
17526       break;
17527     }
17528     case 6:
17529     {
17530       switch (currentRegion)
17531       {
17532         case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17533         case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17534         case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17535         case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17536         case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17537         case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17538       }
17539       break;
17540     }
17541     case 7:
17542     {
17543       switch (currentRegion)
17544       {
17545         case 4: { result << coordsToPixels(rectRight, rectTop); break; }
17546         case 8: { result << coordsToPixels(rectRight, rectTop); break; }
17547         case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; }
17548         case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; }
17549         case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17550         case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17551         case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17552           if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17553           { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17554           else
17555           { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17556           break;
17557         }
17558       }
17559       break;
17560     }
17561     case 8:
17562     {
17563       switch (currentRegion)
17564       {
17565         case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17566         case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17567         case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17568         case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17569         case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17570         case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17571       }
17572       break;
17573     }
17574     case 9:
17575     {
17576       switch (currentRegion)
17577       {
17578         case 6: { result << coordsToPixels(rectRight, rectBottom); break; }
17579         case 8: { result << coordsToPixels(rectRight, rectBottom); break; }
17580         case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; }
17581         case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; }
17582         case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17583         case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17584         case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17585           if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17586           { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17587           else
17588           { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17589           break;
17590         }
17591       }
17592       break;
17593     }
17594   }
17595   return result;
17596 }
17597 
17598 /*! \internal
17599 
17600   This function is part of the curve optimization algorithm of \ref getCurveData.
17601 
17602   This method returns whether a segment going from \a prevRegion to \a currentRegion (see \ref
17603   getRegion) may traverse the visible region 5. This function assumes that neither \a prevRegion
17604   nor \a currentRegion is 5 itself.
17605 
17606   If this method returns false, the segment for sure doesn't pass region 5. If it returns true, the
17607   segment may or may not pass region 5 and a more fine-grained calculation must be used (\ref
17608   getTraverse).
17609 */
mayTraverse(int prevRegion,int currentRegion) const17610 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
17611 {
17612   switch (prevRegion)
17613   {
17614     case 1:
17615     {
17616       switch (currentRegion)
17617       {
17618         case 4:
17619         case 7:
17620         case 2:
17621         case 3: return false;
17622         default: return true;
17623       }
17624     }
17625     case 2:
17626     {
17627       switch (currentRegion)
17628       {
17629         case 1:
17630         case 3: return false;
17631         default: return true;
17632       }
17633     }
17634     case 3:
17635     {
17636       switch (currentRegion)
17637       {
17638         case 1:
17639         case 2:
17640         case 6:
17641         case 9: return false;
17642         default: return true;
17643       }
17644     }
17645     case 4:
17646     {
17647       switch (currentRegion)
17648       {
17649         case 1:
17650         case 7: return false;
17651         default: return true;
17652       }
17653     }
17654     case 5: return false; // should never occur
17655     case 6:
17656     {
17657       switch (currentRegion)
17658       {
17659         case 3:
17660         case 9: return false;
17661         default: return true;
17662       }
17663     }
17664     case 7:
17665     {
17666       switch (currentRegion)
17667       {
17668         case 1:
17669         case 4:
17670         case 8:
17671         case 9: return false;
17672         default: return true;
17673       }
17674     }
17675     case 8:
17676     {
17677       switch (currentRegion)
17678       {
17679         case 7:
17680         case 9: return false;
17681         default: return true;
17682       }
17683     }
17684     case 9:
17685     {
17686       switch (currentRegion)
17687       {
17688         case 3:
17689         case 6:
17690         case 8:
17691         case 7: return false;
17692         default: return true;
17693       }
17694     }
17695     default: return true;
17696   }
17697 }
17698 
17699 
17700 /*! \internal
17701 
17702   This function is part of the curve optimization algorithm of \ref getCurveData.
17703 
17704   This method assumes that the \ref mayTraverse test has returned true, so there is a chance the
17705   segment defined by (\a prevKey, \a prevValue) and (\a key, \a value) goes through the visible
17706   region 5.
17707 
17708   The return value of this method indicates whether the segment actually traverses region 5 or not.
17709 
17710   If the segment traverses 5, the output parameters \a crossA and \a crossB indicate the entry and
17711   exit points of region 5. They will become the optimized points for that segment.
17712 */
getTraverse(double prevKey,double prevValue,double key,double value,double rectLeft,double rectTop,double rectRight,double rectBottom,QPointF & crossA,QPointF & crossB) const17713 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
17714 {
17715   QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
17716   if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
17717   {
17718     // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17719     intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method
17720     intersections.append(QPointF(key, rectTop));
17721   } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
17722   {
17723     // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17724     intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method
17725     intersections.append(QPointF(rectRight, value));
17726   } else // line is skewed
17727   {
17728     double gamma;
17729     double keyPerValue = (key-prevKey)/(value-prevValue);
17730     // check top of rect:
17731     gamma = prevKey + (rectTop-prevValue)*keyPerValue;
17732     if (gamma >= rectLeft && gamma <= rectRight)
17733       intersections.append(QPointF(gamma, rectTop));
17734     // check bottom of rect:
17735     gamma = prevKey + (rectBottom-prevValue)*keyPerValue;
17736     if (gamma >= rectLeft && gamma <= rectRight)
17737       intersections.append(QPointF(gamma, rectBottom));
17738     double valuePerKey = 1.0/keyPerValue;
17739     // check left of rect:
17740     gamma = prevValue + (rectLeft-prevKey)*valuePerKey;
17741     if (gamma >= rectBottom && gamma <= rectTop)
17742       intersections.append(QPointF(rectLeft, gamma));
17743     // check right of rect:
17744     gamma = prevValue + (rectRight-prevKey)*valuePerKey;
17745     if (gamma >= rectBottom && gamma <= rectTop)
17746       intersections.append(QPointF(rectRight, gamma));
17747   }
17748 
17749   // handle cases where found points isn't exactly 2:
17750   if (intersections.size() > 2)
17751   {
17752     // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
17753     double distSqrMax = 0;
17754     QPointF pv1, pv2;
17755     for (int i=0; i<intersections.size()-1; ++i)
17756     {
17757       for (int k=i+1; k<intersections.size(); ++k)
17758       {
17759         QPointF distPoint = intersections.at(i)-intersections.at(k);
17760         double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
17761         if (distSqr > distSqrMax)
17762         {
17763           pv1 = intersections.at(i);
17764           pv2 = intersections.at(k);
17765           distSqrMax = distSqr;
17766         }
17767       }
17768     }
17769     intersections = QList<QPointF>() << pv1 << pv2;
17770   } else if (intersections.size() != 2)
17771   {
17772     // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
17773     return false;
17774   }
17775 
17776   // possibly re-sort points so optimized point segment has same direction as original segment:
17777   if ((key-prevKey)*(intersections.at(1).x()-intersections.at(0).x()) + (value-prevValue)*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction
17778     intersections.move(0, 1);
17779   crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
17780   crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
17781   return true;
17782 }
17783 
17784 /*! \internal
17785 
17786   This function is part of the curve optimization algorithm of \ref getCurveData.
17787 
17788   This method assumes that the \ref getTraverse test has returned true, so the segment definitely
17789   traverses the visible region 5 when going from \a prevRegion to \a currentRegion.
17790 
17791   In certain situations it is not sufficient to merely generate the entry and exit points of the
17792   segment into/out of region 5, as \ref getTraverse provides. It may happen that a single segment, in
17793   addition to traversing region 5, skips another region outside of region 5, which makes it
17794   necessary to add an optimized corner point there (very similar to the job \ref
17795   getOptimizedCornerPoints does for segments that are completely in outside regions and don't
17796   traverse 5).
17797 
17798   As an example, consider a segment going from region 1 to region 6, traversing the lower left
17799   corner of region 5. In this configuration, the segment additionally crosses the border between
17800   region 1 and 2 before entering region 5. This makes it necessary to add an additional point in
17801   the top left corner, before adding the optimized traverse points. So in this case, the output
17802   parameter \a beforeTraverse will contain the top left corner point, and \a afterTraverse will be
17803   empty.
17804 
17805   In some cases, such as when going from region 1 to 9, it may even be necessary to add additional
17806   corner points before and after the traverse. Then both \a beforeTraverse and \a afterTraverse
17807   return the respective corner points.
17808 */
getTraverseCornerPoints(int prevRegion,int currentRegion,double rectLeft,double rectTop,double rectRight,double rectBottom,QVector<QPointF> & beforeTraverse,QVector<QPointF> & afterTraverse) const17809 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
17810 {
17811   switch (prevRegion)
17812   {
17813     case 1:
17814     {
17815       switch (currentRegion)
17816       {
17817         case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17818         case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17819         case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17820       }
17821       break;
17822     }
17823     case 2:
17824     {
17825       switch (currentRegion)
17826       {
17827         case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17828         case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17829       }
17830       break;
17831     }
17832     case 3:
17833     {
17834       switch (currentRegion)
17835       {
17836         case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17837         case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17838         case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17839       }
17840       break;
17841     }
17842     case 4:
17843     {
17844       switch (currentRegion)
17845       {
17846         case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17847         case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17848       }
17849       break;
17850     }
17851     case 5: { break; } // shouldn't happen because this method only handles full traverses
17852     case 6:
17853     {
17854       switch (currentRegion)
17855       {
17856         case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17857         case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17858       }
17859       break;
17860     }
17861     case 7:
17862     {
17863       switch (currentRegion)
17864       {
17865         case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17866         case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17867         case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17868       }
17869       break;
17870     }
17871     case 8:
17872     {
17873       switch (currentRegion)
17874       {
17875         case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17876         case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17877       }
17878       break;
17879     }
17880     case 9:
17881     {
17882       switch (currentRegion)
17883       {
17884         case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17885         case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17886         case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17887       }
17888       break;
17889     }
17890   }
17891 }
17892 
17893 /*! \internal
17894 
17895   Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a
17896   pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in
17897   \ref selectTest.
17898 */
pointDistance(const QPointF & pixelPoint) const17899 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
17900 {
17901   if (mData->isEmpty())
17902   {
17903     qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
17904     return 500;
17905   }
17906   if (mData->size() == 1)
17907   {
17908     QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
17909     return QVector2D(dataPoint-pixelPoint).length();
17910   }
17911 
17912   // calculate minimum distance to line segments:
17913   QVector<QPointF> *lineData = new QVector<QPointF>;
17914   getCurveData(lineData);
17915   double minDistSqr = std::numeric_limits<double>::max();
17916   for (int i=0; i<lineData->size()-1; ++i)
17917   {
17918     double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
17919     if (currentDistSqr < minDistSqr)
17920       minDistSqr = currentDistSqr;
17921   }
17922   delete lineData;
17923   return qSqrt(minDistSqr);
17924 }
17925 
17926 /* inherits documentation from base class */
getKeyRange(bool & foundRange,SignDomain inSignDomain) const17927 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17928 {
17929   QCPRange range;
17930   bool haveLower = false;
17931   bool haveUpper = false;
17932 
17933   double current;
17934 
17935   for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
17936   {
17937     current = it.value().key;
17938     if (!qIsNaN(current) && !qIsNaN(it.value().value))
17939     {
17940       if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17941       {
17942         if (current < range.lower || !haveLower)
17943         {
17944           range.lower = current;
17945           haveLower = true;
17946         }
17947         if (current > range.upper || !haveUpper)
17948         {
17949           range.upper = current;
17950           haveUpper = true;
17951         }
17952       }
17953     }
17954   }
17955 
17956   foundRange = haveLower && haveUpper;
17957   return range;
17958 }
17959 
17960 /* inherits documentation from base class */
getValueRange(bool & foundRange,SignDomain inSignDomain) const17961 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17962 {
17963   QCPRange range;
17964   bool haveLower = false;
17965   bool haveUpper = false;
17966 
17967   double current;
17968 
17969   for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
17970   {
17971     current = it.value().value;
17972     if (!qIsNaN(current) && !qIsNaN(it.value().key))
17973     {
17974       if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17975       {
17976         if (current < range.lower || !haveLower)
17977         {
17978           range.lower = current;
17979           haveLower = true;
17980         }
17981         if (current > range.upper || !haveUpper)
17982         {
17983           range.upper = current;
17984           haveUpper = true;
17985         }
17986       }
17987     }
17988   }
17989 
17990   foundRange = haveLower && haveUpper;
17991   return range;
17992 }
17993 
17994 
17995 ////////////////////////////////////////////////////////////////////////////////////////////////////
17996 //////////////////// QCPBarsGroup
17997 ////////////////////////////////////////////////////////////////////////////////////////////////////
17998 
17999 /*! \class QCPBarsGroup
18000   \brief Groups multiple QCPBars together so they appear side by side
18001 
18002   \image html QCPBarsGroup.png
18003 
18004   When showing multiple QCPBars in one plot which have bars at identical keys, it may be desirable
18005   to have them appearing next to each other at each key. This is what adding the respective QCPBars
18006   plottables to a QCPBarsGroup achieves. (An alternative approach is to stack them on top of each
18007   other, see \ref QCPBars::moveAbove.)
18008 
18009   \section qcpbarsgroup-usage Usage
18010 
18011   To add a QCPBars plottable to the group, create a new group and then add the respective bars
18012   intances:
18013   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbarsgroup-creation
18014   Alternatively to appending to the group like shown above, you can also set the group on the
18015   QCPBars plottable via \ref QCPBars::setBarsGroup.
18016 
18017   The spacing between the bars can be configured via \ref setSpacingType and \ref setSpacing. The
18018   bars in this group appear in the plot in the order they were appended. To insert a bars plottable
18019   at a certain index position, or to reposition a bars plottable which is already in the group, use
18020   \ref insert.
18021 
18022   To remove specific bars from the group, use either \ref remove or call \ref
18023   QCPBars::setBarsGroup "QCPBars::setBarsGroup(0)" on the respective bars plottable.
18024 
18025   To clear the entire group, call \ref clear, or simply delete the group.
18026 
18027   \section qcpbarsgroup-example Example
18028 
18029   The image above is generated with the following code:
18030   \snippet documentation/doc-image-generator/mainwindow.cpp qcpbarsgroup-example
18031 */
18032 
18033 /* start of documentation of inline functions */
18034 
18035 /*! \fn QList<QCPBars*> QCPBarsGroup::bars() const
18036 
18037   Returns all bars currently in this group.
18038 
18039   \see bars(int index)
18040 */
18041 
18042 /*! \fn int QCPBarsGroup::size() const
18043 
18044   Returns the number of QCPBars plottables that are part of this group.
18045 
18046 */
18047 
18048 /*! \fn bool QCPBarsGroup::isEmpty() const
18049 
18050   Returns whether this bars group is empty.
18051 
18052   \see size
18053 */
18054 
18055 /*! \fn bool QCPBarsGroup::contains(QCPBars *bars)
18056 
18057   Returns whether the specified \a bars plottable is part of this group.
18058 
18059 */
18060 
18061 /* end of documentation of inline functions */
18062 
18063 /*!
18064   Constructs a new bars group for the specified QCustomPlot instance.
18065 */
QCPBarsGroup(QCustomPlot * parentPlot)18066 QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) :
18067   QObject(parentPlot),
18068   mParentPlot(parentPlot),
18069   mSpacingType(stAbsolute),
18070   mSpacing(4)
18071 {
18072 }
18073 
~QCPBarsGroup()18074 QCPBarsGroup::~QCPBarsGroup()
18075 {
18076   clear();
18077 }
18078 
18079 /*!
18080   Sets how the spacing between adjacent bars is interpreted. See \ref SpacingType.
18081 
18082   The actual spacing can then be specified with \ref setSpacing.
18083 
18084   \see setSpacing
18085 */
setSpacingType(SpacingType spacingType)18086 void QCPBarsGroup::setSpacingType(SpacingType spacingType)
18087 {
18088   mSpacingType = spacingType;
18089 }
18090 
18091 /*!
18092   Sets the spacing between adjacent bars. What the number passed as \a spacing actually means, is
18093   defined by the current \ref SpacingType, which can be set with \ref setSpacingType.
18094 
18095   \see setSpacingType
18096 */
setSpacing(double spacing)18097 void QCPBarsGroup::setSpacing(double spacing)
18098 {
18099   mSpacing = spacing;
18100 }
18101 
18102 /*!
18103   Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars
18104   exists, returns 0.
18105 
18106   \see bars(), size
18107 */
bars(int index) const18108 QCPBars *QCPBarsGroup::bars(int index) const
18109 {
18110   if (index >= 0 && index < mBars.size())
18111   {
18112     return mBars.at(index);
18113   } else
18114   {
18115     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
18116     return 0;
18117   }
18118 }
18119 
18120 /*!
18121   Removes all QCPBars plottables from this group.
18122 
18123   \see isEmpty
18124 */
clear()18125 void QCPBarsGroup::clear()
18126 {
18127   for (auto* bars : mBars) // since range-based for takes a copy, removing bars in the loop is okay
18128     bars->setBarsGroup(0); // removes itself via removeBars
18129 }
18130 
18131 /*!
18132   Adds the specified \a bars plottable to this group. Alternatively, you can also use \ref
18133   QCPBars::setBarsGroup on the \a bars instance.
18134 
18135   \see insert, remove
18136 */
append(QCPBars * bars)18137 void QCPBarsGroup::append(QCPBars *bars)
18138 {
18139   if (!bars)
18140   {
18141     qDebug() << Q_FUNC_INFO << "bars is 0";
18142     return;
18143   }
18144 
18145   if (!mBars.contains(bars))
18146     bars->setBarsGroup(this);
18147   else
18148     qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
18149 }
18150 
18151 /*!
18152   Inserts the specified \a bars plottable into this group at the specified index position \a i.
18153   This gives you full control over the ordering of the bars.
18154 
18155   \a bars may already be part of this group. In that case, \a bars is just moved to the new index
18156   position.
18157 
18158   \see append, remove
18159 */
insert(int i,QCPBars * bars)18160 void QCPBarsGroup::insert(int i, QCPBars *bars)
18161 {
18162   if (!bars)
18163   {
18164     qDebug() << Q_FUNC_INFO << "bars is 0";
18165     return;
18166   }
18167 
18168   // first append to bars list normally:
18169   if (!mBars.contains(bars))
18170     bars->setBarsGroup(this);
18171   // then move to according position:
18172   mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
18173 }
18174 
18175 /*!
18176   Removes the specified \a bars plottable from this group.
18177 
18178   \see contains, clear
18179 */
remove(QCPBars * bars)18180 void QCPBarsGroup::remove(QCPBars *bars)
18181 {
18182   if (!bars)
18183   {
18184     qDebug() << Q_FUNC_INFO << "bars is 0";
18185     return;
18186   }
18187 
18188   if (mBars.contains(bars))
18189     bars->setBarsGroup(0);
18190   else
18191     qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
18192 }
18193 
18194 /*! \internal
18195 
18196   Adds the specified \a bars to the internal mBars list of bars. This method does not change the
18197   barsGroup property on \a bars.
18198 
18199   \see unregisterBars
18200 */
registerBars(QCPBars * bars)18201 void QCPBarsGroup::registerBars(QCPBars *bars)
18202 {
18203   if (!mBars.contains(bars))
18204     mBars.append(bars);
18205 }
18206 
18207 /*! \internal
18208 
18209   Removes the specified \a bars from the internal mBars list of bars. This method does not change
18210   the barsGroup property on \a bars.
18211 
18212   \see registerBars
18213 */
unregisterBars(QCPBars * bars)18214 void QCPBarsGroup::unregisterBars(QCPBars *bars)
18215 {
18216   mBars.removeOne(bars);
18217 }
18218 
18219 /*! \internal
18220 
18221   Returns the pixel offset in the key dimension the specified \a bars plottable should have at the
18222   given key coordinate \a keyCoord. The offset is relative to the pixel position of the key
18223   coordinate \a keyCoord.
18224 */
keyPixelOffset(const QCPBars * bars,double keyCoord)18225 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
18226 {
18227   // find list of all base bars in case some mBars are stacked:
18228   QList<const QCPBars*> baseBars;
18229   for (const auto* b : mBars)
18230   {
18231     while (b->barBelow())
18232       b = b->barBelow();
18233     if (!baseBars.contains(b))
18234       baseBars.append(b);
18235   }
18236   // find base bar this "bars" is stacked on:
18237   const QCPBars *thisBase = bars;
18238   while (thisBase->barBelow())
18239     thisBase = thisBase->barBelow();
18240 
18241   // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
18242   double result = 0;
18243   int index = baseBars.indexOf(thisBase);
18244   if (index >= 0)
18245   {
18246     int startIndex;
18247     double lowerPixelWidth, upperPixelWidth;
18248     if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
18249     {
18250       return result;
18251     } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center
18252     {
18253       if (baseBars.size() % 2 == 0) // even number of bars
18254       {
18255         startIndex = baseBars.size()/2-1;
18256         result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18257       } else // uneven number of bars
18258       {
18259         startIndex = (baseBars.size()-1)/2-1;
18260         baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18261         result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18262         result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18263       }
18264       for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars
18265       {
18266         baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18267         result -= qAbs(upperPixelWidth-lowerPixelWidth);
18268         result -= getPixelSpacing(baseBars.at(i), keyCoord);
18269       }
18270       // finally half of our bars width:
18271       baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18272       result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18273     } else // bar is to the right of center
18274     {
18275       if (baseBars.size() % 2 == 0) // even number of bars
18276       {
18277         startIndex = baseBars.size()/2;
18278         result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18279       } else // uneven number of bars
18280       {
18281         startIndex = (baseBars.size()-1)/2+1;
18282         baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18283         result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18284         result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18285       }
18286       for (int i=startIndex; i<index; ++i) // add widths and spacings of bars in between center and our bars
18287       {
18288         baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18289         result += qAbs(upperPixelWidth-lowerPixelWidth);
18290         result += getPixelSpacing(baseBars.at(i), keyCoord);
18291       }
18292       // finally half of our bars width:
18293       baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18294       result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18295     }
18296   }
18297   return result;
18298 }
18299 
18300 /*! \internal
18301 
18302   Returns the spacing in pixels which is between this \a bars and the following one, both at the
18303   key coordinate \a keyCoord.
18304 
18305   \note Typically the returned value doesn't depend on \a bars or \a keyCoord. \a bars is only
18306   needed to get acces to the key axis transformation and axis rect for the modes \ref
18307   stAxisRectRatio and \ref stPlotCoords. The \a keyCoord is only relevant for spacings given in
18308   \ref stPlotCoords on a logarithmic axis.
18309 */
getPixelSpacing(const QCPBars * bars,double keyCoord)18310 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
18311 {
18312   switch (mSpacingType)
18313   {
18314     case stAbsolute:
18315     {
18316       return mSpacing;
18317     }
18318     case stAxisRectRatio:
18319     {
18320       if (bars->keyAxis()->orientation() == Qt::Horizontal)
18321         return bars->keyAxis()->axisRect()->width()*mSpacing;
18322       else
18323         return bars->keyAxis()->axisRect()->height()*mSpacing;
18324     }
18325     case stPlotCoords:
18326     {
18327       double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
18328       return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel;
18329     }
18330   }
18331   return 0;
18332 }
18333 
18334 
18335 ////////////////////////////////////////////////////////////////////////////////////////////////////
18336 //////////////////// QCPBarData
18337 ////////////////////////////////////////////////////////////////////////////////////////////////////
18338 
18339 /*! \class QCPBarData
18340   \brief Holds the data of one single data point (one bar) for QCPBars.
18341 
18342   The container for storing multiple data points is \ref QCPBarDataMap.
18343 
18344   The stored data is:
18345   \li \a key: coordinate on the key axis of this bar
18346   \li \a value: height coordinate on the value axis of this bar
18347 
18348   \see QCPBarDataaMap
18349 */
18350 
18351 /*!
18352   Constructs a bar data point with key and value set to zero.
18353 */
QCPBarData()18354 QCPBarData::QCPBarData() :
18355   key(0),
18356   value(0)
18357 {
18358 }
18359 
18360 /*!
18361   Constructs a bar data point with the specified \a key and \a value.
18362 */
QCPBarData(double key,double value)18363 QCPBarData::QCPBarData(double key, double value) :
18364   key(key),
18365   value(value)
18366 {
18367 }
18368 
18369 
18370 ////////////////////////////////////////////////////////////////////////////////////////////////////
18371 //////////////////// QCPBars
18372 ////////////////////////////////////////////////////////////////////////////////////////////////////
18373 
18374 /*! \class QCPBars
18375   \brief A plottable representing a bar chart in a plot.
18376 
18377   \image html QCPBars.png
18378 
18379   To plot data, assign it with the \ref setData or \ref addData functions.
18380 
18381   \section appearance Changing the appearance
18382 
18383   The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush).
18384   The width of the individual bars can be controlled with \ref setWidthType and \ref setWidth.
18385 
18386   Bar charts are stackable. This means, two QCPBars plottables can be placed on top of each other
18387   (see \ref QCPBars::moveAbove). So when two bars are at the same key position, they will appear
18388   stacked.
18389 
18390   If you would like to group multiple QCPBars plottables together so they appear side by side as
18391   shown below, use QCPBarsGroup.
18392 
18393   \image html QCPBarsGroup.png
18394 
18395   \section usage Usage
18396 
18397   Like all data representing objects in QCustomPlot, the QCPBars is a plottable
18398   (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
18399   (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
18400 
18401   Usually, you first create an instance:
18402   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1
18403   add it to the customPlot with QCustomPlot::addPlottable:
18404   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2
18405   and then modify the properties of the newly created plottable, e.g.:
18406   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-3
18407 */
18408 
18409 /* start of documentation of inline functions */
18410 
18411 /*! \fn QCPBars *QCPBars::barBelow() const
18412   Returns the bars plottable that is directly below this bars plottable.
18413   If there is no such plottable, returns 0.
18414 
18415   \see barAbove, moveBelow, moveAbove
18416 */
18417 
18418 /*! \fn QCPBars *QCPBars::barAbove() const
18419   Returns the bars plottable that is directly above this bars plottable.
18420   If there is no such plottable, returns 0.
18421 
18422   \see barBelow, moveBelow, moveAbove
18423 */
18424 
18425 /* end of documentation of inline functions */
18426 
18427 /*!
18428   Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
18429   axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
18430   the same orientation. If either of these restrictions is violated, a corresponding message is
18431   printed to the debug output (qDebug), the construction is not aborted, though.
18432 
18433   The constructed QCPBars can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
18434   then takes ownership of the bar chart.
18435 */
QCPBars(QCPAxis * keyAxis,QCPAxis * valueAxis)18436 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
18437   QCPAbstractPlottable(keyAxis, valueAxis),
18438   mData(new QCPBarDataMap),
18439   mWidth(0.75),
18440   mWidthType(wtPlotCoords),
18441   mBarsGroup(0),
18442   mBaseValue(0)
18443 {
18444   // modify inherited properties from abstract plottable:
18445   mPen.setColor(Qt::blue);
18446   mPen.setStyle(Qt::SolidLine);
18447   mBrush.setColor(QColor(40, 50, 255, 30));
18448   mBrush.setStyle(Qt::SolidPattern);
18449   mSelectedPen = mPen;
18450   mSelectedPen.setWidthF(2.5);
18451   mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
18452   mSelectedBrush = mBrush;
18453 }
18454 
~QCPBars()18455 QCPBars::~QCPBars()
18456 {
18457   setBarsGroup(0);
18458   if (mBarBelow || mBarAbove)
18459     connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
18460   delete mData;
18461 }
18462 
18463 /*!
18464   Sets the width of the bars.
18465 
18466   How the number passed as \a width is interpreted (e.g. screen pixels, plot coordinates,...),
18467   depends on the currently set width type, see \ref setWidthType and \ref WidthType.
18468 */
setWidth(double width)18469 void QCPBars::setWidth(double width)
18470 {
18471   mWidth = width;
18472 }
18473 
18474 /*!
18475   Sets how the width of the bars is defined. See the documentation of \ref WidthType for an
18476   explanation of the possible values for \a widthType.
18477 
18478   The default value is \ref wtPlotCoords.
18479 
18480   \see setWidth
18481 */
setWidthType(QCPBars::WidthType widthType)18482 void QCPBars::setWidthType(QCPBars::WidthType widthType)
18483 {
18484   mWidthType = widthType;
18485 }
18486 
18487 /*!
18488   Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref
18489   QCPBarsGroup::append.
18490 
18491   To remove this QCPBars from any group, set \a barsGroup to 0.
18492 */
setBarsGroup(QCPBarsGroup * barsGroup)18493 void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup)
18494 {
18495   // deregister at old group:
18496   if (mBarsGroup)
18497     mBarsGroup->unregisterBars(this);
18498   mBarsGroup = barsGroup;
18499   // register at new group:
18500   if (mBarsGroup)
18501     mBarsGroup->registerBars(this);
18502 }
18503 
18504 /*!
18505   Sets the base value of this bars plottable.
18506 
18507   The base value defines where on the value coordinate the bars start. How far the bars extend from
18508   the base value is given by their individual value data. For example, if the base value is set to
18509   1, a bar with data value 2 will have its lowest point at value coordinate 1 and highest point at
18510   3.
18511 
18512   For stacked bars, only the base value of the bottom-most QCPBars has meaning.
18513 
18514   The default base value is 0.
18515 */
setBaseValue(double baseValue)18516 void QCPBars::setBaseValue(double baseValue)
18517 {
18518   mBaseValue = baseValue;
18519 }
18520 
18521 /*!
18522   Replaces the current data with the provided \a data.
18523 
18524   If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
18525   takes ownership of the passed data and replaces the internal data pointer with it. This is
18526   significantly faster than copying for large datasets.
18527 */
setData(QCPBarDataMap * data,bool copy)18528 void QCPBars::setData(QCPBarDataMap *data, bool copy)
18529 {
18530   if (mData == data)
18531   {
18532     qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
18533     return;
18534   }
18535   if (copy)
18536   {
18537     *mData = *data;
18538   } else
18539   {
18540     delete mData;
18541     mData = data;
18542   }
18543 }
18544 
18545 /*! \overload
18546 
18547   Replaces the current data with the provided points in \a key and \a value tuples. The
18548   provided vectors should have equal length. Else, the number of added points will be the size of
18549   the smallest vector.
18550 */
setData(const QVector<double> & key,const QVector<double> & value)18551 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
18552 {
18553   mData->clear();
18554   int n = key.size();
18555   n = qMin(n, value.size());
18556   QCPBarData newData;
18557   for (int i=0; i<n; ++i)
18558   {
18559     newData.key = key[i];
18560     newData.value = value[i];
18561     mData->insertMulti(newData.key, newData);
18562   }
18563 }
18564 
18565 /*!
18566   Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear
18567   below the bars of \a bars. The move target \a bars must use the same key and value axis as this
18568   plottable.
18569 
18570   Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
18571   has a bars object below itself, this bars object is inserted between the two. If this bars object
18572   is already between two other bars, the two other bars will be stacked on top of each other after
18573   the operation.
18574 
18575   To remove this bars plottable from any stacking, set \a bars to 0.
18576 
18577   \see moveBelow, barAbove, barBelow
18578 */
moveBelow(QCPBars * bars)18579 void QCPBars::moveBelow(QCPBars *bars)
18580 {
18581   if (bars == this) return;
18582   if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18583   {
18584     qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18585     return;
18586   }
18587   // remove from stacking:
18588   connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18589   // if new bar given, insert this bar below it:
18590   if (bars)
18591   {
18592     if (bars->mBarBelow)
18593       connectBars(bars->mBarBelow.data(), this);
18594     connectBars(this, bars);
18595   }
18596 }
18597 
18598 /*!
18599   Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear
18600   above the bars of \a bars. The move target \a bars must use the same key and value axis as this
18601   plottable.
18602 
18603   Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
18604   has a bars object above itself, this bars object is inserted between the two. If this bars object
18605   is already between two other bars, the two other bars will be stacked on top of each other after
18606   the operation.
18607 
18608   To remove this bars plottable from any stacking, set \a bars to 0.
18609 
18610   \see moveBelow, barBelow, barAbove
18611 */
moveAbove(QCPBars * bars)18612 void QCPBars::moveAbove(QCPBars *bars)
18613 {
18614   if (bars == this) return;
18615   if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18616   {
18617     qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18618     return;
18619   }
18620   // remove from stacking:
18621   connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18622   // if new bar given, insert this bar above it:
18623   if (bars)
18624   {
18625     if (bars->mBarAbove)
18626       connectBars(this, bars->mBarAbove.data());
18627     connectBars(bars, this);
18628   }
18629 }
18630 
18631 /*!
18632   Adds the provided data points in \a dataMap to the current data.
18633   \see removeData
18634 */
addData(const QCPBarDataMap & dataMap)18635 void QCPBars::addData(const QCPBarDataMap &dataMap)
18636 {
18637   mData->unite(dataMap);
18638 }
18639 
18640 /*! \overload
18641   Adds the provided single data point in \a data to the current data.
18642   \see removeData
18643 */
addData(const QCPBarData & data)18644 void QCPBars::addData(const QCPBarData &data)
18645 {
18646   mData->insertMulti(data.key, data);
18647 }
18648 
18649 /*! \overload
18650   Adds the provided single data point as \a key and \a value tuple to the current data
18651   \see removeData
18652 */
addData(double key,double value)18653 void QCPBars::addData(double key, double value)
18654 {
18655   QCPBarData newData;
18656   newData.key = key;
18657   newData.value = value;
18658   mData->insertMulti(newData.key, newData);
18659 }
18660 
18661 /*! \overload
18662   Adds the provided data points as \a key and \a value tuples to the current data.
18663   \see removeData
18664 */
addData(const QVector<double> & keys,const QVector<double> & values)18665 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
18666 {
18667   int n = keys.size();
18668   n = qMin(n, values.size());
18669   QCPBarData newData;
18670   for (int i=0; i<n; ++i)
18671   {
18672     newData.key = keys[i];
18673     newData.value = values[i];
18674     mData->insertMulti(newData.key, newData);
18675   }
18676 }
18677 
18678 /*!
18679   Removes all data points with key smaller than \a key.
18680   \see addData, clearData
18681 */
removeDataBefore(double key)18682 void QCPBars::removeDataBefore(double key)
18683 {
18684   auto it = mData->begin();
18685   while (it != mData->end() && it.key() < key)
18686     it = mData->erase(it);
18687 }
18688 
18689 /*!
18690   Removes all data points with key greater than \a key.
18691   \see addData, clearData
18692 */
removeDataAfter(double key)18693 void QCPBars::removeDataAfter(double key)
18694 {
18695   if (mData->isEmpty()) return;
18696   auto it = mData->upperBound(key);
18697   while (it != mData->end())
18698     it = mData->erase(it);
18699 }
18700 
18701 /*!
18702   Removes all data points with key between \a fromKey and \a toKey. if \a fromKey is
18703   greater or equal to \a toKey, the function does nothing. To remove a single data point with known
18704   key, use \ref removeData(double key).
18705 
18706   \see addData, clearData
18707 */
removeData(double fromKey,double toKey)18708 void QCPBars::removeData(double fromKey, double toKey)
18709 {
18710   if (fromKey >= toKey || mData->isEmpty()) return;
18711   auto it = mData->upperBound(fromKey);
18712   auto itEnd = mData->upperBound(toKey);
18713   while (it != itEnd)
18714     it = mData->erase(it);
18715 }
18716 
18717 /*! \overload
18718 
18719   Removes a single data point at \a key. If the position is not known with absolute precision,
18720   consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
18721   around the suspected position, depeding on the precision with which the key is known.
18722 
18723   \see addData, clearData
18724 */
removeData(double key)18725 void QCPBars::removeData(double key)
18726 {
18727   mData->remove(key);
18728 }
18729 
18730 /*!
18731   Removes all data points.
18732   \see removeData, removeDataAfter, removeDataBefore
18733 */
clearData()18734 void QCPBars::clearData()
18735 {
18736   mData->clear();
18737 }
18738 
18739 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const18740 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18741 {
18742   Q_UNUSED(details)
18743   if (onlySelectable && !mSelectable)
18744     return -1;
18745   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18746 
18747   if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
18748   {
18749     QCPBarDataMap::ConstIterator it;
18750     for (it = mData->constBegin(); it != mData->constEnd(); ++it)
18751     {
18752       if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos))
18753         return mParentPlot->selectionTolerance()*0.99;
18754     }
18755   }
18756   return -1;
18757 }
18758 
18759 /* inherits documentation from base class */
draw(QCPPainter * painter)18760 void QCPBars::draw(QCPPainter *painter)
18761 {
18762   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
18763   if (mData->isEmpty()) return;
18764 
18765   QCPBarDataMap::const_iterator lower, upperEnd;
18766   getVisibleDataBounds(lower, upperEnd);
18767   for (auto it = lower; it != upperEnd; ++it)
18768   {
18769     // check data validity if flag set:
18770 #ifdef QCUSTOMPLOT_CHECK_DATA
18771     if (QCP::isInvalidData(it.value().key, it.value().value))
18772       qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
18773 #endif
18774     QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
18775     // draw bar fill:
18776     if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
18777     {
18778       applyFillAntialiasingHint(painter);
18779       painter->setPen(Qt::NoPen);
18780       painter->setBrush(mainBrush());
18781       painter->drawPolygon(barPolygon);
18782     }
18783     // draw bar line:
18784     if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
18785     {
18786       applyDefaultAntialiasingHint(painter);
18787       painter->setPen(mainPen());
18788       painter->setBrush(Qt::NoBrush);
18789       painter->drawPolyline(barPolygon);
18790     }
18791   }
18792 }
18793 
18794 /* inherits documentation from base class */
drawLegendIcon(QCPPainter * painter,const QRectF & rect) const18795 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
18796 {
18797   // draw filled rect:
18798   applyDefaultAntialiasingHint(painter);
18799   painter->setBrush(mBrush);
18800   painter->setPen(mPen);
18801   QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
18802   r.moveCenter(rect.center());
18803   painter->drawRect(r);
18804 }
18805 
18806 /*!  \internal
18807 
18808   called by \ref draw to determine which data (key) range is visible at the current key axis range
18809   setting, so only that needs to be processed. It also takes into account the bar width.
18810 
18811   \a lower returns an iterator to the lowest data point that needs to be taken into account when
18812   plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
18813   lower may still be just outside the visible range.
18814 
18815   \a upperEnd returns an iterator one higher than the highest visible data point. Same as before, \a
18816   upperEnd may also lie just outside of the visible range.
18817 
18818   if the bars plottable contains no data, both \a lower and \a upperEnd point to constEnd.
18819 */
getVisibleDataBounds(QCPBarDataMap::const_iterator & lower,QCPBarDataMap::const_iterator & upperEnd) const18820 void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
18821 {
18822   if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
18823   if (mData->isEmpty())
18824   {
18825     lower = mData->constEnd();
18826     upperEnd = mData->constEnd();
18827     return;
18828   }
18829 
18830   // get visible data range as QMap iterators
18831   lower = mData->lowerBound(mKeyAxis->range().lower);
18832   upperEnd = mData->upperBound(mKeyAxis->range().upper);
18833   double lowerPixelBound = mKeyAxis->coordToPixel(mKeyAxis->range().lower);
18834   double upperPixelBound = mKeyAxis->coordToPixel(mKeyAxis->range().upper);
18835   bool isVisible = false;
18836   // walk left from lbound to find lower bar that actually is completely outside visible pixel range:
18837   auto it = lower;
18838   while (it != mData->constBegin())
18839   {
18840     --it;
18841     QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect();
18842     if (mKeyAxis->orientation() == Qt::Horizontal)
18843       isVisible = ((!mKeyAxis->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis->rangeReversed() && barBounds.left() <= lowerPixelBound));
18844     else // keyaxis is vertical
18845       isVisible = ((!mKeyAxis->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis->rangeReversed() && barBounds.bottom() >= lowerPixelBound));
18846     if (isVisible)
18847       lower = it;
18848     else
18849       break;
18850   }
18851   // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
18852   it = upperEnd;
18853   while (it != mData->constEnd())
18854   {
18855     QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect();
18856     if (mKeyAxis->orientation() == Qt::Horizontal)
18857       isVisible = ((!mKeyAxis->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis->rangeReversed() && barBounds.right() >= upperPixelBound));
18858     else // keyaxis is vertical
18859       isVisible = ((!mKeyAxis->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis->rangeReversed() && barBounds.top() <= upperPixelBound));
18860     if (isVisible)
18861       upperEnd = it+1;
18862     else
18863       break;
18864     ++it;
18865   }
18866 }
18867 
18868 /*! \internal
18869 
18870   Returns the polygon of a single bar with \a key and \a value. The Polygon is open at the bottom
18871   and shifted according to the bar stacking (see \ref moveAbove) and base value (see \ref
18872   setBaseValue).
18873 */
getBarPolygon(double key,double value) const18874 QPolygonF QCPBars::getBarPolygon(double key, double value) const
18875 {
18876   QCPAxis *keyAxis = mKeyAxis.data();
18877   QCPAxis *valueAxis = mValueAxis.data();
18878   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
18879 
18880   QPolygonF result;
18881   double lowerPixelWidth, upperPixelWidth;
18882   getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
18883   double base = getStackedBaseValue(key, value >= 0);
18884   double basePixel = valueAxis->coordToPixel(base);
18885   double valuePixel = valueAxis->coordToPixel(base+value);
18886   double keyPixel = keyAxis->coordToPixel(key);
18887   if (mBarsGroup)
18888     keyPixel += mBarsGroup->keyPixelOffset(this, key);
18889   if (keyAxis->orientation() == Qt::Horizontal)
18890   {
18891     result << QPointF(keyPixel+lowerPixelWidth, basePixel);
18892     result << QPointF(keyPixel+lowerPixelWidth, valuePixel);
18893     result << QPointF(keyPixel+upperPixelWidth, valuePixel);
18894     result << QPointF(keyPixel+upperPixelWidth, basePixel);
18895   } else
18896   {
18897     result << QPointF(basePixel, keyPixel+lowerPixelWidth);
18898     result << QPointF(valuePixel, keyPixel+lowerPixelWidth);
18899     result << QPointF(valuePixel, keyPixel+upperPixelWidth);
18900     result << QPointF(basePixel, keyPixel+upperPixelWidth);
18901   }
18902   return result;
18903 }
18904 
18905 /*! \internal
18906 
18907   This function is used to determine the width of the bar at coordinate \a key, according to the
18908   specified width (\ref setWidth) and width type (\ref setWidthType).
18909 
18910   The output parameters \a lower and \a upper return the number of pixels the bar extends to lower
18911   and higher keys, relative to the \a key coordinate (so with a non-reversed horizontal axis, \a
18912   lower is negative and \a upper positive).
18913 */
getPixelWidth(double key,double & lower,double & upper) const18914 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
18915 {
18916   switch (mWidthType)
18917   {
18918     case wtAbsolute:
18919     {
18920       upper = mWidth*0.5;
18921       lower = -upper;
18922       if (mKeyAxis && (mKeyAxis->rangeReversed() ^ (mKeyAxis->orientation() == Qt::Vertical)))
18923         qSwap(lower, upper);
18924       break;
18925     }
18926     case wtAxisRectRatio:
18927     {
18928       if (mKeyAxis && mKeyAxis->axisRect())
18929       {
18930         if (mKeyAxis->orientation() == Qt::Horizontal)
18931           upper = mKeyAxis->axisRect()->width()*mWidth*0.5;
18932         else
18933           upper = mKeyAxis->axisRect()->height()*mWidth*0.5;
18934         lower = -upper;
18935         if (mKeyAxis && (mKeyAxis->rangeReversed() ^ (mKeyAxis->orientation() == Qt::Vertical)))
18936           qSwap(lower, upper);
18937       } else
18938         qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
18939       break;
18940     }
18941     case wtPlotCoords:
18942     {
18943       if (mKeyAxis)
18944       {
18945         double keyPixel = mKeyAxis->coordToPixel(key);
18946         upper = mKeyAxis->coordToPixel(key+mWidth*0.5)-keyPixel;
18947         lower = mKeyAxis->coordToPixel(key-mWidth*0.5)-keyPixel;
18948         // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
18949         // coordinate transform which includes range direction
18950       } else
18951         qDebug() << Q_FUNC_INFO << "No key axis defined";
18952       break;
18953     }
18954   }
18955 }
18956 
18957 /*! \internal
18958 
18959   This function is called to find at which value to start drawing the base of a bar at \a key, when
18960   it is stacked on top of another QCPBars (e.g. with \ref moveAbove).
18961 
18962   positive and negative bars are separated per stack (positive are stacked above baseValue upwards,
18963   negative are stacked below baseValue downwards). This can be indicated with \a positive. So if the
18964   bar for which we need the base value is negative, set \a positive to false.
18965 */
getStackedBaseValue(double key,bool positive) const18966 double QCPBars::getStackedBaseValue(double key, bool positive) const
18967 {
18968   if (mBarBelow)
18969   {
18970     double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
18971     // find bars of mBarBelow that are approximately at key and find largest one:
18972     double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point
18973     if (key == 0)
18974       epsilon = 1e-6;
18975     for (auto it = mBarBelow->mData->lowerBound(key-epsilon), itEnd = mBarBelow->mData->upperBound(key+epsilon); it != itEnd; ++it)
18976     {
18977       if ((positive && it.value().value > max) ||
18978           (!positive && it.value().value < max))
18979         max = it.value().value;
18980     }
18981     // recurse down the bar-stack to find the total height:
18982     return max + mBarBelow->getStackedBaseValue(key, positive);
18983   } else
18984     return mBaseValue;
18985 }
18986 
18987 /*! \internal
18988 
18989   Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties. The bar(s)
18990   currently above lower and below upper will become disconnected to lower/upper.
18991 
18992   If lower is zero, upper will be disconnected at the bottom.
18993   If upper is zero, lower will be disconnected at the top.
18994 */
connectBars(QCPBars * lower,QCPBars * upper)18995 void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
18996 {
18997   if (!lower && !upper) return;
18998 
18999   if (!lower) // disconnect upper at bottom
19000   {
19001     // disconnect old bar below upper:
19002     if (upper->mBarBelow && upper->mBarBelow->mBarAbove.data() == upper)
19003       upper->mBarBelow->mBarAbove = 0;
19004     upper->mBarBelow = 0;
19005   } else if (!upper) // disconnect lower at top
19006   {
19007     // disconnect old bar above lower:
19008     if (lower->mBarAbove && lower->mBarAbove->mBarBelow.data() == lower)
19009       lower->mBarAbove->mBarBelow = 0;
19010     lower->mBarAbove = 0;
19011   } else // connect lower and upper
19012   {
19013     // disconnect old bar above lower:
19014     if (lower->mBarAbove && lower->mBarAbove->mBarBelow.data() == lower)
19015       lower->mBarAbove->mBarBelow = 0;
19016     // disconnect old bar below upper:
19017     if (upper->mBarBelow && upper->mBarBelow->mBarAbove.data() == upper)
19018       upper->mBarBelow->mBarAbove = 0;
19019     lower->mBarAbove = upper;
19020     upper->mBarBelow = lower;
19021   }
19022 }
19023 
19024 /* inherits documentation from base class */
getKeyRange(bool & foundRange,SignDomain inSignDomain) const19025 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19026 {
19027   QCPRange range;
19028   bool haveLower = false;
19029   bool haveUpper = false;
19030 
19031   double current;
19032   for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
19033   {
19034     current = it.value().key;
19035     if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19036     {
19037       if (current < range.lower || !haveLower)
19038       {
19039         range.lower = current;
19040         haveLower = true;
19041       }
19042       if (current > range.upper || !haveUpper)
19043       {
19044         range.upper = current;
19045         haveUpper = true;
19046       }
19047     }
19048   }
19049   // determine exact range of bars by including bar width and barsgroup offset:
19050   if (haveLower && mKeyAxis)
19051   {
19052     double lowerPixelWidth, upperPixelWidth, keyPixel;
19053     getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
19054     keyPixel = mKeyAxis->coordToPixel(range.lower) + lowerPixelWidth;
19055     if (mBarsGroup)
19056       keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
19057     range.lower = mKeyAxis->pixelToCoord(keyPixel);
19058   }
19059   if (haveUpper && mKeyAxis)
19060   {
19061     double lowerPixelWidth, upperPixelWidth, keyPixel;
19062     getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
19063     keyPixel = mKeyAxis->coordToPixel(range.upper) + upperPixelWidth;
19064     if (mBarsGroup)
19065       keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
19066     range.upper = mKeyAxis->pixelToCoord(keyPixel);
19067   }
19068   foundRange = haveLower && haveUpper;
19069   return range;
19070 }
19071 
19072 /* inherits documentation from base class */
getValueRange(bool & foundRange,SignDomain inSignDomain) const19073 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19074 {
19075   QCPRange range;
19076   range.lower = mBaseValue;
19077   range.upper = mBaseValue;
19078   bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
19079   bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
19080   double current;
19081 
19082   for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
19083   {
19084     current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0);
19085     if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19086     {
19087       if (current < range.lower || !haveLower)
19088       {
19089         range.lower = current;
19090         haveLower = true;
19091       }
19092       if (current > range.upper || !haveUpper)
19093       {
19094         range.upper = current;
19095         haveUpper = true;
19096       }
19097     }
19098   }
19099 
19100   foundRange = true; // return true because bar charts always have the 0-line visible
19101   return range;
19102 }
19103 
19104 
19105 ////////////////////////////////////////////////////////////////////////////////////////////////////
19106 //////////////////// QCPStatisticalBox
19107 ////////////////////////////////////////////////////////////////////////////////////////////////////
19108 
19109 /*! \class QCPStatisticalBox
19110   \brief A plottable representing a single statistical box in a plot.
19111 
19112   \image html QCPStatisticalBox.png
19113 
19114   To plot data, assign it with the individual parameter functions or use \ref setData to set all
19115   parameters at once. The individual functions are:
19116   \li \ref setMinimum
19117   \li \ref setLowerQuartile
19118   \li \ref setMedian
19119   \li \ref setUpperQuartile
19120   \li \ref setMaximum
19121 
19122   Additionally you can define a list of outliers, drawn as scatter datapoints:
19123   \li \ref setOutliers
19124 
19125   \section appearance Changing the appearance
19126 
19127   The appearance of the box itself is controlled via \ref setPen and \ref setBrush. You may change
19128   the width of the box with \ref setWidth in plot coordinates (not pixels).
19129 
19130   Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen, \ref
19131   setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of the bar at the top
19132   (maximum) and bottom (minimum).
19133 
19134   The median indicator line has its own pen, \ref setMedianPen.
19135 
19136   If the whisker backbone pen is changed, make sure to set the capStyle to Qt::FlatCap. Else, the
19137   backbone line might exceed the whisker bars by a few pixels due to the pen cap being not
19138   perfectly flat.
19139 
19140   The Outlier data points are drawn as normal scatter points. Their look can be controlled with
19141   \ref setOutlierStyle
19142 
19143   \section usage Usage
19144 
19145   Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable.
19146   Usually, you first create an instance and add it to the customPlot:
19147   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-1
19148   and then modify the properties of the newly created plottable, e.g.:
19149   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2
19150 */
19151 
19152 /*!
19153   Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its
19154   value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and
19155   not have the same orientation. If either of these restrictions is violated, a corresponding
19156   message is printed to the debug output (qDebug), the construction is not aborted, though.
19157 
19158   The constructed statistical box can be added to the plot with QCustomPlot::addPlottable,
19159   QCustomPlot then takes ownership of the statistical box.
19160 */
QCPStatisticalBox(QCPAxis * keyAxis,QCPAxis * valueAxis)19161 QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :
19162   QCPAbstractPlottable(keyAxis, valueAxis),
19163   mKey(0),
19164   mMinimum(0),
19165   mLowerQuartile(0),
19166   mMedian(0),
19167   mUpperQuartile(0),
19168   mMaximum(0)
19169 {
19170   setOutlierStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, 6));
19171   setWhiskerWidth(0.2);
19172   setWidth(0.5);
19173 
19174   setPen(QPen(Qt::black));
19175   setSelectedPen(QPen(Qt::blue, 2.5));
19176   setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
19177   setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
19178   setWhiskerBarPen(QPen(Qt::black));
19179   setBrush(Qt::NoBrush);
19180   setSelectedBrush(Qt::NoBrush);
19181 }
19182 
19183 /*!
19184   Sets the key coordinate of the statistical box.
19185 */
setKey(double key)19186 void QCPStatisticalBox::setKey(double key)
19187 {
19188   mKey = key;
19189 }
19190 
19191 /*!
19192   Sets the parameter "minimum" of the statistical box plot. This is the position of the lower
19193   whisker, typically the minimum measurement of the sample that's not considered an outlier.
19194 
19195   \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19196 */
setMinimum(double value)19197 void QCPStatisticalBox::setMinimum(double value)
19198 {
19199   mMinimum = value;
19200 }
19201 
19202 /*!
19203   Sets the parameter "lower Quartile" of the statistical box plot. This is the lower end of the
19204   box. The lower and the upper quartiles are the two statistical quartiles around the median of the
19205   sample, they contain 50% of the sample data.
19206 
19207   \see setUpperQuartile, setPen, setBrush, setWidth
19208 */
setLowerQuartile(double value)19209 void QCPStatisticalBox::setLowerQuartile(double value)
19210 {
19211   mLowerQuartile = value;
19212 }
19213 
19214 /*!
19215   Sets the parameter "median" of the statistical box plot. This is the value of the median mark
19216   inside the quartile box. The median separates the sample data in half (50% of the sample data is
19217   below/above the median).
19218 
19219   \see setMedianPen
19220 */
setMedian(double value)19221 void QCPStatisticalBox::setMedian(double value)
19222 {
19223   mMedian = value;
19224 }
19225 
19226 /*!
19227   Sets the parameter "upper Quartile" of the statistical box plot. This is the upper end of the
19228   box. The lower and the upper quartiles are the two statistical quartiles around the median of the
19229   sample, they contain 50% of the sample data.
19230 
19231   \see setLowerQuartile, setPen, setBrush, setWidth
19232 */
setUpperQuartile(double value)19233 void QCPStatisticalBox::setUpperQuartile(double value)
19234 {
19235   mUpperQuartile = value;
19236 }
19237 
19238 /*!
19239   Sets the parameter "maximum" of the statistical box plot. This is the position of the upper
19240   whisker, typically the maximum measurement of the sample that's not considered an outlier.
19241 
19242   \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19243 */
setMaximum(double value)19244 void QCPStatisticalBox::setMaximum(double value)
19245 {
19246   mMaximum = value;
19247 }
19248 
19249 /*!
19250   Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample
19251   that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers
19252   and displayed as such.
19253 
19254   \see setOutlierStyle
19255 */
setOutliers(const QVector<double> & values)19256 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
19257 {
19258   mOutliers = values;
19259 }
19260 
19261 /*!
19262   Sets all parameters of the statistical box plot at once.
19263 
19264   \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile, setMaximum
19265 */
setData(double key,double minimum,double lowerQuartile,double median,double upperQuartile,double maximum)19266 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
19267 {
19268   setKey(key);
19269   setMinimum(minimum);
19270   setLowerQuartile(lowerQuartile);
19271   setMedian(median);
19272   setUpperQuartile(upperQuartile);
19273   setMaximum(maximum);
19274 }
19275 
19276 /*!
19277   Sets the width of the box in key coordinates.
19278 
19279   \see setWhiskerWidth
19280 */
setWidth(double width)19281 void QCPStatisticalBox::setWidth(double width)
19282 {
19283   mWidth = width;
19284 }
19285 
19286 /*!
19287   Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key coordinates.
19288 
19289   \see setWidth
19290 */
setWhiskerWidth(double width)19291 void QCPStatisticalBox::setWhiskerWidth(double width)
19292 {
19293   mWhiskerWidth = width;
19294 }
19295 
19296 /*!
19297   Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
19298 
19299   Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the whisker backbone from reaching
19300   a few pixels past the whisker bars, when using a non-zero pen width.
19301 
19302   \see setWhiskerBarPen
19303 */
setWhiskerPen(const QPen & pen)19304 void QCPStatisticalBox::setWhiskerPen(const QPen &pen)
19305 {
19306   mWhiskerPen = pen;
19307 }
19308 
19309 /*!
19310   Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at
19311   each end of the whisker backbone).
19312 
19313   \see setWhiskerPen
19314 */
setWhiskerBarPen(const QPen & pen)19315 void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)
19316 {
19317   mWhiskerBarPen = pen;
19318 }
19319 
19320 /*!
19321   Sets the pen used for drawing the median indicator line inside the statistical box.
19322 */
setMedianPen(const QPen & pen)19323 void QCPStatisticalBox::setMedianPen(const QPen &pen)
19324 {
19325   mMedianPen = pen;
19326 }
19327 
19328 /*!
19329   Sets the appearance of the outlier data points.
19330 
19331   \see setOutliers
19332 */
setOutlierStyle(const QCPScatterStyle & style)19333 void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style)
19334 {
19335   mOutlierStyle = style;
19336 }
19337 
19338 /* inherits documentation from base class */
clearData()19339 void QCPStatisticalBox::clearData()
19340 {
19341   setOutliers(QVector<double>());
19342   setKey(0);
19343   setMinimum(0);
19344   setLowerQuartile(0);
19345   setMedian(0);
19346   setUpperQuartile(0);
19347   setMaximum(0);
19348 }
19349 
19350 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const19351 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19352 {
19353   Q_UNUSED(details)
19354   if (onlySelectable && !mSelectable)
19355     return -1;
19356   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19357 
19358   if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
19359   {
19360     double posKey, posValue;
19361     pixelsToCoords(pos, posKey, posValue);
19362     // quartile box:
19363     QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19364     QCPRange valueRange(mLowerQuartile, mUpperQuartile);
19365     if (keyRange.contains(posKey) && valueRange.contains(posValue))
19366       return mParentPlot->selectionTolerance()*0.99;
19367 
19368     // min/max whiskers:
19369     if (QCPRange(mMinimum, mMaximum).contains(posValue))
19370       return qAbs(mKeyAxis->coordToPixel(mKey)-mKeyAxis->coordToPixel(posKey));
19371   }
19372   return -1;
19373 }
19374 
19375 /* inherits documentation from base class */
draw(QCPPainter * painter)19376 void QCPStatisticalBox::draw(QCPPainter *painter)
19377 {
19378   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
19379 
19380   // check data validity if flag set:
19381 #ifdef QCUSTOMPLOT_CHECK_DATA
19382   if (QCP::isInvalidData(mKey, mMedian) ||
19383       QCP::isInvalidData(mLowerQuartile, mUpperQuartile) ||
19384       QCP::isInvalidData(mMinimum, mMaximum))
19385     qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
19386   for (int i=0; i<mOutliers.size(); ++i)
19387     if (QCP::isInvalidData(mOutliers.at(i)))
19388       qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
19389 #endif
19390 
19391   QRectF quartileBox;
19392   drawQuartileBox(painter, &quartileBox);
19393 
19394   painter->save();
19395   painter->setClipRect(quartileBox, Qt::IntersectClip);
19396   drawMedian(painter);
19397   painter->restore();
19398 
19399   drawWhiskers(painter);
19400   drawOutliers(painter);
19401 }
19402 
19403 /* inherits documentation from base class */
drawLegendIcon(QCPPainter * painter,const QRectF & rect) const19404 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19405 {
19406   // draw filled rect:
19407   applyDefaultAntialiasingHint(painter);
19408   painter->setPen(mPen);
19409   painter->setBrush(mBrush);
19410   QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
19411   r.moveCenter(rect.center());
19412   painter->drawRect(r);
19413 }
19414 
19415 /*! \internal
19416 
19417   Draws the quartile box. \a box is an output parameter that returns the quartile box (in pixel
19418   coordinates) which is used to set the clip rect of the painter before calling \ref drawMedian (so
19419   the median doesn't draw outside the quartile box).
19420 */
drawQuartileBox(QCPPainter * painter,QRectF * quartileBox) const19421 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
19422 {
19423   QRectF box;
19424   box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
19425   box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
19426   applyDefaultAntialiasingHint(painter);
19427   painter->setPen(mainPen());
19428   painter->setBrush(mainBrush());
19429   painter->drawRect(box);
19430   if (quartileBox)
19431     *quartileBox = box;
19432 }
19433 
19434 /*! \internal
19435 
19436   Draws the median line inside the quartile box.
19437 */
drawMedian(QCPPainter * painter) const19438 void QCPStatisticalBox::drawMedian(QCPPainter *painter) const
19439 {
19440   QLineF medianLine;
19441   medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
19442   medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
19443   applyDefaultAntialiasingHint(painter);
19444   painter->setPen(mMedianPen);
19445   painter->drawLine(medianLine);
19446 }
19447 
19448 /*! \internal
19449 
19450   Draws both whisker backbones and bars.
19451 */
drawWhiskers(QCPPainter * painter) const19452 void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const
19453 {
19454   QLineF backboneMin, backboneMax, barMin, barMax;
19455   backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
19456   backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
19457   barMax.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMaximum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMaximum));
19458   barMin.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMinimum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMinimum));
19459   applyErrorBarsAntialiasingHint(painter);
19460   painter->setPen(mWhiskerPen);
19461   painter->drawLine(backboneMin);
19462   painter->drawLine(backboneMax);
19463   painter->setPen(mWhiskerBarPen);
19464   painter->drawLine(barMin);
19465   painter->drawLine(barMax);
19466 }
19467 
19468 /*! \internal
19469 
19470   Draws the outlier scatter points.
19471 */
drawOutliers(QCPPainter * painter) const19472 void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const
19473 {
19474   applyScattersAntialiasingHint(painter);
19475   mOutlierStyle.applyTo(painter, mPen);
19476   for (int i=0; i<mOutliers.size(); ++i)
19477     mOutlierStyle.drawShape(painter, coordsToPixels(mKey, mOutliers.at(i)));
19478 }
19479 
19480 /* inherits documentation from base class */
getKeyRange(bool & foundRange,SignDomain inSignDomain) const19481 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19482 {
19483   foundRange = true;
19484   if (inSignDomain == sdBoth)
19485   {
19486     return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19487   } else if (inSignDomain == sdNegative)
19488   {
19489     if (mKey+mWidth*0.5 < 0)
19490       return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19491     else if (mKey < 0)
19492       return QCPRange(mKey-mWidth*0.5, mKey);
19493     else
19494     {
19495       foundRange = false;
19496       return QCPRange();
19497     }
19498   } else if (inSignDomain == sdPositive)
19499   {
19500     if (mKey-mWidth*0.5 > 0)
19501       return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19502     else if (mKey > 0)
19503       return QCPRange(mKey, mKey+mWidth*0.5);
19504     else
19505     {
19506       foundRange = false;
19507       return QCPRange();
19508     }
19509   }
19510   foundRange = false;
19511   return QCPRange();
19512 }
19513 
19514 /* inherits documentation from base class */
getValueRange(bool & foundRange,SignDomain inSignDomain) const19515 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19516 {
19517   QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
19518   values.reserve(mOutliers.size() + 5);
19519   values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
19520   values << mOutliers;
19521   // go through values and find the ones in legal range:
19522   bool haveUpper = false;
19523   bool haveLower = false;
19524   double upper = 0;
19525   double lower = 0;
19526   for (int i=0; i<values.size(); ++i)
19527   {
19528     if ((inSignDomain == sdNegative && values.at(i) < 0) ||
19529         (inSignDomain == sdPositive && values.at(i) > 0) ||
19530         (inSignDomain == sdBoth))
19531     {
19532       if (values.at(i) > upper || !haveUpper)
19533       {
19534         upper = values.at(i);
19535         haveUpper = true;
19536       }
19537       if (values.at(i) < lower || !haveLower)
19538       {
19539         lower = values.at(i);
19540         haveLower = true;
19541       }
19542     }
19543   }
19544   // return the bounds if we found some sensible values:
19545   if (haveLower && haveUpper)
19546   {
19547     foundRange = true;
19548     return QCPRange(lower, upper);
19549   } else // might happen if all values are in other sign domain
19550   {
19551     foundRange = false;
19552     return QCPRange();
19553   }
19554 }
19555 
19556 
19557 ////////////////////////////////////////////////////////////////////////////////////////////////////
19558 //////////////////// QCPColorMapData
19559 ////////////////////////////////////////////////////////////////////////////////////////////////////
19560 
19561 /*! \class QCPColorMapData
19562   \brief Holds the two-dimensional data of a QCPColorMap plottable.
19563 
19564   This class is a data storage for \ref QCPColorMap. It holds a two-dimensional array, which \ref
19565   QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a
19566   color, depending on the value.
19567 
19568   The size of the array can be controlled via \ref setSize (or \ref setKeySize, \ref setValueSize).
19569   Which plot coordinates these cells correspond to can be configured with \ref setRange (or \ref
19570   setKeyRange, \ref setValueRange).
19571 
19572   The data cells can be accessed in two ways: They can be directly addressed by an integer index
19573   with \ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot
19574   coordinate with \ref setData. plot coordinate to cell index transformations and vice versa are
19575   provided by the functions \ref coordToCell and \ref cellToCoord.
19576 
19577   This class also buffers the minimum and maximum values that are in the data set, to provide
19578   QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value
19579   that is greater than the current maximum increases this maximum to the new value. However,
19580   setting the cell that currently holds the maximum value to a smaller value doesn't decrease the
19581   maximum again, because finding the true new maximum would require going through the entire data
19582   array, which might be time consuming. The same holds for the data minimum. This functionality is
19583   given by \ref recalculateDataBounds, such that you can decide when it is sensible to find the
19584   true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience
19585   parameter \a recalculateDataBounds which may be set to true to automatically call \ref
19586   recalculateDataBounds internally.
19587 */
19588 
19589 /* start of documentation of inline functions */
19590 
19591 /*! \fn bool QCPColorMapData::isEmpty() const
19592 
19593   Returns whether this instance carries no data. This is equivalent to having a size where at least
19594   one of the dimensions is 0 (see \ref setSize).
19595 */
19596 
19597 /* end of documentation of inline functions */
19598 
19599 /*!
19600   Constructs a new QCPColorMapData instance. The instance has \a keySize cells in the key direction
19601   and \a valueSize cells in the value direction. These cells will be displayed by the \ref QCPColorMap
19602   at the coordinates \a keyRange and \a valueRange.
19603 
19604   \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange
19605 */
QCPColorMapData(int keySize,int valueSize,const QCPRange & keyRange,const QCPRange & valueRange)19606 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
19607   mKeySize(0),
19608   mValueSize(0),
19609   mKeyRange(keyRange),
19610   mValueRange(valueRange),
19611   mIsEmpty(true),
19612   mData(0),
19613   mDataModified(true)
19614 {
19615   setSize(keySize, valueSize);
19616   fill(0);
19617 }
19618 
~QCPColorMapData()19619 QCPColorMapData::~QCPColorMapData()
19620 {
19621   if (mData)
19622     delete[] mData;
19623 }
19624 
19625 /*!
19626   Constructs a new QCPColorMapData instance copying the data and range of \a other.
19627 */
QCPColorMapData(const QCPColorMapData & other)19628 QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :
19629   mKeySize(0),
19630   mValueSize(0),
19631   mIsEmpty(true),
19632   mData(0),
19633   mDataModified(true)
19634 {
19635   *this = other;
19636 }
19637 
19638 /*!
19639   Overwrites this color map data instance with the data stored in \a other.
19640 */
operator =(const QCPColorMapData & other)19641 QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
19642 {
19643   if (&other != this)
19644   {
19645     const int keySize = other.keySize();
19646     const int valueSize = other.valueSize();
19647     setSize(keySize, valueSize);
19648     setRange(other.keyRange(), other.valueRange());
19649     if (!mIsEmpty)
19650       memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
19651     mDataBounds = other.mDataBounds;
19652     mDataModified = true;
19653   }
19654   return *this;
19655 }
19656 
19657 /* undocumented getter */
data(double key,double value)19658 double QCPColorMapData::data(double key, double value)
19659 {
19660   int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19661   int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19662   if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19663     return mData[valueCell*mKeySize + keyCell];
19664   else
19665     return 0;
19666 }
19667 
19668 /* undocumented getter */
cell(int keyIndex,int valueIndex)19669 double QCPColorMapData::cell(int keyIndex, int valueIndex)
19670 {
19671   if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19672     return mData[valueIndex*mKeySize + keyIndex];
19673   else
19674     return 0;
19675 }
19676 
19677 /*!
19678   Resizes the data array to have \a keySize cells in the key dimension and \a valueSize cells in
19679   the value dimension.
19680 
19681   The current data is discarded and the map cells are set to 0, unless the map had already the
19682   requested size.
19683 
19684   Setting at least one of \a keySize or \a valueSize to zero frees the internal data array and \ref
19685   isEmpty returns true.
19686 
19687   \see setRange, setKeySize, setValueSize
19688 */
setSize(int keySize,int valueSize)19689 void QCPColorMapData::setSize(int keySize, int valueSize)
19690 {
19691   if (keySize != mKeySize || valueSize != mValueSize)
19692   {
19693     mKeySize = keySize;
19694     mValueSize = valueSize;
19695     if (mData)
19696       delete[] mData;
19697     mIsEmpty = mKeySize == 0 || mValueSize == 0;
19698     if (!mIsEmpty)
19699     {
19700 #ifdef __EXCEPTIONS
19701       try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
19702 #endif
19703       mData = new double[mKeySize*mValueSize];
19704 #ifdef __EXCEPTIONS
19705       } catch (...) { mData = 0; }
19706 #endif
19707       if (mData)
19708         fill(0);
19709       else
19710         qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
19711     } else
19712       mData = 0;
19713     mDataModified = true;
19714   }
19715 }
19716 
19717 /*!
19718   Resizes the data array to have \a keySize cells in the key dimension.
19719 
19720   The current data is discarded and the map cells are set to 0, unless the map had already the
19721   requested size.
19722 
19723   Setting \a keySize to zero frees the internal data array and \ref isEmpty returns true.
19724 
19725   \see setKeyRange, setSize, setValueSize
19726 */
setKeySize(int keySize)19727 void QCPColorMapData::setKeySize(int keySize)
19728 {
19729   setSize(keySize, mValueSize);
19730 }
19731 
19732 /*!
19733   Resizes the data array to have \a valueSize cells in the value dimension.
19734 
19735   The current data is discarded and the map cells are set to 0, unless the map had already the
19736   requested size.
19737 
19738   Setting \a valueSize to zero frees the internal data array and \ref isEmpty returns true.
19739 
19740   \see setValueRange, setSize, setKeySize
19741 */
setValueSize(int valueSize)19742 void QCPColorMapData::setValueSize(int valueSize)
19743 {
19744   setSize(mKeySize, valueSize);
19745 }
19746 
19747 /*!
19748   Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area
19749   covered by the color map in plot coordinates.
19750 
19751   The outer cells will be centered on the range boundaries given to this function. For example, if
19752   the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
19753   be cells centered on the key coordinates 2, 2.5 and 3.
19754 
19755   \see setSize
19756 */
setRange(const QCPRange & keyRange,const QCPRange & valueRange)19757 void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
19758 {
19759   setKeyRange(keyRange);
19760   setValueRange(valueRange);
19761 }
19762 
19763 /*!
19764   Sets the coordinate range the data shall be distributed over in the key dimension. Together with
19765   the value range, This defines the rectangular area covered by the color map in plot coordinates.
19766 
19767   The outer cells will be centered on the range boundaries given to this function. For example, if
19768   the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
19769   be cells centered on the key coordinates 2, 2.5 and 3.
19770 
19771   \see setRange, setValueRange, setSize
19772 */
setKeyRange(const QCPRange & keyRange)19773 void QCPColorMapData::setKeyRange(const QCPRange &keyRange)
19774 {
19775   mKeyRange = keyRange;
19776 }
19777 
19778 /*!
19779   Sets the coordinate range the data shall be distributed over in the value dimension. Together with
19780   the key range, This defines the rectangular area covered by the color map in plot coordinates.
19781 
19782   The outer cells will be centered on the range boundaries given to this function. For example, if
19783   the value size (\ref setValueSize) is 3 and \a valueRange is set to <tt>QCPRange(2, 3)</tt> there
19784   will be cells centered on the value coordinates 2, 2.5 and 3.
19785 
19786   \see setRange, setKeyRange, setSize
19787 */
setValueRange(const QCPRange & valueRange)19788 void QCPColorMapData::setValueRange(const QCPRange &valueRange)
19789 {
19790   mValueRange = valueRange;
19791 }
19792 
19793 /*!
19794   Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a
19795   z.
19796 
19797   \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19798   value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19799   you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
19800   determine the cell index. Rather directly access the cell index with \ref
19801   QCPColorMapData::setCell.
19802 
19803   \see setCell, setRange
19804 */
setData(double key,double value,double z)19805 void QCPColorMapData::setData(double key, double value, double z)
19806 {
19807   int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19808   int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19809   if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19810   {
19811     mData[valueCell*mKeySize + keyCell] = z;
19812     if (z < mDataBounds.lower)
19813       mDataBounds.lower = z;
19814     if (z > mDataBounds.upper)
19815       mDataBounds.upper = z;
19816      mDataModified = true;
19817   }
19818 }
19819 
19820 /*!
19821   Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z. The indices
19822   enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see
19823   \ref setSize).
19824 
19825   In the standard plot configuration (horizontal key axis and vertical value axis, both not
19826   range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with
19827   indices (keySize-1, valueSize-1) is in the top right corner of the color map.
19828 
19829   \see setData, setSize
19830 */
setCell(int keyIndex,int valueIndex,double z)19831 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
19832 {
19833   if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19834   {
19835     mData[valueIndex*mKeySize + keyIndex] = z;
19836     if (z < mDataBounds.lower)
19837       mDataBounds.lower = z;
19838     if (z > mDataBounds.upper)
19839       mDataBounds.upper = z;
19840      mDataModified = true;
19841   }
19842 }
19843 
19844 /*!
19845   Goes through the data and updates the buffered minimum and maximum data values.
19846 
19847   Calling this method is only advised if you are about to call \ref QCPColorMap::rescaleDataRange
19848   and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten
19849   with a smaller or larger value respectively, since the buffered maximum/minimum values have been
19850   updated the last time. Why this is the case is explained in the class description (\ref
19851   QCPColorMapData).
19852 
19853   Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter \a
19854   recalculateDataBounds for convenience. Setting this to true will call this method for you, before
19855   doing the rescale.
19856 */
recalculateDataBounds()19857 void QCPColorMapData::recalculateDataBounds()
19858 {
19859   if (mKeySize > 0 && mValueSize > 0)
19860   {
19861     double minHeight = mData[0];
19862     double maxHeight = mData[0];
19863     const int dataCount = mValueSize*mKeySize;
19864     for (int i=0; i<dataCount; ++i)
19865     {
19866       if (mData[i] > maxHeight)
19867         maxHeight = mData[i];
19868       if (mData[i] < minHeight)
19869         minHeight = mData[i];
19870     }
19871     mDataBounds.lower = minHeight;
19872     mDataBounds.upper = maxHeight;
19873   }
19874 }
19875 
19876 /*!
19877   Frees the internal data memory.
19878 
19879   This is equivalent to calling \ref setSize "setSize(0, 0)".
19880 */
clear()19881 void QCPColorMapData::clear()
19882 {
19883   setSize(0, 0);
19884 }
19885 
19886 /*!
19887   Sets all cells to the value \a z.
19888 */
fill(double z)19889 void QCPColorMapData::fill(double z)
19890 {
19891   const int dataCount = mValueSize*mKeySize;
19892   for (int i=0; i<dataCount; ++i)
19893     mData[i] = z;
19894   mDataBounds = QCPRange(z, z);
19895   mDataModified = true;
19896 }
19897 
19898 /*!
19899   Transforms plot coordinates given by \a key and \a value to cell indices of this QCPColorMapData
19900   instance. The resulting cell indices are returned via the output parameters \a keyIndex and \a
19901   valueIndex.
19902 
19903   The retrieved key/value cell indices can then be used for example with \ref setCell.
19904 
19905   If you are only interested in a key or value index, you may pass 0 as \a valueIndex or \a
19906   keyIndex.
19907 
19908   \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19909   value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19910   you shouldn't use the \ref QCPColorMapData::coordToCell method as it uses a linear transformation to
19911   determine the cell index.
19912 
19913   \see cellToCoord, QCPAxis::coordToPixel
19914 */
coordToCell(double key,double value,int * keyIndex,int * valueIndex) const19915 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
19916 {
19917   if (keyIndex)
19918     *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19919   if (valueIndex)
19920     *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19921 }
19922 
19923 /*!
19924   Transforms cell indices given by \a keyIndex and \a valueIndex to cell indices of this QCPColorMapData
19925   instance. The resulting coordinates are returned via the output parameters \a key and \a
19926   value.
19927 
19928   If you are only interested in a key or value coordinate, you may pass 0 as \a key or \a
19929   value.
19930 
19931   \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19932   value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19933   you shouldn't use the \ref QCPColorMapData::cellToCoord method as it uses a linear transformation to
19934   determine the cell index.
19935 
19936   \see coordToCell, QCPAxis::pixelToCoord
19937 */
cellToCoord(int keyIndex,int valueIndex,double * key,double * value) const19938 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
19939 {
19940   if (key)
19941     *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
19942   if (value)
19943     *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
19944 }
19945 
19946 
19947 ////////////////////////////////////////////////////////////////////////////////////////////////////
19948 //////////////////// QCPColorMap
19949 ////////////////////////////////////////////////////////////////////////////////////////////////////
19950 
19951 /*! \class QCPColorMap
19952   \brief A plottable representing a two-dimensional color map in a plot.
19953 
19954   \image html QCPColorMap.png
19955 
19956   The data is stored in the class \ref QCPColorMapData, which can be accessed via the data()
19957   method.
19958 
19959   A color map has three dimensions to represent a data point: The \a key dimension, the \a value
19960   dimension and the \a data dimension. As with other plottables such as graphs, \a key and \a value
19961   correspond to two orthogonal axes on the QCustomPlot surface that you specify in the QCPColorMap
19962   constructor. The \a data dimension however is encoded as the color of the point at (\a key, \a
19963   value).
19964 
19965   Set the number of points (or \a cells) in the key/value dimension via \ref
19966   QCPColorMapData::setSize. The plot coordinate range over which these points will be displayed is
19967   specified via \ref QCPColorMapData::setRange. The first cell will be centered on the lower range
19968   boundary and the last cell will be centered on the upper range boundary. The data can be set by
19969   either accessing the cells directly with QCPColorMapData::setCell or by addressing the cells via
19970   their plot coordinates with \ref QCPColorMapData::setData. If possible, you should prefer
19971   setCell, since it doesn't need to do any coordinate transformation and thus performs a bit
19972   better.
19973 
19974   The cell with index (0, 0) is at the bottom left, if the color map uses normal (i.e. not reversed)
19975   key and value axes.
19976 
19977   To show the user which colors correspond to which \a data values, a \ref QCPColorScale is
19978   typically placed to the right of the axis rect. See the documentation there for details on how to
19979   add and use a color scale.
19980 
19981   \section appearance Changing the appearance
19982 
19983   The central part of the appearance is the color gradient, which can be specified via \ref
19984   setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color
19985   gradient.
19986 
19987   The \a data range that is mapped to the colors of the gradient can be specified with \ref
19988   setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref
19989   rescaleDataRange.
19990 
19991   \section usage Usage
19992 
19993   Like all data representing objects in QCustomPlot, the QCPColorMap is a plottable
19994   (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
19995   (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
19996 
19997   Usually, you first create an instance and add it to the customPlot:
19998   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-1
19999   and then modify the properties of the newly created color map, e.g.:
20000   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-2
20001 
20002   \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
20003   value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
20004   you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
20005   determine the cell index. Rather directly access the cell index with \ref
20006   QCPColorMapData::setCell.
20007 */
20008 
20009 /* start documentation of inline functions */
20010 
20011 /*! \fn QCPColorMapData *QCPColorMap::data() const
20012 
20013   Returns a pointer to the internal data storage of type \ref QCPColorMapData. Access this to
20014   modify data points (cells) and the color map key/value range.
20015 
20016   \see setData
20017 */
20018 
20019 /* end documentation of inline functions */
20020 
20021 /* start documentation of signals */
20022 
20023 /*! \fn void QCPColorMap::dataRangeChanged(QCPRange newRange);
20024 
20025   This signal is emitted when the data range changes.
20026 
20027   \see setDataRange
20028 */
20029 
20030 /*! \fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
20031 
20032   This signal is emitted when the data scale type changes.
20033 
20034   \see setDataScaleType
20035 */
20036 
20037 /*! \fn void QCPColorMap::gradientChanged(QCPColorGradient newGradient);
20038 
20039   This signal is emitted when the gradient changes.
20040 
20041   \see setGradient
20042 */
20043 
20044 /* end documentation of signals */
20045 
20046 /*!
20047   Constructs a color map with the specified \a keyAxis and \a valueAxis.
20048 
20049   The constructed QCPColorMap can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
20050   then takes ownership of the color map.
20051 */
QCPColorMap(QCPAxis * keyAxis,QCPAxis * valueAxis)20052 QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20053   QCPAbstractPlottable(keyAxis, valueAxis),
20054   mDataScaleType(QCPAxis::stLinear),
20055   mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
20056   mInterpolate(true),
20057   mTightBoundary(false),
20058   mMapImageInvalidated(true)
20059 {
20060 }
20061 
~QCPColorMap()20062 QCPColorMap::~QCPColorMap()
20063 {
20064   delete mMapData;
20065 }
20066 
20067 /*!
20068   Replaces the current \ref data with the provided \a data.
20069 
20070   If \a copy is set to true, the \a data object will only be copied. if false, the color map
20071   takes ownership of the passed data and replaces the internal data pointer with it. This is
20072   significantly faster than copying for large datasets.
20073 */
setData(QCPColorMapData * data,bool copy)20074 void QCPColorMap::setData(QCPColorMapData *data, bool copy)
20075 {
20076   if (mMapData == data)
20077   {
20078     qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20079     return;
20080   }
20081   if (copy)
20082   {
20083     *mMapData = *data;
20084   } else
20085   {
20086     delete mMapData;
20087     mMapData = data;
20088   }
20089   mMapImageInvalidated = true;
20090 }
20091 
20092 /*!
20093   Sets the data range of this color map to \a dataRange. The data range defines which data values
20094   are mapped to the color gradient.
20095 
20096   To make the data range span the full range of the data set, use \ref rescaleDataRange.
20097 
20098   \see QCPColorScale::setDataRange
20099 */
setDataRange(const QCPRange & dataRange)20100 void QCPColorMap::setDataRange(const QCPRange &dataRange)
20101 {
20102   if (!QCPRange::validRange(dataRange)) return;
20103   if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
20104   {
20105     if (mDataScaleType == QCPAxis::stLogarithmic)
20106       mDataRange = dataRange.sanitizedForLogScale();
20107     else
20108       mDataRange = dataRange.sanitizedForLinScale();
20109     mMapImageInvalidated = true;
20110     emit dataRangeChanged(mDataRange);
20111   }
20112 }
20113 
20114 /*!
20115   Sets whether the data is correlated with the color gradient linearly or logarithmically.
20116 
20117   \see QCPColorScale::setDataScaleType
20118 */
setDataScaleType(QCPAxis::ScaleType scaleType)20119 void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType)
20120 {
20121   if (mDataScaleType != scaleType)
20122   {
20123     mDataScaleType = scaleType;
20124     mMapImageInvalidated = true;
20125     emit dataScaleTypeChanged(mDataScaleType);
20126     if (mDataScaleType == QCPAxis::stLogarithmic)
20127       setDataRange(mDataRange.sanitizedForLogScale());
20128   }
20129 }
20130 
20131 /*!
20132   Sets the color gradient that is used to represent the data. For more details on how to create an
20133   own gradient or use one of the preset gradients, see \ref QCPColorGradient.
20134 
20135   The colors defined by the gradient will be used to represent data values in the currently set
20136   data range, see \ref setDataRange. Data points that are outside this data range will either be
20137   colored uniformly with the respective gradient boundary color, or the gradient will repeat,
20138   depending on \ref QCPColorGradient::setPeriodic.
20139 
20140   \see QCPColorScale::setGradient
20141 */
setGradient(const QCPColorGradient & gradient)20142 void QCPColorMap::setGradient(const QCPColorGradient &gradient)
20143 {
20144   if (mGradient != gradient)
20145   {
20146     mGradient = gradient;
20147     mMapImageInvalidated = true;
20148     emit gradientChanged(mGradient);
20149   }
20150 }
20151 
20152 /*!
20153   Sets whether the color map image shall use bicubic interpolation when displaying the color map
20154   shrinked or expanded, and not at a 1:1 pixel-to-data scale.
20155 
20156   \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation and without interpolation enabled"
20157 */
setInterpolate(bool enabled)20158 void QCPColorMap::setInterpolate(bool enabled)
20159 {
20160   mInterpolate = enabled;
20161   mMapImageInvalidated = true; // because oversampling factors might need to change
20162 }
20163 
20164 /*!
20165   Sets whether the outer most data rows and columns are clipped to the specified key and value
20166   range (see \ref QCPColorMapData::setKeyRange, \ref QCPColorMapData::setValueRange).
20167 
20168   if \a enabled is set to false, the data points at the border of the color map are drawn with the
20169   same width and height as all other data points. Since the data points are represented by
20170   rectangles of one color centered on the data coordinate, this means that the shown color map
20171   extends by half a data point over the specified key/value range in each direction.
20172 
20173   \image html QCPColorMap-tightboundary.png "A color map, with tight boundary enabled and disabled"
20174 */
setTightBoundary(bool enabled)20175 void QCPColorMap::setTightBoundary(bool enabled)
20176 {
20177   mTightBoundary = enabled;
20178 }
20179 
20180 /*!
20181   Associates the color scale \a colorScale with this color map.
20182 
20183   This means that both the color scale and the color map synchronize their gradient, data range and
20184   data scale type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple color maps
20185   can be associated with one single color scale. This causes the color maps to also synchronize
20186   those properties, via the mutual color scale.
20187 
20188   This function causes the color map to adopt the current color gradient, data range and data scale
20189   type of \a colorScale. After this call, you may change these properties at either the color map
20190   or the color scale, and the setting will be applied to both.
20191 
20192   Pass 0 as \a colorScale to disconnect the color scale from this color map again.
20193 */
setColorScale(QCPColorScale * colorScale)20194 void QCPColorMap::setColorScale(QCPColorScale *colorScale)
20195 {
20196   if (mColorScale) // unconnect signals from old color scale
20197   {
20198     disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20199     disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20200     disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20201     disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20202     disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20203     disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20204   }
20205   mColorScale = colorScale;
20206   if (mColorScale) // connect signals to new color scale
20207   {
20208     setGradient(mColorScale->gradient());
20209     setDataRange(mColorScale->dataRange());
20210     setDataScaleType(mColorScale->dataScaleType());
20211     connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20212     connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20213     connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20214     connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20215     connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20216     connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20217   }
20218 }
20219 
20220 /*!
20221   Sets the data range (\ref setDataRange) to span the minimum and maximum values that occur in the
20222   current data set. This corresponds to the \ref rescaleKeyAxis or \ref rescaleValueAxis methods,
20223   only for the third data dimension of the color map.
20224 
20225   The minimum and maximum values of the data set are buffered in the internal QCPColorMapData
20226   instance (\ref data). As data is updated via its \ref QCPColorMapData::setCell or \ref
20227   QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For
20228   performance reasons, however, they are only updated in an expanding fashion. So the buffered
20229   maximum can only increase and the buffered minimum can only decrease. In consequence, changes to
20230   the data that actually lower the maximum of the data set (by overwriting the cell holding the
20231   current maximum with a smaller value), aren't recognized and the buffered maximum overestimates
20232   the true maximum of the data set. The same happens for the buffered minimum. To recalculate the
20233   true minimum and maximum by explicitly looking at each cell, the method
20234   QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \a
20235   recalculateDataBounds calls this method before setting the data range to the buffered minimum and
20236   maximum.
20237 
20238   \see setDataRange
20239 */
rescaleDataRange(bool recalculateDataBounds)20240 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
20241 {
20242   if (recalculateDataBounds)
20243     mMapData->recalculateDataBounds();
20244   setDataRange(mMapData->dataBounds());
20245 }
20246 
20247 /*!
20248   Takes the current appearance of the color map and updates the legend icon, which is used to
20249   represent this color map in the legend (see \ref QCPLegend).
20250 
20251   The \a transformMode specifies whether the rescaling is done by a faster, low quality image
20252   scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm
20253   (Qt::SmoothTransformation).
20254 
20255   The current color map appearance is scaled down to \a thumbSize. Ideally, this should be equal to
20256   the size of the legend icon (see \ref QCPLegend::setIconSize). If it isn't exactly the configured
20257   legend icon size, the thumb will be rescaled during drawing of the legend item.
20258 
20259   \see setDataRange
20260 */
updateLegendIcon(Qt::TransformationMode transformMode,const QSize & thumbSize)20261 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
20262 {
20263   if (mMapImage.isNull() && !data()->isEmpty())
20264     updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
20265 
20266   if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
20267   {
20268     bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20269     bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20270     mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
20271   }
20272 }
20273 
20274 /*!
20275   Clears the colormap data by calling \ref QCPColorMapData::clear() on the internal data. This also
20276   resizes the map to 0x0 cells.
20277 */
clearData()20278 void QCPColorMap::clearData()
20279 {
20280   mMapData->clear();
20281 }
20282 
20283 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const20284 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20285 {
20286   Q_UNUSED(details)
20287   if (onlySelectable && !mSelectable)
20288     return -1;
20289   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20290 
20291   if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
20292   {
20293     double posKey, posValue;
20294     pixelsToCoords(pos, posKey, posValue);
20295     if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
20296       return mParentPlot->selectionTolerance()*0.99;
20297   }
20298   return -1;
20299 }
20300 
20301 /*! \internal
20302 
20303   Updates the internal map image buffer by going through the internal \ref QCPColorMapData and
20304   turning the data values into color pixels with \ref QCPColorGradient::colorize.
20305 
20306   This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image
20307   has been invalidated for a different reason (e.g. a change of the data range with \ref
20308   setDataRange).
20309 
20310   If the map cell count is low, the image created will be oversampled in order to avoid a
20311   QPainter::drawImage bug which makes inner pixel boundaries jitter when stretch-drawing images
20312   without smooth transform enabled. Accordingly, oversampling isn't performed if \ref
20313   setInterpolate is true.
20314 */
updateMapImage()20315 void QCPColorMap::updateMapImage()
20316 {
20317   QCPAxis *keyAxis = mKeyAxis.data();
20318   if (!keyAxis) return;
20319   if (mMapData->isEmpty()) return;
20320 
20321   const int keySize = mMapData->keySize();
20322   const int valueSize = mMapData->valueSize();
20323   int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
20324   int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
20325 
20326   // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
20327   if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
20328     mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), QImage::Format_RGB32);
20329   else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
20330     mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), QImage::Format_RGB32);
20331 
20332   QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
20333   if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20334   {
20335     // resize undersampled map image to actual key/value cell sizes:
20336     if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
20337       mUndersampledMapImage = QImage(QSize(keySize, valueSize), QImage::Format_RGB32);
20338     else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
20339       mUndersampledMapImage = QImage(QSize(valueSize, keySize), QImage::Format_RGB32);
20340     localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
20341   } else if (!mUndersampledMapImage.isNull())
20342     mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
20343 
20344   const double *rawData = mMapData->mData;
20345   if (keyAxis->orientation() == Qt::Horizontal)
20346   {
20347     const int lineCount = valueSize;
20348     const int rowCount = keySize;
20349     for (int line=0; line<lineCount; ++line)
20350     {
20351       QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
20352       mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
20353     }
20354   } else // keyAxis->orientation() == Qt::Vertical
20355   {
20356     const int lineCount = keySize;
20357     const int rowCount = valueSize;
20358     for (int line=0; line<lineCount; ++line)
20359     {
20360       QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
20361       mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
20362     }
20363   }
20364 
20365   if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20366   {
20367     if (keyAxis->orientation() == Qt::Horizontal)
20368       mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20369     else
20370       mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20371   }
20372   mMapData->mDataModified = false;
20373   mMapImageInvalidated = false;
20374 }
20375 
20376 /* inherits documentation from base class */
draw(QCPPainter * painter)20377 void QCPColorMap::draw(QCPPainter *painter)
20378 {
20379   if (mMapData->isEmpty()) return;
20380   if (!mKeyAxis || !mValueAxis) return;
20381   applyDefaultAntialiasingHint(painter);
20382 
20383   if (mMapData->mDataModified || mMapImageInvalidated)
20384     updateMapImage();
20385 
20386   // use buffer if painting vectorized (PDF):
20387   bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
20388   QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
20389   QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
20390   QPixmap mapBuffer;
20391   double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
20392   if (useBuffer)
20393   {
20394     mapBufferTarget = painter->clipRegion().boundingRect();
20395     mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
20396     mapBuffer.fill(Qt::transparent);
20397     localPainter = new QCPPainter(&mapBuffer);
20398     localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
20399     localPainter->translate(-mapBufferTarget.topLeft());
20400   }
20401 
20402   QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20403                             coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
20404   // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
20405   double halfCellWidth = 0; // in pixels
20406   double halfCellHeight = 0; // in pixels
20407   if (keyAxis()->orientation() == Qt::Horizontal)
20408   {
20409     if (mMapData->keySize() > 1)
20410       halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
20411     if (mMapData->valueSize() > 1)
20412       halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
20413   } else // keyAxis orientation is Qt::Vertical
20414   {
20415     if (mMapData->keySize() > 1)
20416       halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
20417     if (mMapData->valueSize() > 1)
20418       halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
20419   }
20420   imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
20421   bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20422   bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20423   bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
20424   localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
20425   QRegion clipBackup;
20426   if (mTightBoundary)
20427   {
20428     clipBackup = localPainter->clipRegion();
20429     QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20430                                   coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
20431     localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
20432   }
20433   localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
20434   if (mTightBoundary)
20435     localPainter->setClipRegion(clipBackup);
20436   localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
20437 
20438   if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
20439   {
20440     delete localPainter;
20441     painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
20442   }
20443 }
20444 
20445 /* inherits documentation from base class */
drawLegendIcon(QCPPainter * painter,const QRectF & rect) const20446 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20447 {
20448   applyDefaultAntialiasingHint(painter);
20449   // draw map thumbnail:
20450   if (!mLegendIcon.isNull())
20451   {
20452     QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
20453     QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
20454     iconRect.moveCenter(rect.center());
20455     painter->drawPixmap(iconRect.topLeft(), scaledIcon);
20456   }
20457   /*
20458   // draw frame:
20459   painter->setBrush(Qt::NoBrush);
20460   painter->setPen(Qt::black);
20461   painter->drawRect(rect.adjusted(1, 1, 0, 0));
20462   */
20463 }
20464 
20465 /* inherits documentation from base class */
getKeyRange(bool & foundRange,SignDomain inSignDomain) const20466 QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
20467 {
20468   foundRange = true;
20469   QCPRange result = mMapData->keyRange();
20470   result.normalize();
20471   if (inSignDomain == QCPAbstractPlottable::sdPositive)
20472   {
20473     if (result.lower <= 0 && result.upper > 0)
20474       result.lower = result.upper*1e-3;
20475     else if (result.lower <= 0 && result.upper <= 0)
20476       foundRange = false;
20477   } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20478   {
20479     if (result.upper >= 0 && result.lower < 0)
20480       result.upper = result.lower*1e-3;
20481     else if (result.upper >= 0 && result.lower >= 0)
20482       foundRange = false;
20483   }
20484   return result;
20485 }
20486 
20487 /* inherits documentation from base class */
getValueRange(bool & foundRange,SignDomain inSignDomain) const20488 QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
20489 {
20490   foundRange = true;
20491   QCPRange result = mMapData->valueRange();
20492   result.normalize();
20493   if (inSignDomain == QCPAbstractPlottable::sdPositive)
20494   {
20495     if (result.lower <= 0 && result.upper > 0)
20496       result.lower = result.upper*1e-3;
20497     else if (result.lower <= 0 && result.upper <= 0)
20498       foundRange = false;
20499   } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20500   {
20501     if (result.upper >= 0 && result.lower < 0)
20502       result.upper = result.lower*1e-3;
20503     else if (result.upper >= 0 && result.lower >= 0)
20504       foundRange = false;
20505   }
20506   return result;
20507 }
20508 
20509 
20510 ////////////////////////////////////////////////////////////////////////////////////////////////////
20511 //////////////////// QCPFinancialData
20512 ////////////////////////////////////////////////////////////////////////////////////////////////////
20513 
20514 /*! \class QCPFinancialData
20515   \brief Holds the data of one single data point for QCPFinancial.
20516 
20517   The container for storing multiple data points is \ref QCPFinancialDataMap.
20518 
20519   The stored data is:
20520   \li \a key: coordinate on the key axis of this data point
20521   \li \a open: The opening value at the data point
20522   \li \a high: The high/maximum value at the data point
20523   \li \a low: The low/minimum value at the data point
20524   \li \a close: The closing value at the data point
20525 
20526   \see QCPFinancialDataMap
20527 */
20528 
20529 /*!
20530   Constructs a data point with key and all values set to zero.
20531 */
QCPFinancialData()20532 QCPFinancialData::QCPFinancialData() :
20533   key(0),
20534   open(0),
20535   high(0),
20536   low(0),
20537   close(0)
20538 {
20539 }
20540 
20541 /*!
20542   Constructs a data point with the specified \a key and OHLC values.
20543 */
QCPFinancialData(double key,double open,double high,double low,double close)20544 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
20545   key(key),
20546   open(open),
20547   high(high),
20548   low(low),
20549   close(close)
20550 {
20551 }
20552 
20553 
20554 ////////////////////////////////////////////////////////////////////////////////////////////////////
20555 //////////////////// QCPFinancial
20556 ////////////////////////////////////////////////////////////////////////////////////////////////////
20557 
20558 /*! \class QCPFinancial
20559   \brief A plottable representing a financial stock chart
20560 
20561   \image html QCPFinancial.png
20562 
20563   This plottable represents time series data binned to certain intervals, mainly used for stock
20564   charts. The two common representations OHLC (Open-High-Low-Close) bars and Candlesticks can be
20565   set via \ref setChartStyle.
20566 
20567   The data is passed via \ref setData as a set of open/high/low/close values at certain keys
20568   (typically times). This means the data must be already binned appropriately. If data is only
20569   available as a series of values (e.g. \a price against \a time), you can use the static
20570   convenience function \ref timeSeriesToOhlc to generate binned OHLC-data which can then be passed
20571   to \ref setData.
20572 
20573   The width of the OHLC bars/candlesticks can be controlled with \ref setWidth and is given in plot
20574   key coordinates. A typical choice is to set it to (or slightly less than) one bin interval width.
20575 
20576   \section appearance Changing the appearance
20577 
20578   Charts can be either single- or two-colored (\ref setTwoColored). If set to be single-colored,
20579   lines are drawn with the plottable's pen (\ref setPen) and fills with the brush (\ref setBrush).
20580 
20581   If set to two-colored, positive changes of the value during an interval (\a close >= \a open) are
20582   represented with a different pen and brush than negative changes (\a close < \a open). These can
20583   be configured with \ref setPenPositive, \ref setPenNegative, \ref setBrushPositive, and \ref
20584   setBrushNegative. In two-colored mode, the normal plottable pen/brush is ignored. Upon selection
20585   however, the normal selected pen/brush (\ref setSelectedPen, \ref setSelectedBrush) is used,
20586   irrespective of whether the chart is single- or two-colored.
20587 
20588 */
20589 
20590 /* start of documentation of inline functions */
20591 
20592 /*! \fn QCPFinancialDataMap *QCPFinancial::data() const
20593 
20594   Returns a pointer to the internal data storage of type \ref QCPFinancialDataMap. You may use it to
20595   directly manipulate the data, which may be more convenient and faster than using the regular \ref
20596   setData or \ref addData methods, in certain situations.
20597 */
20598 
20599 /* end of documentation of inline functions */
20600 
20601 /*!
20602   Constructs a financial chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
20603   axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
20604   the same orientation. If either of these restrictions is violated, a corresponding message is
20605   printed to the debug output (qDebug), the construction is not aborted, though.
20606 
20607   The constructed QCPFinancial can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
20608   then takes ownership of the financial chart.
20609 */
QCPFinancial(QCPAxis * keyAxis,QCPAxis * valueAxis)20610 QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20611   QCPAbstractPlottable(keyAxis, valueAxis),
20612   mData(0),
20613   mChartStyle(csOhlc),
20614   mWidth(0.5),
20615   mTwoColored(false),
20616   mBrushPositive(QBrush(QColor(210, 210, 255))),
20617   mBrushNegative(QBrush(QColor(255, 210, 210))),
20618   mPenPositive(QPen(QColor(10, 40, 180))),
20619   mPenNegative(QPen(QColor(180, 40, 10)))
20620 {
20621   mData = new QCPFinancialDataMap;
20622 
20623   setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
20624   setSelectedBrush(QBrush(QColor(80, 80, 255)));
20625 }
20626 
~QCPFinancial()20627 QCPFinancial::~QCPFinancial()
20628 {
20629   delete mData;
20630 }
20631 
20632 /*!
20633   Replaces the current data with the provided \a data.
20634 
20635   If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
20636   takes ownership of the passed data and replaces the internal data pointer with it. This is
20637   significantly faster than copying for large datasets.
20638 
20639   Alternatively, you can also access and modify the plottable's data via the \ref data method, which
20640   returns a pointer to the internal \ref QCPFinancialDataMap.
20641 
20642   \see timeSeriesToOhlc
20643 */
setData(QCPFinancialDataMap * data,bool copy)20644 void QCPFinancial::setData(QCPFinancialDataMap *data, bool copy)
20645 {
20646   if (mData == data)
20647   {
20648     qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20649     return;
20650   }
20651   if (copy)
20652   {
20653     *mData = *data;
20654   } else
20655   {
20656     delete mData;
20657     mData = data;
20658   }
20659 }
20660 
20661 /*! \overload
20662 
20663   Replaces the current data with the provided open/high/low/close data. The provided vectors should
20664   have equal length. Else, the number of added points will be the size of the smallest vector.
20665 
20666   \see timeSeriesToOhlc
20667 */
setData(const QVector<double> & key,const QVector<double> & open,const QVector<double> & high,const QVector<double> & low,const QVector<double> & close)20668 void QCPFinancial::setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20669 {
20670   mData->clear();
20671   int n = key.size();
20672   n = qMin(n, open.size());
20673   n = qMin(n, high.size());
20674   n = qMin(n, low.size());
20675   n = qMin(n, close.size());
20676   for (int i=0; i<n; ++i)
20677   {
20678     mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20679   }
20680 }
20681 
20682 /*!
20683   Sets which representation style shall be used to display the OHLC data.
20684 */
setChartStyle(QCPFinancial::ChartStyle style)20685 void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style)
20686 {
20687   mChartStyle = style;
20688 }
20689 
20690 /*!
20691   Sets the width of the individual bars/candlesticks to \a width in plot key coordinates.
20692 
20693   A typical choice is to set it to (or slightly less than) one bin interval width.
20694 */
setWidth(double width)20695 void QCPFinancial::setWidth(double width)
20696 {
20697   mWidth = width;
20698 }
20699 
20700 /*!
20701   Sets whether this chart shall contrast positive from negative trends per data point by using two
20702   separate colors to draw the respective bars/candlesticks.
20703 
20704   If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20705   setBrush).
20706 
20707   \see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative
20708 */
setTwoColored(bool twoColored)20709 void QCPFinancial::setTwoColored(bool twoColored)
20710 {
20711   mTwoColored = twoColored;
20712 }
20713 
20714 /*!
20715   If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
20716   of data points with a positive trend (i.e. bars/candlesticks with close >= open).
20717 
20718   If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20719   setBrush).
20720 
20721   \see setBrushNegative, setPenPositive, setPenNegative
20722 */
setBrushPositive(const QBrush & brush)20723 void QCPFinancial::setBrushPositive(const QBrush &brush)
20724 {
20725   mBrushPositive = brush;
20726 }
20727 
20728 /*!
20729   If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
20730   of data points with a negative trend (i.e. bars/candlesticks with close < open).
20731 
20732   If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20733   setBrush).
20734 
20735   \see setBrushPositive, setPenNegative, setPenPositive
20736 */
setBrushNegative(const QBrush & brush)20737 void QCPFinancial::setBrushNegative(const QBrush &brush)
20738 {
20739   mBrushNegative = brush;
20740 }
20741 
20742 /*!
20743   If \ref setTwoColored is set to true, this function controls the pen that is used to draw
20744   outlines of data points with a positive trend (i.e. bars/candlesticks with close >= open).
20745 
20746   If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20747   setBrush).
20748 
20749   \see setPenNegative, setBrushPositive, setBrushNegative
20750 */
setPenPositive(const QPen & pen)20751 void QCPFinancial::setPenPositive(const QPen &pen)
20752 {
20753   mPenPositive = pen;
20754 }
20755 
20756 /*!
20757   If \ref setTwoColored is set to true, this function controls the pen that is used to draw
20758   outlines of data points with a negative trend (i.e. bars/candlesticks with close < open).
20759 
20760   If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20761   setBrush).
20762 
20763   \see setPenPositive, setBrushNegative, setBrushPositive
20764 */
setPenNegative(const QPen & pen)20765 void QCPFinancial::setPenNegative(const QPen &pen)
20766 {
20767   mPenNegative = pen;
20768 }
20769 
20770 /*!
20771   Adds the provided data points in \a dataMap to the current data.
20772 
20773   Alternatively, you can also access and modify the data via the \ref data method, which returns a
20774   pointer to the internal \ref QCPFinancialDataMap.
20775 
20776   \see removeData
20777 */
addData(const QCPFinancialDataMap & dataMap)20778 void QCPFinancial::addData(const QCPFinancialDataMap &dataMap)
20779 {
20780   mData->unite(dataMap);
20781 }
20782 
20783 /*! \overload
20784 
20785   Adds the provided single data point in \a data to the current data.
20786 
20787   Alternatively, you can also access and modify the data via the \ref data method, which returns a
20788   pointer to the internal \ref QCPFinancialData.
20789 
20790   \see removeData
20791 */
addData(const QCPFinancialData & data)20792 void QCPFinancial::addData(const QCPFinancialData &data)
20793 {
20794   mData->insertMulti(data.key, data);
20795 }
20796 
20797 /*! \overload
20798 
20799   Adds the provided single data point given by \a key, \a open, \a high, \a low, and \a close to
20800   the current data.
20801 
20802   Alternatively, you can also access and modify the data via the \ref data method, which returns a
20803   pointer to the internal \ref QCPFinancialData.
20804 
20805   \see removeData
20806 */
addData(double key,double open,double high,double low,double close)20807 void QCPFinancial::addData(double key, double open, double high, double low, double close)
20808 {
20809   mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
20810 }
20811 
20812 /*! \overload
20813 
20814   Adds the provided open/high/low/close data to the current data.
20815 
20816   Alternatively, you can also access and modify the data via the \ref data method, which returns a
20817   pointer to the internal \ref QCPFinancialData.
20818 
20819   \see removeData
20820 */
addData(const QVector<double> & key,const QVector<double> & open,const QVector<double> & high,const QVector<double> & low,const QVector<double> & close)20821 void QCPFinancial::addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20822 {
20823   int n = key.size();
20824   n = qMin(n, open.size());
20825   n = qMin(n, high.size());
20826   n = qMin(n, low.size());
20827   n = qMin(n, close.size());
20828   for (int i=0; i<n; ++i)
20829   {
20830     mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20831   }
20832 }
20833 
20834 /*!
20835   Removes all data points with keys smaller than \a key.
20836 
20837   \see addData, clearData
20838 */
removeDataBefore(double key)20839 void QCPFinancial::removeDataBefore(double key)
20840 {
20841   auto it = mData->begin();
20842   while (it != mData->end() && it.key() < key)
20843     it = mData->erase(it);
20844 }
20845 
20846 /*!
20847   Removes all data points with keys greater than \a key.
20848 
20849   \see addData, clearData
20850 */
removeDataAfter(double key)20851 void QCPFinancial::removeDataAfter(double key)
20852 {
20853   if (mData->isEmpty()) return;
20854   auto it = mData->upperBound(key);
20855   while (it != mData->end())
20856     it = mData->erase(it);
20857 }
20858 
20859 /*!
20860   Removes all data points with keys between \a fromKey and \a toKey. if \a fromKey is greater or
20861   equal to \a toKey, the function does nothing. To remove a single data point with known key, use
20862   \ref removeData(double key).
20863 
20864   \see addData, clearData
20865 */
removeData(double fromKey,double toKey)20866 void QCPFinancial::removeData(double fromKey, double toKey)
20867 {
20868   if (fromKey >= toKey || mData->isEmpty()) return;
20869   auto it = mData->upperBound(fromKey);
20870   auto itEnd = mData->upperBound(toKey);
20871   while (it != itEnd)
20872     it = mData->erase(it);
20873 }
20874 
20875 /*! \overload
20876 
20877   Removes a single data point at \a key. If the position is not known with absolute precision,
20878   consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
20879   around the suspected position, depeding on the precision with which the key is known.
20880 
20881   \see addData, clearData
20882 */
removeData(double key)20883 void QCPFinancial::removeData(double key)
20884 {
20885   mData->remove(key);
20886 }
20887 
20888 /*!
20889   Removes all data points.
20890 
20891   \see removeData, removeDataAfter, removeDataBefore
20892 */
clearData()20893 void QCPFinancial::clearData()
20894 {
20895   mData->clear();
20896 }
20897 
20898 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const20899 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20900 {
20901   Q_UNUSED(details)
20902   if (onlySelectable && !mSelectable)
20903     return -1;
20904   if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20905 
20906   if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
20907   {
20908     // get visible data range:
20909     QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20910     getVisibleDataBounds(lower, upper);
20911     if (lower == mData->constEnd() || upper == mData->constEnd())
20912       return -1;
20913     // perform select test according to configured style:
20914     switch (mChartStyle)
20915     {
20916       case QCPFinancial::csOhlc:
20917         return ohlcSelectTest(pos, lower, upper+1); break;
20918       case QCPFinancial::csCandlestick:
20919         return candlestickSelectTest(pos, lower, upper+1); break;
20920     }
20921   }
20922   return -1;
20923 }
20924 
20925 /*!
20926   A convenience function that converts time series data (\a value against \a time) to OHLC binned
20927   data points. The return value can then be passed on to \ref setData.
20928 
20929   The size of the bins can be controlled with \a timeBinSize in the same units as \a time is given.
20930   For example, if the unit of \a time is seconds and single OHLC/Candlesticks should span an hour
20931   each, set \a timeBinSize to 3600.
20932 
20933   \a timeBinOffset allows to control precisely at what \a time coordinate a bin should start. The
20934   value passed as \a timeBinOffset doesn't need to be in the range encompassed by the \a time keys.
20935   It merely defines the mathematical offset/phase of the bins that will be used to process the
20936   data.
20937 */
timeSeriesToOhlc(const QVector<double> & time,const QVector<double> & value,double timeBinSize,double timeBinOffset)20938 QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
20939 {
20940   QCPFinancialDataMap map;
20941   int count = qMin(time.size(), value.size());
20942   if (count == 0)
20943     return QCPFinancialDataMap();
20944 
20945   QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
20946   int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
20947   for (int i=0; i<count; ++i)
20948   {
20949     int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
20950     if (currentBinIndex == index) // data point still in current bin, extend high/low:
20951     {
20952       if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
20953       if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
20954       if (i == count-1) // last data point is in current bin, finalize bin:
20955       {
20956         currentBinData.close = value.at(i);
20957         currentBinData.key = timeBinOffset+(index)*timeBinSize;
20958         map.insert(currentBinData.key, currentBinData);
20959       }
20960     } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
20961     {
20962       // finalize current bin:
20963       currentBinData.close = value.at(i-1);
20964       currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
20965       map.insert(currentBinData.key, currentBinData);
20966       // start next bin:
20967       currentBinIndex = index;
20968       currentBinData.open = value.at(i);
20969       currentBinData.high = value.at(i);
20970       currentBinData.low = value.at(i);
20971     }
20972   }
20973 
20974   return map;
20975 }
20976 
20977 /* inherits documentation from base class */
draw(QCPPainter * painter)20978 void QCPFinancial::draw(QCPPainter *painter)
20979 {
20980   // get visible data range:
20981   QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20982   getVisibleDataBounds(lower, upper);
20983   if (lower == mData->constEnd() || upper == mData->constEnd())
20984     return;
20985 
20986   // draw visible data range according to configured style:
20987   switch (mChartStyle)
20988   {
20989     case QCPFinancial::csOhlc:
20990       drawOhlcPlot(painter, lower, upper+1); break;
20991     case QCPFinancial::csCandlestick:
20992       drawCandlestickPlot(painter, lower, upper+1); break;
20993   }
20994 }
20995 
20996 /* inherits documentation from base class */
drawLegendIcon(QCPPainter * painter,const QRectF & rect) const20997 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20998 {
20999   painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
21000   if (mChartStyle == csOhlc)
21001   {
21002     if (mTwoColored)
21003     {
21004       // draw upper left half icon with positive color:
21005       painter->setBrush(mBrushPositive);
21006       painter->setPen(mPenPositive);
21007       painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21008       painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21009       painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21010       painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21011       // draw bottom right hald icon with negative color:
21012       painter->setBrush(mBrushNegative);
21013       painter->setPen(mPenNegative);
21014       painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21015       painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21016       painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21017       painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21018     } else
21019     {
21020       painter->setBrush(mBrush);
21021       painter->setPen(mPen);
21022       painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21023       painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21024       painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21025     }
21026   } else if (mChartStyle == csCandlestick)
21027   {
21028     if (mTwoColored)
21029     {
21030       // draw upper left half icon with positive color:
21031       painter->setBrush(mBrushPositive);
21032       painter->setPen(mPenPositive);
21033       painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21034       painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21035       painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21036       painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21037       // draw bottom right hald icon with negative color:
21038       painter->setBrush(mBrushNegative);
21039       painter->setPen(mPenNegative);
21040       painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21041       painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21042       painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21043       painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21044     } else
21045     {
21046       painter->setBrush(mBrush);
21047       painter->setPen(mPen);
21048       painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21049       painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21050       painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21051     }
21052   }
21053 }
21054 
21055 /* inherits documentation from base class */
getKeyRange(bool & foundRange,QCPAbstractPlottable::SignDomain inSignDomain) const21056 QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
21057 {
21058   QCPRange range;
21059   bool haveLower = false;
21060   bool haveUpper = false;
21061 
21062   double current;
21063   for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
21064   {
21065     current = it.value().key;
21066     if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
21067     {
21068       if (current < range.lower || !haveLower)
21069       {
21070         range.lower = current;
21071         haveLower = true;
21072       }
21073       if (current > range.upper || !haveUpper)
21074       {
21075         range.upper = current;
21076         haveUpper = true;
21077       }
21078     }
21079   }
21080   // determine exact range by including width of bars/flags:
21081   if (haveLower && mKeyAxis)
21082     range.lower = range.lower-mWidth*0.5;
21083   if (haveUpper && mKeyAxis)
21084     range.upper = range.upper+mWidth*0.5;
21085   foundRange = haveLower && haveUpper;
21086   return range;
21087 }
21088 
21089 /* inherits documentation from base class */
getValueRange(bool & foundRange,QCPAbstractPlottable::SignDomain inSignDomain) const21090 QCPRange QCPFinancial::getValueRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
21091 {
21092   QCPRange range;
21093   bool haveLower = false;
21094   bool haveUpper = false;
21095 
21096   for (auto it = mData->constBegin(); it != mData->constEnd(); ++it)
21097   {
21098     // high:
21099     if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0))
21100     {
21101       if (it.value().high < range.lower || !haveLower)
21102       {
21103         range.lower = it.value().high;
21104         haveLower = true;
21105       }
21106       if (it.value().high > range.upper || !haveUpper)
21107       {
21108         range.upper = it.value().high;
21109         haveUpper = true;
21110       }
21111     }
21112     // low:
21113     if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0))
21114     {
21115       if (it.value().low < range.lower || !haveLower)
21116       {
21117         range.lower = it.value().low;
21118         haveLower = true;
21119       }
21120       if (it.value().low > range.upper || !haveUpper)
21121       {
21122         range.upper = it.value().low;
21123         haveUpper = true;
21124       }
21125     }
21126   }
21127 
21128   foundRange = haveLower && haveUpper;
21129   return range;
21130 }
21131 
21132 /*! \internal
21133 
21134   Draws the data from \a begin to \a end as OHLC bars with the provided \a painter.
21135 
21136   This method is a helper function for \ref draw. It is used when the chart style is \ref csOhlc.
21137 */
drawOhlcPlot(QCPPainter * painter,const QCPFinancialDataMap::const_iterator & begin,const QCPFinancialDataMap::const_iterator & end)21138 void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21139 {
21140   QCPAxis *keyAxis = mKeyAxis.data();
21141   QCPAxis *valueAxis = mValueAxis.data();
21142   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21143 
21144   QPen linePen;
21145 
21146   if (keyAxis->orientation() == Qt::Horizontal)
21147   {
21148     for (auto it = begin; it != end; ++it)
21149     {
21150       if (mSelected)
21151         linePen = mSelectedPen;
21152       else if (mTwoColored)
21153         linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21154       else
21155         linePen = mPen;
21156       painter->setPen(linePen);
21157       double keyPixel = keyAxis->coordToPixel(it.value().key);
21158       double openPixel = valueAxis->coordToPixel(it.value().open);
21159       double closePixel = valueAxis->coordToPixel(it.value().close);
21160       // draw backbone:
21161       painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
21162       // draw open:
21163       double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21164       painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel));
21165       // draw close:
21166       painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel));
21167     }
21168   } else
21169   {
21170     for (auto it = begin; it != end; ++it)
21171     {
21172       if (mSelected)
21173         linePen = mSelectedPen;
21174       else if (mTwoColored)
21175         linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21176       else
21177         linePen = mPen;
21178       painter->setPen(linePen);
21179       double keyPixel = keyAxis->coordToPixel(it.value().key);
21180       double openPixel = valueAxis->coordToPixel(it.value().open);
21181       double closePixel = valueAxis->coordToPixel(it.value().close);
21182       // draw backbone:
21183       painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
21184       // draw open:
21185       double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21186       painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel));
21187       // draw close:
21188       painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels));
21189     }
21190   }
21191 }
21192 
21193 /*! \internal
21194 
21195   Draws the data from \a begin to \a end as Candlesticks with the provided \a painter.
21196 
21197   This method is a helper function for \ref draw. It is used when the chart style is \ref csCandlestick.
21198 */
drawCandlestickPlot(QCPPainter * painter,const QCPFinancialDataMap::const_iterator & begin,const QCPFinancialDataMap::const_iterator & end)21199 void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21200 {
21201   QCPAxis *keyAxis = mKeyAxis.data();
21202   QCPAxis *valueAxis = mValueAxis.data();
21203   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21204 
21205   QPen linePen;
21206   QBrush boxBrush;
21207 
21208   if (keyAxis->orientation() == Qt::Horizontal)
21209   {
21210     for (auto it = begin; it != end; ++it)
21211     {
21212       if (mSelected)
21213       {
21214         linePen = mSelectedPen;
21215         boxBrush = mSelectedBrush;
21216       } else if (mTwoColored)
21217       {
21218         if (it.value().close >= it.value().open)
21219         {
21220           linePen = mPenPositive;
21221           boxBrush = mBrushPositive;
21222         } else
21223         {
21224           linePen = mPenNegative;
21225           boxBrush = mBrushNegative;
21226         }
21227       } else
21228       {
21229         linePen = mPen;
21230         boxBrush = mBrush;
21231       }
21232       painter->setPen(linePen);
21233       painter->setBrush(boxBrush);
21234       double keyPixel = keyAxis->coordToPixel(it.value().key);
21235       double openPixel = valueAxis->coordToPixel(it.value().open);
21236       double closePixel = valueAxis->coordToPixel(it.value().close);
21237       // draw high:
21238       painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))));
21239       // draw low:
21240       painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))));
21241       // draw open-close box:
21242       double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21243       painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel)));
21244     }
21245   } else // keyAxis->orientation() == Qt::Vertical
21246   {
21247     for (auto it = begin; it != end; ++it)
21248     {
21249       if (mSelected)
21250       {
21251         linePen = mSelectedPen;
21252         boxBrush = mSelectedBrush;
21253       } else if (mTwoColored)
21254       {
21255         if (it.value().close >= it.value().open)
21256         {
21257           linePen = mPenPositive;
21258           boxBrush = mBrushPositive;
21259         } else
21260         {
21261           linePen = mPenNegative;
21262           boxBrush = mBrushNegative;
21263         }
21264       } else
21265       {
21266         linePen = mPen;
21267         boxBrush = mBrush;
21268       }
21269       painter->setPen(linePen);
21270       painter->setBrush(boxBrush);
21271       double keyPixel = keyAxis->coordToPixel(it.value().key);
21272       double openPixel = valueAxis->coordToPixel(it.value().open);
21273       double closePixel = valueAxis->coordToPixel(it.value().close);
21274       // draw high:
21275       painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel));
21276       // draw low:
21277       painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel));
21278       // draw open-close box:
21279       double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21280       painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels)));
21281     }
21282   }
21283 }
21284 
21285 /*! \internal
21286 
21287   This method is a helper function for \ref selectTest. It is used to test for selection when the
21288   chart style is \ref csOhlc. It only tests against the data points between \a begin and \a end.
21289 */
ohlcSelectTest(const QPointF & pos,const QCPFinancialDataMap::const_iterator & begin,const QCPFinancialDataMap::const_iterator & end) const21290 double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21291 {
21292   QCPAxis *keyAxis = mKeyAxis.data();
21293   QCPAxis *valueAxis = mValueAxis.data();
21294   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21295 
21296   double minDistSqr = std::numeric_limits<double>::max();
21297   if (keyAxis->orientation() == Qt::Horizontal)
21298   {
21299     for (auto it = begin; it != end; ++it)
21300     {
21301       double keyPixel = keyAxis->coordToPixel(it.value().key);
21302       // calculate distance to backbone:
21303       double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
21304       if (currentDistSqr < minDistSqr)
21305         minDistSqr = currentDistSqr;
21306     }
21307   } else // keyAxis->orientation() == Qt::Vertical
21308   {
21309     for (auto it = begin; it != end; ++it)
21310     {
21311       double keyPixel = keyAxis->coordToPixel(it.value().key);
21312       // calculate distance to backbone:
21313       double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
21314       if (currentDistSqr < minDistSqr)
21315         minDistSqr = currentDistSqr;
21316     }
21317   }
21318   return qSqrt(minDistSqr);
21319 }
21320 
21321 /*! \internal
21322 
21323   This method is a helper function for \ref selectTest. It is used to test for selection when the
21324   chart style is \ref csCandlestick. It only tests against the data points between \a begin and \a
21325   end.
21326 */
candlestickSelectTest(const QPointF & pos,const QCPFinancialDataMap::const_iterator & begin,const QCPFinancialDataMap::const_iterator & end) const21327 double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21328 {
21329   QCPAxis *keyAxis = mKeyAxis.data();
21330   QCPAxis *valueAxis = mValueAxis.data();
21331   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21332 
21333   double minDistSqr = std::numeric_limits<double>::max();
21334   if (keyAxis->orientation() == Qt::Horizontal)
21335   {
21336     for (auto it = begin; it != end; ++it)
21337     {
21338       double currentDistSqr;
21339       // determine whether pos is in open-close-box:
21340       QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21341       QCPRange boxValueRange(it.value().close, it.value().open);
21342       double posKey, posValue;
21343       pixelsToCoords(pos, posKey, posValue);
21344       if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21345       {
21346         currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21347       } else
21348       {
21349         // calculate distance to high/low lines:
21350         double keyPixel = keyAxis->coordToPixel(it.value().key);
21351         double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos);
21352         double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos);
21353         currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21354       }
21355       if (currentDistSqr < minDistSqr)
21356         minDistSqr = currentDistSqr;
21357     }
21358   } else // keyAxis->orientation() == Qt::Vertical
21359   {
21360     for (auto it = begin; it != end; ++it)
21361     {
21362       double currentDistSqr;
21363       // determine whether pos is in open-close-box:
21364       QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21365       QCPRange boxValueRange(it.value().close, it.value().open);
21366       double posKey, posValue;
21367       pixelsToCoords(pos, posKey, posValue);
21368       if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21369       {
21370         currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21371       } else
21372       {
21373         // calculate distance to high/low lines:
21374         double keyPixel = keyAxis->coordToPixel(it.value().key);
21375         double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos);
21376         double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos);
21377         currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21378       }
21379       if (currentDistSqr < minDistSqr)
21380         minDistSqr = currentDistSqr;
21381     }
21382   }
21383   return qSqrt(minDistSqr);
21384 }
21385 
21386 /*!  \internal
21387 
21388   called by the drawing methods to determine which data (key) range is visible at the current key
21389   axis range setting, so only that needs to be processed.
21390 
21391   \a lower returns an iterator to the lowest data point that needs to be taken into account when
21392   plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
21393   lower may still be just outside the visible range.
21394 
21395   \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
21396   just outside of the visible range.
21397 
21398   if the plottable contains no data, both \a lower and \a upper point to constEnd.
21399 
21400   \see QCPGraph::getVisibleDataBounds
21401 */
getVisibleDataBounds(QCPFinancialDataMap::const_iterator & lower,QCPFinancialDataMap::const_iterator & upper) const21402 void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
21403 {
21404   if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
21405   if (mData->isEmpty())
21406   {
21407     lower = mData->constEnd();
21408     upper = mData->constEnd();
21409     return;
21410   }
21411 
21412   // get visible data range as QMap iterators
21413   auto lbound = mData->lowerBound(mKeyAxis->range().lower);
21414   auto ubound = mData->upperBound(mKeyAxis->range().upper);
21415   bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
21416   bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
21417 
21418   lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
21419   upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
21420 }
21421 
21422 
21423 ////////////////////////////////////////////////////////////////////////////////////////////////////
21424 //////////////////// QCPItemStraightLine
21425 ////////////////////////////////////////////////////////////////////////////////////////////////////
21426 
21427 /*! \class QCPItemStraightLine
21428   \brief A straight line that spans infinitely in both directions
21429 
21430   \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions."
21431 
21432   It has two positions, \a point1 and \a point2, which define the straight line.
21433 */
21434 
21435 /*!
21436   Creates a straight line item and sets default values.
21437 
21438   The constructed item can be added to the plot with QCustomPlot::addItem.
21439 */
QCPItemStraightLine(QCustomPlot * parentPlot)21440 QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :
21441   QCPAbstractItem(parentPlot),
21442   point1(createPosition(QLatin1String("point1"))),
21443   point2(createPosition(QLatin1String("point2")))
21444 {
21445   point1->setCoords(0, 0);
21446   point2->setCoords(1, 1);
21447 
21448   setPen(QPen(Qt::black));
21449   setSelectedPen(QPen(Qt::blue,2));
21450 }
21451 
~QCPItemStraightLine()21452 QCPItemStraightLine::~QCPItemStraightLine()
21453 {
21454 }
21455 
21456 /*!
21457   Sets the pen that will be used to draw the line
21458 
21459   \see setSelectedPen
21460 */
setPen(const QPen & pen)21461 void QCPItemStraightLine::setPen(const QPen &pen)
21462 {
21463   mPen = pen;
21464 }
21465 
21466 /*!
21467   Sets the pen that will be used to draw the line when selected
21468 
21469   \see setPen, setSelected
21470 */
setSelectedPen(const QPen & pen)21471 void QCPItemStraightLine::setSelectedPen(const QPen &pen)
21472 {
21473   mSelectedPen = pen;
21474 }
21475 
21476 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const21477 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21478 {
21479   Q_UNUSED(details)
21480   if (onlySelectable && !mSelectable)
21481     return -1;
21482 
21483   return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
21484 }
21485 
21486 /* inherits documentation from base class */
draw(QCPPainter * painter)21487 void QCPItemStraightLine::draw(QCPPainter *painter)
21488 {
21489   QVector2D start(point1->pixelPoint());
21490   QVector2D end(point2->pixelPoint());
21491   // get visible segment of straight line inside clipRect:
21492   double clipPad = mainPen().widthF();
21493   QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21494   // paint visible segment, if existent:
21495   if (!line.isNull())
21496   {
21497     painter->setPen(mainPen());
21498     painter->drawLine(line);
21499   }
21500 }
21501 
21502 /*! \internal
21503 
21504   finds the shortest distance of \a point to the straight line defined by the base point \a
21505   base and the direction vector \a vec.
21506 
21507   This is a helper function for \ref selectTest.
21508 */
distToStraightLine(const QVector2D & base,const QVector2D & vec,const QVector2D & point) const21509 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
21510 {
21511   return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
21512 }
21513 
21514 /*! \internal
21515 
21516   Returns the section of the straight line defined by \a base and direction vector \a
21517   vec, that is visible in the specified \a rect.
21518 
21519   This is a helper function for \ref draw.
21520 */
getRectClippedStraightLine(const QVector2D & base,const QVector2D & vec,const QRect & rect) const21521 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
21522 {
21523   double bx, by;
21524   double gamma;
21525   QLineF result;
21526   if (vec.x() == 0 && vec.y() == 0)
21527     return result;
21528   if (qFuzzyIsNull(vec.x())) // line is vertical
21529   {
21530     // check top of rect:
21531     bx = rect.left();
21532     by = rect.top();
21533     gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21534     if (gamma >= 0 && gamma <= rect.width())
21535       result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
21536   } else if (qFuzzyIsNull(vec.y())) // line is horizontal
21537   {
21538     // check left of rect:
21539     bx = rect.left();
21540     by = rect.top();
21541     gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21542     if (gamma >= 0 && gamma <= rect.height())
21543       result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
21544   } else // line is skewed
21545   {
21546     QList<QVector2D> pointVectors;
21547     // check top of rect:
21548     bx = rect.left();
21549     by = rect.top();
21550     gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21551     if (gamma >= 0 && gamma <= rect.width())
21552       pointVectors.append(QVector2D(bx+gamma, by));
21553     // check bottom of rect:
21554     bx = rect.left();
21555     by = rect.bottom();
21556     gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21557     if (gamma >= 0 && gamma <= rect.width())
21558       pointVectors.append(QVector2D(bx+gamma, by));
21559     // check left of rect:
21560     bx = rect.left();
21561     by = rect.top();
21562     gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21563     if (gamma >= 0 && gamma <= rect.height())
21564       pointVectors.append(QVector2D(bx, by+gamma));
21565     // check right of rect:
21566     bx = rect.right();
21567     by = rect.top();
21568     gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21569     if (gamma >= 0 && gamma <= rect.height())
21570       pointVectors.append(QVector2D(bx, by+gamma));
21571 
21572     // evaluate points:
21573     if (pointVectors.size() == 2)
21574     {
21575       result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21576     } else if (pointVectors.size() > 2)
21577     {
21578       // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21579       double distSqrMax = 0;
21580       QVector2D pv1, pv2;
21581       for (int i=0; i<pointVectors.size()-1; ++i)
21582       {
21583         for (int k=i+1; k<pointVectors.size(); ++k)
21584         {
21585           double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21586           if (distSqr > distSqrMax)
21587           {
21588             pv1 = pointVectors.at(i);
21589             pv2 = pointVectors.at(k);
21590             distSqrMax = distSqr;
21591           }
21592         }
21593       }
21594       result.setPoints(pv1.toPointF(), pv2.toPointF());
21595     }
21596   }
21597   return result;
21598 }
21599 
21600 /*! \internal
21601 
21602   Returns the pen that should be used for drawing lines. Returns mPen when the
21603   item is not selected and mSelectedPen when it is.
21604 */
mainPen() const21605 QPen QCPItemStraightLine::mainPen() const
21606 {
21607   return mSelected ? mSelectedPen : mPen;
21608 }
21609 
21610 
21611 ////////////////////////////////////////////////////////////////////////////////////////////////////
21612 //////////////////// QCPItemLine
21613 ////////////////////////////////////////////////////////////////////////////////////////////////////
21614 
21615 /*! \class QCPItemLine
21616   \brief A line from one point to another
21617 
21618   \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions."
21619 
21620   It has two positions, \a start and \a end, which define the end points of the line.
21621 
21622   With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow.
21623 */
21624 
21625 /*!
21626   Creates a line item and sets default values.
21627 
21628   The constructed item can be added to the plot with QCustomPlot::addItem.
21629 */
QCPItemLine(QCustomPlot * parentPlot)21630 QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :
21631   QCPAbstractItem(parentPlot),
21632   start(createPosition(QLatin1String("start"))),
21633   end(createPosition(QLatin1String("end")))
21634 {
21635   start->setCoords(0, 0);
21636   end->setCoords(1, 1);
21637 
21638   setPen(QPen(Qt::black));
21639   setSelectedPen(QPen(Qt::blue,2));
21640 }
21641 
~QCPItemLine()21642 QCPItemLine::~QCPItemLine()
21643 {
21644 }
21645 
21646 /*!
21647   Sets the pen that will be used to draw the line
21648 
21649   \see setSelectedPen
21650 */
setPen(const QPen & pen)21651 void QCPItemLine::setPen(const QPen &pen)
21652 {
21653   mPen = pen;
21654 }
21655 
21656 /*!
21657   Sets the pen that will be used to draw the line when selected
21658 
21659   \see setPen, setSelected
21660 */
setSelectedPen(const QPen & pen)21661 void QCPItemLine::setSelectedPen(const QPen &pen)
21662 {
21663   mSelectedPen = pen;
21664 }
21665 
21666 /*!
21667   Sets the line ending style of the head. The head corresponds to the \a end position.
21668 
21669   Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21670   a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
21671 
21672   \see setTail
21673 */
setHead(const QCPLineEnding & head)21674 void QCPItemLine::setHead(const QCPLineEnding &head)
21675 {
21676   mHead = head;
21677 }
21678 
21679 /*!
21680   Sets the line ending style of the tail. The tail corresponds to the \a start position.
21681 
21682   Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21683   a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
21684 
21685   \see setHead
21686 */
setTail(const QCPLineEnding & tail)21687 void QCPItemLine::setTail(const QCPLineEnding &tail)
21688 {
21689   mTail = tail;
21690 }
21691 
21692 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const21693 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21694 {
21695   Q_UNUSED(details)
21696   if (onlySelectable && !mSelectable)
21697     return -1;
21698 
21699   return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
21700 }
21701 
21702 /* inherits documentation from base class */
draw(QCPPainter * painter)21703 void QCPItemLine::draw(QCPPainter *painter)
21704 {
21705   QVector2D startVec(start->pixelPoint());
21706   QVector2D endVec(end->pixelPoint());
21707   if (startVec.toPoint() == endVec.toPoint())
21708     return;
21709   // get visible segment of straight line inside clipRect:
21710   double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
21711   clipPad = qMax(clipPad, (double)mainPen().widthF());
21712   QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21713   // paint visible segment, if existent:
21714   if (!line.isNull())
21715   {
21716     painter->setPen(mainPen());
21717     painter->drawLine(line);
21718     painter->setBrush(Qt::SolidPattern);
21719     if (mTail.style() != QCPLineEnding::esNone)
21720       mTail.draw(painter, startVec, startVec-endVec);
21721     if (mHead.style() != QCPLineEnding::esNone)
21722       mHead.draw(painter, endVec, endVec-startVec);
21723   }
21724 }
21725 
21726 /*! \internal
21727 
21728   Returns the section of the line defined by \a start and \a end, that is visible in the specified
21729   \a rect.
21730 
21731   This is a helper function for \ref draw.
21732 */
getRectClippedLine(const QVector2D & start,const QVector2D & end,const QRect & rect) const21733 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
21734 {
21735   bool containsStart = rect.contains(start.x(), start.y());
21736   bool containsEnd = rect.contains(end.x(), end.y());
21737   if (containsStart && containsEnd)
21738     return QLineF(start.toPointF(), end.toPointF());
21739 
21740   QVector2D base = start;
21741   QVector2D vec = end-start;
21742   double bx, by;
21743   double gamma, mu;
21744   QLineF result;
21745   QList<QVector2D> pointVectors;
21746 
21747   if (!qFuzzyIsNull(vec.y())) // line is not horizontal
21748   {
21749     // check top of rect:
21750     bx = rect.left();
21751     by = rect.top();
21752     mu = (by-base.y())/vec.y();
21753     if (mu >= 0 && mu <= 1)
21754     {
21755       gamma = base.x()-bx + mu*vec.x();
21756       if (gamma >= 0 && gamma <= rect.width())
21757         pointVectors.append(QVector2D(bx+gamma, by));
21758     }
21759     // check bottom of rect:
21760     bx = rect.left();
21761     by = rect.bottom();
21762     mu = (by-base.y())/vec.y();
21763     if (mu >= 0 && mu <= 1)
21764     {
21765       gamma = base.x()-bx + mu*vec.x();
21766       if (gamma >= 0 && gamma <= rect.width())
21767         pointVectors.append(QVector2D(bx+gamma, by));
21768     }
21769   }
21770   if (!qFuzzyIsNull(vec.x())) // line is not vertical
21771   {
21772     // check left of rect:
21773     bx = rect.left();
21774     by = rect.top();
21775     mu = (bx-base.x())/vec.x();
21776     if (mu >= 0 && mu <= 1)
21777     {
21778       gamma = base.y()-by + mu*vec.y();
21779       if (gamma >= 0 && gamma <= rect.height())
21780         pointVectors.append(QVector2D(bx, by+gamma));
21781     }
21782     // check right of rect:
21783     bx = rect.right();
21784     by = rect.top();
21785     mu = (bx-base.x())/vec.x();
21786     if (mu >= 0 && mu <= 1)
21787     {
21788       gamma = base.y()-by + mu*vec.y();
21789       if (gamma >= 0 && gamma <= rect.height())
21790         pointVectors.append(QVector2D(bx, by+gamma));
21791     }
21792   }
21793 
21794   if (containsStart)
21795     pointVectors.append(start);
21796   if (containsEnd)
21797     pointVectors.append(end);
21798 
21799   // evaluate points:
21800   if (pointVectors.size() == 2)
21801   {
21802     result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21803   } else if (pointVectors.size() > 2)
21804   {
21805     // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21806     double distSqrMax = 0;
21807     QVector2D pv1, pv2;
21808     for (int i=0; i<pointVectors.size()-1; ++i)
21809     {
21810       for (int k=i+1; k<pointVectors.size(); ++k)
21811       {
21812         double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21813         if (distSqr > distSqrMax)
21814         {
21815           pv1 = pointVectors.at(i);
21816           pv2 = pointVectors.at(k);
21817           distSqrMax = distSqr;
21818         }
21819       }
21820     }
21821     result.setPoints(pv1.toPointF(), pv2.toPointF());
21822   }
21823   return result;
21824 }
21825 
21826 /*! \internal
21827 
21828   Returns the pen that should be used for drawing lines. Returns mPen when the
21829   item is not selected and mSelectedPen when it is.
21830 */
mainPen() const21831 QPen QCPItemLine::mainPen() const
21832 {
21833   return mSelected ? mSelectedPen : mPen;
21834 }
21835 
21836 
21837 ////////////////////////////////////////////////////////////////////////////////////////////////////
21838 //////////////////// QCPItemCurve
21839 ////////////////////////////////////////////////////////////////////////////////////////////////////
21840 
21841 /*! \class QCPItemCurve
21842   \brief A curved line from one point to another
21843 
21844   \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions."
21845 
21846   It has four positions, \a start and \a end, which define the end points of the line, and two
21847   control points which define the direction the line exits from the start and the direction from
21848   which it approaches the end: \a startDir and \a endDir.
21849 
21850   With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an
21851   arrow.
21852 
21853   Often it is desirable for the control points to stay at fixed relative positions to the start/end
21854   point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start,
21855   and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir.
21856 */
21857 
21858 /*!
21859   Creates a curve item and sets default values.
21860 
21861   The constructed item can be added to the plot with QCustomPlot::addItem.
21862 */
QCPItemCurve(QCustomPlot * parentPlot)21863 QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :
21864   QCPAbstractItem(parentPlot),
21865   start(createPosition(QLatin1String("start"))),
21866   startDir(createPosition(QLatin1String("startDir"))),
21867   endDir(createPosition(QLatin1String("endDir"))),
21868   end(createPosition(QLatin1String("end")))
21869 {
21870   start->setCoords(0, 0);
21871   startDir->setCoords(0.5, 0);
21872   endDir->setCoords(0, 0.5);
21873   end->setCoords(1, 1);
21874 
21875   setPen(QPen(Qt::black));
21876   setSelectedPen(QPen(Qt::blue,2));
21877 }
21878 
~QCPItemCurve()21879 QCPItemCurve::~QCPItemCurve()
21880 {
21881 }
21882 
21883 /*!
21884   Sets the pen that will be used to draw the line
21885 
21886   \see setSelectedPen
21887 */
setPen(const QPen & pen)21888 void QCPItemCurve::setPen(const QPen &pen)
21889 {
21890   mPen = pen;
21891 }
21892 
21893 /*!
21894   Sets the pen that will be used to draw the line when selected
21895 
21896   \see setPen, setSelected
21897 */
setSelectedPen(const QPen & pen)21898 void QCPItemCurve::setSelectedPen(const QPen &pen)
21899 {
21900   mSelectedPen = pen;
21901 }
21902 
21903 /*!
21904   Sets the line ending style of the head. The head corresponds to the \a end position.
21905 
21906   Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21907   a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
21908 
21909   \see setTail
21910 */
setHead(const QCPLineEnding & head)21911 void QCPItemCurve::setHead(const QCPLineEnding &head)
21912 {
21913   mHead = head;
21914 }
21915 
21916 /*!
21917   Sets the line ending style of the tail. The tail corresponds to the \a start position.
21918 
21919   Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21920   a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
21921 
21922   \see setHead
21923 */
setTail(const QCPLineEnding & tail)21924 void QCPItemCurve::setTail(const QCPLineEnding &tail)
21925 {
21926   mTail = tail;
21927 }
21928 
21929 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const21930 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21931 {
21932   Q_UNUSED(details)
21933   if (onlySelectable && !mSelectable)
21934     return -1;
21935 
21936   QPointF startVec(start->pixelPoint());
21937   QPointF startDirVec(startDir->pixelPoint());
21938   QPointF endDirVec(endDir->pixelPoint());
21939   QPointF endVec(end->pixelPoint());
21940 
21941   QPainterPath cubicPath(startVec);
21942   cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21943 
21944   QPolygonF polygon = cubicPath.toSubpathPolygons().first();
21945   double minDistSqr = std::numeric_limits<double>::max();
21946   for (int i=1; i<polygon.size(); ++i)
21947   {
21948     double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
21949     if (distSqr < minDistSqr)
21950       minDistSqr = distSqr;
21951   }
21952   return qSqrt(minDistSqr);
21953 }
21954 
21955 /* inherits documentation from base class */
draw(QCPPainter * painter)21956 void QCPItemCurve::draw(QCPPainter *painter)
21957 {
21958   QPointF startVec(start->pixelPoint());
21959   QPointF startDirVec(startDir->pixelPoint());
21960   QPointF endDirVec(endDir->pixelPoint());
21961   QPointF endVec(end->pixelPoint());
21962   if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
21963     return;
21964 
21965   QPainterPath cubicPath(startVec);
21966   cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21967 
21968   // paint visible segment, if existent:
21969   QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
21970   QRect cubicRect = cubicPath.controlPointRect().toRect();
21971   if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
21972     cubicRect.adjust(0, 0, 1, 1);
21973   if (clip.intersects(cubicRect))
21974   {
21975     painter->setPen(mainPen());
21976     painter->drawPath(cubicPath);
21977     painter->setBrush(Qt::SolidPattern);
21978     if (mTail.style() != QCPLineEnding::esNone)
21979       mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
21980     if (mHead.style() != QCPLineEnding::esNone)
21981       mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
21982   }
21983 }
21984 
21985 /*! \internal
21986 
21987   Returns the pen that should be used for drawing lines. Returns mPen when the
21988   item is not selected and mSelectedPen when it is.
21989 */
mainPen() const21990 QPen QCPItemCurve::mainPen() const
21991 {
21992   return mSelected ? mSelectedPen : mPen;
21993 }
21994 
21995 
21996 ////////////////////////////////////////////////////////////////////////////////////////////////////
21997 //////////////////// QCPItemRect
21998 ////////////////////////////////////////////////////////////////////////////////////////////////////
21999 
22000 /*! \class QCPItemRect
22001   \brief A rectangle
22002 
22003   \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions."
22004 
22005   It has two positions, \a topLeft and \a bottomRight, which define the rectangle.
22006 */
22007 
22008 /*!
22009   Creates a rectangle item and sets default values.
22010 
22011   The constructed item can be added to the plot with QCustomPlot::addItem.
22012 */
QCPItemRect(QCustomPlot * parentPlot)22013 QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :
22014   QCPAbstractItem(parentPlot),
22015   topLeft(createPosition(QLatin1String("topLeft"))),
22016   bottomRight(createPosition(QLatin1String("bottomRight"))),
22017   top(createAnchor(QLatin1String("top"), aiTop)),
22018   topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22019   right(createAnchor(QLatin1String("right"), aiRight)),
22020   bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22021   bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22022   left(createAnchor(QLatin1String("left"), aiLeft))
22023 {
22024   topLeft->setCoords(0, 1);
22025   bottomRight->setCoords(1, 0);
22026 
22027   setPen(QPen(Qt::black));
22028   setSelectedPen(QPen(Qt::blue,2));
22029   setBrush(Qt::NoBrush);
22030   setSelectedBrush(Qt::NoBrush);
22031 }
22032 
~QCPItemRect()22033 QCPItemRect::~QCPItemRect()
22034 {
22035 }
22036 
22037 /*!
22038   Sets the pen that will be used to draw the line of the rectangle
22039 
22040   \see setSelectedPen, setBrush
22041 */
setPen(const QPen & pen)22042 void QCPItemRect::setPen(const QPen &pen)
22043 {
22044   mPen = pen;
22045 }
22046 
22047 /*!
22048   Sets the pen that will be used to draw the line of the rectangle when selected
22049 
22050   \see setPen, setSelected
22051 */
setSelectedPen(const QPen & pen)22052 void QCPItemRect::setSelectedPen(const QPen &pen)
22053 {
22054   mSelectedPen = pen;
22055 }
22056 
22057 /*!
22058   Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to
22059   Qt::NoBrush.
22060 
22061   \see setSelectedBrush, setPen
22062 */
setBrush(const QBrush & brush)22063 void QCPItemRect::setBrush(const QBrush &brush)
22064 {
22065   mBrush = brush;
22066 }
22067 
22068 /*!
22069   Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a
22070   brush to Qt::NoBrush.
22071 
22072   \see setBrush
22073 */
setSelectedBrush(const QBrush & brush)22074 void QCPItemRect::setSelectedBrush(const QBrush &brush)
22075 {
22076   mSelectedBrush = brush;
22077 }
22078 
22079 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const22080 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22081 {
22082   Q_UNUSED(details)
22083   if (onlySelectable && !mSelectable)
22084     return -1;
22085 
22086   QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
22087   bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
22088   return rectSelectTest(rect, pos, filledRect);
22089 }
22090 
22091 /* inherits documentation from base class */
draw(QCPPainter * painter)22092 void QCPItemRect::draw(QCPPainter *painter)
22093 {
22094   QPointF p1 = topLeft->pixelPoint();
22095   QPointF p2 = bottomRight->pixelPoint();
22096   if (p1.toPoint() == p2.toPoint())
22097     return;
22098   QRectF rect = QRectF(p1, p2).normalized();
22099   double clipPad = mainPen().widthF();
22100   QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22101   if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
22102   {
22103     painter->setPen(mainPen());
22104     painter->setBrush(mainBrush());
22105     painter->drawRect(rect);
22106   }
22107 }
22108 
22109 /* inherits documentation from base class */
anchorPixelPoint(int anchorId) const22110 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
22111 {
22112   QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22113   switch (anchorId)
22114   {
22115     case aiTop:         return (rect.topLeft()+rect.topRight())*0.5;
22116     case aiTopRight:    return rect.topRight();
22117     case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;
22118     case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;
22119     case aiBottomLeft:  return rect.bottomLeft();
22120     case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;
22121   }
22122 
22123   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22124   return QPointF();
22125 }
22126 
22127 /*! \internal
22128 
22129   Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22130   and mSelectedPen when it is.
22131 */
mainPen() const22132 QPen QCPItemRect::mainPen() const
22133 {
22134   return mSelected ? mSelectedPen : mPen;
22135 }
22136 
22137 /*! \internal
22138 
22139   Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22140   is not selected and mSelectedBrush when it is.
22141 */
mainBrush() const22142 QBrush QCPItemRect::mainBrush() const
22143 {
22144   return mSelected ? mSelectedBrush : mBrush;
22145 }
22146 
22147 
22148 ////////////////////////////////////////////////////////////////////////////////////////////////////
22149 //////////////////// QCPItemText
22150 ////////////////////////////////////////////////////////////////////////////////////////////////////
22151 
22152 /*! \class QCPItemText
22153   \brief A text label
22154 
22155   \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions."
22156 
22157   Its position is defined by the member \a position and the setting of \ref setPositionAlignment.
22158   The latter controls which part of the text rect shall be aligned with \a position.
22159 
22160   The text alignment itself (i.e. left, center, right) can be controlled with \ref
22161   setTextAlignment.
22162 
22163   The text may be rotated around the \a position point with \ref setRotation.
22164 */
22165 
22166 /*!
22167   Creates a text item and sets default values.
22168 
22169   The constructed item can be added to the plot with QCustomPlot::addItem.
22170 */
QCPItemText(QCustomPlot * parentPlot)22171 QCPItemText::QCPItemText(QCustomPlot *parentPlot) :
22172   QCPAbstractItem(parentPlot),
22173   position(createPosition(QLatin1String("position"))),
22174   topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
22175   top(createAnchor(QLatin1String("top"), aiTop)),
22176   topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22177   right(createAnchor(QLatin1String("right"), aiRight)),
22178   bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
22179   bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22180   bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22181   left(createAnchor(QLatin1String("left"), aiLeft))
22182 {
22183   position->setCoords(0, 0);
22184 
22185   setRotation(0);
22186   setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
22187   setPositionAlignment(Qt::AlignCenter);
22188   setText(QLatin1String("text"));
22189 
22190   setPen(Qt::NoPen);
22191   setSelectedPen(Qt::NoPen);
22192   setBrush(Qt::NoBrush);
22193   setSelectedBrush(Qt::NoBrush);
22194   setColor(Qt::black);
22195   setSelectedColor(Qt::blue);
22196 }
22197 
~QCPItemText()22198 QCPItemText::~QCPItemText()
22199 {
22200 }
22201 
22202 /*!
22203   Sets the color of the text.
22204 */
setColor(const QColor & color)22205 void QCPItemText::setColor(const QColor &color)
22206 {
22207   mColor = color;
22208 }
22209 
22210 /*!
22211   Sets the color of the text that will be used when the item is selected.
22212 */
setSelectedColor(const QColor & color)22213 void QCPItemText::setSelectedColor(const QColor &color)
22214 {
22215   mSelectedColor = color;
22216 }
22217 
22218 /*!
22219   Sets the pen that will be used do draw a rectangular border around the text. To disable the
22220   border, set \a pen to Qt::NoPen.
22221 
22222   \see setSelectedPen, setBrush, setPadding
22223 */
setPen(const QPen & pen)22224 void QCPItemText::setPen(const QPen &pen)
22225 {
22226   mPen = pen;
22227 }
22228 
22229 /*!
22230   Sets the pen that will be used do draw a rectangular border around the text, when the item is
22231   selected. To disable the border, set \a pen to Qt::NoPen.
22232 
22233   \see setPen
22234 */
setSelectedPen(const QPen & pen)22235 void QCPItemText::setSelectedPen(const QPen &pen)
22236 {
22237   mSelectedPen = pen;
22238 }
22239 
22240 /*!
22241   Sets the brush that will be used do fill the background of the text. To disable the
22242   background, set \a brush to Qt::NoBrush.
22243 
22244   \see setSelectedBrush, setPen, setPadding
22245 */
setBrush(const QBrush & brush)22246 void QCPItemText::setBrush(const QBrush &brush)
22247 {
22248   mBrush = brush;
22249 }
22250 
22251 /*!
22252   Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the
22253   background, set \a brush to Qt::NoBrush.
22254 
22255   \see setBrush
22256 */
setSelectedBrush(const QBrush & brush)22257 void QCPItemText::setSelectedBrush(const QBrush &brush)
22258 {
22259   mSelectedBrush = brush;
22260 }
22261 
22262 /*!
22263   Sets the font of the text.
22264 
22265   \see setSelectedFont, setColor
22266 */
setFont(const QFont & font)22267 void QCPItemText::setFont(const QFont &font)
22268 {
22269   mFont = font;
22270 }
22271 
22272 /*!
22273   Sets the font of the text that will be used when the item is selected.
22274 
22275   \see setFont
22276 */
setSelectedFont(const QFont & font)22277 void QCPItemText::setSelectedFont(const QFont &font)
22278 {
22279   mSelectedFont = font;
22280 }
22281 
22282 /*!
22283   Sets the text that will be displayed. Multi-line texts are supported by inserting a line break
22284   character, e.g. '\n'.
22285 
22286   \see setFont, setColor, setTextAlignment
22287 */
setText(const QString & text)22288 void QCPItemText::setText(const QString &text)
22289 {
22290   mText = text;
22291 }
22292 
22293 /*!
22294   Sets which point of the text rect shall be aligned with \a position.
22295 
22296   Examples:
22297   \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such
22298   that the top of the text rect will be horizontally centered on \a position.
22299   \li If \a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \a position will indicate the
22300   bottom left corner of the text rect.
22301 
22302   If you want to control the alignment of (multi-lined) text within the text rect, use \ref
22303   setTextAlignment.
22304 */
setPositionAlignment(Qt::Alignment alignment)22305 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
22306 {
22307   mPositionAlignment = alignment;
22308 }
22309 
22310 /*!
22311   Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
22312 */
setTextAlignment(Qt::Alignment alignment)22313 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
22314 {
22315   mTextAlignment = alignment;
22316 }
22317 
22318 /*!
22319   Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated
22320   around \a position.
22321 */
setRotation(double degrees)22322 void QCPItemText::setRotation(double degrees)
22323 {
22324   mRotation = degrees;
22325 }
22326 
22327 /*!
22328   Sets the distance between the border of the text rectangle and the text. The appearance (and
22329   visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush.
22330 */
setPadding(const QMargins & padding)22331 void QCPItemText::setPadding(const QMargins &padding)
22332 {
22333   mPadding = padding;
22334 }
22335 
22336 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const22337 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22338 {
22339   Q_UNUSED(details)
22340   if (onlySelectable && !mSelectable)
22341     return -1;
22342 
22343   // The rect may be rotated, so we transform the actual clicked pos to the rotated
22344   // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
22345   QPointF positionPixels(position->pixelPoint());
22346   QTransform inputTransform;
22347   inputTransform.translate(positionPixels.x(), positionPixels.y());
22348   inputTransform.rotate(-mRotation);
22349   inputTransform.translate(-positionPixels.x(), -positionPixels.y());
22350   QPointF rotatedPos = inputTransform.map(pos);
22351   QFontMetrics fontMetrics(mFont);
22352   QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22353   QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22354   QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
22355   textBoxRect.moveTopLeft(textPos.toPoint());
22356 
22357   return rectSelectTest(textBoxRect, rotatedPos, true);
22358 }
22359 
22360 /* inherits documentation from base class */
draw(QCPPainter * painter)22361 void QCPItemText::draw(QCPPainter *painter)
22362 {
22363   QPointF pos(position->pixelPoint());
22364   QTransform transform = painter->transform();
22365   transform.translate(pos.x(), pos.y());
22366   if (!qFuzzyIsNull(mRotation))
22367     transform.rotate(mRotation);
22368   painter->setFont(mainFont());
22369   QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22370   QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22371   QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22372   textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
22373   textBoxRect.moveTopLeft(textPos.toPoint());
22374   double clipPad = mainPen().widthF();
22375   QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22376   if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
22377   {
22378     painter->setTransform(transform);
22379     if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
22380         (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
22381     {
22382       painter->setPen(mainPen());
22383       painter->setBrush(mainBrush());
22384       painter->drawRect(textBoxRect);
22385     }
22386     painter->setBrush(Qt::NoBrush);
22387     painter->setPen(QPen(mainColor()));
22388     painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
22389   }
22390 }
22391 
22392 /* inherits documentation from base class */
anchorPixelPoint(int anchorId) const22393 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
22394 {
22395   // get actual rect points (pretty much copied from draw function):
22396   QPointF pos(position->pixelPoint());
22397   QTransform transform;
22398   transform.translate(pos.x(), pos.y());
22399   if (!qFuzzyIsNull(mRotation))
22400     transform.rotate(mRotation);
22401   QFontMetrics fontMetrics(mainFont());
22402   QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22403   QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22404   QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22405   textBoxRect.moveTopLeft(textPos.toPoint());
22406   QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
22407 
22408   switch (anchorId)
22409   {
22410     case aiTopLeft:     return rectPoly.at(0);
22411     case aiTop:         return (rectPoly.at(0)+rectPoly.at(1))*0.5;
22412     case aiTopRight:    return rectPoly.at(1);
22413     case aiRight:       return (rectPoly.at(1)+rectPoly.at(2))*0.5;
22414     case aiBottomRight: return rectPoly.at(2);
22415     case aiBottom:      return (rectPoly.at(2)+rectPoly.at(3))*0.5;
22416     case aiBottomLeft:  return rectPoly.at(3);
22417     case aiLeft:        return (rectPoly.at(3)+rectPoly.at(0))*0.5;
22418   }
22419 
22420   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22421   return QPointF();
22422 }
22423 
22424 /*! \internal
22425 
22426   Returns the point that must be given to the QPainter::drawText function (which expects the top
22427   left point of the text rect), according to the position \a pos, the text bounding box \a rect and
22428   the requested \a positionAlignment.
22429 
22430   For example, if \a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point
22431   will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally
22432   drawn at that point, the lower left corner of the resulting text rect is at \a pos.
22433 */
getTextDrawPoint(const QPointF & pos,const QRectF & rect,Qt::Alignment positionAlignment) const22434 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
22435 {
22436   if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
22437     return pos;
22438 
22439   QPointF result = pos; // start at top left
22440   if (positionAlignment.testFlag(Qt::AlignHCenter))
22441     result.rx() -= rect.width()/2.0;
22442   else if (positionAlignment.testFlag(Qt::AlignRight))
22443     result.rx() -= rect.width();
22444   if (positionAlignment.testFlag(Qt::AlignVCenter))
22445     result.ry() -= rect.height()/2.0;
22446   else if (positionAlignment.testFlag(Qt::AlignBottom))
22447     result.ry() -= rect.height();
22448   return result;
22449 }
22450 
22451 /*! \internal
22452 
22453   Returns the font that should be used for drawing text. Returns mFont when the item is not selected
22454   and mSelectedFont when it is.
22455 */
mainFont() const22456 QFont QCPItemText::mainFont() const
22457 {
22458   return mSelected ? mSelectedFont : mFont;
22459 }
22460 
22461 /*! \internal
22462 
22463   Returns the color that should be used for drawing text. Returns mColor when the item is not
22464   selected and mSelectedColor when it is.
22465 */
mainColor() const22466 QColor QCPItemText::mainColor() const
22467 {
22468   return mSelected ? mSelectedColor : mColor;
22469 }
22470 
22471 /*! \internal
22472 
22473   Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22474   and mSelectedPen when it is.
22475 */
mainPen() const22476 QPen QCPItemText::mainPen() const
22477 {
22478   return mSelected ? mSelectedPen : mPen;
22479 }
22480 
22481 /*! \internal
22482 
22483   Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22484   is not selected and mSelectedBrush when it is.
22485 */
mainBrush() const22486 QBrush QCPItemText::mainBrush() const
22487 {
22488   return mSelected ? mSelectedBrush : mBrush;
22489 }
22490 
22491 
22492 ////////////////////////////////////////////////////////////////////////////////////////////////////
22493 //////////////////// QCPItemEllipse
22494 ////////////////////////////////////////////////////////////////////////////////////////////////////
22495 
22496 /*! \class QCPItemEllipse
22497   \brief An ellipse
22498 
22499   \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions."
22500 
22501   It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in.
22502 */
22503 
22504 /*!
22505   Creates an ellipse item and sets default values.
22506 
22507   The constructed item can be added to the plot with QCustomPlot::addItem.
22508 */
QCPItemEllipse(QCustomPlot * parentPlot)22509 QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :
22510   QCPAbstractItem(parentPlot),
22511   topLeft(createPosition(QLatin1String("topLeft"))),
22512   bottomRight(createPosition(QLatin1String("bottomRight"))),
22513   topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
22514   top(createAnchor(QLatin1String("top"), aiTop)),
22515   topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
22516   right(createAnchor(QLatin1String("right"), aiRight)),
22517   bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
22518   bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22519   bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
22520   left(createAnchor(QLatin1String("left"), aiLeft)),
22521   center(createAnchor(QLatin1String("center"), aiCenter))
22522 {
22523   topLeft->setCoords(0, 1);
22524   bottomRight->setCoords(1, 0);
22525 
22526   setPen(QPen(Qt::black));
22527   setSelectedPen(QPen(Qt::blue, 2));
22528   setBrush(Qt::NoBrush);
22529   setSelectedBrush(Qt::NoBrush);
22530 }
22531 
~QCPItemEllipse()22532 QCPItemEllipse::~QCPItemEllipse()
22533 {
22534 }
22535 
22536 /*!
22537   Sets the pen that will be used to draw the line of the ellipse
22538 
22539   \see setSelectedPen, setBrush
22540 */
setPen(const QPen & pen)22541 void QCPItemEllipse::setPen(const QPen &pen)
22542 {
22543   mPen = pen;
22544 }
22545 
22546 /*!
22547   Sets the pen that will be used to draw the line of the ellipse when selected
22548 
22549   \see setPen, setSelected
22550 */
setSelectedPen(const QPen & pen)22551 void QCPItemEllipse::setSelectedPen(const QPen &pen)
22552 {
22553   mSelectedPen = pen;
22554 }
22555 
22556 /*!
22557   Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to
22558   Qt::NoBrush.
22559 
22560   \see setSelectedBrush, setPen
22561 */
setBrush(const QBrush & brush)22562 void QCPItemEllipse::setBrush(const QBrush &brush)
22563 {
22564   mBrush = brush;
22565 }
22566 
22567 /*!
22568   Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a
22569   brush to Qt::NoBrush.
22570 
22571   \see setBrush
22572 */
setSelectedBrush(const QBrush & brush)22573 void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
22574 {
22575   mSelectedBrush = brush;
22576 }
22577 
22578 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const22579 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22580 {
22581   Q_UNUSED(details)
22582   if (onlySelectable && !mSelectable)
22583     return -1;
22584 
22585   double result = -1;
22586   QPointF p1 = topLeft->pixelPoint();
22587   QPointF p2 = bottomRight->pixelPoint();
22588   QPointF center((p1+p2)/2.0);
22589   double a = qAbs(p1.x()-p2.x())/2.0;
22590   double b = qAbs(p1.y()-p2.y())/2.0;
22591   double x = pos.x()-center.x();
22592   double y = pos.y()-center.y();
22593 
22594   // distance to border:
22595   double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
22596   result = qAbs(c-1)*qSqrt(x*x+y*y);
22597   // filled ellipse, allow click inside to count as hit:
22598   if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
22599   {
22600     if (x*x/(a*a) + y*y/(b*b) <= 1)
22601       result = mParentPlot->selectionTolerance()*0.99;
22602   }
22603   return result;
22604 }
22605 
22606 /* inherits documentation from base class */
draw(QCPPainter * painter)22607 void QCPItemEllipse::draw(QCPPainter *painter)
22608 {
22609   QPointF p1 = topLeft->pixelPoint();
22610   QPointF p2 = bottomRight->pixelPoint();
22611   if (p1.toPoint() == p2.toPoint())
22612     return;
22613   QRectF ellipseRect = QRectF(p1, p2).normalized();
22614   QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22615   if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
22616   {
22617     painter->setPen(mainPen());
22618     painter->setBrush(mainBrush());
22619 #ifdef __EXCEPTIONS
22620     try // drawEllipse sometimes throws exceptions if ellipse is too big
22621     {
22622 #endif
22623       painter->drawEllipse(ellipseRect);
22624 #ifdef __EXCEPTIONS
22625     } catch (...)
22626     {
22627       qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
22628       setVisible(false);
22629     }
22630 #endif
22631   }
22632 }
22633 
22634 /* inherits documentation from base class */
anchorPixelPoint(int anchorId) const22635 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
22636 {
22637   QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22638   switch (anchorId)
22639   {
22640     case aiTopLeftRim:     return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
22641     case aiTop:            return (rect.topLeft()+rect.topRight())*0.5;
22642     case aiTopRightRim:    return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
22643     case aiRight:          return (rect.topRight()+rect.bottomRight())*0.5;
22644     case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
22645     case aiBottom:         return (rect.bottomLeft()+rect.bottomRight())*0.5;
22646     case aiBottomLeftRim:  return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
22647     case aiLeft:           return (rect.topLeft()+rect.bottomLeft())*0.5;
22648     case aiCenter:         return (rect.topLeft()+rect.bottomRight())*0.5;
22649   }
22650 
22651   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22652   return QPointF();
22653 }
22654 
22655 /*! \internal
22656 
22657   Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22658   and mSelectedPen when it is.
22659 */
mainPen() const22660 QPen QCPItemEllipse::mainPen() const
22661 {
22662   return mSelected ? mSelectedPen : mPen;
22663 }
22664 
22665 /*! \internal
22666 
22667   Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22668   is not selected and mSelectedBrush when it is.
22669 */
mainBrush() const22670 QBrush QCPItemEllipse::mainBrush() const
22671 {
22672   return mSelected ? mSelectedBrush : mBrush;
22673 }
22674 
22675 
22676 ////////////////////////////////////////////////////////////////////////////////////////////////////
22677 //////////////////// QCPItemPixmap
22678 ////////////////////////////////////////////////////////////////////////////////////////////////////
22679 
22680 /*! \class QCPItemPixmap
22681   \brief An arbitrary pixmap
22682 
22683   \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions."
22684 
22685   It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will
22686   be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to
22687   fit the rectangle or be drawn aligned to the topLeft position.
22688 
22689   If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown
22690   on the right side of the example image), the pixmap will be flipped in the respective
22691   orientations.
22692 */
22693 
22694 /*!
22695   Creates a rectangle item and sets default values.
22696 
22697   The constructed item can be added to the plot with QCustomPlot::addItem.
22698 */
QCPItemPixmap(QCustomPlot * parentPlot)22699 QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :
22700   QCPAbstractItem(parentPlot),
22701   topLeft(createPosition(QLatin1String("topLeft"))),
22702   bottomRight(createPosition(QLatin1String("bottomRight"))),
22703   top(createAnchor(QLatin1String("top"), aiTop)),
22704   topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22705   right(createAnchor(QLatin1String("right"), aiRight)),
22706   bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22707   bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22708   left(createAnchor(QLatin1String("left"), aiLeft)),
22709   mScaledPixmapInvalidated(true)
22710 {
22711   topLeft->setCoords(0, 1);
22712   bottomRight->setCoords(1, 0);
22713 
22714   setPen(Qt::NoPen);
22715   setSelectedPen(QPen(Qt::blue));
22716   setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
22717 }
22718 
~QCPItemPixmap()22719 QCPItemPixmap::~QCPItemPixmap()
22720 {
22721 }
22722 
22723 /*!
22724   Sets the pixmap that will be displayed.
22725 */
setPixmap(const QPixmap & pixmap)22726 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
22727 {
22728   mPixmap = pixmap;
22729   mScaledPixmapInvalidated = true;
22730   if (mPixmap.isNull())
22731     qDebug() << Q_FUNC_INFO << "pixmap is null";
22732 }
22733 
22734 /*!
22735   Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a
22736   bottomRight positions.
22737 */
setScaled(bool scaled,Qt::AspectRatioMode aspectRatioMode,Qt::TransformationMode transformationMode)22738 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
22739 {
22740   mScaled = scaled;
22741   mAspectRatioMode = aspectRatioMode;
22742   mTransformationMode = transformationMode;
22743   mScaledPixmapInvalidated = true;
22744 }
22745 
22746 /*!
22747   Sets the pen that will be used to draw a border around the pixmap.
22748 
22749   \see setSelectedPen, setBrush
22750 */
setPen(const QPen & pen)22751 void QCPItemPixmap::setPen(const QPen &pen)
22752 {
22753   mPen = pen;
22754 }
22755 
22756 /*!
22757   Sets the pen that will be used to draw a border around the pixmap when selected
22758 
22759   \see setPen, setSelected
22760 */
setSelectedPen(const QPen & pen)22761 void QCPItemPixmap::setSelectedPen(const QPen &pen)
22762 {
22763   mSelectedPen = pen;
22764 }
22765 
22766 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const22767 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22768 {
22769   Q_UNUSED(details)
22770   if (onlySelectable && !mSelectable)
22771     return -1;
22772 
22773   return rectSelectTest(getFinalRect(), pos, true);
22774 }
22775 
22776 /* inherits documentation from base class */
draw(QCPPainter * painter)22777 void QCPItemPixmap::draw(QCPPainter *painter)
22778 {
22779   bool flipHorz = false;
22780   bool flipVert = false;
22781   QRect rect = getFinalRect(&flipHorz, &flipVert);
22782   double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
22783   QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22784   if (boundingRect.intersects(clipRect()))
22785   {
22786     updateScaledPixmap(rect, flipHorz, flipVert);
22787     painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
22788     QPen pen = mainPen();
22789     if (pen.style() != Qt::NoPen)
22790     {
22791       painter->setPen(pen);
22792       painter->setBrush(Qt::NoBrush);
22793       painter->drawRect(rect);
22794     }
22795   }
22796 }
22797 
22798 /* inherits documentation from base class */
anchorPixelPoint(int anchorId) const22799 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
22800 {
22801   bool flipHorz;
22802   bool flipVert;
22803   QRect rect = getFinalRect(&flipHorz, &flipVert);
22804   // we actually want denormal rects (negative width/height) here, so restore
22805   // the flipped state:
22806   if (flipHorz)
22807     rect.adjust(rect.width(), 0, -rect.width(), 0);
22808   if (flipVert)
22809     rect.adjust(0, rect.height(), 0, -rect.height());
22810 
22811   switch (anchorId)
22812   {
22813     case aiTop:         return (rect.topLeft()+rect.topRight())*0.5;
22814     case aiTopRight:    return rect.topRight();
22815     case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;
22816     case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;
22817     case aiBottomLeft:  return rect.bottomLeft();
22818     case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;
22819   }
22820 
22821   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22822   return QPointF();
22823 }
22824 
22825 /*! \internal
22826 
22827   Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The
22828   parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped
22829   horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a
22830   bottomRight.)
22831 
22832   This function only creates the scaled pixmap when the buffered pixmap has a different size than
22833   the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does
22834   not cause expensive rescaling every time.
22835 
22836   If scaling is disabled, sets mScaledPixmap to a null QPixmap.
22837 */
updateScaledPixmap(QRect finalRect,bool flipHorz,bool flipVert)22838 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
22839 {
22840   if (mPixmap.isNull())
22841     return;
22842 
22843   if (mScaled)
22844   {
22845     if (finalRect.isNull())
22846       finalRect = getFinalRect(&flipHorz, &flipVert);
22847     if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size())
22848     {
22849       mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode);
22850       if (flipHorz || flipVert)
22851         mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
22852     }
22853   } else if (!mScaledPixmap.isNull())
22854     mScaledPixmap = QPixmap();
22855   mScaledPixmapInvalidated = false;
22856 }
22857 
22858 /*! \internal
22859 
22860   Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions
22861   and scaling settings.
22862 
22863   The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn
22864   flipped horizontally or vertically in the returned rect. (The returned rect itself is always
22865   normalized, i.e. the top left corner of the rect is actually further to the top/left than the
22866   bottom right corner). This is the case when the item position \a topLeft is further to the
22867   bottom/right than \a bottomRight.
22868 
22869   If scaling is disabled, returns a rect with size of the original pixmap and the top left corner
22870   aligned with the item position \a topLeft. The position \a bottomRight is ignored.
22871 */
getFinalRect(bool * flippedHorz,bool * flippedVert) const22872 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
22873 {
22874   QRect result;
22875   bool flipHorz = false;
22876   bool flipVert = false;
22877   QPoint p1 = topLeft->pixelPoint().toPoint();
22878   QPoint p2 = bottomRight->pixelPoint().toPoint();
22879   if (p1 == p2)
22880     return QRect(p1, QSize(0, 0));
22881   if (mScaled)
22882   {
22883     QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
22884     QPoint topLeft = p1;
22885     if (newSize.width() < 0)
22886     {
22887       flipHorz = true;
22888       newSize.rwidth() *= -1;
22889       topLeft.setX(p2.x());
22890     }
22891     if (newSize.height() < 0)
22892     {
22893       flipVert = true;
22894       newSize.rheight() *= -1;
22895       topLeft.setY(p2.y());
22896     }
22897     QSize scaledSize = mPixmap.size();
22898     scaledSize.scale(newSize, mAspectRatioMode);
22899     result = QRect(topLeft, scaledSize);
22900   } else
22901   {
22902     result = QRect(p1, mPixmap.size());
22903   }
22904   if (flippedHorz)
22905     *flippedHorz = flipHorz;
22906   if (flippedVert)
22907     *flippedVert = flipVert;
22908   return result;
22909 }
22910 
22911 /*! \internal
22912 
22913   Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22914   and mSelectedPen when it is.
22915 */
mainPen() const22916 QPen QCPItemPixmap::mainPen() const
22917 {
22918   return mSelected ? mSelectedPen : mPen;
22919 }
22920 
22921 
22922 ////////////////////////////////////////////////////////////////////////////////////////////////////
22923 //////////////////// QCPItemTracer
22924 ////////////////////////////////////////////////////////////////////////////////////////////////////
22925 
22926 /*! \class QCPItemTracer
22927   \brief Item that sticks to QCPGraph data points
22928 
22929   \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions."
22930 
22931   The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt
22932   the coordinate axes of the graph and update its \a position to be on the graph's data. This means
22933   the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a
22934   QCPGraph is connected, note that setting the coordinates of the tracer item directly via \a
22935   position will have no effect because they will be overriden in the next redraw (this is when the
22936   coordinate update happens).
22937 
22938   If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will
22939   stay at the corresponding end of the graph.
22940 
22941   With \ref setInterpolating you may specify whether the tracer may only stay exactly on data
22942   points or whether it interpolates data points linearly, if given a key that lies between two data
22943   points of the graph.
22944 
22945   The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer
22946   have no own visual appearance (set the style to \ref tsNone), and just connect other item
22947   positions to the tracer \a position (used as an anchor) via \ref
22948   QCPItemPosition::setParentAnchor.
22949 
22950   \note The tracer position is only automatically updated upon redraws. So when the data of the
22951   graph changes and immediately afterwards (without a redraw) the a position coordinates of the
22952   tracer are retrieved, they will not reflect the updated data of the graph. In this case \ref
22953   updatePosition must be called manually, prior to reading the tracer coordinates.
22954 */
22955 
22956 /*!
22957   Creates a tracer item and sets default values.
22958 
22959   The constructed item can be added to the plot with QCustomPlot::addItem.
22960 */
QCPItemTracer(QCustomPlot * parentPlot)22961 QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
22962   QCPAbstractItem(parentPlot),
22963   position(createPosition(QLatin1String("position"))),
22964   mGraph(0)
22965 {
22966   position->setCoords(0, 0);
22967 
22968   setBrush(Qt::NoBrush);
22969   setSelectedBrush(Qt::NoBrush);
22970   setPen(QPen(Qt::black));
22971   setSelectedPen(QPen(Qt::blue, 2));
22972   setStyle(tsCrosshair);
22973   setSize(6);
22974   setInterpolating(false);
22975   setGraphKey(0);
22976 }
22977 
~QCPItemTracer()22978 QCPItemTracer::~QCPItemTracer()
22979 {
22980 }
22981 
22982 /*!
22983   Sets the pen that will be used to draw the line of the tracer
22984 
22985   \see setSelectedPen, setBrush
22986 */
setPen(const QPen & pen)22987 void QCPItemTracer::setPen(const QPen &pen)
22988 {
22989   mPen = pen;
22990 }
22991 
22992 /*!
22993   Sets the pen that will be used to draw the line of the tracer when selected
22994 
22995   \see setPen, setSelected
22996 */
setSelectedPen(const QPen & pen)22997 void QCPItemTracer::setSelectedPen(const QPen &pen)
22998 {
22999   mSelectedPen = pen;
23000 }
23001 
23002 /*!
23003   Sets the brush that will be used to draw any fills of the tracer
23004 
23005   \see setSelectedBrush, setPen
23006 */
setBrush(const QBrush & brush)23007 void QCPItemTracer::setBrush(const QBrush &brush)
23008 {
23009   mBrush = brush;
23010 }
23011 
23012 /*!
23013   Sets the brush that will be used to draw any fills of the tracer, when selected.
23014 
23015   \see setBrush, setSelected
23016 */
setSelectedBrush(const QBrush & brush)23017 void QCPItemTracer::setSelectedBrush(const QBrush &brush)
23018 {
23019   mSelectedBrush = brush;
23020 }
23021 
23022 /*!
23023   Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare
23024   does, \ref tsCrosshair does not).
23025 */
setSize(double size)23026 void QCPItemTracer::setSize(double size)
23027 {
23028   mSize = size;
23029 }
23030 
23031 /*!
23032   Sets the style/visual appearance of the tracer.
23033 
23034   If you only want to use the tracer \a position as an anchor for other items, set \a style to
23035   \ref tsNone.
23036 */
setStyle(QCPItemTracer::TracerStyle style)23037 void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
23038 {
23039   mStyle = style;
23040 }
23041 
23042 /*!
23043   Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type
23044   QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph.
23045 
23046   To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed
23047   freely like any other item position. This is the state the tracer will assume when its graph gets
23048   deleted while still attached to it.
23049 
23050   \see setGraphKey
23051 */
setGraph(QCPGraph * graph)23052 void QCPItemTracer::setGraph(QCPGraph *graph)
23053 {
23054   if (graph)
23055   {
23056     if (graph->parentPlot() == mParentPlot)
23057     {
23058       position->setType(QCPItemPosition::ptPlotCoords);
23059       position->setAxes(graph->keyAxis(), graph->valueAxis());
23060       mGraph = graph;
23061       updatePosition();
23062     } else
23063       qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
23064   } else
23065   {
23066     mGraph = 0;
23067   }
23068 }
23069 
23070 /*!
23071   Sets the key of the graph's data point the tracer will be positioned at. This is the only free
23072   coordinate of a tracer when attached to a graph.
23073 
23074   Depending on \ref setInterpolating, the tracer will be either positioned on the data point
23075   closest to \a key, or will stay exactly at \a key and interpolate the value linearly.
23076 
23077   \see setGraph, setInterpolating
23078 */
setGraphKey(double key)23079 void QCPItemTracer::setGraphKey(double key)
23080 {
23081   mGraphKey = key;
23082 }
23083 
23084 /*!
23085   Sets whether the value of the graph's data points shall be interpolated, when positioning the
23086   tracer.
23087 
23088   If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on
23089   the data point of the graph which is closest to the key, but which is not necessarily exactly
23090   there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and
23091   the appropriate value will be interpolated from the graph's data points linearly.
23092 
23093   \see setGraph, setGraphKey
23094 */
setInterpolating(bool enabled)23095 void QCPItemTracer::setInterpolating(bool enabled)
23096 {
23097   mInterpolating = enabled;
23098 }
23099 
23100 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const23101 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23102 {
23103   Q_UNUSED(details)
23104   if (onlySelectable && !mSelectable)
23105     return -1;
23106 
23107   QPointF center(position->pixelPoint());
23108   double w = mSize/2.0;
23109   QRect clip = clipRect();
23110   switch (mStyle)
23111   {
23112     case tsNone: return -1;
23113     case tsPlus:
23114     {
23115       if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23116         return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
23117                           distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
23118       break;
23119     }
23120     case tsCrosshair:
23121     {
23122       return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
23123                         distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
23124     }
23125     case tsCircle:
23126     {
23127       if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23128       {
23129         // distance to border:
23130         double centerDist = QVector2D(center-pos).length();
23131         double circleLine = w;
23132         double result = qAbs(centerDist-circleLine);
23133         // filled ellipse, allow click inside to count as hit:
23134         if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
23135         {
23136           if (centerDist <= circleLine)
23137             result = mParentPlot->selectionTolerance()*0.99;
23138         }
23139         return result;
23140       }
23141       break;
23142     }
23143     case tsSquare:
23144     {
23145       if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23146       {
23147         QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
23148         bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23149         return rectSelectTest(rect, pos, filledRect);
23150       }
23151       break;
23152     }
23153   }
23154   return -1;
23155 }
23156 
23157 /* inherits documentation from base class */
draw(QCPPainter * painter)23158 void QCPItemTracer::draw(QCPPainter *painter)
23159 {
23160   updatePosition();
23161   if (mStyle == tsNone)
23162     return;
23163 
23164   painter->setPen(mainPen());
23165   painter->setBrush(mainBrush());
23166   QPointF center(position->pixelPoint());
23167   double w = mSize/2.0;
23168   QRect clip = clipRect();
23169   switch (mStyle)
23170   {
23171     case tsNone: return;
23172     case tsPlus:
23173     {
23174       if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23175       {
23176         painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
23177         painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
23178       }
23179       break;
23180     }
23181     case tsCrosshair:
23182     {
23183       if (center.y() > clip.top() && center.y() < clip.bottom())
23184         painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
23185       if (center.x() > clip.left() && center.x() < clip.right())
23186         painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
23187       break;
23188     }
23189     case tsCircle:
23190     {
23191       if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23192         painter->drawEllipse(center, w, w);
23193       break;
23194     }
23195     case tsSquare:
23196     {
23197       if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23198         painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
23199       break;
23200     }
23201   }
23202 }
23203 
23204 /*!
23205   If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a
23206   position to reside on the graph data, depending on the configured key (\ref setGraphKey).
23207 
23208   It is called automatically on every redraw and normally doesn't need to be called manually. One
23209   exception is when you want to read the tracer coordinates via \a position and are not sure that
23210   the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw.
23211   In that situation, call this function before accessing \a position, to make sure you don't get
23212   out-of-date coordinates.
23213 
23214   If there is no graph set on this tracer, this function does nothing.
23215 */
updatePosition()23216 void QCPItemTracer::updatePosition()
23217 {
23218   if (mGraph)
23219   {
23220     if (mParentPlot->hasPlottable(mGraph))
23221     {
23222       if (mGraph->data()->size() > 1)
23223       {
23224         auto first = mGraph->data()->constBegin();
23225         auto last = mGraph->data()->constEnd()-1;
23226         if (mGraphKey < first.key())
23227           position->setCoords(first.key(), first.value().value);
23228         else if (mGraphKey > last.key())
23229           position->setCoords(last.key(), last.value().value);
23230         else
23231         {
23232           auto it = mGraph->data()->lowerBound(mGraphKey);
23233           if (it != first) // mGraphKey is somewhere between iterators
23234           {
23235             auto prevIt = it-1;
23236             if (mInterpolating)
23237             {
23238               // interpolate between iterators around mGraphKey:
23239               double slope = 0;
23240               if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
23241                 slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
23242               position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
23243             } else
23244             {
23245               // find iterator with key closest to mGraphKey:
23246               if (mGraphKey < (prevIt.key()+it.key())*0.5)
23247                 it = prevIt;
23248               position->setCoords(it.key(), it.value().value);
23249             }
23250           } else // mGraphKey is exactly on first iterator
23251             position->setCoords(it.key(), it.value().value);
23252         }
23253       } else if (mGraph->data()->size() == 1)
23254       {
23255         auto it = mGraph->data()->constBegin();
23256         position->setCoords(it.key(), it.value().value);
23257       } else
23258         qDebug() << Q_FUNC_INFO << "graph has no data";
23259     } else
23260       qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
23261   }
23262 }
23263 
23264 /*! \internal
23265 
23266   Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
23267   and mSelectedPen when it is.
23268 */
mainPen() const23269 QPen QCPItemTracer::mainPen() const
23270 {
23271   return mSelected ? mSelectedPen : mPen;
23272 }
23273 
23274 /*! \internal
23275 
23276   Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
23277   is not selected and mSelectedBrush when it is.
23278 */
mainBrush() const23279 QBrush QCPItemTracer::mainBrush() const
23280 {
23281   return mSelected ? mSelectedBrush : mBrush;
23282 }
23283 
23284 
23285 ////////////////////////////////////////////////////////////////////////////////////////////////////
23286 //////////////////// QCPItemBracket
23287 ////////////////////////////////////////////////////////////////////////////////////////////////////
23288 
23289 /*! \class QCPItemBracket
23290   \brief A bracket for referencing/highlighting certain parts in the plot.
23291 
23292   \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions."
23293 
23294   It has two positions, \a left and \a right, which define the span of the bracket. If \a left is
23295   actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the
23296   example image.
23297 
23298   The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket
23299   stretches away from the embraced span, can be controlled with \ref setLength.
23300 
23301   \image html QCPItemBracket-length.png
23302   <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
23303   bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
23304 
23305   It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine
23306   or QCPItemCurve) or a text label (QCPItemText), to the bracket.
23307 */
23308 
23309 /*!
23310   Creates a bracket item and sets default values.
23311 
23312   The constructed item can be added to the plot with QCustomPlot::addItem.
23313 */
QCPItemBracket(QCustomPlot * parentPlot)23314 QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
23315   QCPAbstractItem(parentPlot),
23316   left(createPosition(QLatin1String("left"))),
23317   right(createPosition(QLatin1String("right"))),
23318   center(createAnchor(QLatin1String("center"), aiCenter))
23319 {
23320   left->setCoords(0, 0);
23321   right->setCoords(1, 1);
23322 
23323   setPen(QPen(Qt::black));
23324   setSelectedPen(QPen(Qt::blue, 2));
23325   setLength(8);
23326   setStyle(bsCalligraphic);
23327 }
23328 
~QCPItemBracket()23329 QCPItemBracket::~QCPItemBracket()
23330 {
23331 }
23332 
23333 /*!
23334   Sets the pen that will be used to draw the bracket.
23335 
23336   Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
23337   stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
23338   \ref setLength, which has a similar effect.
23339 
23340   \see setSelectedPen
23341 */
setPen(const QPen & pen)23342 void QCPItemBracket::setPen(const QPen &pen)
23343 {
23344   mPen = pen;
23345 }
23346 
23347 /*!
23348   Sets the pen that will be used to draw the bracket when selected
23349 
23350   \see setPen, setSelected
23351 */
setSelectedPen(const QPen & pen)23352 void QCPItemBracket::setSelectedPen(const QPen &pen)
23353 {
23354   mSelectedPen = pen;
23355 }
23356 
23357 /*!
23358   Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
23359   span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
23360 
23361   \image html QCPItemBracket-length.png
23362   <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
23363   bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
23364 */
setLength(double length)23365 void QCPItemBracket::setLength(double length)
23366 {
23367   mLength = length;
23368 }
23369 
23370 /*!
23371   Sets the style of the bracket, i.e. the shape/visual appearance.
23372 
23373   \see setPen
23374 */
setStyle(QCPItemBracket::BracketStyle style)23375 void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
23376 {
23377   mStyle = style;
23378 }
23379 
23380 /* inherits documentation from base class */
selectTest(const QPointF & pos,bool onlySelectable,QVariant * details) const23381 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23382 {
23383   Q_UNUSED(details)
23384   if (onlySelectable && !mSelectable)
23385     return -1;
23386 
23387   QVector2D leftVec(left->pixelPoint());
23388   QVector2D rightVec(right->pixelPoint());
23389   if (leftVec.toPoint() == rightVec.toPoint())
23390     return -1;
23391 
23392   QVector2D widthVec = (rightVec-leftVec)*0.5f;
23393   QVector2D lengthVec(-widthVec.y(), widthVec.x());
23394   lengthVec = lengthVec.normalized()*mLength;
23395   QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23396 
23397   switch (mStyle)
23398   {
23399     case QCPItemBracket::bsSquare:
23400     case QCPItemBracket::bsRound:
23401     {
23402       double a = distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23403       double b = distSqrToLine((centerVec-widthVec+lengthVec).toPointF(), (centerVec-widthVec).toPointF(), pos);
23404       double c = distSqrToLine((centerVec+widthVec+lengthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23405       return qSqrt(qMin(qMin(a, b), c));
23406     }
23407     case QCPItemBracket::bsCurly:
23408     case QCPItemBracket::bsCalligraphic:
23409     {
23410       double a = distSqrToLine((centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23411       double b = distSqrToLine((centerVec-widthVec+lengthVec*0.7f).toPointF(), (centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23412       double c = distSqrToLine((centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23413       double d = distSqrToLine((centerVec+widthVec+lengthVec*0.7f).toPointF(), (centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23414       return qSqrt(qMin(qMin(a, b), qMin(c, d)));
23415     }
23416   }
23417   return -1;
23418 }
23419 
23420 /* inherits documentation from base class */
draw(QCPPainter * painter)23421 void QCPItemBracket::draw(QCPPainter *painter)
23422 {
23423   QVector2D leftVec(left->pixelPoint());
23424   QVector2D rightVec(right->pixelPoint());
23425   if (leftVec.toPoint() == rightVec.toPoint())
23426     return;
23427 
23428   QVector2D widthVec = (rightVec-leftVec)*0.5f;
23429   QVector2D lengthVec(-widthVec.y(), widthVec.x());
23430   lengthVec = lengthVec.normalized()*mLength;
23431   QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23432 
23433   QPolygon boundingPoly;
23434   boundingPoly << leftVec.toPoint() << rightVec.toPoint()
23435                << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
23436   QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
23437   if (clip.intersects(boundingPoly.boundingRect()))
23438   {
23439     painter->setPen(mainPen());
23440     switch (mStyle)
23441     {
23442       case bsSquare:
23443       {
23444         painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
23445         painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23446         painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23447         break;
23448       }
23449       case bsRound:
23450       {
23451         painter->setBrush(Qt::NoBrush);
23452         QPainterPath path;
23453         path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23454         path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
23455         path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23456         painter->drawPath(path);
23457         break;
23458       }
23459       case bsCurly:
23460       {
23461         painter->setBrush(Qt::NoBrush);
23462         QPainterPath path;
23463         path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23464         path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
23465         path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23466         painter->drawPath(path);
23467         break;
23468       }
23469       case bsCalligraphic:
23470       {
23471         painter->setPen(Qt::NoPen);
23472         painter->setBrush(QBrush(mainPen().color()));
23473         QPainterPath path;
23474         path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23475 
23476         path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
23477         path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23478 
23479         path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
23480         path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23481 
23482         painter->drawPath(path);
23483         break;
23484       }
23485     }
23486   }
23487 }
23488 
23489 /* inherits documentation from base class */
anchorPixelPoint(int anchorId) const23490 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
23491 {
23492   QVector2D leftVec(left->pixelPoint());
23493   QVector2D rightVec(right->pixelPoint());
23494   if (leftVec.toPoint() == rightVec.toPoint())
23495     return leftVec.toPointF();
23496 
23497   QVector2D widthVec = (rightVec-leftVec)*0.5f;
23498   QVector2D lengthVec(-widthVec.y(), widthVec.x());
23499   lengthVec = lengthVec.normalized()*mLength;
23500   QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23501 
23502   switch (anchorId)
23503   {
23504     case aiCenter:
23505       return centerVec.toPointF();
23506   }
23507   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23508   return QPointF();
23509 }
23510 
23511 /*! \internal
23512 
23513   Returns the pen that should be used for drawing lines. Returns mPen when the
23514   item is not selected and mSelectedPen when it is.
23515 */
mainPen() const23516 QPen QCPItemBracket::mainPen() const
23517 {
23518     return mSelected ? mSelectedPen : mPen;
23519 }
23520 
23521