GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: plugins/pyqcustomplot/qcustomplot.cpp Lines: 0 9132 0.0 %
Date: 2024-04-14 11:13:22 Branches: 0 14589 0.0 %

Line Branch Exec Source
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 3 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
//////////////////// QCPPainter
30
////////////////////////////////////////////////////////////////////////////////////////////////////
31
32
/*! \class QCPPainter
33
  \brief QPainter subclass used internally
34
35
  This QPainter subclass is used to provide some extended functionality e.g. for
36
  tweaking position consistency between antialiased and non-antialiased
37
  painting. Further it provides workarounds for QPainter quirks.
38
39
  \warning This class intentionally hides non-virtual functions of QPainter,
40
  e.g. setPen, save and restore. So while it is possible to pass a QCPPainter
41
  instance to a function that expects a QPainter pointer, some of the
42
  workarounds and tweaks will be unavailable to the function (because it will
43
  call the base class implementations of the functions actually hidden by
44
  QCPPainter).
45
*/
46
47
/*!
48
  Creates a new QCPPainter instance and sets default values
49
*/
50
QCPPainter::QCPPainter()
51
    : QPainter(), mModes(pmDefault), mIsAntialiasing(false) {
52
  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter
53
  // isn't active yet and a call to begin() will follow
54
}
55
56
/*!
57
  Creates a new QCPPainter instance on the specified paint \a device and sets
58
  default values. Just like the analogous QPainter constructor, begins painting
59
  on \a device immediately.
60
61
  Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt
62
  versions before Qt5.
63
*/
64
QCPPainter::QCPPainter(QPaintDevice *device)
65
    : QPainter(device), mModes(pmDefault), mIsAntialiasing(false) {
66
#if QT_VERSION <           \
67
    QT_VERSION_CHECK(5, 0, \
68
                     0)  // before Qt5, default pens used to be cosmetic if
69
                         // NonCosmeticDefaultPen flag isn't set. So we set it
70
                         // to get consistency across Qt versions.
71
  if (isActive()) setRenderHint(QPainter::NonCosmeticDefaultPen);
72
#endif
73
}
74
75
QCPPainter::~QCPPainter() {}
76
77
/*!
78
  Sets the pen of the painter and applies certain fixes to it, depending on the
79
  mode of this QCPPainter.
80
81
  \note this function hides the non-virtual base class implementation.
82
*/
83
void QCPPainter::setPen(const QPen &pen) {
84
  QPainter::setPen(pen);
85
  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
86
}
87
88
/*! \overload
89
90
  Sets the pen (by color) of the painter and applies certain fixes to it,
91
  depending on the mode of this QCPPainter.
92
93
  \note this function hides the non-virtual base class implementation.
94
*/
95
void QCPPainter::setPen(const QColor &color) {
96
  QPainter::setPen(color);
97
  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
98
}
99
100
/*! \overload
101
102
  Sets the pen (by style) of the painter and applies certain fixes to it,
103
  depending on the mode of this QCPPainter.
104
105
  \note this function hides the non-virtual base class implementation.
106
*/
107
void QCPPainter::setPen(Qt::PenStyle penStyle) {
108
  QPainter::setPen(penStyle);
109
  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
110
}
111
112
/*! \overload
113
114
  Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF
115
  unpredictable when antialiasing is disabled. Thus when antialiasing is
116
  disabled, it rounds the \a line to integer coordinates and then passes it to
117
  the original drawLine.
118
119
  \note this function hides the non-virtual base class implementation.
120
*/
121
void QCPPainter::drawLine(const QLineF &line) {
122
  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
123
    QPainter::drawLine(line);
124
  else
125
    QPainter::drawLine(line.toLine());
126
}
127
128
/*!
129
  Sets whether painting uses antialiasing or not. Use this method instead of
130
  using setRenderHint with QPainter::Antialiasing directly, as it allows
131
  QCPPainter to regain pixel exactness between antialiased and non-antialiased
132
  painting (Since Qt < 5.0 uses slightly different coordinate systems for
133
  AA/Non-AA painting).
134
*/
135
void QCPPainter::setAntialiasing(bool enabled) {
136
  setRenderHint(QPainter::Antialiasing, enabled);
137
  if (mIsAntialiasing != enabled) {
138
    mIsAntialiasing = enabled;
139
    if (!mModes.testFlag(pmVectorized))  // antialiasing half-pixel shift only
140
                                         // needed for rasterized outputs
141
    {
142
      if (mIsAntialiasing)
143
        translate(0.5, 0.5);
144
      else
145
        translate(-0.5, -0.5);
146
    }
147
  }
148
}
149
150
/*!
151
  Sets the mode of the painter. This controls whether the painter shall adjust
152
  its fixes/workarounds optimized for certain output devices.
153
*/
154
void QCPPainter::setModes(QCPPainter::PainterModes modes) { mModes = modes; }
155
156
/*!
157
  Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after
158
  beginning painting on \a device. This is necessary to get cosmetic pen
159
  consistency across Qt versions, because since Qt5, all pens are non-cosmetic
160
  by default, and in Qt4 this render hint must be set to get that behaviour.
161
162
  The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts
163
  painting also sets the render hint as appropriate.
164
165
  \note this function hides the non-virtual base class implementation.
166
*/
167
bool QCPPainter::begin(QPaintDevice *device) {
168
  bool result = QPainter::begin(device);
169
#if QT_VERSION <           \
170
    QT_VERSION_CHECK(5, 0, \
171
                     0)  // before Qt5, default pens used to be cosmetic if
172
                         // NonCosmeticDefaultPen flag isn't set. So we set it
173
                         // to get consistency across Qt versions.
174
  if (result) setRenderHint(QPainter::NonCosmeticDefaultPen);
175
#endif
176
  return result;
177
}
178
179
/*! \overload
180
181
  Sets the mode of the painter. This controls whether the painter shall adjust
182
  its fixes/workarounds optimized for certain output devices.
183
*/
184
void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled) {
185
  if (!enabled && mModes.testFlag(mode))
186
    mModes &= ~mode;
187
  else if (enabled && !mModes.testFlag(mode))
188
    mModes |= mode;
189
}
190
191
/*!
192
  Saves the painter (see QPainter::save). Since QCPPainter adds some new
193
  internal state to QPainter, the save/restore functions are reimplemented to
194
  also save/restore those members.
195
196
  \note this function hides the non-virtual base class implementation.
197
198
  \see restore
199
*/
200
void QCPPainter::save() {
201
  mAntialiasingStack.push(mIsAntialiasing);
202
  QPainter::save();
203
}
204
205
/*!
206
  Restores the painter (see QPainter::restore). Since QCPPainter adds some new
207
  internal state to QPainter, the save/restore functions are reimplemented to
208
  also save/restore those members.
209
210
  \note this function hides the non-virtual base class implementation.
211
212
  \see save
213
*/
214
void QCPPainter::restore() {
215
  if (!mAntialiasingStack.isEmpty())
216
    mIsAntialiasing = mAntialiasingStack.pop();
217
  else
218
    qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
219
  QPainter::restore();
220
}
221
222
/*!
223
  Changes the pen width to 1 if it currently is 0. This function is called in
224
  the \ref setPen overrides when the \ref pmNonCosmetic mode is set.
225
*/
226
void QCPPainter::makeNonCosmetic() {
227
  if (qFuzzyIsNull(pen().widthF())) {
228
    QPen p = pen();
229
    p.setWidth(1);
230
    QPainter::setPen(p);
231
  }
232
}
233
234
////////////////////////////////////////////////////////////////////////////////////////////////////
235
//////////////////// QCPScatterStyle
236
////////////////////////////////////////////////////////////////////////////////////////////////////
237
238
/*! \class QCPScatterStyle
239
  \brief Represents the visual appearance of scatter points
240
241
  This class holds information about shape, color and size of scatter points. In
242
  plottables like QCPGraph it is used to store how scatter points shall be
243
  drawn. For example, \ref QCPGraph::setScatterStyle takes a QCPScatterStyle
244
  instance.
245
246
  A scatter style consists of a shape (\ref setShape), a line color (\ref
247
  setPen) and possibly a fill (\ref setBrush), if the shape provides a fillable
248
  area. Further, the size of the shape can be controlled with \ref setSize.
249
250
  \section QCPScatterStyle-defining Specifying a scatter style
251
252
  You can set all these configurations either by calling the respective
253
  functions on an instance: \snippet
254
  documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1
255
256
  Or you can use one of the various constructors that take different parameter
257
  combinations, making it easy to specify a scatter style in a single call, like
258
  so: \snippet documentation/doc-code-snippets/mainwindow.cpp
259
  qcpscatterstyle-creation-2
260
261
  \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the
262
  plottable
263
264
  There are two constructors which leave the pen undefined: \ref
265
  QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size).
266
  If those constructors are used, a call to \ref isPenDefined will return false.
267
  It leads to scatter points that inherit the pen from the plottable that uses
268
  the scatter style. Thus, if such a scatter style is passed to QCPGraph, the
269
  line color of the graph (\ref QCPGraph::setPen) will be used by the scatter
270
  points. This makes it very convenient to set up typical scatter settings:
271
272
  \snippet documentation/doc-code-snippets/mainwindow.cpp
273
  qcpscatterstyle-shortcreation
274
275
  Notice that it wasn't even necessary to explicitly call a QCPScatterStyle
276
  constructor. This works because QCPScatterStyle provides a constructor that
277
  can transform a \ref ScatterShape directly into a QCPScatterStyle instance
278
  (that's the \ref QCPScatterStyle(ScatterShape shape, double size) constructor
279
  with a default for \a size). In those cases, C++ allows directly supplying a
280
  \ref ScatterShape, where actually a QCPScatterStyle is expected.
281
282
  \section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps
283
284
  QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as
285
  scatter points.
286
287
  For custom shapes, you can provide a QPainterPath with the desired shape to
288
  the \ref setCustomPath function or call the constructor that takes a painter
289
  path. The scatter shape will automatically be set to \ref ssCustom.
290
291
  For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively
292
  you can use the constructor that takes a QPixmap. The scatter shape will
293
  automatically be set to \ref ssPixmap. Note that \ref setSize does not
294
  influence the appearance of the pixmap.
295
*/
296
297
/* start documentation of inline functions */
298
299
/*! \fn bool QCPScatterStyle::isNone() const
300
301
  Returns whether the scatter shape is \ref ssNone.
302
303
  \see setShape
304
*/
305
306
/*! \fn bool QCPScatterStyle::isPenDefined() const
307
308
  Returns whether a pen has been defined for this scatter style.
309
310
  The pen is undefined if a constructor is called that does not carry \a pen as
311
  parameter. Those are \ref QCPScatterStyle() and \ref
312
  QCPScatterStyle(ScatterShape shape, double size). If the pen is left
313
  undefined, the scatter color will be inherited from the plottable that uses
314
  this scatter style.
315
316
  \see setPen
317
*/
318
319
/* end documentation of inline functions */
320
321
/*!
322
  Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or
323
  brush is defined.
324
325
  Since the pen is undefined (\ref isPenDefined returns false), the scatter
326
  color will be inherited from the plottable that uses this scatter style.
327
*/
328
QCPScatterStyle::QCPScatterStyle()
329
    : mSize(6),
330
      mShape(ssNone),
331
      mPen(Qt::NoPen),
332
      mBrush(Qt::NoBrush),
333
      mPenDefined(false) {}
334
335
/*!
336
  Creates a new QCPScatterStyle instance with shape set to \a shape and size to
337
  \a size. No pen or brush is defined.
338
339
  Since the pen is undefined (\ref isPenDefined returns false), the scatter
340
  color will be inherited from the plottable that uses this scatter style.
341
*/
342
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size)
343
    : mSize(size),
344
      mShape(shape),
345
      mPen(Qt::NoPen),
346
      mBrush(Qt::NoBrush),
347
      mPenDefined(false) {}
348
349
/*!
350
  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen
351
  color set to \a color, and size to \a size. No brush is defined, i.e. the
352
  scatter point will not be filled.
353
*/
354
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color,
355
                                 double size)
356
    : mSize(size),
357
      mShape(shape),
358
      mPen(QPen(color)),
359
      mBrush(Qt::NoBrush),
360
      mPenDefined(true) {}
361
362
/*!
363
  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen
364
  color set to \a color, the brush color to \a fill (with a solid pattern), and
365
  size to \a size.
366
*/
367
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color,
368
                                 const QColor &fill, double size)
369
    : mSize(size),
370
      mShape(shape),
371
      mPen(QPen(color)),
372
      mBrush(QBrush(fill)),
373
      mPenDefined(true) {}
374
375
/*!
376
  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set
377
  to \a pen, the brush to \a brush, and size to \a size.
378
379
  \warning In some cases it might be tempting to directly use a pen style like
380
  <tt>Qt::NoPen</tt> as \a pen and a color like <tt>Qt::blue</tt> as \a brush.
381
  Notice however, that the corresponding call\n
382
  <tt>QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)</tt>\n
383
  doesn't necessarily lead C++ to use this constructor in some cases, but might
384
  mistake <tt>Qt::NoPen</tt> for a QColor and use the \ref
385
  QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill,
386
  double size) constructor instead (which will lead to an unexpected look of the
387
  scatter points). To prevent this, be more explicit with the parameter types.
388
  For example, use <tt>QBrush(Qt::blue)</tt> instead of just <tt>Qt::blue</tt>,
389
  to clearly point out to the compiler that this constructor is wanted.
390
*/
391
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen,
392
                                 const QBrush &brush, double size)
393
    : mSize(size),
394
      mShape(shape),
395
      mPen(pen),
396
      mBrush(brush),
397
      mPenDefined(pen.style() != Qt::NoPen) {}
398
399
/*!
400
  Creates a new QCPScatterStyle instance which will show the specified \a
401
  pixmap. The scatter shape is set to \ref ssPixmap.
402
*/
403
QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap)
404
    : mSize(5),
405
      mShape(ssPixmap),
406
      mPen(Qt::NoPen),
407
      mBrush(Qt::NoBrush),
408
      mPixmap(pixmap),
409
      mPenDefined(false) {}
410
411
/*!
412
  Creates a new QCPScatterStyle instance with a custom shape that is defined via
413
  \a customPath. The scatter shape is set to \ref ssCustom.
414
415
  The custom shape line will be drawn with \a pen and filled with \a brush. The
416
  size has a slightly different meaning than for built-in scatter points: The
417
  custom path will be drawn scaled by a factor of \a size/6.0. Since the default
418
  \a size is 6, the custom path will appear at a its natural size by default. To
419
  double the size of the path for example, set \a size to 12.
420
*/
421
QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath,
422
                                 const QPen &pen, const QBrush &brush,
423
                                 double size)
424
    : mSize(size),
425
      mShape(ssCustom),
426
      mPen(pen),
427
      mBrush(brush),
428
      mCustomPath(customPath),
429
      mPenDefined(pen.style() != Qt::NoPen) {}
430
431
/*!
432
  Sets the size (pixel diameter) of the drawn scatter points to \a size.
433
434
  \see setShape
435
*/
436
void QCPScatterStyle::setSize(double size) { mSize = size; }
437
438
/*!
439
  Sets the shape to \a shape.
440
441
  Note that the calls \ref setPixmap and \ref setCustomPath automatically set
442
  the shape to \ref ssPixmap and \ref ssCustom, respectively.
443
444
  \see setSize
445
*/
446
void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape) {
447
  mShape = shape;
448
}
449
450
/*!
451
  Sets the pen that will be used to draw scatter points to \a pen.
452
453
  If the pen was previously undefined (see \ref isPenDefined), the pen is
454
  considered defined after a call to this function, even if \a pen is
455
  <tt>Qt::NoPen</tt>.
456
457
  \see setBrush
458
*/
459
void QCPScatterStyle::setPen(const QPen &pen) {
460
  mPenDefined = true;
461
  mPen = pen;
462
}
463
464
/*!
465
  Sets the brush that will be used to fill scatter points to \a brush. Note that
466
  not all scatter shapes have fillable areas. For example, \ref ssPlus does not
467
  while \ref ssCircle does.
468
469
  \see setPen
470
*/
471
void QCPScatterStyle::setBrush(const QBrush &brush) { mBrush = brush; }
472
473
/*!
474
  Sets the pixmap that will be drawn as scatter point to \a pixmap.
475
476
  Note that \ref setSize does not influence the appearance of the pixmap.
477
478
  The scatter shape is automatically set to \ref ssPixmap.
479
*/
480
void QCPScatterStyle::setPixmap(const QPixmap &pixmap) {
481
  setShape(ssPixmap);
482
  mPixmap = pixmap;
483
}
484
485
/*!
486
  Sets the custom shape that will be drawn as scatter point to \a customPath.
487
488
  The scatter shape is automatically set to \ref ssCustom.
489
*/
490
void QCPScatterStyle::setCustomPath(const QPainterPath &customPath) {
491
  setShape(ssCustom);
492
  mCustomPath = customPath;
493
}
494
495
/*!
496
  Applies the pen and the brush of this scatter style to \a painter. If this
497
  scatter style has an undefined pen (\ref isPenDefined), sets the pen of \a
498
  painter to \a defaultPen instead.
499
500
  This function is used by plottables (or any class that wants to draw scatters)
501
  just before a number of scatters with this style shall be drawn with the \a
502
  painter.
503
504
  \see drawShape
505
*/
506
void QCPScatterStyle::applyTo(QCPPainter *painter,
507
                              const QPen &defaultPen) const {
508
  painter->setPen(mPenDefined ? mPen : defaultPen);
509
  painter->setBrush(mBrush);
510
}
511
512
/*!
513
  Draws the scatter shape with \a painter at position \a pos.
514
515
  This function does not modify the pen or the brush on the painter, as \ref
516
  applyTo is meant to be called before scatter points are drawn with \ref
517
  drawShape.
518
519
  \see applyTo
520
*/
521
void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const {
522
  drawShape(painter, pos.x(), pos.y());
523
}
524
525
/*! \overload
526
  Draws the scatter shape with \a painter at position \a x and \a y.
527
*/
528
void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const {
529
  double w = mSize / 2.0;
530
  switch (mShape) {
531
    case ssNone:
532
      break;
533
    case ssDot: {
534
      painter->drawLine(QPointF(x, y), QPointF(x + 0.0001, y));
535
      break;
536
    }
537
    case ssCross: {
538
      painter->drawLine(QLineF(x - w, y - w, x + w, y + w));
539
      painter->drawLine(QLineF(x - w, y + w, x + w, y - w));
540
      break;
541
    }
542
    case ssPlus: {
543
      painter->drawLine(QLineF(x - w, y, x + w, y));
544
      painter->drawLine(QLineF(x, y + w, x, y - w));
545
      break;
546
    }
547
    case ssCircle: {
548
      painter->drawEllipse(QPointF(x, y), w, w);
549
      break;
550
    }
551
    case ssDisc: {
552
      QBrush b = painter->brush();
553
      painter->setBrush(painter->pen().color());
554
      painter->drawEllipse(QPointF(x, y), w, w);
555
      painter->setBrush(b);
556
      break;
557
    }
558
    case ssSquare: {
559
      painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
560
      break;
561
    }
562
    case ssDiamond: {
563
      painter->drawLine(QLineF(x - w, y, x, y - w));
564
      painter->drawLine(QLineF(x, y - w, x + w, y));
565
      painter->drawLine(QLineF(x + w, y, x, y + w));
566
      painter->drawLine(QLineF(x, y + w, x - w, y));
567
      break;
568
    }
569
    case ssStar: {
570
      painter->drawLine(QLineF(x - w, y, x + w, y));
571
      painter->drawLine(QLineF(x, y + w, x, y - w));
572
      painter->drawLine(
573
          QLineF(x - w * 0.707, y - w * 0.707, x + w * 0.707, y + w * 0.707));
574
      painter->drawLine(
575
          QLineF(x - w * 0.707, y + w * 0.707, x + w * 0.707, y - w * 0.707));
576
      break;
577
    }
578
    case ssTriangle: {
579
      painter->drawLine(QLineF(x - w, y + 0.755 * w, x + w, y + 0.755 * w));
580
      painter->drawLine(QLineF(x + w, y + 0.755 * w, x, y - 0.977 * w));
581
      painter->drawLine(QLineF(x, y - 0.977 * w, x - w, y + 0.755 * w));
582
      break;
583
    }
584
    case ssTriangleInverted: {
585
      painter->drawLine(QLineF(x - w, y - 0.755 * w, x + w, y - 0.755 * w));
586
      painter->drawLine(QLineF(x + w, y - 0.755 * w, x, y + 0.977 * w));
587
      painter->drawLine(QLineF(x, y + 0.977 * w, x - w, y - 0.755 * w));
588
      break;
589
    }
590
    case ssCrossSquare: {
591
      painter->drawLine(QLineF(x - w, y - w, x + w * 0.95, y + w * 0.95));
592
      painter->drawLine(QLineF(x - w, y + w * 0.95, x + w * 0.95, y - w));
593
      painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
594
      break;
595
    }
596
    case ssPlusSquare: {
597
      painter->drawLine(QLineF(x - w, y, x + w * 0.95, y));
598
      painter->drawLine(QLineF(x, y + w, x, y - w));
599
      painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
600
      break;
601
    }
602
    case ssCrossCircle: {
603
      painter->drawLine(
604
          QLineF(x - w * 0.707, y - w * 0.707, x + w * 0.670, y + w * 0.670));
605
      painter->drawLine(
606
          QLineF(x - w * 0.707, y + w * 0.670, x + w * 0.670, y - w * 0.707));
607
      painter->drawEllipse(QPointF(x, y), w, w);
608
      break;
609
    }
610
    case ssPlusCircle: {
611
      painter->drawLine(QLineF(x - w, y, x + w, y));
612
      painter->drawLine(QLineF(x, y + w, x, y - w));
613
      painter->drawEllipse(QPointF(x, y), w, w);
614
      break;
615
    }
616
    case ssPeace: {
617
      painter->drawLine(QLineF(x, y - w, x, y + w));
618
      painter->drawLine(QLineF(x, y, x - w * 0.707, y + w * 0.707));
619
      painter->drawLine(QLineF(x, y, x + w * 0.707, y + w * 0.707));
620
      painter->drawEllipse(QPointF(x, y), w, w);
621
      break;
622
    }
623
    case ssPixmap: {
624
      painter->drawPixmap(x - mPixmap.width() * 0.5, y - mPixmap.height() * 0.5,
625
                          mPixmap);
626
      break;
627
    }
628
    case ssCustom: {
629
      QTransform oldTransform = painter->transform();
630
      painter->translate(x, y);
631
      painter->scale(mSize / 6.0, mSize / 6.0);
632
      painter->drawPath(mCustomPath);
633
      painter->setTransform(oldTransform);
634
      break;
635
    }
636
  }
637
}
638
639
////////////////////////////////////////////////////////////////////////////////////////////////////
640
//////////////////// QCPLayer
641
////////////////////////////////////////////////////////////////////////////////////////////////////
642
643
/*! \class QCPLayer
644
  \brief A layer that may contain objects, to control the rendering order
645
646
  The Layering system of QCustomPlot is the mechanism to control the rendering
647
  order of the elements inside the plot.
648
649
  It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an
650
  ordered list of one or more instances of QCPLayer (see QCustomPlot::addLayer,
651
  QCustomPlot::layer, QCustomPlot::moveLayer, etc.). When replotting,
652
  QCustomPlot goes through the list of layers bottom to top and successively
653
  draws the layerables of the layers.
654
655
  A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is
656
  an abstract base class from which almost all visible objects derive, like
657
  axes, grids, graphs, items, etc.
658
659
  Initially, QCustomPlot has five layers: "background", "grid", "main", "axes"
660
  and "legend" (in that order). The top two layers "axes" and "legend" contain
661
  the default axes and legend, so they will be drawn on top. In the middle,
662
  there is the "main" layer. It is initially empty and set as the current layer
663
  (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc.
664
  are created on this layer by default. Then comes the "grid" layer which
665
  contains the QCPGrid instances (which belong tightly to QCPAxis, see \ref
666
  QCPAxis::grid). The Axis rect background shall be drawn behind everything
667
  else, thus the default QCPAxisRect instance is placed on the "background"
668
  layer. Of course, the layer affiliation of the individual objects can be
669
  changed as required (\ref QCPLayerable::setLayer).
670
671
  Controlling the ordering of objects is easy: Create a new layer in the
672
  position you want it to be, e.g. above "main", with QCustomPlot::addLayer.
673
  Then set the current layer with QCustomPlot::setCurrentLayer to that new layer
674
  and finally create the objects normally. They will be placed on the new layer
675
  automatically, due to the current layer setting. Alternatively you could have
676
  also ignored the current layer setting and just moved the objects with
677
  QCPLayerable::setLayer to the desired layer after creating them.
678
679
  It is also possible to move whole layers. For example, If you want the grid to
680
  be shown in front of all plottables/items on the "main" layer, just move it
681
  above "main" with QCustomPlot::moveLayer.
682
683
  The rendering order within one layer is simply by order of creation or
684
  insertion. The item created last (or added last to the layer), is drawn on top
685
  of all other objects on that layer.
686
687
  When a layer is deleted, the objects on it are not deleted with it, but fall
688
  on the layer below the deleted layer, see QCustomPlot::removeLayer.
689
*/
690
691
/* start documentation of inline functions */
692
693
/*! \fn QList<QCPLayerable*> QCPLayer::children() const
694
695
  Returns a list of all layerables on this layer. The order corresponds to the
696
  rendering order: layerables with higher indices are drawn above layerables
697
  with lower indices.
698
*/
699
700
/*! \fn int QCPLayer::index() const
701
702
  Returns the index this layer has in the QCustomPlot. The index is the integer
703
  number by which this layer can be accessed via \ref QCustomPlot::layer.
704
705
  Layers with higher indices will be drawn above layers with lower indices.
706
*/
707
708
/* end documentation of inline functions */
709
710
/*!
711
  Creates a new QCPLayer instance.
712
713
  Normally you shouldn't directly instantiate layers, use \ref
714
  QCustomPlot::addLayer instead.
715
716
  \warning It is not checked that \a layerName is actually a unique layer name
717
  in \a parentPlot. This check is only performed by \ref QCustomPlot::addLayer.
718
*/
719
QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
720
    : QObject(parentPlot),
721
      mParentPlot(parentPlot),
722
      mName(layerName),
723
      mIndex(-1),  // will be set to a proper value by the QCustomPlot layer
724
                   // creation function
725
      mVisible(true) {
726
  // Note: no need to make sure layerName is unique, because layer
727
  // management is done with QCustomPlot functions.
728
}
729
730
QCPLayer::~QCPLayer() {
731
  // If child layerables are still on this layer, detach them, so they don't try
732
  // to reach back to this then invalid layer once they get deleted/moved
733
  // themselves. This only happens when layers are deleted directly, like in the
734
  // QCustomPlot destructor. (The regular layer removal procedure for the user
735
  // is to call QCustomPlot::removeLayer, which moves all layerables off this
736
  // layer before deleting it.)
737
738
  while (!mChildren.isEmpty())
739
    mChildren.last()->setLayer(
740
        0);  // removes itself from mChildren via removeChild()
741
742
  if (mParentPlot->currentLayer() == this)
743
    qDebug() << Q_FUNC_INFO
744
             << "The parent plot's mCurrentLayer will be a dangling pointer. "
745
                "Should have been set to a valid layer or 0 beforehand.";
746
}
747
748
/*!
749
  Sets whether this layer is visible or not. If \a visible is set to false, all
750
  layerables on this layer will be invisible.
751
752
  This function doesn't change the visibility property of the layerables (\ref
753
  QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each
754
  layerable takes the visibility of the parent layer into account.
755
*/
756
void QCPLayer::setVisible(bool visible) { mVisible = visible; }
757
758
/*! \internal
759
760
  Adds the \a layerable to the list of this layer. If \a prepend is set to true,
761
  the layerable will be prepended to the list, i.e. be drawn beneath the other
762
  layerables already in the list.
763
764
  This function does not change the \a mLayer member of \a layerable to this
765
  layer. (Use QCPLayerable::setLayer to change the layer of an object, not this
766
  function.)
767
768
  \see removeChild
769
*/
770
void QCPLayer::addChild(QCPLayerable *layerable, bool prepend) {
771
  if (!mChildren.contains(layerable)) {
772
    if (prepend)
773
      mChildren.prepend(layerable);
774
    else
775
      mChildren.append(layerable);
776
  } else
777
    qDebug() << Q_FUNC_INFO << "layerable is already child of this layer"
778
             << reinterpret_cast<quintptr>(layerable);
779
}
780
781
/*! \internal
782
783
  Removes the \a layerable from the list of this layer.
784
785
  This function does not change the \a mLayer member of \a layerable. (Use
786
  QCPLayerable::setLayer to change the layer of an object, not this function.)
787
788
  \see addChild
789
*/
790
void QCPLayer::removeChild(QCPLayerable *layerable) {
791
  if (!mChildren.removeOne(layerable))
792
    qDebug() << Q_FUNC_INFO << "layerable is not child of this layer"
793
             << reinterpret_cast<quintptr>(layerable);
794
}
795
796
////////////////////////////////////////////////////////////////////////////////////////////////////
797
//////////////////// QCPLayerable
798
////////////////////////////////////////////////////////////////////////////////////////////////////
799
800
/*! \class QCPLayerable
801
  \brief Base class for all drawable objects
802
803
  This is the abstract base class most visible objects derive from, e.g.
804
  plottables, axes, grid etc.
805
806
  Every layerable is on a layer (QCPLayer) which allows controlling the
807
  rendering order by stacking the layers accordingly.
808
809
  For details about the layering mechanism, see the QCPLayer documentation.
810
*/
811
812
/* start documentation of inline functions */
813
814
/*! \fn QCPLayerable *QCPLayerable::parentLayerable() const
815
816
  Returns the parent layerable of this layerable. The parent layerable is used
817
  to provide visibility hierarchies in conjunction with the method \ref
818
  realVisibility. This way, layerables only get drawn if their parent layerables
819
  are visible, too.
820
821
  Note that a parent layerable is not necessarily also the QObject parent for
822
  memory management. Further, a layerable doesn't always have a parent
823
  layerable, so this function may return 0.
824
825
  A parent layerable is set implicitly with when placed inside layout elements
826
  and doesn't need to be set manually by the user.
827
*/
828
829
/* end documentation of inline functions */
830
/* start documentation of pure virtual functions */
831
832
/*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter
833
  *painter) const = 0 \internal
834
835
  This function applies the default antialiasing setting to the specified \a
836
  painter, using the function \ref applyAntialiasingHint. It is the antialiasing
837
  state the painter is put in, when \ref draw is called on the layerable. If the
838
  layerable has multiple entities whose antialiasing setting may be specified
839
  individually, this function should set the antialiasing state of the most
840
  prominent entity. In this case however, the \ref draw function usually calls
841
  the specialized versions of this function before drawing each entity,
842
  effectively overriding the setting of the default antialiasing hint.
843
844
  <b>First example:</b> QCPGraph has multiple entities that have an antialiasing
845
  setting: The graph line, fills, scatters and error bars. Those can be
846
  configured via QCPGraph::setAntialiased, QCPGraph::setAntialiasedFill,
847
  QCPGraph::setAntialiasedScatters etc. Consequently, there isn't only the
848
  QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the
849
  graph line's antialiasing), but specialized ones like
850
  QCPGraph::applyFillAntialiasingHint and
851
  QCPGraph::applyScattersAntialiasingHint. So before drawing one of those
852
  entities, QCPGraph::draw calls the respective specialized
853
  applyAntialiasingHint function.
854
855
  <b>Second example:</b> QCPItemLine consists only of a line so there is only
856
  one antialiasing setting which can be controlled with
857
  QCPItemLine::setAntialiased. (This function is inherited by all layerables.
858
  The specialized functions, as seen on QCPGraph, must be added explicitly to
859
  the respective layerable subclass.) Consequently it only has the normal
860
  QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function
861
  doesn't need to care about setting any antialiasing states, because the
862
  default antialiasing hint is already set on the painter when the \ref draw
863
  function is called, and that's the state it wants to draw the line with.
864
*/
865
866
/*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
867
  \internal
868
869
  This function draws the layerable with the specified \a painter. It is only
870
  called by QCustomPlot, if the layerable is visible (\ref setVisible).
871
872
  Before this function is called, the painter's antialiasing state is set via
873
  \ref applyDefaultAntialiasingHint, see the documentation there. Further, the
874
  clipping rectangle was set to \ref clipRect.
875
*/
876
877
/* end documentation of pure virtual functions */
878
/* start documentation of signals */
879
880
/*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
881
882
  This signal is emitted when the layer of this layerable changes, i.e. this
883
  layerable is moved to a different layer.
884
885
  \see setLayer
886
*/
887
888
/* end documentation of signals */
889
890
/*!
891
  Creates a new QCPLayerable instance.
892
893
  Since QCPLayerable is an abstract base class, it can't be instantiated
894
  directly. Use one of the derived classes.
895
896
  If \a plot is provided, it automatically places itself on the layer named \a
897
  targetLayer. If \a targetLayer is an empty string, it places itself on the
898
  current layer of the plot (see \ref QCustomPlot::setCurrentLayer).
899
900
  It is possible to provide 0 as \a plot. In that case, you should assign a
901
  parent plot at a later time with \ref initializeParentPlot.
902
903
  The layerable's parent layerable is set to \a parentLayerable, if provided.
904
  Direct layerable parents are mainly used to control visibility in a hierarchy
905
  of layerables. This means a layerable is only drawn, if all its ancestor
906
  layerables are also visible. Note that \a parentLayerable does not become the
907
  QObject-parent (for memory management) of this layerable, \a plot does. It is
908
  not uncommon to set the QObject-parent to something else in the constructors
909
  of QCPLayerable subclasses, to guarantee a working destruction hierarchy.
910
*/
911
QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer,
912
                           QCPLayerable *parentLayerable)
913
    : QObject(plot),
914
      mVisible(true),
915
      mParentPlot(plot),
916
      mParentLayerable(parentLayerable),
917
      mLayer(0),
918
      mAntialiased(true) {
919
  if (mParentPlot) {
920
    if (targetLayer.isEmpty())
921
      setLayer(mParentPlot->currentLayer());
922
    else if (!setLayer(targetLayer))
923
      qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to"
924
               << targetLayer << "failed.";
925
  }
926
}
927
928
QCPLayerable::~QCPLayerable() {
929
  if (mLayer) {
930
    mLayer->removeChild(this);
931
    mLayer = 0;
932
  }
933
}
934
935
/*!
936
  Sets the visibility of this layerable object. If an object is not visible, it
937
  will not be drawn on the QCustomPlot surface, and user interaction with it
938
  (e.g. click and selection) is not possible.
939
*/
940
void QCPLayerable::setVisible(bool on) { mVisible = on; }
941
942
/*!
943
  Sets the \a layer of this layerable object. The object will be placed on top
944
  of the other objects already on \a layer.
945
946
  If \a layer is 0, this layerable will not be on any layer and thus not appear
947
  in the plot (or interact/receive events).
948
949
  Returns true if the layer of this layerable was successfully changed to \a
950
  layer.
951
*/
952
bool QCPLayerable::setLayer(QCPLayer *layer) {
953
  return moveToLayer(layer, false);
954
}
955
956
/*! \overload
957
  Sets the layer of this layerable object by name
958
959
  Returns true on success, i.e. if \a layerName is a valid layer name.
960
*/
961
bool QCPLayerable::setLayer(const QString &layerName) {
962
  if (!mParentPlot) {
963
    qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
964
    return false;
965
  }
966
  if (QCPLayer *layer = mParentPlot->layer(layerName)) {
967
    return setLayer(layer);
968
  } else {
969
    qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
970
    return false;
971
  }
972
}
973
974
/*!
975
  Sets whether this object will be drawn antialiased or not.
976
977
  Note that antialiasing settings may be overridden by
978
  QCustomPlot::setAntialiasedElements and
979
  QCustomPlot::setNotAntialiasedElements.
980
*/
981
void QCPLayerable::setAntialiased(bool enabled) { mAntialiased = enabled; }
982
983
/*!
984
  Returns whether this layerable is visible, taking the visibility of the
985
  layerable parent and the visibility of the layer this layerable is on into
986
  account. This is the method that is consulted to decide whether a layerable
987
  shall be drawn or not.
988
989
  If this layerable has a direct layerable parent (usually set via hierarchies
990
  implemented in subclasses, like in the case of QCPLayoutElement), this
991
  function returns true only if this layerable has its visibility set to true
992
  and the parent layerable's \ref realVisibility returns true.
993
994
  If this layerable doesn't have a direct layerable parent, returns the state of
995
  this layerable's visibility.
996
*/
997
bool QCPLayerable::realVisibility() const {
998
  return mVisible && (!mLayer || mLayer->visible()) &&
999
         (!mParentLayerable || mParentLayerable.data()->realVisibility());
1000
}
1001
1002
/*!
1003
  This function is used to decide whether a click hits a layerable object or
1004
  not.
1005
1006
  \a pos is a point in pixel coordinates on the QCustomPlot surface. This
1007
  function returns the shortest pixel distance of this point to the object. If
1008
  the object is either invisible or the distance couldn't be determined, -1.0 is
1009
  returned. Further, if \a onlySelectable is true and the object is not
1010
  selectable, -1.0 is returned, too.
1011
1012
  If the object is represented not by single lines but by an area like a \ref
1013
  QCPItemText or the bars of a \ref QCPBars plottable, a click inside the area
1014
  should also be considered a hit. In these cases this function thus returns a
1015
  constant value greater zero but still below the parent plot's selection
1016
  tolerance. (typically the selectionTolerance multiplied by 0.99).
1017
1018
  Providing a constant value for area objects allows selecting line objects even
1019
  when they are obscured by such area objects, by clicking close to the lines
1020
  (i.e. closer than 0.99*selectionTolerance).
1021
1022
  The actual setting of the selection state is not done by this function. This
1023
  is handled by the parent QCustomPlot when the mouseReleaseEvent occurs, and
1024
  the finally selected object is notified via the selectEvent/deselectEvent
1025
  methods.
1026
1027
  \a details is an optional output parameter. Every layerable subclass may place
1028
  any information in \a details. This information will be passed to \ref
1029
  selectEvent when the parent QCustomPlot decides on the basis of this
1030
  selectTest call, that the object was successfully selected. The subsequent
1031
  call to \ref selectEvent will carry the \a details. This is useful for
1032
  multi-part objects (like QCPAxis). This way, a possibly complex calculation to
1033
  decide which part was clicked is only done once in \ref selectTest. The result
1034
  (i.e. the actually clicked part) can then be placed in \a details. So in the
1035
  subsequent \ref selectEvent, the decision which part was selected doesn't have
1036
  to be done a second time for a single selection operation.
1037
1038
  You may pass 0 as \a details to indicate that you are not interested in those
1039
  selection details.
1040
1041
  \see selectEvent, deselectEvent, QCustomPlot::setInteractions
1042
*/
1043
double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable,
1044
                                QVariant *details) const {
1045
  Q_UNUSED(pos)
1046
  Q_UNUSED(onlySelectable)
1047
  Q_UNUSED(details)
1048
  return -1.0;
1049
}
1050
1051
/*! \internal
1052
1053
  Sets the parent plot of this layerable. Use this function once to set the
1054
  parent plot if you have passed 0 in the constructor. It can not be used to
1055
  move a layerable from one QCustomPlot to another one.
1056
1057
  Note that, unlike when passing a non-null parent plot in the constructor, this
1058
  function does not make \a parentPlot the QObject-parent of this layerable. If
1059
  you want this, call QObject::setParent(\a parentPlot) in addition to this
1060
  function.
1061
1062
  Further, you will probably want to set a layer (\ref setLayer) after calling
1063
  this function, to make the layerable appear on the QCustomPlot.
1064
1065
  The parent plot change will be propagated to subclasses via a call to \ref
1066
  parentPlotInitialized so they can react accordingly (e.g. also initialize the
1067
  parent plot of child layerables, like QCPLayout does).
1068
*/
1069
void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot) {
1070
  if (mParentPlot) {
1071
    qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1072
    return;
1073
  }
1074
1075
  if (!parentPlot) qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1076
1077
  mParentPlot = parentPlot;
1078
  parentPlotInitialized(mParentPlot);
1079
}
1080
1081
/*! \internal
1082
1083
  Sets the parent layerable of this layerable to \a parentLayerable. Note that
1084
  \a parentLayerable does not become the QObject-parent (for memory management)
1085
  of this layerable.
1086
1087
  The parent layerable has influence on the return value of the \ref
1088
  realVisibility method. Only layerables with a fully visible parent tree will
1089
  return true for \ref realVisibility, and thus be drawn.
1090
1091
  \see realVisibility
1092
*/
1093
void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable) {
1094
  mParentLayerable = parentLayerable;
1095
}
1096
1097
/*! \internal
1098
1099
  Moves this layerable object to \a layer. If \a prepend is true, this object
1100
  will be prepended to the new layer's list, i.e. it will be drawn below the
1101
  objects already on the layer. If it is false, the object will be appended.
1102
1103
  Returns true on success, i.e. if \a layer is a valid layer.
1104
*/
1105
bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend) {
1106
  if (layer && !mParentPlot) {
1107
    qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1108
    return false;
1109
  }
1110
  if (layer && layer->parentPlot() != mParentPlot) {
1111
    qDebug() << Q_FUNC_INFO << "layer" << layer->name()
1112
             << "is not in same QCustomPlot as this layerable";
1113
    return false;
1114
  }
1115
1116
  QCPLayer *oldLayer = mLayer;
1117
  if (mLayer) mLayer->removeChild(this);
1118
  mLayer = layer;
1119
  if (mLayer) mLayer->addChild(this, prepend);
1120
  if (mLayer != oldLayer) emit layerChanged(mLayer);
1121
  return true;
1122
}
1123
1124
/*! \internal
1125
1126
  Sets the QCPainter::setAntialiasing state on the provided \a painter,
1127
  depending on the \a localAntialiased value as well as the overrides \ref
1128
  QCustomPlot::setAntialiasedElements and \ref
1129
  QCustomPlot::setNotAntialiasedElements. Which override enum this function
1130
  takes into account is controlled via \a overrideElement.
1131
*/
1132
void QCPLayerable::applyAntialiasingHint(
1133
    QCPPainter *painter, bool localAntialiased,
1134
    QCP::AntialiasedElement overrideElement) const {
1135
  if (mParentPlot &&
1136
      mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1137
    painter->setAntialiasing(false);
1138
  else if (mParentPlot &&
1139
           mParentPlot->antialiasedElements().testFlag(overrideElement))
1140
    painter->setAntialiasing(true);
1141
  else
1142
    painter->setAntialiasing(localAntialiased);
1143
}
1144
1145
/*! \internal
1146
1147
  This function is called by \ref initializeParentPlot, to allow subclasses to
1148
  react on the setting of a parent plot. This is the case when 0 was passed as
1149
  parent plot in the constructor, and the parent plot is set at a later time.
1150
1151
  For example, QCPLayoutElement/QCPLayout hierarchies may be created
1152
  independently of any QCustomPlot at first. When they are then added to a
1153
  layout inside the QCustomPlot, the top level element of the hierarchy gets its
1154
  parent plot initialized with \ref initializeParentPlot. To propagate the
1155
  parent plot to all the children of the hierarchy, the top level element then
1156
  uses this function to pass the parent plot on to its child elements.
1157
1158
  The default implementation does nothing.
1159
1160
  \see initializeParentPlot
1161
*/
1162
void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot) {
1163
  Q_UNUSED(parentPlot)
1164
}
1165
1166
/*! \internal
1167
1168
  Returns the selection category this layerable shall belong to. The selection
1169
  category is used in conjunction with \ref QCustomPlot::setInteractions to
1170
  control which objects are selectable and which aren't.
1171
1172
  Subclasses that don't fit any of the normal \ref QCP::Interaction values can
1173
  use \ref QCP::iSelectOther. This is what the default implementation returns.
1174
1175
  \see QCustomPlot::setInteractions
1176
*/
1177
QCP::Interaction QCPLayerable::selectionCategory() const {
1178
  return QCP::iSelectOther;
1179
}
1180
1181
/*! \internal
1182
1183
  Returns the clipping rectangle of this layerable object. By default, this is
1184
  the viewport of the parent QCustomPlot. Specific subclasses may reimplement
1185
  this function to provide different clipping rects.
1186
1187
  The returned clipping rect is set on the painter before the draw function of
1188
  the respective object is called.
1189
*/
1190
QRect QCPLayerable::clipRect() const {
1191
  if (mParentPlot)
1192
    return mParentPlot->viewport();
1193
  else
1194
    return QRect();
1195
}
1196
1197
/*! \internal
1198
1199
  This event is called when the layerable shall be selected, as a consequence of
1200
  a click by the user. Subclasses should react to it by setting their selection
1201
  state appropriately. The default implementation does nothing.
1202
1203
  \a event is the mouse event that caused the selection. \a additive indicates,
1204
  whether the user was holding the multi-select-modifier while performing the
1205
  selection (see \ref QCustomPlot::setMultiSelectModifier). if \a additive is
1206
  true, the selection state must be toggled (i.e. become selected when
1207
  unselected and unselected when selected).
1208
1209
  Every selectEvent is preceded by a call to \ref selectTest, which has returned
1210
  positively (i.e. returned a value greater than 0 and less than the selection
1211
  tolerance of the parent QCustomPlot). The \a details data you output from \ref
1212
  selectTest is fed back via \a details here. You may use it to transport any
1213
  kind of information from the selectTest to the possibly subsequent
1214
  selectEvent. Usually \a details is used to transfer which part was clicked, if
1215
  it is a layerable that has multiple individually selectable parts (like
1216
  QCPAxis). This way selectEvent doesn't need to do the calculation again to
1217
  find out which part was actually clicked.
1218
1219
  \a selectionStateChanged is an output parameter. If the pointer is non-null,
1220
  this function must set the value either to true or false, depending on whether
1221
  the selection state of this layerable was actually changed. For layerables
1222
  that only are selectable as a whole and not in parts, this is simple: if \a
1223
  additive is true, \a selectionStateChanged must also be set to true, because
1224
  the selection toggles. If \a additive is false, \a selectionStateChanged is
1225
  only set to true, if the layerable was previously unselected and now is
1226
  switched to the selected state.
1227
1228
  \see selectTest, deselectEvent
1229
*/
1230
void QCPLayerable::selectEvent(QMouseEvent *event, bool additive,
1231
                               const QVariant &details,
1232
                               bool *selectionStateChanged) {
1233
  Q_UNUSED(event)
1234
  Q_UNUSED(additive)
1235
  Q_UNUSED(details)
1236
  Q_UNUSED(selectionStateChanged)
1237
}
1238
1239
/*! \internal
1240
1241
  This event is called when the layerable shall be deselected, either as
1242
  consequence of a user interaction or a call to \ref QCustomPlot::deselectAll.
1243
  Subclasses should react to it by unsetting their selection appropriately.
1244
1245
  just as in \ref selectEvent, the output parameter \a selectionStateChanged (if
1246
  non-null), must return true or false when the selection state of this
1247
  layerable has changed or not changed, respectively.
1248
1249
  \see selectTest, selectEvent
1250
*/
1251
void QCPLayerable::deselectEvent(bool *selectionStateChanged) {
1252
  Q_UNUSED(selectionStateChanged)
1253
}
1254
1255
////////////////////////////////////////////////////////////////////////////////////////////////////
1256
//////////////////// QCPRange
1257
////////////////////////////////////////////////////////////////////////////////////////////////////
1258
/*! \class QCPRange
1259
  \brief Represents the range an axis is encompassing.
1260
1261
  contains a \a lower and \a upper double value and provides convenience input,
1262
  output and modification functions.
1263
1264
  \see QCPAxis::setRange
1265
*/
1266
1267
/*!
1268
  Minimum range size (\a upper - \a lower) the range changing functions will
1269
  accept. Smaller intervals would cause errors due to the 11-bit exponent of
1270
  double precision numbers, corresponding to a minimum magnitude of roughly
1271
  1e-308. \see validRange, maxRange
1272
*/
1273
const double QCPRange::minRange = 1e-280;
1274
1275
/*!
1276
  Maximum values (negative and positive) the range will accept in range-changing
1277
  functions. Larger absolute values would cause errors due to the 11-bit
1278
  exponent of double precision numbers, corresponding to a maximum magnitude of
1279
  roughly 1e308. Since the number of planck-volumes in the entire visible
1280
  universe is only ~1e183, this should be enough. \see validRange, minRange
1281
*/
1282
const double QCPRange::maxRange = 1e250;
1283
1284
/*!
1285
  Constructs a range with \a lower and \a upper set to zero.
1286
*/
1287
QCPRange::QCPRange() : lower(0), upper(0) {}
1288
1289
/*! \overload
1290
  Constructs a range with the specified \a lower and \a upper values.
1291
*/
1292
QCPRange::QCPRange(double lower, double upper) : lower(lower), upper(upper) {
1293
  normalize();
1294
}
1295
1296
/*!
1297
  Returns the size of the range, i.e. \a upper-\a lower
1298
*/
1299
double QCPRange::size() const { return upper - lower; }
1300
1301
/*!
1302
  Returns the center of the range, i.e. (\a upper+\a lower)*0.5
1303
*/
1304
double QCPRange::center() const { return (upper + lower) * 0.5; }
1305
1306
/*!
1307
  Makes sure \a lower is numerically smaller than \a upper. If this is not the
1308
  case, the values are swapped.
1309
*/
1310
void QCPRange::normalize() {
1311
  if (lower > upper) qSwap(lower, upper);
1312
}
1313
1314
/*!
1315
  Expands this range such that \a otherRange is contained in the new range. It
1316
  is assumed that both this range and \a otherRange are normalized (see \ref
1317
  normalize).
1318
1319
  If \a otherRange is already inside the current range, this function does
1320
  nothing.
1321
1322
  \see expanded
1323
*/
1324
void QCPRange::expand(const QCPRange &otherRange) {
1325
  if (lower > otherRange.lower) lower = otherRange.lower;
1326
  if (upper < otherRange.upper) upper = otherRange.upper;
1327
}
1328
1329
/*!
1330
  Returns an expanded range that contains this and \a otherRange. It is assumed
1331
  that both this range and \a otherRange are normalized (see \ref normalize).
1332
1333
  \see expand
1334
*/
1335
QCPRange QCPRange::expanded(const QCPRange &otherRange) const {
1336
  QCPRange result = *this;
1337
  result.expand(otherRange);
1338
  return result;
1339
}
1340
1341
/*!
1342
  Returns a sanitized version of the range. Sanitized means for logarithmic
1343
  scales, that the range won't span the positive and negative sign domain, i.e.
1344
  contain zero. Further \a lower will always be numerically smaller (or equal)
1345
  to \a upper.
1346
1347
  If the original range does span positive and negative sign domains or contains
1348
  zero, the returned range will try to approximate the original range as good as
1349
  possible. If the positive interval of the original range is wider than the
1350
  negative interval, the returned range will only contain the positive interval,
1351
  with lower bound set to \a rangeFac or \a rangeFac *\a upper, whichever is
1352
  closer to zero. Same procedure is used if the negative interval is wider than
1353
  the positive interval, this time by changing the \a upper bound.
1354
*/
1355
QCPRange QCPRange::sanitizedForLogScale() const {
1356
  double rangeFac = 1e-3;
1357
  QCPRange sanitizedRange(lower, upper);
1358
  sanitizedRange.normalize();
1359
  // can't have range spanning negative and positive values in log plot, so
1360
  // change range to fix it
1361
  // if (qFuzzyCompare(sanitizedRange.lower+1, 1) &&
1362
  // !qFuzzyCompare(sanitizedRange.upper+1, 1))
1363
  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0) {
1364
    // case lower is 0
1365
    if (rangeFac < sanitizedRange.upper * rangeFac)
1366
      sanitizedRange.lower = rangeFac;
1367
    else
1368
      sanitizedRange.lower = sanitizedRange.upper * rangeFac;
1369
  }  // else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1370
  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0) {
1371
    // case upper is 0
1372
    if (-rangeFac > sanitizedRange.lower * rangeFac)
1373
      sanitizedRange.upper = -rangeFac;
1374
    else
1375
      sanitizedRange.upper = sanitizedRange.lower * rangeFac;
1376
  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0) {
1377
    // find out whether negative or positive interval is wider to decide which
1378
    // sign domain will be chosen
1379
    if (-sanitizedRange.lower > sanitizedRange.upper) {
1380
      // negative is wider, do same as in case upper is 0
1381
      if (-rangeFac > sanitizedRange.lower * rangeFac)
1382
        sanitizedRange.upper = -rangeFac;
1383
      else
1384
        sanitizedRange.upper = sanitizedRange.lower * rangeFac;
1385
    } else {
1386
      // positive is wider, do same as in case lower is 0
1387
      if (rangeFac < sanitizedRange.upper * rangeFac)
1388
        sanitizedRange.lower = rangeFac;
1389
      else
1390
        sanitizedRange.lower = sanitizedRange.upper * rangeFac;
1391
    }
1392
  }
1393
  // due to normalization, case lower>0 && upper<0 should never occur, because
1394
  // that implies upper<lower
1395
  return sanitizedRange;
1396
}
1397
1398
/*!
1399
  Returns a sanitized version of the range. Sanitized means for linear scales,
1400
  that \a lower will always be numerically smaller (or equal) to \a upper.
1401
*/
1402
QCPRange QCPRange::sanitizedForLinScale() const {
1403
  QCPRange sanitizedRange(lower, upper);
1404
  sanitizedRange.normalize();
1405
  return sanitizedRange;
1406
}
1407
1408
/*!
1409
  Returns true when \a value lies within or exactly on the borders of the range.
1410
*/
1411
bool QCPRange::contains(double value) const {
1412
  return value >= lower && value <= upper;
1413
}
1414
1415
/*!
1416
  Checks, whether the specified range is within valid bounds, which are defined
1417
  as QCPRange::maxRange and QCPRange::minRange.
1418
  A valid range means:
1419
  \li range bounds within -maxRange and maxRange
1420
  \li range size above minRange
1421
  \li range size below maxRange
1422
*/
1423
bool QCPRange::validRange(double lower, double upper) {
1424
  return (lower > -maxRange && upper < maxRange &&
1425
          qAbs(lower - upper) > minRange && qAbs(lower - upper) < maxRange &&
1426
          !(lower > 0 && qIsInf(upper / lower)) &&
1427
          !(upper < 0 && qIsInf(lower / upper)));
1428
}
1429
1430
/*!
1431
  \overload
1432
  Checks, whether the specified range is within valid bounds, which are defined
1433
  as QCPRange::maxRange and QCPRange::minRange.
1434
  A valid range means:
1435
  \li range bounds within -maxRange and maxRange
1436
  \li range size above minRange
1437
  \li range size below maxRange
1438
*/
1439
bool QCPRange::validRange(const QCPRange &range) {
1440
  return (range.lower > -maxRange && range.upper < maxRange &&
1441
          qAbs(range.lower - range.upper) > minRange &&
1442
          qAbs(range.lower - range.upper) < maxRange &&
1443
          !(range.lower > 0 && qIsInf(range.upper / range.lower)) &&
1444
          !(range.upper < 0 && qIsInf(range.lower / range.upper)));
1445
}
1446
1447
////////////////////////////////////////////////////////////////////////////////////////////////////
1448
//////////////////// QCPMarginGroup
1449
////////////////////////////////////////////////////////////////////////////////////////////////////
1450
1451
/*! \class QCPMarginGroup
1452
  \brief A margin group allows synchronization of margin sides if working with
1453
  multiple layout elements.
1454
1455
  QCPMarginGroup allows you to tie a margin side of two or more layout elements
1456
  together, such that they will all have the same size, based on the largest
1457
  required margin in the group.
1458
1459
  \n
1460
  \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup"
1461
  \n
1462
1463
  In certain situations it is desirable that margins at specific sides are
1464
  synchronized across layout elements. For example, if one QCPAxisRect is below
1465
  another one in a grid layout, it will provide a cleaner look to the user if
1466
  the left and right margins of the two axis rects are of the same size. The
1467
  left axis of the top axis rect will then be at the same horizontal position as
1468
  the left axis of the lower axis rect, making them appear aligned. The same
1469
  applies for the right axes. This is what QCPMarginGroup makes possible.
1470
1471
  To add/remove a specific side of a layout element to/from a margin group, use
1472
  the \ref QCPLayoutElement::setMarginGroup method. To completely break apart
1473
  the margin group, either call \ref clear, or just delete the margin group.
1474
1475
  \section QCPMarginGroup-example Example
1476
1477
  First create a margin group:
1478
  \snippet documentation/doc-code-snippets/mainwindow.cpp
1479
  qcpmargingroup-creation-1 Then set this group on the layout element sides:
1480
  \snippet documentation/doc-code-snippets/mainwindow.cpp
1481
  qcpmargingroup-creation-2 Here, we've used the first two axis rects of the
1482
  plot and synchronized their left margins with each other and their right
1483
  margins with each other.
1484
*/
1485
1486
/* start documentation of inline functions */
1487
1488
/*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side)
1489
  const
1490
1491
  Returns a list of all layout elements that have their margin \a side
1492
  associated with this margin group.
1493
*/
1494
1495
/* end documentation of inline functions */
1496
1497
/*!
1498
  Creates a new QCPMarginGroup instance in \a parentPlot.
1499
*/
1500
QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot)
1501
    : QObject(parentPlot), mParentPlot(parentPlot) {
1502
  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement *>());
1503
  mChildren.insert(QCP::msRight, QList<QCPLayoutElement *>());
1504
  mChildren.insert(QCP::msTop, QList<QCPLayoutElement *>());
1505
  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement *>());
1506
}
1507
1508
QCPMarginGroup::~QCPMarginGroup() { clear(); }
1509
1510
/*!
1511
  Returns whether this margin group is empty. If this function returns true, no
1512
  layout elements use this margin group to synchronize margin sides.
1513
*/
1514
bool QCPMarginGroup::isEmpty() const {
1515
  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *> > it(mChildren);
1516
  while (it.hasNext()) {
1517
    it.next();
1518
    if (!it.value().isEmpty()) return false;
1519
  }
1520
  return true;
1521
}
1522
1523
/*!
1524
  Clears this margin group. The synchronization of the margin sides that use
1525
  this margin group is lifted and they will use their individual margin sizes
1526
  again.
1527
*/
1528
void QCPMarginGroup::clear() {
1529
  // make all children remove themselves from this margin group:
1530
  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *> > it(mChildren);
1531
  while (it.hasNext()) {
1532
    it.next();
1533
    const QList<QCPLayoutElement *> elements = it.value();
1534
    for (int i = elements.size() - 1; i >= 0; --i)
1535
      elements.at(i)->setMarginGroup(
1536
          it.key(), 0);  // removes itself from mChildren via removeChild
1537
  }
1538
}
1539
1540
/*! \internal
1541
1542
  Returns the synchronized common margin for \a side. This is the margin value
1543
  that will be used by the layout element on the respective side, if it is part
1544
  of this margin group.
1545
1546
  The common margin is calculated by requesting the automatic margin (\ref
1547
  QCPLayoutElement::calculateAutoMargin) of each element associated with \a side
1548
  in this margin group, and choosing the largest returned value.
1549
  (QCPLayoutElement::minimumMargins is taken into account, too.)
1550
*/
1551
int QCPMarginGroup::commonMargin(QCP::MarginSide side) const {
1552
  // query all automatic margins of the layout elements in this margin group
1553
  // side and find maximum:
1554
  int result = 0;
1555
  const QList<QCPLayoutElement *> elements = mChildren.value(side);
1556
  for (int i = 0; i < elements.size(); ++i) {
1557
    if (!elements.at(i)->autoMargins().testFlag(side)) continue;
1558
    int m = qMax(elements.at(i)->calculateAutoMargin(side),
1559
                 QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1560
    if (m > result) result = m;
1561
  }
1562
  return result;
1563
}
1564
1565
/*! \internal
1566
1567
  Adds \a element to the internal list of child elements, for the margin \a
1568
  side.
1569
1570
  This function does not modify the margin group property of \a element.
1571
*/
1572
void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element) {
1573
  if (!mChildren[side].contains(element))
1574
    mChildren[side].append(element);
1575
  else
1576
    qDebug() << Q_FUNC_INFO
1577
             << "element is already child of this margin group side"
1578
             << reinterpret_cast<quintptr>(element);
1579
}
1580
1581
/*! \internal
1582
1583
  Removes \a element from the internal list of child elements, for the margin \a
1584
  side.
1585
1586
  This function does not modify the margin group property of \a element.
1587
*/
1588
void QCPMarginGroup::removeChild(QCP::MarginSide side,
1589
                                 QCPLayoutElement *element) {
1590
  if (!mChildren[side].removeOne(element))
1591
    qDebug() << Q_FUNC_INFO << "element is not child of this margin group side"
1592
             << reinterpret_cast<quintptr>(element);
1593
}
1594
1595
////////////////////////////////////////////////////////////////////////////////////////////////////
1596
//////////////////// QCPLayoutElement
1597
////////////////////////////////////////////////////////////////////////////////////////////////////
1598
1599
/*! \class QCPLayoutElement
1600
  \brief The abstract base class for all objects that form \ref thelayoutsystem
1601
  "the layout system".
1602
1603
  This is an abstract base class. As such, it can't be instantiated directly,
1604
  rather use one of its subclasses.
1605
1606
  A Layout element is a rectangular object which can be placed in layouts. It
1607
  has an outer rect (QCPLayoutElement::outerRect) and an inner rect (\ref
1608
  QCPLayoutElement::rect). The difference between outer and inner rect is called
1609
  its margin. The margin can either be set to automatic or manual (\ref
1610
  setAutoMargins) on a per-side basis. If a side is set to manual, that margin
1611
  can be set explicitly with \ref setMargins and will stay fixed at that value.
1612
  If it's set to automatic, the layout element subclass will control the value
1613
  itself (via \ref calculateAutoMargin).
1614
1615
  Layout elements can be placed in layouts (base class QCPLayout) like
1616
  QCPLayoutGrid. The top level layout is reachable via \ref
1617
  QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref QCPLayout
1618
  itself derives from \ref QCPLayoutElement, layouts can be nested.
1619
1620
  Thus in QCustomPlot one can divide layout elements into two categories: The
1621
  ones that are invisible by themselves, because they don't draw anything. Their
1622
  only purpose is to manage the position and size of other layout elements. This
1623
  category of layout elements usually use QCPLayout as base class. Then there is
1624
  the category of layout elements which actually draw something. For example,
1625
  QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does not
1626
  necessarily mean that the latter category can't have child layout elements.
1627
  QCPLegend for instance, actually derives from QCPLayoutGrid and the individual
1628
  legend items are child layout elements in the grid layout.
1629
*/
1630
1631
/* start documentation of inline functions */
1632
1633
/*! \fn QCPLayout *QCPLayoutElement::layout() const
1634
1635
  Returns the parent layout of this layout element.
1636
*/
1637
1638
/*! \fn QRect QCPLayoutElement::rect() const
1639
1640
  Returns the inner rect of this layout element. The inner rect is the outer
1641
  rect (\ref setOuterRect) shrinked by the margins (\ref setMargins, \ref
1642
  setAutoMargins).
1643
1644
  In some cases, the area between outer and inner rect is left blank. In other
1645
  cases the margin area is used to display peripheral graphics while the main
1646
  content is in the inner rect. This is where automatic margin calculation
1647
  becomes interesting because it allows the layout element to adapt the margins
1648
  to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect
1649
  draws the axis labels and tick labels in the margin area, thus needs to adjust
1650
  the margins (if \ref setAutoMargins is enabled) according to the space
1651
  required by the labels of the axes.
1652
*/
1653
1654
/*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event)
1655
1656
  This event is called, if the mouse was pressed while being inside the outer
1657
  rect of this layout element.
1658
*/
1659
1660
/*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event)
1661
1662
  This event is called, if the mouse is moved inside the outer rect of this
1663
  layout element.
1664
*/
1665
1666
/*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event)
1667
1668
  This event is called, if the mouse was previously pressed inside the outer
1669
  rect of this layout element and is now released.
1670
*/
1671
1672
/*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event)
1673
1674
  This event is called, if the mouse is double-clicked inside the outer rect of
1675
  this layout element.
1676
*/
1677
1678
/*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event)
1679
1680
  This event is called, if the mouse wheel is scrolled while the cursor is
1681
  inside the rect of this layout element.
1682
*/
1683
1684
/* end documentation of inline functions */
1685
1686
/*!
1687
  Creates an instance of QCPLayoutElement and sets default values.
1688
*/
1689
QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot)
1690
    : QCPLayerable(
1691
          parentPlot),  // parenthood is changed as soon as layout element gets
1692
                        // inserted into a layout (except for top level layout)
1693
      mParentLayout(0),
1694
      mMinimumSize(),
1695
      mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1696
      mRect(0, 0, 0, 0),
1697
      mOuterRect(0, 0, 0, 0),
1698
      mMargins(0, 0, 0, 0),
1699
      mMinimumMargins(0, 0, 0, 0),
1700
      mAutoMargins(QCP::msAll) {}
1701
1702
QCPLayoutElement::~QCPLayoutElement() {
1703
  setMarginGroup(QCP::msAll,
1704
                 0);  // unregister at margin groups, if there are any
1705
  // unregister at layout:
1706
  if (qobject_cast<QCPLayout *>(
1707
          mParentLayout))  // the qobject_cast is just a safeguard in case the
1708
                           // layout forgets to call clear() in its dtor and
1709
                           // this dtor is called by QObject dtor
1710
    mParentLayout->take(this);
1711
}
1712
1713
/*!
1714
  Sets the outer rect of this layout element. If the layout element is inside a
1715
  layout, the layout sets the position and size of this layout element using
1716
  this function.
1717
1718
  Calling this function externally has no effect, since the layout will
1719
  overwrite any changes to the outer rect upon the next replot.
1720
1721
  The layout element will adapt its inner \ref rect by applying the margins
1722
  inward to the outer rect.
1723
1724
  \see rect
1725
*/
1726
void QCPLayoutElement::setOuterRect(const QRect &rect) {
1727
  if (mOuterRect != rect) {
1728
    mOuterRect = rect;
1729
    mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(),
1730
                                -mMargins.right(), -mMargins.bottom());
1731
  }
1732
}
1733
1734
/*!
1735
  Sets the margins of this layout element. If \ref setAutoMargins is disabled
1736
  for some or all sides, this function is used to manually set the margin on
1737
  those sides. Sides that are still set to be handled automatically are ignored
1738
  and may have any value in \a margins.
1739
1740
  The margin is the distance between the outer rect (controlled by the parent
1741
  layout via \ref setOuterRect) and the inner \ref rect (which usually contains
1742
  the main content of this layout element).
1743
1744
  \see setAutoMargins
1745
*/
1746
void QCPLayoutElement::setMargins(const QMargins &margins) {
1747
  if (mMargins != margins) {
1748
    mMargins = margins;
1749
    mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(),
1750
                                -mMargins.right(), -mMargins.bottom());
1751
  }
1752
}
1753
1754
/*!
1755
  If \ref setAutoMargins is enabled on some or all margins, this function is
1756
  used to provide minimum values for those margins.
1757
1758
  The minimum values are not enforced on margin sides that were set to be under
1759
  manual control via \ref setAutoMargins.
1760
1761
  \see setAutoMargins
1762
*/
1763
void QCPLayoutElement::setMinimumMargins(const QMargins &margins) {
1764
  if (mMinimumMargins != margins) {
1765
    mMinimumMargins = margins;
1766
  }
1767
}
1768
1769
/*!
1770
  Sets on which sides the margin shall be calculated automatically. If a side is
1771
  calculated automatically, a minimum margin value may be provided with \ref
1772
  setMinimumMargins. If a side is set to be controlled manually, the value may
1773
  be specified with \ref setMargins.
1774
1775
  Margin sides that are under automatic control may participate in a \ref
1776
  QCPMarginGroup (see \ref setMarginGroup), to synchronize (align) it with other
1777
  layout elements in the plot.
1778
1779
  \see setMinimumMargins, setMargins
1780
*/
1781
void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides) {
1782
  mAutoMargins = sides;
1783
}
1784
1785
/*!
1786
  Sets the minimum size for the inner \ref rect of this layout element. A parent
1787
  layout tries to respect the \a size here by changing row/column sizes in the
1788
  layout accordingly.
1789
1790
  If the parent layout size is not sufficient to satisfy all minimum size
1791
  constraints of its child layout elements, the layout may set a size that is
1792
  actually smaller than \a size. QCustomPlot propagates the layout's size
1793
  constraints to the outside by setting its own minimum QWidget size
1794
  accordingly, so violations of \a size should be exceptions.
1795
*/
1796
void QCPLayoutElement::setMinimumSize(const QSize &size) {
1797
  if (mMinimumSize != size) {
1798
    mMinimumSize = size;
1799
    if (mParentLayout) mParentLayout->sizeConstraintsChanged();
1800
  }
1801
}
1802
1803
/*! \overload
1804
1805
  Sets the minimum size for the inner \ref rect of this layout element.
1806
*/
1807
void QCPLayoutElement::setMinimumSize(int width, int height) {
1808
  setMinimumSize(QSize(width, height));
1809
}
1810
1811
/*!
1812
  Sets the maximum size for the inner \ref rect of this layout element. A parent
1813
  layout tries to respect the \a size here by changing row/column sizes in the
1814
  layout accordingly.
1815
*/
1816
void QCPLayoutElement::setMaximumSize(const QSize &size) {
1817
  if (mMaximumSize != size) {
1818
    mMaximumSize = size;
1819
    if (mParentLayout) mParentLayout->sizeConstraintsChanged();
1820
  }
1821
}
1822
1823
/*! \overload
1824
1825
  Sets the maximum size for the inner \ref rect of this layout element.
1826
*/
1827
void QCPLayoutElement::setMaximumSize(int width, int height) {
1828
  setMaximumSize(QSize(width, height));
1829
}
1830
1831
/*!
1832
  Sets the margin \a group of the specified margin \a sides.
1833
1834
  Margin groups allow synchronizing specified margins across layout elements,
1835
  see the documentation of \ref QCPMarginGroup.
1836
1837
  To unset the margin group of \a sides, set \a group to 0.
1838
1839
  Note that margin groups only work for margin sides that are set to automatic
1840
  (\ref setAutoMargins).
1841
*/
1842
void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides,
1843
                                      QCPMarginGroup *group) {
1844
  QVector<QCP::MarginSide> sideVector;
1845
  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1846
  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1847
  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1848
  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1849
1850
  for (int i = 0; i < sideVector.size(); ++i) {
1851
    QCP::MarginSide side = sideVector.at(i);
1852
    if (marginGroup(side) != group) {
1853
      QCPMarginGroup *oldGroup = marginGroup(side);
1854
      if (oldGroup)  // unregister at old group
1855
        oldGroup->removeChild(side, this);
1856
1857
      if (!group)  // if setting to 0, remove hash entry. Else set hash entry to
1858
                   // new group and register there
1859
      {
1860
        mMarginGroups.remove(side);
1861
      } else  // setting to a new group
1862
      {
1863
        mMarginGroups[side] = group;
1864
        group->addChild(side, this);
1865
      }
1866
    }
1867
  }
1868
}
1869
1870
/*!
1871
  Updates the layout element and sub-elements. This function is automatically
1872
  called before every replot by the parent layout element. It is called multiple
1873
  times, once for every \ref UpdatePhase. The phases are run through in the
1874
  order of the enum values. For details about what happens at the different
1875
  phases, see the documentation of \ref UpdatePhase.
1876
1877
  Layout elements that have child elements should call the \ref update method of
1878
  their child elements, and pass the current \a phase unchanged.
1879
1880
  The default implementation executes the automatic margin mechanism in the \ref
1881
  upMargins phase. Subclasses should make sure to call the base class
1882
  implementation.
1883
*/
1884
void QCPLayoutElement::update(UpdatePhase phase) {
1885
  if (phase == upMargins) {
1886
    if (mAutoMargins != QCP::msNone) {
1887
      // set the margins of this layout element according to automatic margin
1888
      // calculation, either directly or via a margin group:
1889
      QMargins newMargins = mMargins;
1890
      QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>()
1891
                                              << QCP::msLeft << QCP::msRight
1892
                                              << QCP::msTop << QCP::msBottom;
1893
      foreach (QCP::MarginSide side, allMarginSides) {
1894
        if (mAutoMargins.testFlag(
1895
                side))  // this side's margin shall be calculated automatically
1896
        {
1897
          if (mMarginGroups.contains(side))
1898
            QCP::setMarginValue(
1899
                newMargins, side,
1900
                mMarginGroups[side]->commonMargin(
1901
                    side));  // this side is part of a margin group, so get the
1902
                             // margin value from that group
1903
          else
1904
            QCP::setMarginValue(
1905
                newMargins, side,
1906
                calculateAutoMargin(
1907
                    side));  // this side is not part of a group, so calculate
1908
                             // the value directly
1909
          // apply minimum margin restrictions:
1910
          if (QCP::getMarginValue(newMargins, side) <
1911
              QCP::getMarginValue(mMinimumMargins, side))
1912
            QCP::setMarginValue(newMargins, side,
1913
                                QCP::getMarginValue(mMinimumMargins, side));
1914
        }
1915
      }
1916
      setMargins(newMargins);
1917
    }
1918
  }
1919
}
1920
1921
/*!
1922
  Returns the minimum size this layout element (the inner \ref rect) may be
1923
  compressed to.
1924
1925
  if a minimum size (\ref setMinimumSize) was not set manually, parent layouts
1926
  consult this function to determine the minimum allowed size of this layout
1927
  element. (A manual minimum size is considered set if it is non-zero.)
1928
*/
1929
QSize QCPLayoutElement::minimumSizeHint() const { return mMinimumSize; }
1930
1931
/*!
1932
  Returns the maximum size this layout element (the inner \ref rect) may be
1933
  expanded to.
1934
1935
  if a maximum size (\ref setMaximumSize) was not set manually, parent layouts
1936
  consult this function to determine the maximum allowed size of this layout
1937
  element. (A manual maximum size is considered set if it is smaller than Qt's
1938
  QWIDGETSIZE_MAX.)
1939
*/
1940
QSize QCPLayoutElement::maximumSizeHint() const { return mMaximumSize; }
1941
1942
/*!
1943
  Returns a list of all child elements in this layout element. If \a recursive
1944
  is true, all sub-child elements are included in the list, too.
1945
1946
  \warning There may be entries with value 0 in the returned list. (For example,
1947
  QCPLayoutGrid may have empty cells which yield 0 at the respective index.)
1948
*/
1949
QList<QCPLayoutElement *> QCPLayoutElement::elements(bool recursive) const {
1950
  Q_UNUSED(recursive)
1951
  return QList<QCPLayoutElement *>();
1952
}
1953
1954
/*!
1955
  Layout elements are sensitive to events inside their outer rect. If \a pos is
1956
  within the outer rect, this method returns a value corresponding to 0.99 times
1957
  the parent plot's selection tolerance. However, layout elements are not
1958
  selectable by default. So if \a onlySelectable is true, -1.0 is returned.
1959
1960
  See \ref QCPLayerable::selectTest for a general explanation of this virtual
1961
  method.
1962
1963
  QCPLayoutElement subclasses may reimplement this method to provide more
1964
  specific selection test behaviour.
1965
*/
1966
double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable,
1967
                                    QVariant *details) const {
1968
  Q_UNUSED(details)
1969
1970
  if (onlySelectable) return -1;
1971
1972
  if (QRectF(mOuterRect).contains(pos)) {
1973
    if (mParentPlot)
1974
      return mParentPlot->selectionTolerance() * 0.99;
1975
    else {
1976
      qDebug() << Q_FUNC_INFO << "parent plot not defined";
1977
      return -1;
1978
    }
1979
  } else
1980
    return -1;
1981
}
1982
1983
/*! \internal
1984
1985
  propagates the parent plot initialization to all child elements, by calling
1986
  \ref QCPLayerable::initializeParentPlot on them.
1987
*/
1988
void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot) {
1989
  foreach (QCPLayoutElement *el, elements(false)) {
1990
    if (!el->parentPlot()) el->initializeParentPlot(parentPlot);
1991
  }
1992
}
1993
1994
/*! \internal
1995
1996
  Returns the margin size for this \a side. It is used if automatic margins is
1997
  enabled for this \a side (see \ref setAutoMargins). If a minimum margin was
1998
  set with \ref setMinimumMargins, the returned value will not be smaller than
1999
  the specified minimum margin.
2000
2001
  The default implementation just returns the respective manual margin (\ref
2002
  setMargins) or the minimum margin, whichever is larger.
2003
*/
2004
int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side) {
2005
  return qMax(QCP::getMarginValue(mMargins, side),
2006
              QCP::getMarginValue(mMinimumMargins, side));
2007
}
2008
2009
////////////////////////////////////////////////////////////////////////////////////////////////////
2010
//////////////////// QCPLayout
2011
////////////////////////////////////////////////////////////////////////////////////////////////////
2012
2013
/*! \class QCPLayout
2014
  \brief The abstract base class for layouts
2015
2016
  This is an abstract base class for layout elements whose main purpose is to
2017
  define the position and size of other child layout elements. In most cases,
2018
  layouts don't draw anything themselves (but there are exceptions to this, e.g.
2019
  QCPLegend).
2020
2021
  QCPLayout derives from QCPLayoutElement, and thus can itself be nested in
2022
  other layouts.
2023
2024
  QCPLayout introduces a common interface for accessing and manipulating the
2025
  child elements. Those functions are most notably \ref elementCount, \ref
2026
  elementAt, \ref takeAt, \ref take, \ref simplify, \ref removeAt, \ref remove
2027
  and \ref clear. Individual subclasses may add more functions to this interface
2028
  which are more specialized to the form of the layout. For example, \ref
2029
  QCPLayoutGrid adds functions that take row and column indices to access cells
2030
  of the layout grid more conveniently.
2031
2032
  Since this is an abstract base class, you can't instantiate it directly.
2033
  Rather use one of its subclasses like QCPLayoutGrid or QCPLayoutInset.
2034
2035
  For a general introduction to the layout system, see the dedicated
2036
  documentation page \ref thelayoutsystem "The Layout System".
2037
*/
2038
2039
/* start documentation of pure virtual functions */
2040
2041
/*! \fn virtual int QCPLayout::elementCount() const = 0
2042
2043
  Returns the number of elements/cells in the layout.
2044
2045
  \see elements, elementAt
2046
*/
2047
2048
/*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
2049
2050
  Returns the element in the cell with the given \a index. If \a index is
2051
  invalid, returns 0.
2052
2053
  Note that even if \a index is valid, the respective cell may be empty in some
2054
  layouts (e.g. QCPLayoutGrid), so this function may return 0 in those cases.
2055
  You may use this function to check whether a cell is empty or not.
2056
2057
  \see elements, elementCount, takeAt
2058
*/
2059
2060
/*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0
2061
2062
  Removes the element with the given \a index from the layout and returns it.
2063
2064
  If the \a index is invalid or the cell with that index is empty, returns 0.
2065
2066
  Note that some layouts don't remove the respective cell right away but leave
2067
  an empty cell after successful removal of the layout element. To collapse
2068
  empty cells, use \ref simplify.
2069
2070
  \see elementAt, take
2071
*/
2072
2073
/*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0
2074
2075
  Removes the specified \a element from the layout and returns true on success.
2076
2077
  If the \a element isn't in this layout, returns false.
2078
2079
  Note that some layouts don't remove the respective cell right away but leave
2080
  an empty cell after successful removal of the layout element. To collapse
2081
  empty cells, use \ref simplify.
2082
2083
  \see takeAt
2084
*/
2085
2086
/* end documentation of pure virtual functions */
2087
2088
/*!
2089
  Creates an instance of QCPLayout and sets default values. Note that since
2090
  QCPLayout is an abstract base class, it can't be instantiated directly.
2091
*/
2092
QCPLayout::QCPLayout() {}
2093
2094
/*!
2095
  First calls the QCPLayoutElement::update base class implementation to update
2096
  the margins on this layout.
2097
2098
  Then calls \ref updateLayout which subclasses reimplement to reposition and
2099
  resize their cells.
2100
2101
  Finally, \ref update is called on all child elements.
2102
*/
2103
void QCPLayout::update(UpdatePhase phase) {
2104
  QCPLayoutElement::update(phase);
2105
2106
  // set child element rects according to layout:
2107
  if (phase == upLayout) updateLayout();
2108
2109
  // propagate update call to child elements:
2110
  const int elCount = elementCount();
2111
  for (int i = 0; i < elCount; ++i) {
2112
    if (QCPLayoutElement *el = elementAt(i)) el->update(phase);
2113
  }
2114
}
2115
2116
/* inherits documentation from base class */
2117
QList<QCPLayoutElement *> QCPLayout::elements(bool recursive) const {
2118
  const int c = elementCount();
2119
  QList<QCPLayoutElement *> result;
2120
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2121
  result.reserve(c);
2122
#endif
2123
  for (int i = 0; i < c; ++i) result.append(elementAt(i));
2124
  if (recursive) {
2125
    for (int i = 0; i < c; ++i) {
2126
      if (result.at(i)) result << result.at(i)->elements(recursive);
2127
    }
2128
  }
2129
  return result;
2130
}
2131
2132
/*!
2133
  Simplifies the layout by collapsing empty cells. The exact behavior depends on
2134
  subclasses, the default implementation does nothing.
2135
2136
  Not all layouts need simplification. For example, QCPLayoutInset doesn't use
2137
  explicit simplification while QCPLayoutGrid does.
2138
*/
2139
void QCPLayout::simplify() {}
2140
2141
/*!
2142
  Removes and deletes the element at the provided \a index. Returns true on
2143
  success. If \a index is invalid or points to an empty cell, returns false.
2144
2145
  This function internally uses \ref takeAt to remove the element from the
2146
  layout and then deletes the returned element. Note that some layouts don't
2147
  remove the respective cell right away but leave an empty cell after successful
2148
  removal of the layout element. To collapse empty cells, use \ref simplify.
2149
2150
  \see remove, takeAt
2151
*/
2152
bool QCPLayout::removeAt(int index) {
2153
  if (QCPLayoutElement *el = takeAt(index)) {
2154
    delete el;
2155
    return true;
2156
  } else
2157
    return false;
2158
}
2159
2160
/*!
2161
  Removes and deletes the provided \a element. Returns true on success. If \a
2162
  element is not in the layout, returns false.
2163
2164
  This function internally uses \ref takeAt to remove the element from the
2165
  layout and then deletes the element. Note that some layouts don't remove the
2166
  respective cell right away but leave an empty cell after successful removal of
2167
  the layout element. To collapse empty cells, use \ref simplify.
2168
2169
  \see removeAt, take
2170
*/
2171
bool QCPLayout::remove(QCPLayoutElement *element) {
2172
  if (take(element)) {
2173
    delete element;
2174
    return true;
2175
  } else
2176
    return false;
2177
}
2178
2179
/*!
2180
  Removes and deletes all layout elements in this layout. Finally calls \ref
2181
  simplify to make sure all empty cells are collapsed.
2182
2183
  \see remove, removeAt
2184
*/
2185
void QCPLayout::clear() {
2186
  for (int i = elementCount() - 1; i >= 0; --i) {
2187
    if (elementAt(i)) removeAt(i);
2188
  }
2189
  simplify();
2190
}
2191
2192
/*!
2193
  Subclasses call this method to report changed (minimum/maximum) size
2194
  constraints.
2195
2196
  If the parent of this layout is again a QCPLayout, forwards the call to the
2197
  parent's \ref sizeConstraintsChanged. If the parent is a QWidget (i.e. is the
2198
  \ref QCustomPlot::plotLayout of QCustomPlot), calls QWidget::updateGeometry,
2199
  so if the QCustomPlot widget is inside a Qt QLayout, it may update itself and
2200
  resize cells accordingly.
2201
*/
2202
void QCPLayout::sizeConstraintsChanged() const {
2203
  if (QWidget *w = qobject_cast<QWidget *>(parent()))
2204
    w->updateGeometry();
2205
  else if (QCPLayout *l = qobject_cast<QCPLayout *>(parent()))
2206
    l->sizeConstraintsChanged();
2207
}
2208
2209
/*! \internal
2210
2211
  Subclasses reimplement this method to update the position and sizes of the
2212
  child elements/cells via calling their \ref QCPLayoutElement::setOuterRect.
2213
  The default implementation does nothing.
2214
2215
  The geometry used as a reference is the inner \ref rect of this layout. Child
2216
  elements should stay within that rect.
2217
2218
  \ref getSectionSizes may help with the reimplementation of this function.
2219
2220
  \see update
2221
*/
2222
void QCPLayout::updateLayout() {}
2223
2224
/*! \internal
2225
2226
  Associates \a el with this layout. This is done by setting the \ref
2227
  QCPLayoutElement::layout, the \ref QCPLayerable::parentLayerable and the
2228
  QObject parent to this layout.
2229
2230
  Further, if \a el didn't previously have a parent plot, calls \ref
2231
  QCPLayerable::initializeParentPlot on \a el to set the paret plot.
2232
2233
  This method is used by subclass specific methods that add elements to the
2234
  layout. Note that this method only changes properties in \a el. The removal
2235
  from the old layout and the insertion into the new layout must be done
2236
  additionally.
2237
*/
2238
void QCPLayout::adoptElement(QCPLayoutElement *el) {
2239
  if (el) {
2240
    el->mParentLayout = this;
2241
    el->setParentLayerable(this);
2242
    el->setParent(this);
2243
    if (!el->parentPlot()) el->initializeParentPlot(mParentPlot);
2244
  } else
2245
    qDebug() << Q_FUNC_INFO << "Null element passed";
2246
}
2247
2248
/*! \internal
2249
2250
  Disassociates \a el from this layout. This is done by setting the \ref
2251
  QCPLayoutElement::layout and the \ref QCPLayerable::parentLayerable to zero.
2252
  The QObject parent is set to the parent QCustomPlot.
2253
2254
  This method is used by subclass specific methods that remove elements from the
2255
  layout (e.g. \ref take or \ref takeAt). Note that this method only changes
2256
  properties in \a el. The removal from the old layout must be done
2257
  additionally.
2258
*/
2259
void QCPLayout::releaseElement(QCPLayoutElement *el) {
2260
  if (el) {
2261
    el->mParentLayout = 0;
2262
    el->setParentLayerable(0);
2263
    el->setParent(mParentPlot);
2264
    // Note: Don't initializeParentPlot(0) here, because layout element will
2265
    // stay in same parent plot
2266
  } else
2267
    qDebug() << Q_FUNC_INFO << "Null element passed";
2268
}
2269
2270
/*! \internal
2271
2272
  This is a helper function for the implementation of \ref updateLayout in
2273
  subclasses.
2274
2275
  It calculates the sizes of one-dimensional sections with provided constraints
2276
  on maximum section sizes, minimum section sizes, relative stretch factors and
2277
  the final total size of all sections.
2278
2279
  The QVector entries refer to the sections. Thus all QVectors must have the
2280
  same size.
2281
2282
  \a maxSizes gives the maximum allowed size of each section. If there shall be
2283
  no maximum size imposed, set all vector values to Qt's QWIDGETSIZE_MAX.
2284
2285
  \a minSizes gives the minimum allowed size of each section. If there shall be
2286
  no minimum size imposed, set all vector values to zero. If the \a minSizes
2287
  entries add up to a value greater than \a totalSize, sections will be scaled
2288
  smaller than the proposed minimum sizes. (In other words, not exceeding the
2289
  allowed total size is taken to be more important than not going below minimum
2290
  section sizes.)
2291
2292
  \a stretchFactors give the relative proportions of the sections to each other.
2293
  If all sections shall be scaled equally, set all values equal. If the first
2294
  section shall be double the size of each individual other section, set the
2295
  first number of \a stretchFactors to double the value of the other individual
2296
  values (e.g. {2, 1, 1, 1}).
2297
2298
  \a totalSize is the value that the final section sizes will add up to. Due to
2299
  rounding, the actual sum may differ slightly. If you want the section sizes to
2300
  sum up to exactly that value, you could distribute the remaining difference on
2301
  the sections.
2302
2303
  The return value is a QVector containing the section sizes.
2304
*/
2305
QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes,
2306
                                        QVector<int> minSizes,
2307
                                        QVector<double> stretchFactors,
2308
                                        int totalSize) const {
2309
  if (maxSizes.size() != minSizes.size() ||
2310
      minSizes.size() != stretchFactors.size()) {
2311
    qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes
2312
             << minSizes << stretchFactors;
2313
    return QVector<int>();
2314
  }
2315
  if (stretchFactors.isEmpty()) return QVector<int>();
2316
  int sectionCount = stretchFactors.size();
2317
  QVector<double> sectionSizes(sectionCount);
2318
  // if provided total size is forced smaller than total minimum size, ignore
2319
  // minimum sizes (squeeze sections):
2320
  int minSizeSum = 0;
2321
  for (int i = 0; i < sectionCount; ++i) minSizeSum += minSizes.at(i);
2322
  if (totalSize < minSizeSum) {
2323
    // new stretch factors are minimum sizes and minimum sizes are set to zero:
2324
    for (int i = 0; i < sectionCount; ++i) {
2325
      stretchFactors[i] = minSizes.at(i);
2326
      minSizes[i] = 0;
2327
    }
2328
  }
2329
2330
  QList<int> minimumLockedSections;
2331
  QList<int> unfinishedSections;
2332
  for (int i = 0; i < sectionCount; ++i) unfinishedSections.append(i);
2333
  double freeSize = totalSize;
2334
2335
  int outerIterations = 0;
2336
  while (!unfinishedSections.isEmpty() &&
2337
         outerIterations <
2338
             sectionCount * 2)  // the iteration check ist just a failsafe in
2339
                                // case something really strange happens
2340
  {
2341
    ++outerIterations;
2342
    int innerIterations = 0;
2343
    while (!unfinishedSections.isEmpty() &&
2344
           innerIterations <
2345
               sectionCount * 2)  // the iteration check ist just a failsafe in
2346
                                  // case something really strange happens
2347
    {
2348
      ++innerIterations;
2349
      // find section that hits its maximum next:
2350
      int nextId = -1;
2351
      double nextMax = 1e12;
2352
      for (int i = 0; i < unfinishedSections.size(); ++i) {
2353
        int secId = unfinishedSections.at(i);
2354
        double hitsMaxAt = (maxSizes.at(secId) - sectionSizes.at(secId)) /
2355
                           stretchFactors.at(secId);
2356
        if (hitsMaxAt < nextMax) {
2357
          nextMax = hitsMaxAt;
2358
          nextId = secId;
2359
        }
2360
      }
2361
      // check if that maximum is actually within the bounds of the total size
2362
      // (i.e. can we stretch all remaining sections so far that the found
2363
      // section actually hits its maximum, without exceeding the total size
2364
      // when we add up all sections)
2365
      double stretchFactorSum = 0;
2366
      for (int i = 0; i < unfinishedSections.size(); ++i)
2367
        stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2368
      double nextMaxLimit = freeSize / stretchFactorSum;
2369
      if (nextMax <
2370
          nextMaxLimit)  // next maximum is actually hit, move forward to that
2371
                         // point and fix the size of that section
2372
      {
2373
        for (int i = 0; i < unfinishedSections.size(); ++i) {
2374
          sectionSizes[unfinishedSections.at(i)] +=
2375
              nextMax * stretchFactors.at(unfinishedSections.at(
2376
                            i));  // increment all sections
2377
          freeSize -= nextMax * stretchFactors.at(unfinishedSections.at(i));
2378
        }
2379
        unfinishedSections.removeOne(
2380
            nextId);  // exclude the section that is now at maximum from further
2381
                      // changes
2382
      } else  // next maximum isn't hit, just distribute rest of free space on
2383
              // remaining sections
2384
      {
2385
        for (int i = 0; i < unfinishedSections.size(); ++i)
2386
          sectionSizes[unfinishedSections.at(i)] +=
2387
              nextMaxLimit * stretchFactors.at(unfinishedSections.at(
2388
                                 i));  // increment all sections
2389
        unfinishedSections.clear();
2390
      }
2391
    }
2392
    if (innerIterations == sectionCount * 2)
2393
      qDebug() << Q_FUNC_INFO
2394
               << "Exceeded maximum expected inner iteration count, layouting "
2395
                  "aborted. Input was:"
2396
               << maxSizes << minSizes << stretchFactors << totalSize;
2397
2398
    // now check whether the resulting section sizes violate minimum
2399
    // restrictions:
2400
    bool foundMinimumViolation = false;
2401
    for (int i = 0; i < sectionSizes.size(); ++i) {
2402
      if (minimumLockedSections.contains(i)) continue;
2403
      if (sectionSizes.at(i) < minSizes.at(i))  // section violates minimum
2404
      {
2405
        sectionSizes[i] = minSizes.at(i);  // set it to minimum
2406
        foundMinimumViolation =
2407
            true;  // make sure we repeat the whole optimization process
2408
        minimumLockedSections.append(i);
2409
      }
2410
    }
2411
    if (foundMinimumViolation) {
2412
      freeSize = totalSize;
2413
      for (int i = 0; i < sectionCount; ++i) {
2414
        if (!minimumLockedSections.contains(
2415
                i))  // only put sections that haven't hit their minimum back
2416
                     // into the pool
2417
          unfinishedSections.append(i);
2418
        else
2419
          freeSize -=
2420
              sectionSizes.at(i);  // remove size of minimum locked sections
2421
                                   // from available space in next round
2422
      }
2423
      // reset all section sizes to zero that are in unfinished sections (all
2424
      // others have been set to their minimum):
2425
      for (int i = 0; i < unfinishedSections.size(); ++i)
2426
        sectionSizes[unfinishedSections.at(i)] = 0;
2427
    }
2428
  }
2429
  if (outerIterations == sectionCount * 2)
2430
    qDebug() << Q_FUNC_INFO
2431
             << "Exceeded maximum expected outer iteration count, layouting "
2432
                "aborted. Input was:"
2433
             << maxSizes << minSizes << stretchFactors << totalSize;
2434
2435
  QVector<int> result(sectionCount);
2436
  for (int i = 0; i < sectionCount; ++i) result[i] = qRound(sectionSizes.at(i));
2437
  return result;
2438
}
2439
2440
////////////////////////////////////////////////////////////////////////////////////////////////////
2441
//////////////////// QCPLayoutGrid
2442
////////////////////////////////////////////////////////////////////////////////////////////////////
2443
2444
/*! \class QCPLayoutGrid
2445
  \brief A layout that arranges child elements in a grid
2446
2447
  Elements are laid out in a grid with configurable stretch factors (\ref
2448
  setColumnStretchFactor, \ref setRowStretchFactor) and spacing (\ref
2449
  setColumnSpacing, \ref setRowSpacing).
2450
2451
  Elements can be added to cells via \ref addElement. The grid is expanded if
2452
  the specified row or column doesn't exist yet. Whether a cell contains a valid
2453
  layout element can be checked with \ref hasElement, that element can be
2454
  retrieved with \ref element. If rows and columns that only have empty cells
2455
  shall be removed, call \ref simplify. Removal of elements is either done by
2456
  just adding the element to a different layout or by using the QCPLayout
2457
  interface \ref take or \ref remove.
2458
2459
  Row and column insertion can be performed with \ref insertRow and \ref
2460
  insertColumn.
2461
*/
2462
2463
/*!
2464
  Creates an instance of QCPLayoutGrid and sets default values.
2465
*/
2466
QCPLayoutGrid::QCPLayoutGrid() : mColumnSpacing(5), mRowSpacing(5) {}
2467
2468
QCPLayoutGrid::~QCPLayoutGrid() {
2469
  // clear all child layout elements. This is important because only the
2470
  // specific layouts know how to handle removing elements (clear calls virtual
2471
  // removeAt method to do that).
2472
  clear();
2473
}
2474
2475
/*!
2476
  Returns the element in the cell in \a row and \a column.
2477
2478
  Returns 0 if either the row/column is invalid or if the cell is empty. In
2479
  those cases, a qDebug message is printed. To check whether a cell exists and
2480
  isn't empty, use \ref hasElement.
2481
2482
  \see addElement, hasElement
2483
*/
2484
QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const {
2485
  if (row >= 0 && row < mElements.size()) {
2486
    if (column >= 0 && column < mElements.first().size()) {
2487
      if (QCPLayoutElement *result = mElements.at(row).at(column))
2488
        return result;
2489
      else
2490
        qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row
2491
                 << "Column:" << column;
2492
    } else
2493
      qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row
2494
               << "Column:" << column;
2495
  } else
2496
    qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row
2497
             << "Column:" << column;
2498
  return 0;
2499
}
2500
2501
/*!
2502
  Returns the number of rows in the layout.
2503
2504
  \see columnCount
2505
*/
2506
int QCPLayoutGrid::rowCount() const { return mElements.size(); }
2507
2508
/*!
2509
  Returns the number of columns in the layout.
2510
2511
  \see rowCount
2512
*/
2513
int QCPLayoutGrid::columnCount() const {
2514
  if (mElements.size() > 0)
2515
    return mElements.first().size();
2516
  else
2517
    return 0;
2518
}
2519
2520
/*!
2521
  Adds the \a element to cell with \a row and \a column. If \a element is
2522
  already in a layout, it is first removed from there. If \a row or \a column
2523
  don't exist yet, the layout is expanded accordingly.
2524
2525
  Returns true if the element was added successfully, i.e. if the cell at \a row
2526
  and \a column didn't already have an element.
2527
2528
  \see element, hasElement, take, remove
2529
*/
2530
bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element) {
2531
  if (element) {
2532
    if (!hasElement(row, column)) {
2533
      if (element->layout())  // remove from old layout first
2534
        element->layout()->take(element);
2535
      expandTo(row + 1, column + 1);
2536
      mElements[row][column] = element;
2537
      adoptElement(element);
2538
      return true;
2539
    } else
2540
      qDebug() << Q_FUNC_INFO
2541
               << "There is already an element in the specified row/column:"
2542
               << row << column;
2543
  } else
2544
    qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row
2545
             << column;
2546
  return false;
2547
}
2548
2549
/*!
2550
  Returns whether the cell at \a row and \a column exists and contains a valid
2551
  element, i.e. isn't empty.
2552
2553
  \see element
2554
*/
2555
bool QCPLayoutGrid::hasElement(int row, int column) {
2556
  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2557
    return mElements.at(row).at(column);
2558
  else
2559
    return false;
2560
}
2561
2562
/*!
2563
  Sets the stretch \a factor of \a column.
2564
2565
  Stretch factors control the relative sizes of rows and columns. Cells will not
2566
  be resized beyond their minimum and maximum widths/heights (\ref
2567
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize),
2568
  regardless of the stretch factor.
2569
2570
  The default stretch factor of newly created rows/columns is 1.
2571
2572
  \see setColumnStretchFactors, setRowStretchFactor
2573
*/
2574
void QCPLayoutGrid::setColumnStretchFactor(int column, double factor) {
2575
  if (column >= 0 && column < columnCount()) {
2576
    if (factor > 0)
2577
      mColumnStretchFactors[column] = factor;
2578
    else
2579
      qDebug() << Q_FUNC_INFO
2580
               << "Invalid stretch factor, must be positive:" << factor;
2581
  } else
2582
    qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2583
}
2584
2585
/*!
2586
  Sets the stretch \a factors of all columns. \a factors must have the size \ref
2587
  columnCount.
2588
2589
  Stretch factors control the relative sizes of rows and columns. Cells will not
2590
  be resized beyond their minimum and maximum widths/heights (\ref
2591
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize),
2592
  regardless of the stretch factor.
2593
2594
  The default stretch factor of newly created rows/columns is 1.
2595
2596
  \see setColumnStretchFactor, setRowStretchFactors
2597
*/
2598
void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors) {
2599
  if (factors.size() == mColumnStretchFactors.size()) {
2600
    mColumnStretchFactors = factors;
2601
    for (int i = 0; i < mColumnStretchFactors.size(); ++i) {
2602
      if (mColumnStretchFactors.at(i) <= 0) {
2603
        qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:"
2604
                 << mColumnStretchFactors.at(i);
2605
        mColumnStretchFactors[i] = 1;
2606
      }
2607
    }
2608
  } else
2609
    qDebug() << Q_FUNC_INFO
2610
             << "Column count not equal to passed stretch factor count:"
2611
             << factors;
2612
}
2613
2614
/*!
2615
  Sets the stretch \a factor of \a row.
2616
2617
  Stretch factors control the relative sizes of rows and columns. Cells will not
2618
  be resized beyond their minimum and maximum widths/heights (\ref
2619
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize),
2620
  regardless of the stretch factor.
2621
2622
  The default stretch factor of newly created rows/columns is 1.
2623
2624
  \see setColumnStretchFactors, setRowStretchFactor
2625
*/
2626
void QCPLayoutGrid::setRowStretchFactor(int row, double factor) {
2627
  if (row >= 0 && row < rowCount()) {
2628
    if (factor > 0)
2629
      mRowStretchFactors[row] = factor;
2630
    else
2631
      qDebug() << Q_FUNC_INFO
2632
               << "Invalid stretch factor, must be positive:" << factor;
2633
  } else
2634
    qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2635
}
2636
2637
/*!
2638
  Sets the stretch \a factors of all rows. \a factors must have the size \ref
2639
  rowCount.
2640
2641
  Stretch factors control the relative sizes of rows and columns. Cells will not
2642
  be resized beyond their minimum and maximum widths/heights (\ref
2643
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize),
2644
  regardless of the stretch factor.
2645
2646
  The default stretch factor of newly created rows/columns is 1.
2647
2648
  \see setRowStretchFactor, setColumnStretchFactors
2649
*/
2650
void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors) {
2651
  if (factors.size() == mRowStretchFactors.size()) {
2652
    mRowStretchFactors = factors;
2653
    for (int i = 0; i < mRowStretchFactors.size(); ++i) {
2654
      if (mRowStretchFactors.at(i) <= 0) {
2655
        qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:"
2656
                 << mRowStretchFactors.at(i);
2657
        mRowStretchFactors[i] = 1;
2658
      }
2659
    }
2660
  } else
2661
    qDebug() << Q_FUNC_INFO
2662
             << "Row count not equal to passed stretch factor count:"
2663
             << factors;
2664
}
2665
2666
/*!
2667
  Sets the gap that is left blank between columns to \a pixels.
2668
2669
  \see setRowSpacing
2670
*/
2671
void QCPLayoutGrid::setColumnSpacing(int pixels) { mColumnSpacing = pixels; }
2672
2673
/*!
2674
  Sets the gap that is left blank between rows to \a pixels.
2675
2676
  \see setColumnSpacing
2677
*/
2678
void QCPLayoutGrid::setRowSpacing(int pixels) { mRowSpacing = pixels; }
2679
2680
/*!
2681
  Expands the layout to have \a newRowCount rows and \a newColumnCount columns.
2682
  So the last valid row index will be \a newRowCount-1, the last valid column
2683
  index will be \a newColumnCount-1.
2684
2685
  If the current column/row count is already larger or equal to \a
2686
  newColumnCount/\a newRowCount, this function does nothing in that dimension.
2687
2688
  Newly created cells are empty, new rows and columns have the stretch factor 1.
2689
2690
  Note that upon a call to \ref addElement, the layout is expanded automatically
2691
  to contain the specified row and column, using this function.
2692
2693
  \see simplify
2694
*/
2695
void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount) {
2696
  // add rows as necessary:
2697
  while (rowCount() < newRowCount) {
2698
    mElements.append(QList<QCPLayoutElement *>());
2699
    mRowStretchFactors.append(1);
2700
  }
2701
  // go through rows and expand columns as necessary:
2702
  int newColCount = qMax(columnCount(), newColumnCount);
2703
  for (int i = 0; i < rowCount(); ++i) {
2704
    while (mElements.at(i).size() < newColCount) mElements[i].append(0);
2705
  }
2706
  while (mColumnStretchFactors.size() < newColCount)
2707
    mColumnStretchFactors.append(1);
2708
}
2709
2710
/*!
2711
  Inserts a new row with empty cells at the row index \a newIndex. Valid values
2712
  for \a newIndex range from 0 (inserts a row at the top) to \a rowCount
2713
  (appends a row at the bottom).
2714
2715
  \see insertColumn
2716
*/
2717
void QCPLayoutGrid::insertRow(int newIndex) {
2718
  if (mElements.isEmpty() ||
2719
      mElements.first()
2720
          .isEmpty())  // if grid is completely empty, add first cell
2721
  {
2722
    expandTo(1, 1);
2723
    return;
2724
  }
2725
2726
  if (newIndex < 0) newIndex = 0;
2727
  if (newIndex > rowCount()) newIndex = rowCount();
2728
2729
  mRowStretchFactors.insert(newIndex, 1);
2730
  QList<QCPLayoutElement *> newRow;
2731
  for (int col = 0; col < columnCount(); ++col)
2732
    newRow.append((QCPLayoutElement *)0);
2733
  mElements.insert(newIndex, newRow);
2734
}
2735
2736
/*!
2737
  Inserts a new column with empty cells at the column index \a newIndex. Valid
2738
  values for \a newIndex range from 0 (inserts a row at the left) to \a rowCount
2739
  (appends a row at the right).
2740
2741
  \see insertRow
2742
*/
2743
void QCPLayoutGrid::insertColumn(int newIndex) {
2744
  if (mElements.isEmpty() ||
2745
      mElements.first()
2746
          .isEmpty())  // if grid is completely empty, add first cell
2747
  {
2748
    expandTo(1, 1);
2749
    return;
2750
  }
2751
2752
  if (newIndex < 0) newIndex = 0;
2753
  if (newIndex > columnCount()) newIndex = columnCount();
2754
2755
  mColumnStretchFactors.insert(newIndex, 1);
2756
  for (int row = 0; row < rowCount(); ++row)
2757
    mElements[row].insert(newIndex, (QCPLayoutElement *)0);
2758
}
2759
2760
/* inherits documentation from base class */
2761
void QCPLayoutGrid::updateLayout() {
2762
  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2763
  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2764
  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2765
2766
  int totalRowSpacing = (rowCount() - 1) * mRowSpacing;
2767
  int totalColSpacing = (columnCount() - 1) * mColumnSpacing;
2768
  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths,
2769
                                           mColumnStretchFactors.toVector(),
2770
                                           mRect.width() - totalColSpacing);
2771
  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights,
2772
                                            mRowStretchFactors.toVector(),
2773
                                            mRect.height() - totalRowSpacing);
2774
2775
  // go through cells and set rects accordingly:
2776
  int yOffset = mRect.top();
2777
  for (int row = 0; row < rowCount(); ++row) {
2778
    if (row > 0) yOffset += rowHeights.at(row - 1) + mRowSpacing;
2779
    int xOffset = mRect.left();
2780
    for (int col = 0; col < columnCount(); ++col) {
2781
      if (col > 0) xOffset += colWidths.at(col - 1) + mColumnSpacing;
2782
      if (mElements.at(row).at(col))
2783
        mElements.at(row).at(col)->setOuterRect(
2784
            QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2785
    }
2786
  }
2787
}
2788
2789
/* inherits documentation from base class */
2790
int QCPLayoutGrid::elementCount() const { return rowCount() * columnCount(); }
2791
2792
/* inherits documentation from base class */
2793
QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const {
2794
  if (index >= 0 && index < elementCount())
2795
    return mElements.at(index / columnCount()).at(index % columnCount());
2796
  else
2797
    return 0;
2798
}
2799
2800
/* inherits documentation from base class */
2801
QCPLayoutElement *QCPLayoutGrid::takeAt(int index) {
2802
  if (QCPLayoutElement *el = elementAt(index)) {
2803
    releaseElement(el);
2804
    mElements[index / columnCount()][index % columnCount()] = 0;
2805
    return el;
2806
  } else {
2807
    qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2808
    return 0;
2809
  }
2810
}
2811
2812
/* inherits documentation from base class */
2813
bool QCPLayoutGrid::take(QCPLayoutElement *element) {
2814
  if (element) {
2815
    for (int i = 0; i < elementCount(); ++i) {
2816
      if (elementAt(i) == element) {
2817
        takeAt(i);
2818
        return true;
2819
      }
2820
    }
2821
    qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2822
  } else
2823
    qDebug() << Q_FUNC_INFO << "Can't take null element";
2824
  return false;
2825
}
2826
2827
/* inherits documentation from base class */
2828
QList<QCPLayoutElement *> QCPLayoutGrid::elements(bool recursive) const {
2829
  QList<QCPLayoutElement *> result;
2830
  int colC = columnCount();
2831
  int rowC = rowCount();
2832
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2833
  result.reserve(colC * rowC);
2834
#endif
2835
  for (int row = 0; row < rowC; ++row) {
2836
    for (int col = 0; col < colC; ++col) {
2837
      result.append(mElements.at(row).at(col));
2838
    }
2839
  }
2840
  if (recursive) {
2841
    int c = result.size();
2842
    for (int i = 0; i < c; ++i) {
2843
      if (result.at(i)) result << result.at(i)->elements(recursive);
2844
    }
2845
  }
2846
  return result;
2847
}
2848
2849
/*!
2850
  Simplifies the layout by collapsing rows and columns which only contain empty
2851
  cells.
2852
*/
2853
void QCPLayoutGrid::simplify() {
2854
  // remove rows with only empty cells:
2855
  for (int row = rowCount() - 1; row >= 0; --row) {
2856
    bool hasElements = false;
2857
    for (int col = 0; col < columnCount(); ++col) {
2858
      if (mElements.at(row).at(col)) {
2859
        hasElements = true;
2860
        break;
2861
      }
2862
    }
2863
    if (!hasElements) {
2864
      mRowStretchFactors.removeAt(row);
2865
      mElements.removeAt(row);
2866
      if (mElements.isEmpty())  // removed last element, also remove stretch
2867
                                // factor (wouldn't happen below because also
2868
                                // columnCount changed to 0 now)
2869
        mColumnStretchFactors.clear();
2870
    }
2871
  }
2872
2873
  // remove columns with only empty cells:
2874
  for (int col = columnCount() - 1; col >= 0; --col) {
2875
    bool hasElements = false;
2876
    for (int row = 0; row < rowCount(); ++row) {
2877
      if (mElements.at(row).at(col)) {
2878
        hasElements = true;
2879
        break;
2880
      }
2881
    }
2882
    if (!hasElements) {
2883
      mColumnStretchFactors.removeAt(col);
2884
      for (int row = 0; row < rowCount(); ++row) mElements[row].removeAt(col);
2885
    }
2886
  }
2887
}
2888
2889
/* inherits documentation from base class */
2890
QSize QCPLayoutGrid::minimumSizeHint() const {
2891
  QVector<int> minColWidths, minRowHeights;
2892
  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2893
  QSize result(0, 0);
2894
  for (int i = 0; i < minColWidths.size(); ++i)
2895
    result.rwidth() += minColWidths.at(i);
2896
  for (int i = 0; i < minRowHeights.size(); ++i)
2897
    result.rheight() += minRowHeights.at(i);
2898
  result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing +
2899
                     mMargins.left() + mMargins.right();
2900
  result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing + mMargins.top() +
2901
                      mMargins.bottom();
2902
  return result;
2903
}
2904
2905
/* inherits documentation from base class */
2906
QSize QCPLayoutGrid::maximumSizeHint() const {
2907
  QVector<int> maxColWidths, maxRowHeights;
2908
  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2909
2910
  QSize result(0, 0);
2911
  for (int i = 0; i < maxColWidths.size(); ++i)
2912
    result.setWidth(qMin(result.width() + maxColWidths.at(i), QWIDGETSIZE_MAX));
2913
  for (int i = 0; i < maxRowHeights.size(); ++i)
2914
    result.setHeight(
2915
        qMin(result.height() + maxRowHeights.at(i), QWIDGETSIZE_MAX));
2916
  result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing +
2917
                     mMargins.left() + mMargins.right();
2918
  result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing + mMargins.top() +
2919
                      mMargins.bottom();
2920
  return result;
2921
}
2922
2923
/*! \internal
2924
2925
  Places the minimum column widths and row heights into \a minColWidths and \a
2926
  minRowHeights respectively.
2927
2928
  The minimum height of a row is the largest minimum height of any element in
2929
  that row. The minimum width of a column is the largest minimum width of any
2930
  element in that column.
2931
2932
  This is a helper function for \ref updateLayout.
2933
2934
  \see getMaximumRowColSizes
2935
*/
2936
void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths,
2937
                                          QVector<int> *minRowHeights) const {
2938
  *minColWidths = QVector<int>(columnCount(), 0);
2939
  *minRowHeights = QVector<int>(rowCount(), 0);
2940
  for (int row = 0; row < rowCount(); ++row) {
2941
    for (int col = 0; col < columnCount(); ++col) {
2942
      if (mElements.at(row).at(col)) {
2943
        QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
2944
        QSize min = mElements.at(row).at(col)->minimumSize();
2945
        QSize final(min.width() > 0 ? min.width() : minHint.width(),
2946
                    min.height() > 0 ? min.height() : minHint.height());
2947
        if (minColWidths->at(col) < final.width())
2948
          (*minColWidths)[col] = final.width();
2949
        if (minRowHeights->at(row) < final.height())
2950
          (*minRowHeights)[row] = final.height();
2951
      }
2952
    }
2953
  }
2954
}
2955
2956
/*! \internal
2957
2958
  Places the maximum column widths and row heights into \a maxColWidths and \a
2959
  maxRowHeights respectively.
2960
2961
  The maximum height of a row is the smallest maximum height of any element in
2962
  that row. The maximum width of a column is the smallest maximum width of any
2963
  element in that column.
2964
2965
  This is a helper function for \ref updateLayout.
2966
2967
  \see getMinimumRowColSizes
2968
*/
2969
void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths,
2970
                                          QVector<int> *maxRowHeights) const {
2971
  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
2972
  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
2973
  for (int row = 0; row < rowCount(); ++row) {
2974
    for (int col = 0; col < columnCount(); ++col) {
2975
      if (mElements.at(row).at(col)) {
2976
        QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
2977
        QSize max = mElements.at(row).at(col)->maximumSize();
2978
        QSize final(
2979
            max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(),
2980
            max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
2981
        if (maxColWidths->at(col) > final.width())
2982
          (*maxColWidths)[col] = final.width();
2983
        if (maxRowHeights->at(row) > final.height())
2984
          (*maxRowHeights)[row] = final.height();
2985
      }
2986
    }
2987
  }
2988
}
2989
2990
////////////////////////////////////////////////////////////////////////////////////////////////////
2991
//////////////////// QCPLayoutInset
2992
////////////////////////////////////////////////////////////////////////////////////////////////////
2993
/*! \class QCPLayoutInset
2994
  \brief A layout that places child elements aligned to the border or
2995
  arbitrarily positioned
2996
2997
  Elements are placed either aligned to the border or at arbitrary position in
2998
  the area of the layout. Which placement applies is controlled with the \ref
2999
  InsetPlacement (\ref setInsetPlacement).
3000
3001
  Elements are added via \ref addElement(QCPLayoutElement *element,
3002
  Qt::Alignment alignment) or addElement(QCPLayoutElement *element, const QRectF
3003
  &rect). If the first method is used, the inset placement will default to \ref
3004
  ipBorderAligned and the element will be aligned according to the \a alignment
3005
  parameter. The second method defaults to \ref ipFree and allows placing
3006
  elements at arbitrary position and size, defined by \a rect.
3007
3008
  The alignment or rect can be set via \ref setInsetAlignment or \ref
3009
  setInsetRect, respectively.
3010
3011
  This is the layout that every QCPAxisRect has as \ref
3012
  QCPAxisRect::insetLayout.
3013
*/
3014
3015
/* start documentation of inline functions */
3016
3017
/*! \fn virtual void QCPLayoutInset::simplify()
3018
3019
  The QCPInsetLayout does not need simplification since it can never have empty
3020
  cells due to its linear index structure. This method does nothing.
3021
*/
3022
3023
/* end documentation of inline functions */
3024
3025
/*!
3026
  Creates an instance of QCPLayoutInset and sets default values.
3027
*/
3028
QCPLayoutInset::QCPLayoutInset() {}
3029
3030
QCPLayoutInset::~QCPLayoutInset() {
3031
  // clear all child layout elements. This is important because only the
3032
  // specific layouts know how to handle removing elements (clear calls virtual
3033
  // removeAt method to do that).
3034
  clear();
3035
}
3036
3037
/*!
3038
  Returns the placement type of the element with the specified \a index.
3039
*/
3040
QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const {
3041
  if (elementAt(index))
3042
    return mInsetPlacement.at(index);
3043
  else {
3044
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3045
    return ipFree;
3046
  }
3047
}
3048
3049
/*!
3050
  Returns the alignment of the element with the specified \a index. The
3051
  alignment only has a meaning, if the inset placement (\ref setInsetPlacement)
3052
  is \ref ipBorderAligned.
3053
*/
3054
Qt::Alignment QCPLayoutInset::insetAlignment(int index) const {
3055
  if (elementAt(index))
3056
    return mInsetAlignment.at(index);
3057
  else {
3058
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3059
    return 0;
3060
  }
3061
}
3062
3063
/*!
3064
  Returns the rect of the element with the specified \a index. The rect only has
3065
  a meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree.
3066
*/
3067
QRectF QCPLayoutInset::insetRect(int index) const {
3068
  if (elementAt(index))
3069
    return mInsetRect.at(index);
3070
  else {
3071
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3072
    return QRectF();
3073
  }
3074
}
3075
3076
/*!
3077
  Sets the inset placement type of the element with the specified \a index to \a
3078
  placement.
3079
3080
  \see InsetPlacement
3081
*/
3082
void QCPLayoutInset::setInsetPlacement(
3083
    int index, QCPLayoutInset::InsetPlacement placement) {
3084
  if (elementAt(index))
3085
    mInsetPlacement[index] = placement;
3086
  else
3087
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3088
}
3089
3090
/*!
3091
  If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this
3092
  function is used to set the alignment of the element with the specified \a
3093
  index to \a alignment.
3094
3095
  \a alignment is an or combination of the following alignment flags:
3096
  Qt::AlignLeft, Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop,
3097
  Qt::AlignVCenter, Qt::AlignBottom. Any other alignment flags will be ignored.
3098
*/
3099
void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment) {
3100
  if (elementAt(index))
3101
    mInsetAlignment[index] = alignment;
3102
  else
3103
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3104
}
3105
3106
/*!
3107
  If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function
3108
  is used to set the position and size of the element with the specified \a
3109
  index to \a rect.
3110
3111
  \a rect is given in fractions of the whole inset layout rect. So an inset with
3112
  rect (0, 0, 1, 1) will span the entire layout. An inset with rect (0.6, 0.1,
3113
  0.35, 0.35) will be in the top right corner of the layout, with 35% width and
3114
  height of the parent layout.
3115
3116
  Note that the minimum and maximum sizes of the embedded element (\ref
3117
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are
3118
  enforced.
3119
*/
3120
void QCPLayoutInset::setInsetRect(int index, const QRectF &rect) {
3121
  if (elementAt(index))
3122
    mInsetRect[index] = rect;
3123
  else
3124
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3125
}
3126
3127
/* inherits documentation from base class */
3128
void QCPLayoutInset::updateLayout() {
3129
  for (int i = 0; i < mElements.size(); ++i) {
3130
    QRect insetRect;
3131
    QSize finalMinSize, finalMaxSize;
3132
    QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3133
    QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3134
    finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0
3135
                              ? mElements.at(i)->minimumSize().width()
3136
                              : minSizeHint.width());
3137
    finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0
3138
                               ? mElements.at(i)->minimumSize().height()
3139
                               : minSizeHint.height());
3140
    finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() <
3141
                                  QWIDGETSIZE_MAX
3142
                              ? mElements.at(i)->maximumSize().width()
3143
                              : maxSizeHint.width());
3144
    finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() <
3145
                                   QWIDGETSIZE_MAX
3146
                               ? mElements.at(i)->maximumSize().height()
3147
                               : maxSizeHint.height());
3148
    if (mInsetPlacement.at(i) == ipFree) {
3149
      insetRect = QRect(rect().x() + rect().width() * mInsetRect.at(i).x(),
3150
                        rect().y() + rect().height() * mInsetRect.at(i).y(),
3151
                        rect().width() * mInsetRect.at(i).width(),
3152
                        rect().height() * mInsetRect.at(i).height());
3153
      if (insetRect.size().width() < finalMinSize.width())
3154
        insetRect.setWidth(finalMinSize.width());
3155
      if (insetRect.size().height() < finalMinSize.height())
3156
        insetRect.setHeight(finalMinSize.height());
3157
      if (insetRect.size().width() > finalMaxSize.width())
3158
        insetRect.setWidth(finalMaxSize.width());
3159
      if (insetRect.size().height() > finalMaxSize.height())
3160
        insetRect.setHeight(finalMaxSize.height());
3161
    } else if (mInsetPlacement.at(i) == ipBorderAligned) {
3162
      insetRect.setSize(finalMinSize);
3163
      Qt::Alignment al = mInsetAlignment.at(i);
3164
      if (al.testFlag(Qt::AlignLeft))
3165
        insetRect.moveLeft(rect().x());
3166
      else if (al.testFlag(Qt::AlignRight))
3167
        insetRect.moveRight(rect().x() + rect().width());
3168
      else
3169
        insetRect.moveLeft(rect().x() + rect().width() * 0.5 -
3170
                           finalMinSize.width() *
3171
                               0.5);  // default to Qt::AlignHCenter
3172
      if (al.testFlag(Qt::AlignTop))
3173
        insetRect.moveTop(rect().y());
3174
      else if (al.testFlag(Qt::AlignBottom))
3175
        insetRect.moveBottom(rect().y() + rect().height());
3176
      else
3177
        insetRect.moveTop(rect().y() + rect().height() * 0.5 -
3178
                          finalMinSize.height() *
3179
                              0.5);  // default to Qt::AlignVCenter
3180
    }
3181
    mElements.at(i)->setOuterRect(insetRect);
3182
  }
3183
}
3184
3185
/* inherits documentation from base class */
3186
int QCPLayoutInset::elementCount() const { return mElements.size(); }
3187
3188
/* inherits documentation from base class */
3189
QCPLayoutElement *QCPLayoutInset::elementAt(int index) const {
3190
  if (index >= 0 && index < mElements.size())
3191
    return mElements.at(index);
3192
  else
3193
    return 0;
3194
}
3195
3196
/* inherits documentation from base class */
3197
QCPLayoutElement *QCPLayoutInset::takeAt(int index) {
3198
  if (QCPLayoutElement *el = elementAt(index)) {
3199
    releaseElement(el);
3200
    mElements.removeAt(index);
3201
    mInsetPlacement.removeAt(index);
3202
    mInsetAlignment.removeAt(index);
3203
    mInsetRect.removeAt(index);
3204
    return el;
3205
  } else {
3206
    qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3207
    return 0;
3208
  }
3209
}
3210
3211
/* inherits documentation from base class */
3212
bool QCPLayoutInset::take(QCPLayoutElement *element) {
3213
  if (element) {
3214
    for (int i = 0; i < elementCount(); ++i) {
3215
      if (elementAt(i) == element) {
3216
        takeAt(i);
3217
        return true;
3218
      }
3219
    }
3220
    qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3221
  } else
3222
    qDebug() << Q_FUNC_INFO << "Can't take null element";
3223
  return false;
3224
}
3225
3226
/*!
3227
  The inset layout is sensitive to events only at areas where its (visible)
3228
  child elements are sensitive. If the selectTest method of any of the child
3229
  elements returns a positive number for \a pos, this method returns a value
3230
  corresponding to 0.99 times the parent plot's selection tolerance. The inset
3231
  layout is not selectable itself by default. So if \a onlySelectable is true,
3232
  -1.0 is returned.
3233
3234
  See \ref QCPLayerable::selectTest for a general explanation of this virtual
3235
  method.
3236
*/
3237
double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable,
3238
                                  QVariant *details) const {
3239
  Q_UNUSED(details)
3240
  if (onlySelectable) return -1;
3241
3242
  for (int i = 0; i < mElements.size(); ++i) {
3243
    // inset layout shall only return positive selectTest, if actually an inset
3244
    // object is at pos else it would block the entire underlying QCPAxisRect
3245
    // with its surface.
3246
    if (mElements.at(i)->realVisibility() &&
3247
        mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3248
      return mParentPlot->selectionTolerance() * 0.99;
3249
  }
3250
  return -1;
3251
}
3252
3253
/*!
3254
  Adds the specified \a element to the layout as an inset aligned at the border
3255
  (\ref setInsetAlignment is initialized with \ref ipBorderAligned). The
3256
  alignment is set to \a alignment.
3257
3258
  \a alignment is an or combination of the following alignment flags:
3259
  Qt::AlignLeft, Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop,
3260
  Qt::AlignVCenter, Qt::AlignBottom. Any other alignment flags will be ignored.
3261
3262
  \see addElement(QCPLayoutElement *element, const QRectF &rect)
3263
*/
3264
void QCPLayoutInset::addElement(QCPLayoutElement *element,
3265
                                Qt::Alignment alignment) {
3266
  if (element) {
3267
    if (element->layout())  // remove from old layout first
3268
      element->layout()->take(element);
3269
    mElements.append(element);
3270
    mInsetPlacement.append(ipBorderAligned);
3271
    mInsetAlignment.append(alignment);
3272
    mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3273
    adoptElement(element);
3274
  } else
3275
    qDebug() << Q_FUNC_INFO << "Can't add null element";
3276
}
3277
3278
/*!
3279
  Adds the specified \a element to the layout as an inset with free
3280
  positioning/sizing (\ref setInsetAlignment is initialized with \ref ipFree).
3281
  The position and size is set to \a rect.
3282
3283
  \a rect is given in fractions of the whole inset layout rect. So an inset with
3284
  rect (0, 0, 1, 1) will span the entire layout. An inset with rect (0.6, 0.1,
3285
  0.35, 0.35) will be in the top right corner of the layout, with 35% width and
3286
  height of the parent layout.
3287
3288
  \see addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3289
*/
3290
void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) {
3291
  if (element) {
3292
    if (element->layout())  // remove from old layout first
3293
      element->layout()->take(element);
3294
    mElements.append(element);
3295
    mInsetPlacement.append(ipFree);
3296
    mInsetAlignment.append(Qt::AlignRight | Qt::AlignTop);
3297
    mInsetRect.append(rect);
3298
    adoptElement(element);
3299
  } else
3300
    qDebug() << Q_FUNC_INFO << "Can't add null element";
3301
}
3302
3303
////////////////////////////////////////////////////////////////////////////////////////////////////
3304
//////////////////// QCPLineEnding
3305
////////////////////////////////////////////////////////////////////////////////////////////////////
3306
3307
/*! \class QCPLineEnding
3308
  \brief Handles the different ending decorations for line-like items
3309
3310
  \image html QCPLineEnding.png "The various ending styles currently supported"
3311
3312
  For every ending a line-like item has, an instance of this class exists. For
3313
  example, QCPItemLine has two endings which can be set with
3314
  QCPItemLine::setHead and QCPItemLine::setTail.
3315
3316
  The styles themselves are defined via the enum QCPLineEnding::EndingStyle.
3317
  Most decorations can be modified regarding width and length, see \ref setWidth
3318
  and \ref setLength. The direction of the ending decoration (e.g. direction an
3319
  arrow is pointing) is controlled by the line-like item. For example, when both
3320
  endings of a QCPItemLine are set to be arrows, they will point to opposite
3321
  directions, e.g. "outward". This can be changed by \ref setInverted, which
3322
  would make the respective arrow point inward.
3323
3324
  Note that due to the overloaded QCPLineEnding constructor, you may directly
3325
  specify a QCPLineEnding::EndingStyle where actually a QCPLineEnding is
3326
  expected, e.g. \snippet documentation/doc-code-snippets/mainwindow.cpp
3327
  qcplineending-sethead
3328
*/
3329
3330
/*!
3331
  Creates a QCPLineEnding instance with default values (style \ref esNone).
3332
*/
3333
QCPLineEnding::QCPLineEnding()
3334
    : mStyle(esNone), mWidth(8), mLength(10), mInverted(false) {}
3335
3336
/*!
3337
  Creates a QCPLineEnding instance with the specified values.
3338
*/
3339
QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width,
3340
                             double length, bool inverted)
3341
    : mStyle(style), mWidth(width), mLength(length), mInverted(inverted) {}
3342
3343
/*!
3344
  Sets the style of the ending decoration.
3345
*/
3346
void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style) {
3347
  mStyle = style;
3348
}
3349
3350
/*!
3351
  Sets the width of the ending decoration, if the style supports it. On arrows,
3352
  for example, the width defines the size perpendicular to the arrow's pointing
3353
  direction.
3354
3355
  \see setLength
3356
*/
3357
void QCPLineEnding::setWidth(double width) { mWidth = width; }
3358
3359
/*!
3360
  Sets the length of the ending decoration, if the style supports it. On arrows,
3361
  for example, the length defines the size in pointing direction.
3362
3363
  \see setWidth
3364
*/
3365
void QCPLineEnding::setLength(double length) { mLength = length; }
3366
3367
/*!
3368
  Sets whether the ending decoration shall be inverted. For example, an arrow
3369
  decoration will point inward when \a inverted is set to true.
3370
3371
  Note that also the \a width direction is inverted. For symmetrical ending
3372
  styles like arrows or discs, this doesn't make a difference. However,
3373
  asymmetric styles like \ref esHalfBar are affected by it, which can be used to
3374
  control to which side the half bar points to.
3375
*/
3376
void QCPLineEnding::setInverted(bool inverted) { mInverted = inverted; }
3377
3378
/*! \internal
3379
3380
  Returns the maximum pixel radius the ending decoration might cover, starting
3381
  from the position the decoration is drawn at (typically a line ending/\ref
3382
  QCPItemPosition of an item).
3383
3384
  This is relevant for clipping. Only omit painting of the decoration when the
3385
  position where the decoration is supposed to be drawn is farther away from the
3386
  clipping rect than the returned distance.
3387
*/
3388
double QCPLineEnding::boundingDistance() const {
3389
  switch (mStyle) {
3390
    case esNone:
3391
      return 0;
3392
3393
    case esFlatArrow:
3394
    case esSpikeArrow:
3395
    case esLineArrow:
3396
    case esSkewedBar:
3397
      return qSqrt(mWidth * mWidth +
3398
                   mLength * mLength);  // items that have width and length
3399
3400
    case esDisc:
3401
    case esSquare:
3402
    case esDiamond:
3403
    case esBar:
3404
    case esHalfBar:
3405
      return mWidth * 1.42;  // items that only have a width -> width*sqrt(2)
3406
  }
3407
  return 0;
3408
}
3409
3410
/*!
3411
  Starting from the origin of this line ending (which is style specific),
3412
  returns the length covered by the line ending symbol, in backward direction.
3413
3414
  For example, the \ref esSpikeArrow has a shorter real length than a \ref
3415
  esFlatArrow, even if both have the same \ref setLength value, because the
3416
  spike arrow has an inward curved back, which reduces the length along its
3417
  center axis (the drawing origin for arrows is at the tip).
3418
3419
  This function is used for precise, style specific placement of line endings,
3420
  for example in QCPAxes.
3421
*/
3422
double QCPLineEnding::realLength() const {
3423
  switch (mStyle) {
3424
    case esNone:
3425
    case esLineArrow:
3426
    case esSkewedBar:
3427
    case esBar:
3428
    case esHalfBar:
3429
      return 0;
3430
3431
    case esFlatArrow:
3432
      return mLength;
3433
3434
    case esDisc:
3435
    case esSquare:
3436
    case esDiamond:
3437
      return mWidth * 0.5;
3438
3439
    case esSpikeArrow:
3440
      return mLength * 0.8;
3441
  }
3442
  return 0;
3443
}
3444
3445
/*! \internal
3446
3447
  Draws the line ending with the specified \a painter at the position \a pos.
3448
  The direction of the line ending is controlled with \a dir.
3449
*/
3450
void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos,
3451
                         const QVector2D &dir) const {
3452
  if (mStyle == esNone) return;
3453
3454
  QVector2D lengthVec(dir.normalized());
3455
  if (lengthVec.isNull()) lengthVec = QVector2D(1, 0);
3456
  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3457
  lengthVec *= (float)(mLength * (mInverted ? -1 : 1));
3458
  widthVec *= (float)(mWidth * 0.5 * (mInverted ? -1 : 1));
3459
3460
  QPen penBackup = painter->pen();
3461
  QBrush brushBackup = painter->brush();
3462
  QPen miterPen = penBackup;
3463
  miterPen.setJoinStyle(Qt::MiterJoin);  // to make arrow heads spikey
3464
  QBrush brush(painter->pen().color(), Qt::SolidPattern);
3465
  switch (mStyle) {
3466
    case esNone:
3467
      break;
3468
    case esFlatArrow: {
3469
      QPointF points[3] = {pos.toPointF(),
3470
                           (pos - lengthVec + widthVec).toPointF(),
3471
                           (pos - lengthVec - widthVec).toPointF()};
3472
      painter->setPen(miterPen);
3473
      painter->setBrush(brush);
3474
      painter->drawConvexPolygon(points, 3);
3475
      painter->setBrush(brushBackup);
3476
      painter->setPen(penBackup);
3477
      break;
3478
    }
3479
    case esSpikeArrow: {
3480
      QPointF points[4] = {pos.toPointF(),
3481
                           (pos - lengthVec + widthVec).toPointF(),
3482
                           (pos - lengthVec * 0.8f).toPointF(),
3483
                           (pos - lengthVec - widthVec).toPointF()};
3484
      painter->setPen(miterPen);
3485
      painter->setBrush(brush);
3486
      painter->drawConvexPolygon(points, 4);
3487
      painter->setBrush(brushBackup);
3488
      painter->setPen(penBackup);
3489
      break;
3490
    }
3491
    case esLineArrow: {
3492
      QPointF points[3] = {(pos - lengthVec + widthVec).toPointF(),
3493
                           pos.toPointF(),
3494
                           (pos - lengthVec - widthVec).toPointF()};
3495
      painter->setPen(miterPen);
3496
      painter->drawPolyline(points, 3);
3497
      painter->setPen(penBackup);
3498
      break;
3499
    }
3500
    case esDisc: {
3501
      painter->setBrush(brush);
3502
      painter->drawEllipse(pos.toPointF(), mWidth * 0.5, mWidth * 0.5);
3503
      painter->setBrush(brushBackup);
3504
      break;
3505
    }
3506
    case esSquare: {
3507
      QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3508
      QPointF points[4] = {(pos - widthVecPerp + widthVec).toPointF(),
3509
                           (pos - widthVecPerp - widthVec).toPointF(),
3510
                           (pos + widthVecPerp - widthVec).toPointF(),
3511
                           (pos + widthVecPerp + widthVec).toPointF()};
3512
      painter->setPen(miterPen);
3513
      painter->setBrush(brush);
3514
      painter->drawConvexPolygon(points, 4);
3515
      painter->setBrush(brushBackup);
3516
      painter->setPen(penBackup);
3517
      break;
3518
    }
3519
    case esDiamond: {
3520
      QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3521
      QPointF points[4] = {
3522
          (pos - widthVecPerp).toPointF(), (pos - widthVec).toPointF(),
3523
          (pos + widthVecPerp).toPointF(), (pos + widthVec).toPointF()};
3524
      painter->setPen(miterPen);
3525
      painter->setBrush(brush);
3526
      painter->drawConvexPolygon(points, 4);
3527
      painter->setBrush(brushBackup);
3528
      painter->setPen(penBackup);
3529
      break;
3530
    }
3531
    case esBar: {
3532
      painter->drawLine((pos + widthVec).toPointF(),
3533
                        (pos - widthVec).toPointF());
3534
      break;
3535
    }
3536
    case esHalfBar: {
3537
      painter->drawLine((pos + widthVec).toPointF(), pos.toPointF());
3538
      break;
3539
    }
3540
    case esSkewedBar: {
3541
      if (qFuzzyIsNull(painter->pen().widthF()) &&
3542
          !painter->modes().testFlag(QCPPainter::pmNonCosmetic)) {
3543
        // if drawing with cosmetic pen (perfectly thin stroke, happens only in
3544
        // vector exports), draw bar exactly on tip of line
3545
        painter->drawLine(
3546
            (pos + widthVec + lengthVec * 0.2f * (mInverted ? -1 : 1))
3547
                .toPointF(),
3548
            (pos - widthVec - lengthVec * 0.2f * (mInverted ? -1 : 1))
3549
                .toPointF());
3550
      } else {
3551
        // if drawing with thick (non-cosmetic) pen, shift bar a little in line
3552
        // direction to prevent line from sticking through bar slightly
3553
        painter->drawLine(
3554
            (pos + widthVec + lengthVec * 0.2f * (mInverted ? -1 : 1) +
3555
             dir.normalized() * qMax(1.0f, (float)painter->pen().widthF()) *
3556
                 0.5f)
3557
                .toPointF(),
3558
            (pos - widthVec - lengthVec * 0.2f * (mInverted ? -1 : 1) +
3559
             dir.normalized() * qMax(1.0f, (float)painter->pen().widthF()) *
3560
                 0.5f)
3561
                .toPointF());
3562
      }
3563
      break;
3564
    }
3565
  }
3566
}
3567
3568
/*! \internal
3569
  \overload
3570
3571
  Draws the line ending. The direction is controlled with the \a angle parameter
3572
  in radians.
3573
*/
3574
void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos,
3575
                         double angle) const {
3576
  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3577
}
3578
3579
////////////////////////////////////////////////////////////////////////////////////////////////////
3580
//////////////////// QCPGrid
3581
////////////////////////////////////////////////////////////////////////////////////////////////////
3582
3583
/*! \class QCPGrid
3584
  \brief Responsible for drawing the grid of a QCPAxis.
3585
3586
  This class is tightly bound to QCPAxis. Every axis owns a grid instance and
3587
  uses it to draw the grid lines, sub grid lines and zero-line. You can interact
3588
  with the grid of an axis via \ref QCPAxis::grid. Normally, you don't need to
3589
  create an instance of QCPGrid yourself.
3590
3591
  The axis and grid drawing was split into two classes to allow them to be
3592
  placed on different layers (both QCPAxis and QCPGrid inherit from
3593
  QCPLayerable). Thus it is possible to have the grid in the background and the
3594
  axes in the foreground, and any plottables/items in between. This described
3595
  situation is the default setup, see the QCPLayer documentation.
3596
*/
3597
3598
/*!
3599
  Creates a QCPGrid instance and sets default values.
3600
3601
  You shouldn't instantiate grids on their own, since every QCPAxis brings its
3602
  own QCPGrid.
3603
*/
3604
QCPGrid::QCPGrid(QCPAxis *parentAxis)
3605
    : QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3606
      mParentAxis(parentAxis) {
3607
  // warning: this is called in QCPAxis constructor, so parentAxis members
3608
  // should not be accessed/called
3609
  setParent(parentAxis);
3610
  setPen(QPen(QColor(200, 200, 200), 0, Qt::DotLine));
3611
  setSubGridPen(QPen(QColor(220, 220, 220), 0, Qt::DotLine));
3612
  setZeroLinePen(QPen(QColor(200, 200, 200), 0, Qt::SolidLine));
3613
  setSubGridVisible(false);
3614
  setAntialiased(false);
3615
  setAntialiasedSubGrid(false);
3616
  setAntialiasedZeroLine(false);
3617
}
3618
3619
/*!
3620
  Sets whether grid lines at sub tick marks are drawn.
3621
3622
  \see setSubGridPen
3623
*/
3624
void QCPGrid::setSubGridVisible(bool visible) { mSubGridVisible = visible; }
3625
3626
/*!
3627
  Sets whether sub grid lines are drawn antialiased.
3628
*/
3629
void QCPGrid::setAntialiasedSubGrid(bool enabled) {
3630
  mAntialiasedSubGrid = enabled;
3631
}
3632
3633
/*!
3634
  Sets whether zero lines are drawn antialiased.
3635
*/
3636
void QCPGrid::setAntialiasedZeroLine(bool enabled) {
3637
  mAntialiasedZeroLine = enabled;
3638
}
3639
3640
/*!
3641
  Sets the pen with which (major) grid lines are drawn.
3642
*/
3643
void QCPGrid::setPen(const QPen &pen) { mPen = pen; }
3644
3645
/*!
3646
  Sets the pen with which sub grid lines are drawn.
3647
*/
3648
void QCPGrid::setSubGridPen(const QPen &pen) { mSubGridPen = pen; }
3649
3650
/*!
3651
  Sets the pen with which zero lines are drawn.
3652
3653
  Zero lines are lines at value coordinate 0 which may be drawn with a different
3654
  pen than other grid lines. To disable zero lines and just draw normal grid
3655
  lines at zero, set \a pen to Qt::NoPen.
3656
*/
3657
void QCPGrid::setZeroLinePen(const QPen &pen) { mZeroLinePen = pen; }
3658
3659
/*! \internal
3660
3661
  A convenience function to easily set the QPainter::Antialiased hint on the
3662
  provided \a painter before drawing the major grid lines.
3663
3664
  This is the antialiasing state the painter passed to the \ref draw method is
3665
  in by default.
3666
3667
  This function takes into account the local setting of the antialiasing flag as
3668
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
3669
  \ref QCustomPlot::setNotAntialiasedElements.
3670
3671
  \see setAntialiased
3672
*/
3673
void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const {
3674
  applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
3675
}
3676
3677
/*! \internal
3678
3679
  Draws grid lines and sub grid lines at the positions of (sub) ticks of the
3680
  parent axis, spanning over the complete axis rect. Also draws the zero line,
3681
  if appropriate (\ref setZeroLinePen).
3682
*/
3683
void QCPGrid::draw(QCPPainter *painter) {
3684
  if (!mParentAxis) {
3685
    qDebug() << Q_FUNC_INFO << "invalid parent axis";
3686
    return;
3687
  }
3688
3689
  if (mSubGridVisible) drawSubGridLines(painter);
3690
  drawGridLines(painter);
3691
}
3692
3693
/*! \internal
3694
3695
  Draws the main grid lines and possibly a zero line with the specified painter.
3696
3697
  This is a helper function called by \ref draw.
3698
*/
3699
void QCPGrid::drawGridLines(QCPPainter *painter) const {
3700
  if (!mParentAxis) {
3701
    qDebug() << Q_FUNC_INFO << "invalid parent axis";
3702
    return;
3703
  }
3704
3705
  int lowTick = mParentAxis->mLowestVisibleTick;
3706
  int highTick = mParentAxis->mHighestVisibleTick;
3707
  double t;  // helper variable, result of coordinate-to-pixel transforms
3708
  if (mParentAxis->orientation() == Qt::Horizontal) {
3709
    // draw zeroline:
3710
    int zeroLineIndex = -1;
3711
    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 &&
3712
        mParentAxis->mRange.upper > 0) {
3713
      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3714
      painter->setPen(mZeroLinePen);
3715
      double epsilon =
3716
          mParentAxis->range().size() * 1E-6;  // for comparing double to zero
3717
      for (int i = lowTick; i <= highTick; ++i) {
3718
        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
3719
          zeroLineIndex = i;
3720
          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // x
3721
          painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
3722
                                   mParentAxis->mAxisRect->top()));
3723
          break;
3724
        }
3725
      }
3726
    }
3727
    // draw grid lines:
3728
    applyDefaultAntialiasingHint(painter);
3729
    painter->setPen(mPen);
3730
    for (int i = lowTick; i <= highTick; ++i) {
3731
      if (i == zeroLineIndex)
3732
        continue;  // don't draw a gridline on top of the zeroline
3733
      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // x
3734
      painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
3735
                               mParentAxis->mAxisRect->top()));
3736
    }
3737
  } else {
3738
    // draw zeroline:
3739
    int zeroLineIndex = -1;
3740
    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 &&
3741
        mParentAxis->mRange.upper > 0) {
3742
      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3743
      painter->setPen(mZeroLinePen);
3744
      double epsilon =
3745
          mParentAxis->mRange.size() * 1E-6;  // for comparing double to zero
3746
      for (int i = lowTick; i <= highTick; ++i) {
3747
        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
3748
          zeroLineIndex = i;
3749
          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // y
3750
          painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
3751
                                   mParentAxis->mAxisRect->right(), t));
3752
          break;
3753
        }
3754
      }
3755
    }
3756
    // draw grid lines:
3757
    applyDefaultAntialiasingHint(painter);
3758
    painter->setPen(mPen);
3759
    for (int i = lowTick; i <= highTick; ++i) {
3760
      if (i == zeroLineIndex)
3761
        continue;  // don't draw a gridline on top of the zeroline
3762
      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // y
3763
      painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
3764
                               mParentAxis->mAxisRect->right(), t));
3765
    }
3766
  }
3767
}
3768
3769
/*! \internal
3770
3771
  Draws the sub grid lines with the specified painter.
3772
3773
  This is a helper function called by \ref draw.
3774
*/
3775
void QCPGrid::drawSubGridLines(QCPPainter *painter) const {
3776
  if (!mParentAxis) {
3777
    qDebug() << Q_FUNC_INFO << "invalid parent axis";
3778
    return;
3779
  }
3780
3781
  applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
3782
  double t;  // helper variable, result of coordinate-to-pixel transforms
3783
  painter->setPen(mSubGridPen);
3784
  if (mParentAxis->orientation() == Qt::Horizontal) {
3785
    for (int i = 0; i < mParentAxis->mSubTickVector.size(); ++i) {
3786
      t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i));  // x
3787
      painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
3788
                               mParentAxis->mAxisRect->top()));
3789
    }
3790
  } else {
3791
    for (int i = 0; i < mParentAxis->mSubTickVector.size(); ++i) {
3792
      t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i));  // y
3793
      painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
3794
                               mParentAxis->mAxisRect->right(), t));
3795
    }
3796
  }
3797
}
3798
3799
////////////////////////////////////////////////////////////////////////////////////////////////////
3800
//////////////////// QCPAxis
3801
////////////////////////////////////////////////////////////////////////////////////////////////////
3802
3803
/*! \class QCPAxis
3804
  \brief Manages a single axis inside a QCustomPlot.
3805
3806
  Usually doesn't need to be instantiated externally. Access %QCustomPlot's
3807
  default four axes via QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left),
3808
  QCustomPlot::xAxis2 (top) and QCustomPlot::yAxis2 (right).
3809
3810
  Axes are always part of an axis rect, see QCPAxisRect.
3811
  \image html AxisNamesOverview.png
3812
  <center>Naming convention of axis parts</center>
3813
  \n
3814
3815
  \image html AxisRectSpacingOverview.png
3816
  <center>Overview of the spacings and paddings that define the geometry of an
3817
  axis. The dashed gray line on the left represents the QCustomPlot widget
3818
  border.</center>
3819
3820
*/
3821
3822
/* start of documentation of inline functions */
3823
3824
/*! \fn Qt::Orientation QCPAxis::orientation() const
3825
3826
  Returns the orientation of this axis. The axis orientation (horizontal or
3827
  vertical) is deduced from the axis type (left, top, right or bottom).
3828
3829
  \see orientation(AxisType type)
3830
*/
3831
3832
/*! \fn QCPGrid *QCPAxis::grid() const
3833
3834
  Returns the \ref QCPGrid instance belonging to this axis. Access it to set
3835
  details about the way the grid is displayed.
3836
*/
3837
3838
/*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type)
3839
3840
  Returns the orientation of the specified axis type
3841
3842
  \see orientation()
3843
*/
3844
3845
/* end of documentation of inline functions */
3846
/* start of documentation of signals */
3847
3848
/*! \fn void QCPAxis::ticksRequest()
3849
3850
  This signal is emitted when \ref setAutoTicks is false and the axis is about
3851
  to generate tick labels for a replot.
3852
3853
  Modifying the tick positions can be done with \ref setTickVector. If you also
3854
  want to control the tick labels, set \ref setAutoTickLabels to false and also
3855
  provide the labels with \ref setTickVectorLabels.
3856
3857
  If you only want static ticks you probably don't need this signal, since you
3858
  can just set the tick vector (and possibly tick label vector) once. However,
3859
  if you want to provide ticks (and maybe labels) dynamically, e.g. depending on
3860
  the current axis range, connect a slot to this signal and set the
3861
  vector/vectors there.
3862
*/
3863
3864
/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange)
3865
3866
  This signal is emitted when the range of this axis has changed. You can
3867
  connect it to the \ref setRange slot of another axis to communicate the new
3868
  range to the other axis, in order for it to be synchronized.
3869
3870
  You may also manipulate/correct the range with \ref setRange in a slot
3871
  connected to this signal. This is useful if for example a maximum range span
3872
  shall not be exceeded, or if the lower/upper range shouldn't go beyond certain
3873
  values. For example, the following slot would limit the x axis to only
3874
  positive ranges: \code if (newRange.lower < 0) plot->xAxis->setRange(0,
3875
  newRange.size()); \endcode
3876
*/
3877
3878
/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange
3879
  &oldRange) \overload
3880
3881
  Additionally to the new range, this signal also provides the previous range
3882
  held by the axis as \a oldRange.
3883
*/
3884
3885
/*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);
3886
3887
  This signal is emitted when the scale type changes, by calls to \ref
3888
  setScaleType
3889
*/
3890
3891
/*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
3892
3893
  This signal is emitted when the selection state of this axis has changed,
3894
  either by user interaction or by a direct call to \ref setSelectedParts.
3895
*/
3896
3897
/*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);
3898
3899
  This signal is emitted when the selectability changes, by calls to \ref
3900
  setSelectableParts
3901
*/
3902
3903
/* end of documentation of signals */
3904
3905
/*!
3906
  Constructs an Axis instance of Type \a type for the axis rect \a parent.
3907
3908
  Usually it isn't necessary to instantiate axes directly, because you can let
3909
  QCustomPlot create them for you with \ref QCPAxisRect::addAxis. If you want to
3910
  use own QCPAxis-subclasses however, create them manually and then inject them
3911
  also via \ref QCPAxisRect::addAxis.
3912
*/
3913
QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type)
3914
    : QCPLayerable(parent->parentPlot(), QString(), parent),
3915
      // axis base:
3916
      mAxisType(type),
3917
      mAxisRect(parent),
3918
      mPadding(5),
3919
      mOrientation(orientation(type)),
3920
      mSelectableParts(spAxis | spTickLabels | spAxisLabel),
3921
      mSelectedParts(spNone),
3922
      mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
3923
      mSelectedBasePen(QPen(Qt::blue, 2)),
3924
      // axis label:
3925
      mLabel(),
3926
      mLabelFont(mParentPlot->font()),
3927
      mSelectedLabelFont(
3928
          QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
3929
      mLabelColor(Qt::black),
3930
      mSelectedLabelColor(Qt::blue),
3931
      // tick labels:
3932
      mTickLabels(true),
3933
      mAutoTickLabels(true),
3934
      mTickLabelType(ltNumber),
3935
      mTickLabelFont(mParentPlot->font()),
3936
      mSelectedTickLabelFont(QFont(mTickLabelFont.family(),
3937
                                   mTickLabelFont.pointSize(), QFont::Bold)),
3938
      mTickLabelColor(Qt::black),
3939
      mSelectedTickLabelColor(Qt::blue),
3940
      mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
3941
      mDateTimeSpec(Qt::LocalTime),
3942
      mNumberPrecision(6),
3943
      mNumberFormatChar('g'),
3944
      mNumberBeautifulPowers(true),
3945
      // ticks and subticks:
3946
      mTicks(true),
3947
      mTickStep(1),
3948
      mSubTickCount(4),
3949
      mAutoTickCount(6),
3950
      mAutoTicks(true),
3951
      mAutoTickStep(true),
3952
      mAutoSubTicks(true),
3953
      mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
3954
      mSelectedTickPen(QPen(Qt::blue, 2)),
3955
      mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
3956
      mSelectedSubTickPen(QPen(Qt::blue, 2)),
3957
      // scale and range:
3958
      mRange(0, 5),
3959
      mRangeReversed(false),
3960
      mScaleType(stLinear),
3961
      mScaleLogBase(10),
3962
      mScaleLogBaseLogInv(1.0 / qLn(mScaleLogBase)),
3963
      // internal members:
3964
      mGrid(new QCPGrid(this)),
3965
      mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
3966
      mLowestVisibleTick(0),
3967
      mHighestVisibleTick(-1),
3968
      mCachedMarginValid(false),
3969
      mCachedMargin(0) {
3970
  setParent(parent);
3971
  mGrid->setVisible(false);
3972
  setAntialiased(false);
3973
  setLayer(
3974
      mParentPlot->currentLayer());  // it's actually on that layer already, but
3975
                                     // we want it in front of the grid, so we
3976
                                     // place it on there again
3977
3978
  if (type == atTop) {
3979
    setTickLabelPadding(3);
3980
    setLabelPadding(6);
3981
  } else if (type == atRight) {
3982
    setTickLabelPadding(7);
3983
    setLabelPadding(12);
3984
  } else if (type == atBottom) {
3985
    setTickLabelPadding(3);
3986
    setLabelPadding(3);
3987
  } else if (type == atLeft) {
3988
    setTickLabelPadding(5);
3989
    setLabelPadding(10);
3990
  }
3991
}
3992
3993
QCPAxis::~QCPAxis() {
3994
  delete mAxisPainter;
3995
  delete mGrid;  // delete grid here instead of via parent ~QObject for better
3996
                 // defined deletion order
3997
}
3998
3999
/* No documentation as it is a property getter */
4000
int QCPAxis::tickLabelPadding() const { return mAxisPainter->tickLabelPadding; }
4001
4002
/* No documentation as it is a property getter */
4003
double QCPAxis::tickLabelRotation() const {
4004
  return mAxisPainter->tickLabelRotation;
4005
}
4006
4007
/* No documentation as it is a property getter */
4008
QCPAxis::LabelSide QCPAxis::tickLabelSide() const {
4009
  return mAxisPainter->tickLabelSide;
4010
}
4011
4012
/* No documentation as it is a property getter */
4013
QString QCPAxis::numberFormat() const {
4014
  QString result;
4015
  result.append(mNumberFormatChar);
4016
  if (mNumberBeautifulPowers) {
4017
    result.append(QLatin1Char('b'));
4018
    if (mAxisPainter->numberMultiplyCross) result.append(QLatin1Char('c'));
4019
  }
4020
  return result;
4021
}
4022
4023
/* No documentation as it is a property getter */
4024
int QCPAxis::tickLengthIn() const { return mAxisPainter->tickLengthIn; }
4025
4026
/* No documentation as it is a property getter */
4027
int QCPAxis::tickLengthOut() const { return mAxisPainter->tickLengthOut; }
4028
4029
/* No documentation as it is a property getter */
4030
int QCPAxis::subTickLengthIn() const { return mAxisPainter->subTickLengthIn; }
4031
4032
/* No documentation as it is a property getter */
4033
int QCPAxis::subTickLengthOut() const { return mAxisPainter->subTickLengthOut; }
4034
4035
/* No documentation as it is a property getter */
4036
int QCPAxis::labelPadding() const { return mAxisPainter->labelPadding; }
4037
4038
/* No documentation as it is a property getter */
4039
int QCPAxis::offset() const { return mAxisPainter->offset; }
4040
4041
/* No documentation as it is a property getter */
4042
QCPLineEnding QCPAxis::lowerEnding() const { return mAxisPainter->lowerEnding; }
4043
4044
/* No documentation as it is a property getter */
4045
QCPLineEnding QCPAxis::upperEnding() const { return mAxisPainter->upperEnding; }
4046
4047
/*!
4048
  Sets whether the axis uses a linear scale or a logarithmic scale. If \a type
4049
  is set to \ref stLogarithmic, the logarithm base can be set with \ref
4050
  setScaleLogBase. In logarithmic axis scaling, major tick marks appear at all
4051
  powers of the logarithm base. Properties like tick step
4052
  (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal
4053
  base but less major ticks, consider choosing a logarithm base of 100, 1000 or
4054
  even higher.
4055
4056
  If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat)
4057
  uses the 'b' option (beautifully typeset decimal powers), the display usually
4058
  is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for
4059
  logarithmic scaling (the "1 [multiplication sign]" part). To only display the
4060
  decimal power, set the number precision to zero with \ref setNumberPrecision.
4061
*/
4062
void QCPAxis::setScaleType(QCPAxis::ScaleType type) {
4063
  if (mScaleType != type) {
4064
    mScaleType = type;
4065
    if (mScaleType == stLogarithmic) setRange(mRange.sanitizedForLogScale());
4066
    mCachedMarginValid = false;
4067
    emit scaleTypeChanged(mScaleType);
4068
  }
4069
}
4070
4071
/*!
4072
  If \ref setScaleType is set to \ref stLogarithmic, \a base will be the
4073
  logarithm base of the scaling. In logarithmic axis scaling, major tick marks
4074
  appear at all powers of \a base.
4075
4076
  Properties like tick step (\ref setTickStep) don't apply in logarithmic
4077
  scaling. If you wish a decimal base but less major ticks, consider choosing \a
4078
  base 100, 1000 or even higher.
4079
*/
4080
void QCPAxis::setScaleLogBase(double base) {
4081
  if (base > 1) {
4082
    mScaleLogBase = base;
4083
    mScaleLogBaseLogInv =
4084
        1.0 / qLn(mScaleLogBase);  // buffer for faster baseLog() calculation
4085
    mCachedMarginValid = false;
4086
  } else
4087
    qDebug() << Q_FUNC_INFO
4088
             << "Invalid logarithmic scale base (must be greater 1):" << base;
4089
}
4090
4091
/*!
4092
  Sets the range of the axis.
4093
4094
  This slot may be connected with the \ref rangeChanged signal of another axis
4095
  so this axis is always synchronized with the other axis range, when it
4096
  changes.
4097
4098
  To invert the direction of an axis, use \ref setRangeReversed.
4099
*/
4100
void QCPAxis::setRange(const QCPRange &range) {
4101
  if (range.lower == mRange.lower && range.upper == mRange.upper) return;
4102
4103
  if (!QCPRange::validRange(range)) return;
4104
  QCPRange oldRange = mRange;
4105
  if (mScaleType == stLogarithmic) {
4106
    mRange = range.sanitizedForLogScale();
4107
  } else {
4108
    mRange = range.sanitizedForLinScale();
4109
  }
4110
  mCachedMarginValid = false;
4111
  emit rangeChanged(mRange);
4112
  emit rangeChanged(mRange, oldRange);
4113
}
4114
4115
/*!
4116
  Sets whether the user can (de-)select the parts in \a selectable by clicking
4117
  on the QCustomPlot surface. (When \ref QCustomPlot::setInteractions contains
4118
  iSelectAxes.)
4119
4120
  However, even when \a selectable is set to a value not allowing the selection
4121
  of a specific part, it is still possible to set the selection of this part
4122
  manually, by calling \ref setSelectedParts directly.
4123
4124
  \see SelectablePart, setSelectedParts
4125
*/
4126
void QCPAxis::setSelectableParts(const SelectableParts &selectable) {
4127
  if (mSelectableParts != selectable) {
4128
    mSelectableParts = selectable;
4129
    emit selectableChanged(mSelectableParts);
4130
  }
4131
}
4132
4133
/*!
4134
  Sets the selected state of the respective axis parts described by \ref
4135
  SelectablePart. When a part is selected, it uses a different pen/font.
4136
4137
  The entire selection mechanism for axes is handled automatically when \ref
4138
  QCustomPlot::setInteractions contains iSelectAxes. You only need to call this
4139
  function when you wish to change the selection state manually.
4140
4141
  This function can change the selection state of a part, independent of the
4142
  \ref setSelectableParts setting.
4143
4144
  emits the \ref selectionChanged signal when \a selected is different from the
4145
  previous selection state.
4146
4147
  \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen,
4148
  setSelectedTickPen, setSelectedSubTickPen, setSelectedTickLabelFont,
4149
  setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
4150
*/
4151
void QCPAxis::setSelectedParts(const SelectableParts &selected) {
4152
  if (mSelectedParts != selected) {
4153
    mSelectedParts = selected;
4154
    emit selectionChanged(mSelectedParts);
4155
  }
4156
}
4157
4158
/*!
4159
  \overload
4160
4161
  Sets the lower and upper bound of the axis range.
4162
4163
  To invert the direction of an axis, use \ref setRangeReversed.
4164
4165
  There is also a slot to set a range, see \ref setRange(const QCPRange &range).
4166
*/
4167
void QCPAxis::setRange(double lower, double upper) {
4168
  if (lower == mRange.lower && upper == mRange.upper) return;
4169
4170
  if (!QCPRange::validRange(lower, upper)) return;
4171
  QCPRange oldRange = mRange;
4172
  mRange.lower = lower;
4173
  mRange.upper = upper;
4174
  if (mScaleType == stLogarithmic) {
4175
    mRange = mRange.sanitizedForLogScale();
4176
  } else {
4177
    mRange = mRange.sanitizedForLinScale();
4178
  }
4179
  mCachedMarginValid = false;
4180
  emit rangeChanged(mRange);
4181
  emit rangeChanged(mRange, oldRange);
4182
}
4183
4184
/*!
4185
  \overload
4186
4187
  Sets the range of the axis.
4188
4189
  The \a position coordinate indicates together with the \a alignment parameter,
4190
  where the new range will be positioned. \a size defines the size of the new
4191
  axis range. \a alignment may be Qt::AlignLeft, Qt::AlignRight or
4192
  Qt::AlignCenter. This will cause the left border, right border, or center of
4193
  the range to be aligned with \a position. Any other values of \a alignment
4194
  will default to Qt::AlignCenter.
4195
*/
4196
void QCPAxis::setRange(double position, double size,
4197
                       Qt::AlignmentFlag alignment) {
4198
  if (alignment == Qt::AlignLeft)
4199
    setRange(position, position + size);
4200
  else if (alignment == Qt::AlignRight)
4201
    setRange(position - size, position);
4202
  else  // alignment == Qt::AlignCenter
4203
    setRange(position - size / 2.0, position + size / 2.0);
4204
}
4205
4206
/*!
4207
  Sets the lower bound of the axis range. The upper bound is not changed.
4208
  \see setRange
4209
*/
4210
void QCPAxis::setRangeLower(double lower) {
4211
  if (mRange.lower == lower) return;
4212
4213
  QCPRange oldRange = mRange;
4214
  mRange.lower = lower;
4215
  if (mScaleType == stLogarithmic) {
4216
    mRange = mRange.sanitizedForLogScale();
4217
  } else {
4218
    mRange = mRange.sanitizedForLinScale();
4219
  }
4220
  mCachedMarginValid = false;
4221
  emit rangeChanged(mRange);
4222
  emit rangeChanged(mRange, oldRange);
4223
}
4224
4225
/*!
4226
  Sets the upper bound of the axis range. The lower bound is not changed.
4227
  \see setRange
4228
*/
4229
void QCPAxis::setRangeUpper(double upper) {
4230
  if (mRange.upper == upper) return;
4231
4232
  QCPRange oldRange = mRange;
4233
  mRange.upper = upper;
4234
  if (mScaleType == stLogarithmic) {
4235
    mRange = mRange.sanitizedForLogScale();
4236
  } else {
4237
    mRange = mRange.sanitizedForLinScale();
4238
  }
4239
  mCachedMarginValid = false;
4240
  emit rangeChanged(mRange);
4241
  emit rangeChanged(mRange, oldRange);
4242
}
4243
4244
/*!
4245
  Sets whether the axis range (direction) is displayed reversed. Normally, the
4246
  values on horizontal axes increase left to right, on vertical axes bottom to
4247
  top. When \a reversed is set to true, the direction of increasing values is
4248
  inverted.
4249
4250
  Note that the range and data interface stays the same for reversed axes, e.g.
4251
  the \a lower part of the \ref setRange interface will still reference the
4252
  mathematically smaller number than the \a upper part.
4253
*/
4254
void QCPAxis::setRangeReversed(bool reversed) {
4255
  if (mRangeReversed != reversed) {
4256
    mRangeReversed = reversed;
4257
    mCachedMarginValid = false;
4258
  }
4259
}
4260
4261
/*!
4262
  Sets whether the tick positions should be calculated automatically (either
4263
  from an automatically generated tick step or a tick step provided manually via
4264
  \ref setTickStep, see \ref setAutoTickStep).
4265
4266
  If \a on is set to false, you must provide the tick positions manually via
4267
  \ref setTickVector. For these manual ticks you may let QCPAxis generate the
4268
  appropriate labels automatically by leaving \ref setAutoTickLabels set to
4269
  true. If you also wish to control the displayed labels manually, set \ref
4270
  setAutoTickLabels to false and provide the label strings with \ref
4271
  setTickVectorLabels.
4272
4273
  If you need dynamically calculated tick vectors (and possibly tick label
4274
  vectors), set the vectors in a slot connected to the \ref ticksRequest signal.
4275
4276
  \see setAutoTickLabels, setAutoSubTicks, setAutoTickCount, setAutoTickStep
4277
*/
4278
void QCPAxis::setAutoTicks(bool on) {
4279
  if (mAutoTicks != on) {
4280
    mAutoTicks = on;
4281
    mCachedMarginValid = false;
4282
  }
4283
}
4284
4285
/*!
4286
  When \ref setAutoTickStep is true, \a approximateCount determines how many
4287
  ticks should be generated in the visible range, approximately.
4288
4289
  It's not guaranteed that this number of ticks is met exactly, but
4290
  approximately within a tolerance of about two.
4291
4292
  Only values greater than zero are accepted as \a approximateCount.
4293
4294
  \see setAutoTickStep, setAutoTicks, setAutoSubTicks
4295
*/
4296
void QCPAxis::setAutoTickCount(int approximateCount) {
4297
  if (mAutoTickCount != approximateCount) {
4298
    if (approximateCount > 0) {
4299
      mAutoTickCount = approximateCount;
4300
      mCachedMarginValid = false;
4301
    } else
4302
      qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:"
4303
               << approximateCount;
4304
  }
4305
}
4306
4307
/*!
4308
  Sets whether the tick labels are generated automatically. Depending on the
4309
  tick label type (\ref ltNumber or \ref ltDateTime), the labels will either
4310
  show the coordinate as floating point number (\ref setNumberFormat), or a
4311
  date/time formatted according to \ref setDateTimeFormat.
4312
4313
  If \a on is set to false, you should provide the tick labels via \ref
4314
  setTickVectorLabels. This is usually used in a combination with \ref
4315
  setAutoTicks set to false for complete control over tick positions and labels,
4316
  e.g. when the ticks should be at multiples of pi and show "2pi", "3pi" etc. as
4317
  tick labels.
4318
4319
  If you need dynamically calculated tick vectors (and possibly tick label
4320
  vectors), set the vectors in a slot connected to the \ref ticksRequest signal.
4321
4322
  \see setAutoTicks
4323
*/
4324
void QCPAxis::setAutoTickLabels(bool on) {
4325
  if (mAutoTickLabels != on) {
4326
    mAutoTickLabels = on;
4327
    mCachedMarginValid = false;
4328
  }
4329
}
4330
4331
/*!
4332
  Sets whether the tick step, i.e. the interval between two (major) ticks, is
4333
  calculated automatically. If \a on is set to true, the axis finds a tick step
4334
  that is reasonable for human readable plots.
4335
4336
  The number of ticks the algorithm aims for within the visible range can be
4337
  specified with \ref setAutoTickCount.
4338
4339
  If \a on is set to false, you may set the tick step manually with \ref
4340
  setTickStep.
4341
4342
  \see setAutoTicks, setAutoSubTicks, setAutoTickCount
4343
*/
4344
void QCPAxis::setAutoTickStep(bool on) {
4345
  if (mAutoTickStep != on) {
4346
    mAutoTickStep = on;
4347
    mCachedMarginValid = false;
4348
  }
4349
}
4350
4351
/*!
4352
  Sets whether the number of sub ticks in one tick interval is determined
4353
  automatically. This works, as long as the tick step mantissa is a multiple of
4354
  0.5. When \ref setAutoTickStep is enabled, this is always the case.
4355
4356
  When \a on is set to false, you may set the sub tick count with \ref
4357
  setSubTickCount manually.
4358
4359
  \see setAutoTickCount, setAutoTicks, setAutoTickStep
4360
*/
4361
void QCPAxis::setAutoSubTicks(bool on) {
4362
  if (mAutoSubTicks != on) {
4363
    mAutoSubTicks = on;
4364
    mCachedMarginValid = false;
4365
  }
4366
}
4367
4368
/*!
4369
  Sets whether tick marks are displayed.
4370
4371
  Note that setting \a show to false does not imply that tick labels are
4372
  invisible, too. To achieve that, see \ref setTickLabels.
4373
*/
4374
void QCPAxis::setTicks(bool show) {
4375
  if (mTicks != show) {
4376
    mTicks = show;
4377
    mCachedMarginValid = false;
4378
  }
4379
}
4380
4381
/*!
4382
  Sets whether tick labels are displayed. Tick labels are the numbers drawn next
4383
  to tick marks.
4384
*/
4385
void QCPAxis::setTickLabels(bool show) {
4386
  if (mTickLabels != show) {
4387
    mTickLabels = show;
4388
    mCachedMarginValid = false;
4389
  }
4390
}
4391
4392
/*!
4393
  Sets the distance between the axis base line (including any outward ticks) and
4394
  the tick labels. \see setLabelPadding, setPadding
4395
*/
4396
void QCPAxis::setTickLabelPadding(int padding) {
4397
  if (mAxisPainter->tickLabelPadding != padding) {
4398
    mAxisPainter->tickLabelPadding = padding;
4399
    mCachedMarginValid = false;
4400
  }
4401
}
4402
4403
/*!
4404
  Sets whether the tick labels display numbers or dates/times.
4405
4406
  If \a type is set to \ref ltNumber, the format specifications of \ref
4407
  setNumberFormat apply.
4408
4409
  If \a type is set to \ref ltDateTime, the format specifications of \ref
4410
  setDateTimeFormat apply.
4411
4412
  In QCustomPlot, date/time coordinates are <tt>double</tt> numbers representing
4413
  the seconds since 1970-01-01T00:00:00 UTC. This format can be retrieved from
4414
  QDateTime objects with the QDateTime::toTime_t() function. Since this only
4415
  gives a resolution of one second, there is also the
4416
  QDateTime::toMSecsSinceEpoch() function which returns the timespan described
4417
  above in milliseconds. Divide its return value by 1000.0 to get a value with
4418
  the format needed for date/time plotting, with a resolution of one
4419
  millisecond.
4420
4421
  Using the toMSecsSinceEpoch function allows dates that go back to 2nd January
4422
  4713 B.C. (represented by a negative number), unlike the toTime_t function,
4423
  which works with unsigned integers and thus only goes back to 1st January
4424
  1970. So both for range and accuracy, use of toMSecsSinceEpoch()/1000.0 should
4425
  be preferred as key coordinate for date/time axes.
4426
4427
  \see setTickLabels
4428
*/
4429
void QCPAxis::setTickLabelType(LabelType type) {
4430
  if (mTickLabelType != type) {
4431
    mTickLabelType = type;
4432
    mCachedMarginValid = false;
4433
  }
4434
}
4435
4436
/*!
4437
  Sets the font of the tick labels.
4438
4439
  \see setTickLabels, setTickLabelColor
4440
*/
4441
void QCPAxis::setTickLabelFont(const QFont &font) {
4442
  if (font != mTickLabelFont) {
4443
    mTickLabelFont = font;
4444
    mCachedMarginValid = false;
4445
  }
4446
}
4447
4448
/*!
4449
  Sets the color of the tick labels.
4450
4451
  \see setTickLabels, setTickLabelFont
4452
*/
4453
void QCPAxis::setTickLabelColor(const QColor &color) {
4454
  if (color != mTickLabelColor) {
4455
    mTickLabelColor = color;
4456
    mCachedMarginValid = false;
4457
  }
4458
}
4459
4460
/*!
4461
  Sets the rotation of the tick labels. If \a degrees is zero, the labels are
4462
  drawn normally. Else, the tick labels are drawn rotated by \a degrees
4463
  clockwise. The specified angle is bound to values from -90 to 90 degrees.
4464
4465
  If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the
4466
  tick coordinate. For other angles, the label is drawn with an offset such that
4467
  it seems to point toward or away from the tick mark.
4468
*/
4469
void QCPAxis::setTickLabelRotation(double degrees) {
4470
  if (!qFuzzyIsNull(degrees - mAxisPainter->tickLabelRotation)) {
4471
    mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4472
    mCachedMarginValid = false;
4473
  }
4474
}
4475
4476
/*!
4477
  Sets whether the tick labels (numbers) shall appear inside or outside the axis
4478
  rect.
4479
4480
  The usual and default setting is \ref lsOutside. Very compact plots sometimes
4481
  require tick labels to be inside the axis rect, to save space. If \a side is
4482
  set to \ref lsInside, the tick labels appear on the inside are additionally
4483
  clipped to the axis rect.
4484
*/
4485
void QCPAxis::setTickLabelSide(LabelSide side) {
4486
  mAxisPainter->tickLabelSide = side;
4487
  mCachedMarginValid = false;
4488
}
4489
4490
/*!
4491
  Sets the format in which dates and times are displayed as tick labels, if \ref
4492
  setTickLabelType is \ref ltDateTime. for details about the \a format string,
4493
  see the documentation of QDateTime::toString().
4494
4495
  Newlines can be inserted with "\n".
4496
4497
  \see setDateTimeSpec
4498
*/
4499
void QCPAxis::setDateTimeFormat(const QString &format) {
4500
  if (mDateTimeFormat != format) {
4501
    mDateTimeFormat = format;
4502
    mCachedMarginValid = false;
4503
  }
4504
}
4505
4506
/*!
4507
  Sets the time spec that is used for the date time values when \ref
4508
  setTickLabelType is \ref ltDateTime.
4509
4510
  The default value of QDateTime objects (and also QCustomPlot) is
4511
  <tt>Qt::LocalTime</tt>. However, if the date time values passed to QCustomPlot
4512
  are given in the UTC spec, set \a timeSpec to <tt>Qt::UTC</tt> to get the
4513
  correct axis labels.
4514
4515
  \see setDateTimeFormat
4516
*/
4517
void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec) {
4518
  mDateTimeSpec = timeSpec;
4519
}
4520
4521
/*!
4522
  Sets the number format for the numbers drawn as tick labels (if tick label
4523
  type is \ref ltNumber). This \a formatCode is an extended version of the
4524
  format code used e.g. by QString::number() and QLocale::toString(). For
4525
  reference about that, see the "Argument Formats" section in the detailed
4526
  description of the QString class. \a formatCode is a string of one, two or
4527
  three characters. The first character is identical to the normal format code
4528
  used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed format,
4529
  'g'/'G' scientific or fixed, whichever is shorter.
4530
4531
  The second and third characters are optional and specific to QCustomPlot:\n
4532
  If the first char was 'e' or 'g', numbers are/might be displayed in the
4533
  scientific format, e.g. "5.5e9", which is ugly in a plot. So when the second
4534
  char of \a formatCode is set to 'b' (for "beautiful"), those exponential
4535
  numbers are formatted in a more natural way, i.e. "5.5 [multiplication sign]
4536
  10 [superscript] 9". By default, the multiplication sign is a centered dot. If
4537
  instead a cross should be shown (as is usual in the USA), the third char of \a
4538
  formatCode can be set to 'c'. The inserted multiplication signs are the UTF-8
4539
  characters 215 (0xD7) for the cross and 183 (0xB7) for the dot.
4540
4541
  If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a
4542
  formatCode uses the 'b' option (beautifully typeset decimal powers), the
4543
  display usually is "1 [multiplication sign] 10 [superscript] n", which looks
4544
  unnatural for logarithmic scaling (the "1 [multiplication sign]" part). To
4545
  only display the decimal power, set the number precision to zero with \ref
4546
  setNumberPrecision.
4547
4548
  Examples for \a formatCode:
4549
  \li \c g normal format code behaviour. If number is small, fixed format is
4550
  used, if number is large, normal scientific format is used \li \c gb If number
4551
  is small, fixed format is used, if number is large, scientific format is used
4552
  with beautifully typeset decimal powers and a dot as multiplication sign \li
4553
  \c ebc All numbers are in scientific format with beautifully typeset decimal
4554
  power and a cross as multiplication sign \li \c fb illegal format code, since
4555
  fixed format doesn't support (or need) beautifully typeset decimal powers.
4556
  Format code will be reduced to 'f'. \li \c hello illegal format code, since
4557
  first char is not 'e', 'E', 'f', 'g' or 'G'. Current format code will not be
4558
  changed.
4559
*/
4560
void QCPAxis::setNumberFormat(const QString &formatCode) {
4561
  if (formatCode.isEmpty()) {
4562
    qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4563
    return;
4564
  }
4565
  mCachedMarginValid = false;
4566
4567
  // interpret first char as number format char:
4568
  QString allowedFormatChars(QLatin1String("eEfgG"));
4569
  if (allowedFormatChars.contains(formatCode.at(0))) {
4570
    mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4571
  } else {
4572
    qDebug() << Q_FUNC_INFO
4573
             << "Invalid number format code (first char not in 'eEfgG'):"
4574
             << formatCode;
4575
    return;
4576
  }
4577
  if (formatCode.length() < 2) {
4578
    mNumberBeautifulPowers = false;
4579
    mAxisPainter->numberMultiplyCross = false;
4580
    return;
4581
  }
4582
4583
  // interpret second char as indicator for beautiful decimal powers:
4584
  if (formatCode.at(1) == QLatin1Char('b') &&
4585
      (mNumberFormatChar == QLatin1Char('e') ||
4586
       mNumberFormatChar == QLatin1Char('g'))) {
4587
    mNumberBeautifulPowers = true;
4588
  } else {
4589
    qDebug() << Q_FUNC_INFO
4590
             << "Invalid number format code (second char not 'b' or first char "
4591
                "neither 'e' nor 'g'):"
4592
             << formatCode;
4593
    return;
4594
  }
4595
  if (formatCode.length() < 3) {
4596
    mAxisPainter->numberMultiplyCross = false;
4597
    return;
4598
  }
4599
4600
  // interpret third char as indicator for dot or cross multiplication symbol:
4601
  if (formatCode.at(2) == QLatin1Char('c')) {
4602
    mAxisPainter->numberMultiplyCross = true;
4603
  } else if (formatCode.at(2) == QLatin1Char('d')) {
4604
    mAxisPainter->numberMultiplyCross = false;
4605
  } else {
4606
    qDebug() << Q_FUNC_INFO
4607
             << "Invalid number format code (third char neither 'c' nor 'd'):"
4608
             << formatCode;
4609
    return;
4610
  }
4611
}
4612
4613
/*!
4614
  Sets the precision of the tick label numbers. See QLocale::toString(double i,
4615
  char f, int prec) for details. The effect of precisions are most notably for
4616
  number Formats starting with 'e', see \ref setNumberFormat
4617
4618
  If the scale type (\ref setScaleType) is \ref stLogarithmic and the number
4619
  format (\ref setNumberFormat) uses the 'b' format code (beautifully typeset
4620
  decimal powers), the display usually is "1 [multiplication sign] 10
4621
  [superscript] n", which looks unnatural for logarithmic scaling (the redundant
4622
  "1 [multiplication sign]" part). To only display the decimal power "10
4623
  [superscript] n", set \a precision to zero.
4624
*/
4625
void QCPAxis::setNumberPrecision(int precision) {
4626
  if (mNumberPrecision != precision) {
4627
    mNumberPrecision = precision;
4628
    mCachedMarginValid = false;
4629
  }
4630
}
4631
4632
/*!
4633
  If \ref setAutoTickStep is set to false, use this function to set the tick
4634
  step manually. The tick step is the interval between (major) ticks, in plot
4635
  coordinates. \see setSubTickCount
4636
*/
4637
void QCPAxis::setTickStep(double step) {
4638
  if (mTickStep != step) {
4639
    mTickStep = step;
4640
    mCachedMarginValid = false;
4641
  }
4642
}
4643
4644
/*!
4645
  If you want full control over what ticks (and possibly labels) the axes show,
4646
  this function is used to set the coordinates at which ticks will appear.\ref
4647
  setAutoTicks must be disabled, else the provided tick vector will be
4648
  overwritten with automatically generated tick coordinates upon replot. The
4649
  labels of the ticks can be generated automatically when \ref setAutoTickLabels
4650
  is left enabled. If it is disabled, you can set the labels manually with \ref
4651
  setTickVectorLabels.
4652
4653
  \a vec is a vector containing the positions of the ticks, in plot coordinates.
4654
4655
  \warning \a vec must be sorted in ascending order, no additional checks are
4656
  made to ensure this.
4657
4658
  \see setTickVectorLabels
4659
*/
4660
void QCPAxis::setTickVector(const QVector<double> &vec) {
4661
  // don't check whether mTickVector != vec here, because it takes longer than
4662
  // we would save
4663
  mTickVector = vec;
4664
  mCachedMarginValid = false;
4665
}
4666
4667
/*!
4668
  If you want full control over what ticks and labels the axes show, this
4669
  function is used to set a number of QStrings that will be displayed at the
4670
  tick positions which you need to provide with \ref setTickVector. These two
4671
  vectors should have the same size. (Note that you need to disable \ref
4672
  setAutoTicks and \ref setAutoTickLabels first.)
4673
4674
  \a vec is a vector containing the labels of the ticks. The entries correspond
4675
  to the respective indices in the tick vector, passed via \ref setTickVector.
4676
4677
  \see setTickVector
4678
*/
4679
void QCPAxis::setTickVectorLabels(const QVector<QString> &vec) {
4680
  // don't check whether mTickVectorLabels != vec here, because it takes longer
4681
  // than we would save
4682
  mTickVectorLabels = vec;
4683
  mCachedMarginValid = false;
4684
}
4685
4686
/*!
4687
  Sets the length of the ticks in pixels. \a inside is the length the ticks will
4688
  reach inside the plot and \a outside is the length they will reach outside the
4689
  plot. If \a outside is greater than zero, the tick labels and axis label will
4690
  increase their distance to the axis accordingly, so they won't collide with
4691
  the ticks.
4692
4693
  \see setSubTickLength, setTickLengthIn, setTickLengthOut
4694
*/
4695
void QCPAxis::setTickLength(int inside, int outside) {
4696
  setTickLengthIn(inside);
4697
  setTickLengthOut(outside);
4698
}
4699
4700
/*!
4701
  Sets the length of the inward ticks in pixels. \a inside is the length the
4702
  ticks will reach inside the plot.
4703
4704
  \see setTickLengthOut, setTickLength, setSubTickLength
4705
*/
4706
void QCPAxis::setTickLengthIn(int inside) {
4707
  if (mAxisPainter->tickLengthIn != inside) {
4708
    mAxisPainter->tickLengthIn = inside;
4709
  }
4710
}
4711
4712
/*!
4713
  Sets the length of the outward ticks in pixels. \a outside is the length the
4714
  ticks will reach outside the plot. If \a outside is greater than zero, the
4715
  tick labels and axis label will increase their distance to the axis
4716
  accordingly, so they won't collide with the ticks.
4717
4718
  \see setTickLengthIn, setTickLength, setSubTickLength
4719
*/
4720
void QCPAxis::setTickLengthOut(int outside) {
4721
  if (mAxisPainter->tickLengthOut != outside) {
4722
    mAxisPainter->tickLengthOut = outside;
4723
    mCachedMarginValid = false;  // only outside tick length can change margin
4724
  }
4725
}
4726
4727
/*!
4728
  Sets the number of sub ticks in one (major) tick step. A sub tick count of
4729
  three for example, divides the tick intervals in four sub intervals.
4730
4731
  By default, the number of sub ticks is chosen automatically in a reasonable
4732
  manner as long as the mantissa of the tick step is a multiple of 0.5. When
4733
  \ref setAutoTickStep is enabled, this is always the case.
4734
4735
  If you want to disable automatic sub tick count and use this function to set
4736
  the count manually, see \ref setAutoSubTicks.
4737
*/
4738
void QCPAxis::setSubTickCount(int count) { mSubTickCount = count; }
4739
4740
/*!
4741
  Sets the length of the subticks in pixels. \a inside is the length the
4742
  subticks will reach inside the plot and \a outside is the length they will
4743
  reach outside the plot. If \a outside is greater than zero, the tick labels
4744
  and axis label will increase their distance to the axis accordingly, so they
4745
  won't collide with the ticks.
4746
4747
  \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
4748
*/
4749
void QCPAxis::setSubTickLength(int inside, int outside) {
4750
  setSubTickLengthIn(inside);
4751
  setSubTickLengthOut(outside);
4752
}
4753
4754
/*!
4755
  Sets the length of the inward subticks in pixels. \a inside is the length the
4756
  subticks will reach inside the plot.
4757
4758
  \see setSubTickLengthOut, setSubTickLength, setTickLength
4759
*/
4760
void QCPAxis::setSubTickLengthIn(int inside) {
4761
  if (mAxisPainter->subTickLengthIn != inside) {
4762
    mAxisPainter->subTickLengthIn = inside;
4763
  }
4764
}
4765
4766
/*!
4767
  Sets the length of the outward subticks in pixels. \a outside is the length
4768
  the subticks will reach outside the plot. If \a outside is greater than zero,
4769
  the tick labels will increase their distance to the axis accordingly, so they
4770
  won't collide with the ticks.
4771
4772
  \see setSubTickLengthIn, setSubTickLength, setTickLength
4773
*/
4774
void QCPAxis::setSubTickLengthOut(int outside) {
4775
  if (mAxisPainter->subTickLengthOut != outside) {
4776
    mAxisPainter->subTickLengthOut = outside;
4777
    mCachedMarginValid = false;  // only outside tick length can change margin
4778
  }
4779
}
4780
4781
/*!
4782
  Sets the pen, the axis base line is drawn with.
4783
4784
  \see setTickPen, setSubTickPen
4785
*/
4786
void QCPAxis::setBasePen(const QPen &pen) { mBasePen = pen; }
4787
4788
/*!
4789
  Sets the pen, tick marks will be drawn with.
4790
4791
  \see setTickLength, setBasePen
4792
*/
4793
void QCPAxis::setTickPen(const QPen &pen) { mTickPen = pen; }
4794
4795
/*!
4796
  Sets the pen, subtick marks will be drawn with.
4797
4798
  \see setSubTickCount, setSubTickLength, setBasePen
4799
*/
4800
void QCPAxis::setSubTickPen(const QPen &pen) { mSubTickPen = pen; }
4801
4802
/*!
4803
  Sets the font of the axis label.
4804
4805
  \see setLabelColor
4806
*/
4807
void QCPAxis::setLabelFont(const QFont &font) {
4808
  if (mLabelFont != font) {
4809
    mLabelFont = font;
4810
    mCachedMarginValid = false;
4811
  }
4812
}
4813
4814
/*!
4815
  Sets the color of the axis label.
4816
4817
  \see setLabelFont
4818
*/
4819
void QCPAxis::setLabelColor(const QColor &color) { mLabelColor = color; }
4820
4821
/*!
4822
  Sets the text of the axis label that will be shown below/above or next to the
4823
  axis, depending on its orientation. To disable axis labels, pass an empty
4824
  string as \a str.
4825
*/
4826
void QCPAxis::setLabel(const QString &str) {
4827
  if (mLabel != str) {
4828
    mLabel = str;
4829
    mCachedMarginValid = false;
4830
  }
4831
}
4832
4833
/*!
4834
  Sets the distance between the tick labels and the axis label.
4835
4836
  \see setTickLabelPadding, setPadding
4837
*/
4838
void QCPAxis::setLabelPadding(int padding) {
4839
  if (mAxisPainter->labelPadding != padding) {
4840
    mAxisPainter->labelPadding = padding;
4841
    mCachedMarginValid = false;
4842
  }
4843
}
4844
4845
/*!
4846
  Sets the padding of the axis.
4847
4848
  When \ref QCPAxisRect::setAutoMargins is enabled, the padding is the
4849
  additional outer most space, that is left blank.
4850
4851
  The axis padding has no meaning if \ref QCPAxisRect::setAutoMargins is
4852
  disabled.
4853
4854
  \see setLabelPadding, setTickLabelPadding
4855
*/
4856
void QCPAxis::setPadding(int padding) {
4857
  if (mPadding != padding) {
4858
    mPadding = padding;
4859
    mCachedMarginValid = false;
4860
  }
4861
}
4862
4863
/*!
4864
  Sets the offset the axis has to its axis rect side.
4865
4866
  If an axis rect side has multiple axes and automatic margin calculation is
4867
  enabled for that side, only the offset of the inner most axis has meaning
4868
  (even if it is set to be invisible). The offset of the other, outer axes is
4869
  controlled automatically, to place them at appropriate positions.
4870
*/
4871
void QCPAxis::setOffset(int offset) { mAxisPainter->offset = offset; }
4872
4873
/*!
4874
  Sets the font that is used for tick labels when they are selected.
4875
4876
  \see setTickLabelFont, setSelectableParts, setSelectedParts,
4877
  QCustomPlot::setInteractions
4878
*/
4879
void QCPAxis::setSelectedTickLabelFont(const QFont &font) {
4880
  if (font != mSelectedTickLabelFont) {
4881
    mSelectedTickLabelFont = font;
4882
    // don't set mCachedMarginValid to false here because margin calculation is
4883
    // always done with non-selected fonts
4884
  }
4885
}
4886
4887
/*!
4888
  Sets the font that is used for the axis label when it is selected.
4889
4890
  \see setLabelFont, setSelectableParts, setSelectedParts,
4891
  QCustomPlot::setInteractions
4892
*/
4893
void QCPAxis::setSelectedLabelFont(const QFont &font) {
4894
  mSelectedLabelFont = font;
4895
  // don't set mCachedMarginValid to false here because margin calculation is
4896
  // always done with non-selected fonts
4897
}
4898
4899
/*!
4900
  Sets the color that is used for tick labels when they are selected.
4901
4902
  \see setTickLabelColor, setSelectableParts, setSelectedParts,
4903
  QCustomPlot::setInteractions
4904
*/
4905
void QCPAxis::setSelectedTickLabelColor(const QColor &color) {
4906
  if (color != mSelectedTickLabelColor) {
4907
    mSelectedTickLabelColor = color;
4908
  }
4909
}
4910
4911
/*!
4912
  Sets the color that is used for the axis label when it is selected.
4913
4914
  \see setLabelColor, setSelectableParts, setSelectedParts,
4915
  QCustomPlot::setInteractions
4916
*/
4917
void QCPAxis::setSelectedLabelColor(const QColor &color) {
4918
  mSelectedLabelColor = color;
4919
}
4920
4921
/*!
4922
  Sets the pen that is used to draw the axis base line when selected.
4923
4924
  \see setBasePen, setSelectableParts, setSelectedParts,
4925
  QCustomPlot::setInteractions
4926
*/
4927
void QCPAxis::setSelectedBasePen(const QPen &pen) { mSelectedBasePen = pen; }
4928
4929
/*!
4930
  Sets the pen that is used to draw the (major) ticks when selected.
4931
4932
  \see setTickPen, setSelectableParts, setSelectedParts,
4933
  QCustomPlot::setInteractions
4934
*/
4935
void QCPAxis::setSelectedTickPen(const QPen &pen) { mSelectedTickPen = pen; }
4936
4937
/*!
4938
  Sets the pen that is used to draw the subticks when selected.
4939
4940
  \see setSubTickPen, setSelectableParts, setSelectedParts,
4941
  QCustomPlot::setInteractions
4942
*/
4943
void QCPAxis::setSelectedSubTickPen(const QPen &pen) {
4944
  mSelectedSubTickPen = pen;
4945
}
4946
4947
/*!
4948
  Sets the style for the lower axis ending. See the documentation of
4949
  QCPLineEnding for available styles.
4950
4951
  For horizontal axes, this method refers to the left ending, for vertical axes
4952
  the bottom ending. Note that this meaning does not change when the axis range
4953
  is reversed with \ref setRangeReversed.
4954
4955
  \see setUpperEnding
4956
*/
4957
void QCPAxis::setLowerEnding(const QCPLineEnding &ending) {
4958
  mAxisPainter->lowerEnding = ending;
4959
}
4960
4961
/*!
4962
  Sets the style for the upper axis ending. See the documentation of
4963
  QCPLineEnding for available styles.
4964
4965
  For horizontal axes, this method refers to the right ending, for vertical axes
4966
  the top ending. Note that this meaning does not change when the axis range is
4967
  reversed with \ref setRangeReversed.
4968
4969
  \see setLowerEnding
4970
*/
4971
void QCPAxis::setUpperEnding(const QCPLineEnding &ending) {
4972
  mAxisPainter->upperEnding = ending;
4973
}
4974
4975
/*!
4976
  If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to
4977
  the lower and upper bounds of the range. The range is simply moved by \a diff.
4978
4979
  If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a
4980
  diff. This corresponds to an apparent "linear" move in logarithmic scaling by
4981
  a distance of log(diff).
4982
*/
4983
void QCPAxis::moveRange(double diff) {
4984
  QCPRange oldRange = mRange;
4985
  if (mScaleType == stLinear) {
4986
    mRange.lower += diff;
4987
    mRange.upper += diff;
4988
  } else  // mScaleType == stLogarithmic
4989
  {
4990
    mRange.lower *= diff;
4991
    mRange.upper *= diff;
4992
  }
4993
  mCachedMarginValid = false;
4994
  emit rangeChanged(mRange);
4995
  emit rangeChanged(mRange, oldRange);
4996
}
4997
4998
/*!
4999
  Scales the range of this axis by \a factor around the coordinate \a center.
5000
  For example, if \a factor is 2.0, \a center is 1.0, then the axis range will
5001
  double its size, and the point at coordinate 1.0 won't have changed its
5002
  position in the QCustomPlot widget (i.e. coordinates around 1.0 will have
5003
  moved symmetrically closer to 1.0).
5004
*/
5005
void QCPAxis::scaleRange(double factor, double center) {
5006
  QCPRange oldRange = mRange;
5007
  if (mScaleType == stLinear) {
5008
    QCPRange newRange;
5009
    newRange.lower = (mRange.lower - center) * factor + center;
5010
    newRange.upper = (mRange.upper - center) * factor + center;
5011
    if (QCPRange::validRange(newRange))
5012
      mRange = newRange.sanitizedForLinScale();
5013
  } else  // mScaleType == stLogarithmic
5014
  {
5015
    if ((mRange.upper < 0 && center < 0) ||
5016
        (mRange.upper > 0 &&
5017
         center > 0))  // make sure center has same sign as range
5018
    {
5019
      QCPRange newRange;
5020
      newRange.lower = qPow(mRange.lower / center, factor) * center;
5021
      newRange.upper = qPow(mRange.upper / center, factor) * center;
5022
      if (QCPRange::validRange(newRange))
5023
        mRange = newRange.sanitizedForLogScale();
5024
    } else
5025
      qDebug() << Q_FUNC_INFO
5026
               << "Center of scaling operation doesn't lie in same logarithmic "
5027
                  "sign domain as range:"
5028
               << center;
5029
  }
5030
  mCachedMarginValid = false;
5031
  emit rangeChanged(mRange);
5032
  emit rangeChanged(mRange, oldRange);
5033
}
5034
5035
/*!
5036
  Scales the range of this axis to have a certain scale \a ratio to \a
5037
  otherAxis. The scaling will be done around the center of the current axis
5038
  range.
5039
5040
  For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is
5041
  \a xAxis, graphs plotted with those axes will appear in a 1:1 aspect ratio,
5042
  independent of the aspect ratio the axis rect has.
5043
5044
  This is an operation that changes the range of this axis once, it doesn't fix
5045
  the scale ratio indefinitely. Note that calling this function in the
5046
  constructor of the QCustomPlot's parent won't have the desired effect, since
5047
  the widget dimensions aren't defined yet, and a resizeEvent will follow.
5048
*/
5049
void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio) {
5050
  int otherPixelSize, ownPixelSize;
5051
5052
  if (otherAxis->orientation() == Qt::Horizontal)
5053
    otherPixelSize = otherAxis->axisRect()->width();
5054
  else
5055
    otherPixelSize = otherAxis->axisRect()->height();
5056
5057
  if (orientation() == Qt::Horizontal)
5058
    ownPixelSize = axisRect()->width();
5059
  else
5060
    ownPixelSize = axisRect()->height();
5061
5062
  double newRangeSize =
5063
      ratio * otherAxis->range().size() * ownPixelSize / (double)otherPixelSize;
5064
  setRange(range().center(), newRangeSize, Qt::AlignCenter);
5065
}
5066
5067
/*!
5068
  Changes the axis range such that all plottables associated with this axis are
5069
  fully visible in that dimension.
5070
5071
  \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
5072
*/
5073
void QCPAxis::rescale(bool onlyVisiblePlottables) {
5074
  QList<QCPAbstractPlottable *> p = plottables();
5075
  QCPRange newRange;
5076
  bool haveRange = false;
5077
  for (int i = 0; i < p.size(); ++i) {
5078
    if (!p.at(i)->realVisibility() && onlyVisiblePlottables) continue;
5079
    QCPRange plottableRange;
5080
    bool currentFoundRange;
5081
    QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
5082
    if (mScaleType == stLogarithmic)
5083
      signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative
5084
                                     : QCPAbstractPlottable::sdPositive);
5085
    if (p.at(i)->keyAxis() == this)
5086
      plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5087
    else
5088
      plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5089
    if (currentFoundRange) {
5090
      if (!haveRange)
5091
        newRange = plottableRange;
5092
      else
5093
        newRange.expand(plottableRange);
5094
      haveRange = true;
5095
    }
5096
  }
5097
  if (haveRange) {
5098
    if (!QCPRange::validRange(
5099
            newRange))  // likely due to range being zero (plottable has only
5100
                        // constant data in this axis dimension), shift current
5101
                        // range to at least center the plottable
5102
    {
5103
      double center =
5104
          (newRange.lower + newRange.upper) *
5105
          0.5;  // upper and lower should be equal anyway, but just to make
5106
                // sure, incase validRange returned false for other reason
5107
      if (mScaleType == stLinear) {
5108
        newRange.lower = center - mRange.size() / 2.0;
5109
        newRange.upper = center + mRange.size() / 2.0;
5110
      } else  // mScaleType == stLogarithmic
5111
      {
5112
        newRange.lower = center / qSqrt(mRange.upper / mRange.lower);
5113
        newRange.upper = center * qSqrt(mRange.upper / mRange.lower);
5114
      }
5115
    }
5116
    setRange(newRange);
5117
  }
5118
}
5119
5120
/*!
5121
  Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis
5122
  coordinates.
5123
*/
5124
double QCPAxis::pixelToCoord(double value) const {
5125
  if (orientation() == Qt::Horizontal) {
5126
    if (mScaleType == stLinear) {
5127
      if (!mRangeReversed)
5128
        return (value - mAxisRect->left()) / (double)mAxisRect->width() *
5129
                   mRange.size() +
5130
               mRange.lower;
5131
      else
5132
        return -(value - mAxisRect->left()) / (double)mAxisRect->width() *
5133
                   mRange.size() +
5134
               mRange.upper;
5135
    } else  // mScaleType == stLogarithmic
5136
    {
5137
      if (!mRangeReversed)
5138
        return qPow(mRange.upper / mRange.lower,
5139
                    (value - mAxisRect->left()) / (double)mAxisRect->width()) *
5140
               mRange.lower;
5141
      else
5142
        return qPow(mRange.upper / mRange.lower,
5143
                    (mAxisRect->left() - value) / (double)mAxisRect->width()) *
5144
               mRange.upper;
5145
    }
5146
  } else  // orientation() == Qt::Vertical
5147
  {
5148
    if (mScaleType == stLinear) {
5149
      if (!mRangeReversed)
5150
        return (mAxisRect->bottom() - value) / (double)mAxisRect->height() *
5151
                   mRange.size() +
5152
               mRange.lower;
5153
      else
5154
        return -(mAxisRect->bottom() - value) / (double)mAxisRect->height() *
5155
                   mRange.size() +
5156
               mRange.upper;
5157
    } else  // mScaleType == stLogarithmic
5158
    {
5159
      if (!mRangeReversed)
5160
        return qPow(mRange.upper / mRange.lower,
5161
                    (mAxisRect->bottom() - value) /
5162
                        (double)mAxisRect->height()) *
5163
               mRange.lower;
5164
      else
5165
        return qPow(mRange.upper / mRange.lower,
5166
                    (value - mAxisRect->bottom()) /
5167
                        (double)mAxisRect->height()) *
5168
               mRange.upper;
5169
    }
5170
  }
5171
}
5172
5173
/*!
5174
  Transforms \a value, in coordinates of the axis, to pixel coordinates of the
5175
  QCustomPlot widget.
5176
*/
5177
double QCPAxis::coordToPixel(double value) const {
5178
  if (orientation() == Qt::Horizontal) {
5179
    if (mScaleType == stLinear) {
5180
      if (!mRangeReversed)
5181
        return (value - mRange.lower) / mRange.size() * mAxisRect->width() +
5182
               mAxisRect->left();
5183
      else
5184
        return (mRange.upper - value) / mRange.size() * mAxisRect->width() +
5185
               mAxisRect->left();
5186
    } else  // mScaleType == stLogarithmic
5187
    {
5188
      if (value >= 0 &&
5189
          mRange.upper < 0)  // invalid value for logarithmic scale, just draw
5190
                             // it outside visible range
5191
        return !mRangeReversed ? mAxisRect->right() + 200
5192
                               : mAxisRect->left() - 200;
5193
      else if (value <= 0 &&
5194
               mRange.upper > 0)  // invalid value for logarithmic scale, just
5195
                                  // draw it outside visible range
5196
        return !mRangeReversed ? mAxisRect->left() - 200
5197
                               : mAxisRect->right() + 200;
5198
      else {
5199
        if (!mRangeReversed)
5200
          return baseLog(value / mRange.lower) /
5201
                     baseLog(mRange.upper / mRange.lower) * mAxisRect->width() +
5202
                 mAxisRect->left();
5203
        else
5204
          return baseLog(mRange.upper / value) /
5205
                     baseLog(mRange.upper / mRange.lower) * mAxisRect->width() +
5206
                 mAxisRect->left();
5207
      }
5208
    }
5209
  } else  // orientation() == Qt::Vertical
5210
  {
5211
    if (mScaleType == stLinear) {
5212
      if (!mRangeReversed)
5213
        return mAxisRect->bottom() -
5214
               (value - mRange.lower) / mRange.size() * mAxisRect->height();
5215
      else
5216
        return mAxisRect->bottom() -
5217
               (mRange.upper - value) / mRange.size() * mAxisRect->height();
5218
    } else  // mScaleType == stLogarithmic
5219
    {
5220
      if (value >= 0 &&
5221
          mRange.upper < 0)  // invalid value for logarithmic scale, just draw
5222
                             // it outside visible range
5223
        return !mRangeReversed ? mAxisRect->top() - 200
5224
                               : mAxisRect->bottom() + 200;
5225
      else if (value <= 0 &&
5226
               mRange.upper > 0)  // invalid value for logarithmic scale, just
5227
                                  // draw it outside visible range
5228
        return !mRangeReversed ? mAxisRect->bottom() + 200
5229
                               : mAxisRect->top() - 200;
5230
      else {
5231
        if (!mRangeReversed)
5232
          return mAxisRect->bottom() -
5233
                 baseLog(value / mRange.lower) /
5234
                     baseLog(mRange.upper / mRange.lower) * mAxisRect->height();
5235
        else
5236
          return mAxisRect->bottom() -
5237
                 baseLog(mRange.upper / value) /
5238
                     baseLog(mRange.upper / mRange.lower) * mAxisRect->height();
5239
      }
5240
    }
5241
  }
5242
}
5243
5244
/*!
5245
  Returns the part of the axis that is hit by \a pos (in pixels). The return
5246
  value of this function is independent of the user-selectable parts defined
5247
  with \ref setSelectableParts. Further, this function does not change the
5248
  current selection state of the axis.
5249
5250
  If the axis is not visible (\ref setVisible), this function always returns
5251
  \ref spNone.
5252
5253
  \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions
5254
*/
5255
QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const {
5256
  if (!mVisible) return spNone;
5257
5258
  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5259
    return spAxis;
5260
  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5261
    return spTickLabels;
5262
  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5263
    return spAxisLabel;
5264
  else
5265
    return spNone;
5266
}
5267
5268
/* inherits documentation from base class */
5269
double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable,
5270
                           QVariant *details) const {
5271
  if (!mParentPlot) return -1;
5272
  SelectablePart part = getPartAt(pos);
5273
  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5274
    return -1;
5275
5276
  if (details) details->setValue(part);
5277
  return mParentPlot->selectionTolerance() * 0.99;
5278
}
5279
5280
/*!
5281
  Returns a list of all the plottables that have this axis as key or value axis.
5282
5283
  If you are only interested in plottables of type QCPGraph, see \ref graphs.
5284
5285
  \see graphs, items
5286
*/
5287
QList<QCPAbstractPlottable *> QCPAxis::plottables() const {
5288
  QList<QCPAbstractPlottable *> result;
5289
  if (!mParentPlot) return result;
5290
5291
  for (int i = 0; i < mParentPlot->mPlottables.size(); ++i) {
5292
    if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||
5293
        mParentPlot->mPlottables.at(i)->valueAxis() == this)
5294
      result.append(mParentPlot->mPlottables.at(i));
5295
  }
5296
  return result;
5297
}
5298
5299
/*!
5300
  Returns a list of all the graphs that have this axis as key or value axis.
5301
5302
  \see plottables, items
5303
*/
5304
QList<QCPGraph *> QCPAxis::graphs() const {
5305
  QList<QCPGraph *> result;
5306
  if (!mParentPlot) return result;
5307
5308
  for (int i = 0; i < mParentPlot->mGraphs.size(); ++i) {
5309
    if (mParentPlot->mGraphs.at(i)->keyAxis() == this ||
5310
        mParentPlot->mGraphs.at(i)->valueAxis() == this)
5311
      result.append(mParentPlot->mGraphs.at(i));
5312
  }
5313
  return result;
5314
}
5315
5316
/*!
5317
  Returns a list of all the items that are associated with this axis. An item is
5318
  considered associated with an axis if at least one of its positions uses the
5319
  axis as key or value axis.
5320
5321
  \see plottables, graphs
5322
*/
5323
QList<QCPAbstractItem *> QCPAxis::items() const {
5324
  QList<QCPAbstractItem *> result;
5325
  if (!mParentPlot) return result;
5326
5327
  for (int itemId = 0; itemId < mParentPlot->mItems.size(); ++itemId) {
5328
    QList<QCPItemPosition *> positions =
5329
        mParentPlot->mItems.at(itemId)->positions();
5330
    for (int posId = 0; posId < positions.size(); ++posId) {
5331
      if (positions.at(posId)->keyAxis() == this ||
5332
          positions.at(posId)->valueAxis() == this) {
5333
        result.append(mParentPlot->mItems.at(itemId));
5334
        break;
5335
      }
5336
    }
5337
  }
5338
  return result;
5339
}
5340
5341
/*!
5342
  Transforms a margin side to the logically corresponding axis type.
5343
  (QCP::msLeft to QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.)
5344
*/
5345
QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side) {
5346
  switch (side) {
5347
    case QCP::msLeft:
5348
      return atLeft;
5349
    case QCP::msRight:
5350
      return atRight;
5351
    case QCP::msTop:
5352
      return atTop;
5353
    case QCP::msBottom:
5354
      return atBottom;
5355
    default:
5356
      break;
5357
  }
5358
  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5359
  return atLeft;
5360
}
5361
5362
/*!
5363
  Returns the axis type that describes the opposite axis of an axis with the
5364
  specified \a type.
5365
*/
5366
QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type) {
5367
  switch (type) {
5368
    case atLeft:
5369
      return atRight;
5370
      break;
5371
    case atRight:
5372
      return atLeft;
5373
      break;
5374
    case atBottom:
5375
      return atTop;
5376
      break;
5377
    case atTop:
5378
      return atBottom;
5379
      break;
5380
    default:
5381
      qDebug() << Q_FUNC_INFO << "invalid axis type";
5382
      return atLeft;
5383
      break;
5384
  }
5385
}
5386
5387
/*! \internal
5388
5389
  This function is called to prepare the tick vector, sub tick vector and tick
5390
  label vector. If \ref setAutoTicks is set to true, appropriate tick values are
5391
  determined automatically via \ref generateAutoTicks. If it's set to false, the
5392
  signal ticksRequest is emitted, which can be used to provide external tick
5393
  positions. Then the sub tick vectors and tick label vectors are created.
5394
*/
5395
void QCPAxis::setupTickVectors() {
5396
  if (!mParentPlot) return;
5397
  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0)
5398
    return;
5399
5400
  // fill tick vectors, either by auto generating or by notifying user to fill
5401
  // the vectors himself
5402
  if (mAutoTicks) {
5403
    generateAutoTicks();
5404
  } else {
5405
    emit ticksRequest();
5406
  }
5407
5408
  visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick);
5409
  if (mTickVector.isEmpty()) {
5410
    mSubTickVector.clear();
5411
    return;
5412
  }
5413
5414
  // generate subticks between ticks:
5415
  mSubTickVector.resize((mTickVector.size() - 1) * mSubTickCount);
5416
  if (mSubTickCount > 0) {
5417
    double subTickStep = 0;
5418
    double subTickPosition = 0;
5419
    int subTickIndex = 0;
5420
    bool done = false;
5421
    int lowTick =
5422
        mLowestVisibleTick > 0 ? mLowestVisibleTick - 1 : mLowestVisibleTick;
5423
    int highTick = mHighestVisibleTick < mTickVector.size() - 1
5424
                       ? mHighestVisibleTick + 1
5425
                       : mHighestVisibleTick;
5426
    for (int i = lowTick + 1; i <= highTick; ++i) {
5427
      subTickStep = (mTickVector.at(i) - mTickVector.at(i - 1)) /
5428
                    (double)(mSubTickCount + 1);
5429
      for (int k = 1; k <= mSubTickCount; ++k) {
5430
        subTickPosition = mTickVector.at(i - 1) + k * subTickStep;
5431
        if (subTickPosition < mRange.lower) continue;
5432
        if (subTickPosition > mRange.upper) {
5433
          done = true;
5434
          break;
5435
        }
5436
        mSubTickVector[subTickIndex] = subTickPosition;
5437
        subTickIndex++;
5438
      }
5439
      if (done) break;
5440
    }
5441
    mSubTickVector.resize(subTickIndex);
5442
  }
5443
5444
  // generate tick labels according to tick positions:
5445
  if (mAutoTickLabels) {
5446
    int vecsize = mTickVector.size();
5447
    mTickVectorLabels.resize(vecsize);
5448
    if (mTickLabelType == ltNumber) {
5449
      for (int i = mLowestVisibleTick; i <= mHighestVisibleTick; ++i)
5450
        mTickVectorLabels[i] = mParentPlot->locale().toString(
5451
            mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
5452
    } else if (mTickLabelType == ltDateTime) {
5453
      for (int i = mLowestVisibleTick; i <= mHighestVisibleTick; ++i) {
5454
#if QT_VERSION <      \
5455
    QT_VERSION_CHECK( \
5456
        4, 7,         \
5457
        0)  // use fromMSecsSinceEpoch function if available, to gain sub-second
5458
            // accuracy on tick labels (e.g. for format "hh:mm:ss:zzz")
5459
        mTickVectorLabels[i] = mParentPlot->locale().toString(
5460
            QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec),
5461
            mDateTimeFormat);
5462
#else
5463
        mTickVectorLabels[i] = mParentPlot->locale().toString(
5464
            QDateTime::fromMSecsSinceEpoch(mTickVector.at(i) * 1000)
5465
                .toTimeSpec(mDateTimeSpec),
5466
            mDateTimeFormat);
5467
#endif
5468
      }
5469
    }
5470
  } else  // mAutoTickLabels == false
5471
  {
5472
    if (mAutoTicks)  // ticks generated automatically, but not ticklabels, so
5473
                     // emit ticksRequest here for labels
5474
    {
5475
      emit ticksRequest();
5476
    }
5477
    // make sure provided tick label vector has correct (minimal) length:
5478
    if (mTickVectorLabels.size() < mTickVector.size())
5479
      mTickVectorLabels.resize(mTickVector.size());
5480
  }
5481
}
5482
5483
/*! \internal
5484
5485
  If \ref setAutoTicks is set to true, this function is called by \ref
5486
  setupTickVectors to generate reasonable tick positions (and subtick count).
5487
  The algorithm tries to create approximately <tt>mAutoTickCount</tt> ticks (set
5488
  via \ref setAutoTickCount).
5489
5490
  If the scale is logarithmic, \ref setAutoTickCount is ignored, and one tick is
5491
  generated at every power of the current logarithm base, set via \ref
5492
  setScaleLogBase.
5493
*/
5494
void QCPAxis::generateAutoTicks() {
5495
  if (mScaleType == stLinear) {
5496
    if (mAutoTickStep) {
5497
      // Generate tick positions according to linear scaling:
5498
      mTickStep =
5499
          mRange.size() /
5500
          (double)(mAutoTickCount +
5501
                   1e-10);  // mAutoTickCount ticks on average, the small
5502
                            // addition is to prevent jitter on exact integers
5503
      double magnitudeFactor = qPow(
5504
          10.0,
5505
          qFloor(
5506
              qLn(mTickStep) /
5507
              qLn(10.0)));  // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5508
      double tickStepMantissa = mTickStep / magnitudeFactor;
5509
      if (tickStepMantissa < 5) {
5510
        // round digit after decimal point to 0.5
5511
        mTickStep = (int)(tickStepMantissa * 2) / 2.0 * magnitudeFactor;
5512
      } else {
5513
        // round to first digit in multiples of 2
5514
        mTickStep = (int)(tickStepMantissa / 2.0) * 2.0 * magnitudeFactor;
5515
      }
5516
    }
5517
    if (mAutoSubTicks) mSubTickCount = calculateAutoSubTickCount(mTickStep);
5518
    // Generate tick positions according to mTickStep:
5519
    qint64 firstStep = floor(
5520
        mRange.lower /
5521
        mTickStep);  // do not use qFloor here, or we'll lose 64 bit precision
5522
    qint64 lastStep = ceil(
5523
        mRange.upper /
5524
        mTickStep);  // do not use qCeil here, or we'll lose 64 bit precision
5525
    int tickcount = lastStep - firstStep + 1;
5526
    if (tickcount < 0) tickcount = 0;
5527
    mTickVector.resize(tickcount);
5528
    for (int i = 0; i < tickcount; ++i)
5529
      mTickVector[i] = (firstStep + i) * mTickStep;
5530
  } else  // mScaleType == stLogarithmic
5531
  {
5532
    // Generate tick positions according to logbase scaling:
5533
    if (mRange.lower > 0 && mRange.upper > 0)  // positive range
5534
    {
5535
      double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5536
      double currentMag = lowerMag;
5537
      mTickVector.clear();
5538
      mTickVector.append(currentMag);
5539
      while (currentMag < mRange.upper &&
5540
             currentMag > 0)  // currentMag might be zero for ranges ~1e-300,
5541
                              // just cancel in that case
5542
      {
5543
        currentMag *= mScaleLogBase;
5544
        mTickVector.append(currentMag);
5545
      }
5546
    } else if (mRange.lower < 0 && mRange.upper < 0)  // negative range
5547
    {
5548
      double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5549
      double currentMag = lowerMag;
5550
      mTickVector.clear();
5551
      mTickVector.append(currentMag);
5552
      while (currentMag < mRange.upper &&
5553
             currentMag < 0)  // currentMag might be zero for ranges ~1e-300,
5554
                              // just cancel in that case
5555
      {
5556
        currentMag /= mScaleLogBase;
5557
        mTickVector.append(currentMag);
5558
      }
5559
    } else  // invalid range for logarithmic scale, because lower and upper have
5560
            // different sign
5561
    {
5562
      mTickVector.clear();
5563
      qDebug() << Q_FUNC_INFO
5564
               << "Invalid range for logarithmic plot: " << mRange.lower << "-"
5565
               << mRange.upper;
5566
    }
5567
  }
5568
}
5569
5570
/*! \internal
5571
5572
  Called by generateAutoTicks when \ref setAutoSubTicks is set to true.
5573
  Depending on the \a tickStep between two major ticks on the axis, a different
5574
  number of sub ticks is appropriate. For Example taking 4 sub ticks for a \a
5575
  tickStep of 1 makes more sense than taking 5 sub ticks, because this
5576
  corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16667.
5577
  Note that a subtick count of 4 means dividing the major tick step into 5
5578
  sections.
5579
5580
  This is implemented by a hand made lookup for integer tick steps as well as
5581
  fractional tick steps with a fractional part of (approximately) 0.5. If a tick
5582
  step is different (i.e. has no fractional part close to 0.5), the currently
5583
  set sub tick count (\ref setSubTickCount) is returned.
5584
*/
5585
int QCPAxis::calculateAutoSubTickCount(double tickStep) const {
5586
  int result = mSubTickCount;  // default to current setting, if no proper value
5587
                               // can be found
5588
5589
  // get mantissa of tickstep:
5590
  double magnitudeFactor = qPow(
5591
      10.0,
5592
      qFloor(qLn(tickStep) /
5593
             qLn(10.0)));  // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5594
  double tickStepMantissa = tickStep / magnitudeFactor;
5595
5596
  // separate integer and fractional part of mantissa:
5597
  double epsilon = 0.01;
5598
  double intPartf;
5599
  int intPart;
5600
  double fracPart = modf(tickStepMantissa, &intPartf);
5601
  intPart = intPartf;
5602
5603
  // handle cases with (almost) integer mantissa:
5604
  if (fracPart < epsilon || 1.0 - fracPart < epsilon) {
5605
    if (1.0 - fracPart < epsilon) ++intPart;
5606
    switch (intPart) {
5607
      case 1:
5608
        result = 4;
5609
        break;  // 1.0 -> 0.2 substep
5610
      case 2:
5611
        result = 3;
5612
        break;  // 2.0 -> 0.5 substep
5613
      case 3:
5614
        result = 2;
5615
        break;  // 3.0 -> 1.0 substep
5616
      case 4:
5617
        result = 3;
5618
        break;  // 4.0 -> 1.0 substep
5619
      case 5:
5620
        result = 4;
5621
        break;  // 5.0 -> 1.0 substep
5622
      case 6:
5623
        result = 2;
5624
        break;  // 6.0 -> 2.0 substep
5625
      case 7:
5626
        result = 6;
5627
        break;  // 7.0 -> 1.0 substep
5628
      case 8:
5629
        result = 3;
5630
        break;  // 8.0 -> 2.0 substep
5631
      case 9:
5632
        result = 2;
5633
        break;  // 9.0 -> 3.0 substep
5634
    }
5635
  } else {
5636
    // handle cases with significantly fractional mantissa:
5637
    if (qAbs(fracPart - 0.5) < epsilon)  // *.5 mantissa
5638
    {
5639
      switch (intPart) {
5640
        case 1:
5641
          result = 2;
5642
          break;  // 1.5 -> 0.5 substep
5643
        case 2:
5644
          result = 4;
5645
          break;  // 2.5 -> 0.5 substep
5646
        case 3:
5647
          result = 4;
5648
          break;  // 3.5 -> 0.7 substep
5649
        case 4:
5650
          result = 2;
5651
          break;  // 4.5 -> 1.5 substep
5652
        case 5:
5653
          result = 4;
5654
          break;  // 5.5 -> 1.1 substep (won't occur with autoTickStep from here
5655
                  // on)
5656
        case 6:
5657
          result = 4;
5658
          break;  // 6.5 -> 1.3 substep
5659
        case 7:
5660
          result = 2;
5661
          break;  // 7.5 -> 2.5 substep
5662
        case 8:
5663
          result = 4;
5664
          break;  // 8.5 -> 1.7 substep
5665
        case 9:
5666
          result = 4;
5667
          break;  // 9.5 -> 1.9 substep
5668
      }
5669
    }
5670
    // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick
5671
    // marks, leave default
5672
  }
5673
5674
  return result;
5675
}
5676
5677
/* inherits documentation from base class */
5678
void QCPAxis::selectEvent(QMouseEvent *event, bool additive,
5679
                          const QVariant &details,
5680
                          bool *selectionStateChanged) {
5681
  Q_UNUSED(event)
5682
  SelectablePart part = details.value<SelectablePart>();
5683
  if (mSelectableParts.testFlag(part)) {
5684
    SelectableParts selBefore = mSelectedParts;
5685
    setSelectedParts(additive ? mSelectedParts ^ part : part);
5686
    if (selectionStateChanged)
5687
      *selectionStateChanged = mSelectedParts != selBefore;
5688
  }
5689
}
5690
5691
/* inherits documentation from base class */
5692
void QCPAxis::deselectEvent(bool *selectionStateChanged) {
5693
  SelectableParts selBefore = mSelectedParts;
5694
  setSelectedParts(mSelectedParts & ~mSelectableParts);
5695
  if (selectionStateChanged)
5696
    *selectionStateChanged = mSelectedParts != selBefore;
5697
}
5698
5699
/*! \internal
5700
5701
  A convenience function to easily set the QPainter::Antialiased hint on the
5702
  provided \a painter before drawing axis lines.
5703
5704
  This is the antialiasing state the painter passed to the \ref draw method is
5705
  in by default.
5706
5707
  This function takes into account the local setting of the antialiasing flag as
5708
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
5709
  \ref QCustomPlot::setNotAntialiasedElements.
5710
5711
  \see setAntialiased
5712
*/
5713
void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const {
5714
  applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
5715
}
5716
5717
/*! \internal
5718
5719
  Draws the axis with the specified \a painter, using the internal
5720
  QCPAxisPainterPrivate instance.
5721
5722
*/
5723
void QCPAxis::draw(QCPPainter *painter) {
5724
  const int lowTick = mLowestVisibleTick;
5725
  const int highTick = mHighestVisibleTick;
5726
  QVector<double> subTickPositions;  // the final coordToPixel transformed
5727
                                     // vector passed to QCPAxisPainter
5728
  QVector<double> tickPositions;  // the final coordToPixel transformed vector
5729
                                  // passed to QCPAxisPainter
5730
  QVector<QString> tickLabels;    // the final vector passed to QCPAxisPainter
5731
  tickPositions.reserve(highTick - lowTick + 1);
5732
  tickLabels.reserve(highTick - lowTick + 1);
5733
  subTickPositions.reserve(mSubTickVector.size());
5734
5735
  if (mTicks) {
5736
    for (int i = lowTick; i <= highTick; ++i) {
5737
      tickPositions.append(coordToPixel(mTickVector.at(i)));
5738
      if (mTickLabels) tickLabels.append(mTickVectorLabels.at(i));
5739
    }
5740
5741
    if (mSubTickCount > 0) {
5742
      const int subTickCount = mSubTickVector.size();
5743
      for (int i = 0; i < subTickCount;
5744
           ++i)  // no need to check bounds because subticks are always only
5745
                 // created inside current mRange
5746
        subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5747
    }
5748
  }
5749
  // transfer all properties of this axis to QCPAxisPainterPrivate which it
5750
  // needs to draw the axis. Note that some axis painter properties are already
5751
  // set by direct feed-through with QCPAxis setters
5752
  mAxisPainter->type = mAxisType;
5753
  mAxisPainter->basePen = getBasePen();
5754
  mAxisPainter->labelFont = getLabelFont();
5755
  mAxisPainter->labelColor = getLabelColor();
5756
  mAxisPainter->label = mLabel;
5757
  mAxisPainter->substituteExponent =
5758
      mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber;
5759
  mAxisPainter->tickPen = getTickPen();
5760
  mAxisPainter->subTickPen = getSubTickPen();
5761
  mAxisPainter->tickLabelFont = getTickLabelFont();
5762
  mAxisPainter->tickLabelColor = getTickLabelColor();
5763
  mAxisPainter->axisRect = mAxisRect->rect();
5764
  mAxisPainter->viewportRect = mParentPlot->viewport();
5765
  mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
5766
  mAxisPainter->reversedEndings = mRangeReversed;
5767
  mAxisPainter->tickPositions = tickPositions;
5768
  mAxisPainter->tickLabels = tickLabels;
5769
  mAxisPainter->subTickPositions = subTickPositions;
5770
  mAxisPainter->draw(painter);
5771
}
5772
5773
/*! \internal
5774
5775
  Returns via \a lowIndex and \a highIndex, which ticks in the current tick
5776
  vector are visible in the current range. The return values are indices of the
5777
  tick vector, not the positions of the ticks themselves.
5778
5779
  The actual use of this function is when an external tick vector is provided,
5780
  since it might exceed far beyond the currently displayed range, and would
5781
  cause unnecessary calculations e.g. of subticks.
5782
5783
  If all ticks are outside the axis range, an inverted range is returned, i.e.
5784
  highIndex will be smaller than lowIndex. There is one case, where this
5785
  function returns indices that are not really visible in the current axis
5786
  range: When the tick spacing is larger than the axis range size and one tick
5787
  is below the axis range and the next tick is already above the axis range.
5788
  Because in such cases it is usually desirable to know the tick pair, to draw
5789
  proper subticks.
5790
*/
5791
void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const {
5792
  bool lowFound = false;
5793
  bool highFound = false;
5794
  lowIndex = 0;
5795
  highIndex = -1;
5796
5797
  for (int i = 0; i < mTickVector.size(); ++i) {
5798
    if (mTickVector.at(i) >= mRange.lower) {
5799
      lowFound = true;
5800
      lowIndex = i;
5801
      break;
5802
    }
5803
  }
5804
  for (int i = mTickVector.size() - 1; i >= 0; --i) {
5805
    if (mTickVector.at(i) <= mRange.upper) {
5806
      highFound = true;
5807
      highIndex = i;
5808
      break;
5809
    }
5810
  }
5811
5812
  if (!lowFound && highFound)
5813
    lowIndex = highIndex + 1;
5814
  else if (lowFound && !highFound)
5815
    highIndex = lowIndex - 1;
5816
}
5817
5818
/*! \internal
5819
5820
  A log function with the base mScaleLogBase, used mostly for coordinate
5821
  transforms in logarithmic scales with arbitrary log base. Uses the buffered
5822
  mScaleLogBaseLogInv for faster calculation. This is set to
5823
  <tt>1.0/qLn(mScaleLogBase)</tt> in \ref setScaleLogBase.
5824
5825
  \see basePow, setScaleLogBase, setScaleType
5826
*/
5827
double QCPAxis::baseLog(double value) const {
5828
  return qLn(value) * mScaleLogBaseLogInv;
5829
}
5830
5831
/*! \internal
5832
5833
  A power function with the base mScaleLogBase, used mostly for coordinate
5834
  transforms in logarithmic scales with arbitrary log base.
5835
5836
  \see baseLog, setScaleLogBase, setScaleType
5837
*/
5838
double QCPAxis::basePow(double value) const {
5839
  return qPow(mScaleLogBase, value);
5840
}
5841
5842
/*! \internal
5843
5844
  Returns the pen that is used to draw the axis base line. Depending on the
5845
  selection state, this is either mSelectedBasePen or mBasePen.
5846
*/
5847
QPen QCPAxis::getBasePen() const {
5848
  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5849
}
5850
5851
/*! \internal
5852
5853
  Returns the pen that is used to draw the (major) ticks. Depending on the
5854
  selection state, this is either mSelectedTickPen or mTickPen.
5855
*/
5856
QPen QCPAxis::getTickPen() const {
5857
  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5858
}
5859
5860
/*! \internal
5861
5862
  Returns the pen that is used to draw the subticks. Depending on the selection
5863
  state, this is either mSelectedSubTickPen or mSubTickPen.
5864
*/
5865
QPen QCPAxis::getSubTickPen() const {
5866
  return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
5867
}
5868
5869
/*! \internal
5870
5871
  Returns the font that is used to draw the tick labels. Depending on the
5872
  selection state, this is either mSelectedTickLabelFont or mTickLabelFont.
5873
*/
5874
QFont QCPAxis::getTickLabelFont() const {
5875
  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont
5876
                                               : mTickLabelFont;
5877
}
5878
5879
/*! \internal
5880
5881
  Returns the font that is used to draw the axis label. Depending on the
5882
  selection state, this is either mSelectedLabelFont or mLabelFont.
5883
*/
5884
QFont QCPAxis::getLabelFont() const {
5885
  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
5886
}
5887
5888
/*! \internal
5889
5890
  Returns the color that is used to draw the tick labels. Depending on the
5891
  selection state, this is either mSelectedTickLabelColor or mTickLabelColor.
5892
*/
5893
QColor QCPAxis::getTickLabelColor() const {
5894
  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor
5895
                                               : mTickLabelColor;
5896
}
5897
5898
/*! \internal
5899
5900
  Returns the color that is used to draw the axis label. Depending on the
5901
  selection state, this is either mSelectedLabelColor or mLabelColor.
5902
*/
5903
QColor QCPAxis::getLabelColor() const {
5904
  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor
5905
                                              : mLabelColor;
5906
}
5907
5908
/*! \internal
5909
5910
  Returns the appropriate outward margin for this axis. It is needed if \ref
5911
  QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis
5912
  with axis type \ref atLeft will return an appropriate left margin, \ref
5913
  atBottom will return an appropriate bottom margin and so forth. For the
5914
  calculation, this function goes through similar steps as \ref draw, so
5915
  changing one function likely requires the modification of the other one as
5916
  well.
5917
5918
  The margin consists of the outward tick length, tick label padding, tick label
5919
  size, label padding, label size, and padding.
5920
5921
  The margin is cached internally, so repeated calls while leaving the axis
5922
  range, fonts, etc. unchanged are very fast.
5923
*/
5924
int QCPAxis::calculateMargin() {
5925
  if (!mVisible)  // if not visible, directly return 0, don't cache 0 because we
5926
                  // can't react to setVisible in QCPAxis
5927
    return 0;
5928
5929
  if (mCachedMarginValid) return mCachedMargin;
5930
5931
  // run through similar steps as QCPAxis::draw, and caluclate margin needed to
5932
  // fit axis and its labels
5933
  int margin = 0;
5934
5935
  int lowTick, highTick;
5936
  visibleTickBounds(lowTick, highTick);
5937
  QVector<double> tickPositions;  // the final coordToPixel transformed vector
5938
                                  // passed to QCPAxisPainter
5939
  QVector<QString> tickLabels;    // the final vector passed to QCPAxisPainter
5940
  tickPositions.reserve(highTick - lowTick + 1);
5941
  tickLabels.reserve(highTick - lowTick + 1);
5942
  if (mTicks) {
5943
    for (int i = lowTick; i <= highTick; ++i) {
5944
      tickPositions.append(coordToPixel(mTickVector.at(i)));
5945
      if (mTickLabels) tickLabels.append(mTickVectorLabels.at(i));
5946
    }
5947
  }
5948
  // transfer all properties of this axis to QCPAxisPainterPrivate which it
5949
  // needs to calculate the size. Note that some axis painter properties are
5950
  // already set by direct feed-through with QCPAxis setters
5951
  mAxisPainter->type = mAxisType;
5952
  mAxisPainter->labelFont = getLabelFont();
5953
  mAxisPainter->label = mLabel;
5954
  mAxisPainter->tickLabelFont = mTickLabelFont;
5955
  mAxisPainter->axisRect = mAxisRect->rect();
5956
  mAxisPainter->viewportRect = mParentPlot->viewport();
5957
  mAxisPainter->tickPositions = tickPositions;
5958
  mAxisPainter->tickLabels = tickLabels;
5959
  margin += mAxisPainter->size();
5960
  margin += mPadding;
5961
5962
  mCachedMargin = margin;
5963
  mCachedMarginValid = true;
5964
  return margin;
5965
}
5966
5967
/* inherits documentation from base class */
5968
QCP::Interaction QCPAxis::selectionCategory() const { return QCP::iSelectAxes; }
5969
5970
////////////////////////////////////////////////////////////////////////////////////////////////////
5971
//////////////////// QCPAxisPainterPrivate
5972
////////////////////////////////////////////////////////////////////////////////////////////////////
5973
5974
/*! \class QCPAxisPainterPrivate
5975
5976
  \internal
5977
  \brief (Private)
5978
5979
  This is a private class and not part of the public QCustomPlot interface.
5980
5981
  It is used by QCPAxis to do the low-level drawing of axis backbone, tick
5982
  marks, tick labels and axis label. It also buffers the labels to reduce replot
5983
  times. The parameters are configured by directly accessing the public member
5984
  variables.
5985
*/
5986
5987
/*!
5988
  Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new
5989
  instance on every redraw, to utilize the caching mechanisms.
5990
*/
5991
QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot)
5992
    : type(QCPAxis::atLeft),
5993
      basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
5994
      lowerEnding(QCPLineEnding::esNone),
5995
      upperEnding(QCPLineEnding::esNone),
5996
      labelPadding(0),
5997
      tickLabelPadding(0),
5998
      tickLabelRotation(0),
5999
      tickLabelSide(QCPAxis::lsOutside),
6000
      substituteExponent(true),
6001
      numberMultiplyCross(false),
6002
      tickLengthIn(5),
6003
      tickLengthOut(0),
6004
      subTickLengthIn(2),
6005
      subTickLengthOut(0),
6006
      tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6007
      subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6008
      offset(0),
6009
      abbreviateDecimalPowers(false),
6010
      reversedEndings(false),
6011
      mParentPlot(parentPlot),
6012
      mLabelCache(16)  // cache at most 16 (tick) labels
6013
{}
6014
6015
QCPAxisPainterPrivate::~QCPAxisPainterPrivate() {}
6016
6017
/*! \internal
6018
6019
  Draws the axis with the specified \a painter.
6020
6021
  The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox,
6022
  mLabelSelectionBox) are set here, too.
6023
*/
6024
void QCPAxisPainterPrivate::draw(QCPPainter *painter) {
6025
  QByteArray newHash = generateLabelParameterHash();
6026
  if (newHash != mLabelParameterHash) {
6027
    mLabelCache.clear();
6028
    mLabelParameterHash = newHash;
6029
  }
6030
6031
  QPoint origin;
6032
  switch (type) {
6033
    case QCPAxis::atLeft:
6034
      origin = axisRect.bottomLeft() + QPoint(-offset, 0);
6035
      break;
6036
    case QCPAxis::atRight:
6037
      origin = axisRect.bottomRight() + QPoint(+offset, 0);
6038
      break;
6039
    case QCPAxis::atTop:
6040
      origin = axisRect.topLeft() + QPoint(0, -offset);
6041
      break;
6042
    case QCPAxis::atBottom:
6043
      origin = axisRect.bottomLeft() + QPoint(0, +offset);
6044
      break;
6045
  }
6046
6047
  double xCor = 0,
6048
         yCor = 0;  // paint system correction, for pixel exact matches (affects
6049
                    // baselines and ticks of top/right axes)
6050
  switch (type) {
6051
    case QCPAxis::atTop:
6052
      yCor = -1;
6053
      break;
6054
    case QCPAxis::atRight:
6055
      xCor = 1;
6056
      break;
6057
    default:
6058
      break;
6059
  }
6060
  int margin = 0;
6061
  // draw baseline:
6062
  QLineF baseLine;
6063
  painter->setPen(basePen);
6064
  if (QCPAxis::orientation(type) == Qt::Horizontal)
6065
    baseLine.setPoints(origin + QPointF(xCor, yCor),
6066
                       origin + QPointF(axisRect.width() + xCor, yCor));
6067
  else
6068
    baseLine.setPoints(origin + QPointF(xCor, yCor),
6069
                       origin + QPointF(xCor, -axisRect.height() + yCor));
6070
  if (reversedEndings)
6071
    baseLine = QLineF(baseLine.p2(),
6072
                      baseLine.p1());  // won't make a difference for line
6073
                                       // itself, but for line endings later
6074
  painter->drawLine(baseLine);
6075
6076
  // draw ticks:
6077
  if (!tickPositions.isEmpty()) {
6078
    painter->setPen(tickPen);
6079
    int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight)
6080
                      ? -1
6081
                      : 1;  // direction of ticks ("inward" is right for left
6082
                            // axis and left for right axis)
6083
    if (QCPAxis::orientation(type) == Qt::Horizontal) {
6084
      for (int i = 0; i < tickPositions.size(); ++i)
6085
        painter->drawLine(QLineF(tickPositions.at(i) + xCor,
6086
                                 origin.y() - tickLengthOut * tickDir + yCor,
6087
                                 tickPositions.at(i) + xCor,
6088
                                 origin.y() + tickLengthIn * tickDir + yCor));
6089
    } else {
6090
      for (int i = 0; i < tickPositions.size(); ++i)
6091
        painter->drawLine(QLineF(origin.x() - tickLengthOut * tickDir + xCor,
6092
                                 tickPositions.at(i) + yCor,
6093
                                 origin.x() + tickLengthIn * tickDir + xCor,
6094
                                 tickPositions.at(i) + yCor));
6095
    }
6096
  }
6097
6098
  // draw subticks:
6099
  if (!subTickPositions.isEmpty()) {
6100
    painter->setPen(subTickPen);
6101
    // direction of ticks ("inward" is right for left axis and left for right
6102
    // axis)
6103
    int tickDir =
6104
        (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6105
    if (QCPAxis::orientation(type) == Qt::Horizontal) {
6106
      for (int i = 0; i < subTickPositions.size(); ++i)
6107
        painter->drawLine(
6108
            QLineF(subTickPositions.at(i) + xCor,
6109
                   origin.y() - subTickLengthOut * tickDir + yCor,
6110
                   subTickPositions.at(i) + xCor,
6111
                   origin.y() + subTickLengthIn * tickDir + yCor));
6112
    } else {
6113
      for (int i = 0; i < subTickPositions.size(); ++i)
6114
        painter->drawLine(QLineF(origin.x() - subTickLengthOut * tickDir + xCor,
6115
                                 subTickPositions.at(i) + yCor,
6116
                                 origin.x() + subTickLengthIn * tickDir + xCor,
6117
                                 subTickPositions.at(i) + yCor));
6118
    }
6119
  }
6120
  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6121
6122
  // draw axis base endings:
6123
  bool antialiasingBackup = painter->antialiasing();
6124
  painter->setAntialiasing(true);  // always want endings to be antialiased,
6125
                                   // even if base and ticks themselves aren't
6126
  painter->setBrush(QBrush(basePen.color()));
6127
  QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6128
  if (lowerEnding.style() != QCPLineEnding::esNone)
6129
    lowerEnding.draw(
6130
        painter,
6131
        QVector2D(baseLine.p1()) - baseLineVector.normalized() *
6132
                                       lowerEnding.realLength() *
6133
                                       (lowerEnding.inverted() ? -1 : 1),
6134
        -baseLineVector);
6135
  if (upperEnding.style() != QCPLineEnding::esNone)
6136
    upperEnding.draw(
6137
        painter,
6138
        QVector2D(baseLine.p2()) + baseLineVector.normalized() *
6139
                                       upperEnding.realLength() *
6140
                                       (upperEnding.inverted() ? -1 : 1),
6141
        baseLineVector);
6142
  painter->setAntialiasing(antialiasingBackup);
6143
6144
  // tick labels:
6145
  QRect oldClipRect;
6146
  if (tickLabelSide ==
6147
      QCPAxis::lsInside)  // if using inside labels, clip them to the axis rect
6148
  {
6149
    oldClipRect = painter->clipRegion().boundingRect();
6150
    painter->setClipRect(axisRect);
6151
  }
6152
  QSize tickLabelsSize(
6153
      0,
6154
      0);  // size of largest tick label, for offset calculation of axis label
6155
  if (!tickLabels.isEmpty()) {
6156
    if (tickLabelSide == QCPAxis::lsOutside) margin += tickLabelPadding;
6157
    painter->setFont(tickLabelFont);
6158
    painter->setPen(QPen(tickLabelColor));
6159
    const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6160
    int distanceToAxis = margin;
6161
    if (tickLabelSide == QCPAxis::lsInside)
6162
      distanceToAxis =
6163
          -(qMax(tickLengthIn, subTickLengthIn) + tickLabelPadding);
6164
    for (int i = 0; i < maxLabelIndex; ++i)
6165
      placeTickLabel(painter, tickPositions.at(i), distanceToAxis,
6166
                     tickLabels.at(i), &tickLabelsSize);
6167
    if (tickLabelSide == QCPAxis::lsOutside)
6168
      margin += (QCPAxis::orientation(type) == Qt::Horizontal)
6169
                    ? tickLabelsSize.height()
6170
                    : tickLabelsSize.width();
6171
  }
6172
  if (tickLabelSide == QCPAxis::lsInside) painter->setClipRect(oldClipRect);
6173
6174
  // axis label:
6175
  QRect labelBounds;
6176
  if (!label.isEmpty()) {
6177
    margin += labelPadding;
6178
    painter->setFont(labelFont);
6179
    painter->setPen(QPen(labelColor));
6180
    labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0,
6181
                                                      Qt::TextDontClip, label);
6182
    if (type == QCPAxis::atLeft) {
6183
      QTransform oldTransform = painter->transform();
6184
      painter->translate((origin.x() - margin - labelBounds.height()),
6185
                         origin.y());
6186
      painter->rotate(-90);
6187
      painter->drawText(0, 0, axisRect.height(), labelBounds.height(),
6188
                        Qt::TextDontClip | Qt::AlignCenter, label);
6189
      painter->setTransform(oldTransform);
6190
    } else if (type == QCPAxis::atRight) {
6191
      QTransform oldTransform = painter->transform();
6192
      painter->translate((origin.x() + margin + labelBounds.height()),
6193
                         origin.y() - axisRect.height());
6194
      painter->rotate(90);
6195
      painter->drawText(0, 0, axisRect.height(), labelBounds.height(),
6196
                        Qt::TextDontClip | Qt::AlignCenter, label);
6197
      painter->setTransform(oldTransform);
6198
    } else if (type == QCPAxis::atTop)
6199
      painter->drawText(origin.x(), origin.y() - margin - labelBounds.height(),
6200
                        axisRect.width(), labelBounds.height(),
6201
                        Qt::TextDontClip | Qt::AlignCenter, label);
6202
    else if (type == QCPAxis::atBottom)
6203
      painter->drawText(origin.x(), origin.y() + margin, axisRect.width(),
6204
                        labelBounds.height(),
6205
                        Qt::TextDontClip | Qt::AlignCenter, label);
6206
  }
6207
6208
  // set selection boxes:
6209
  int selectionTolerance = 0;
6210
  if (mParentPlot)
6211
    selectionTolerance = mParentPlot->selectionTolerance();
6212
  else
6213
    qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6214
  int selAxisOutSize =
6215
      qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6216
  int selAxisInSize = selectionTolerance;
6217
  int selTickLabelSize;
6218
  int selTickLabelOffset;
6219
  if (tickLabelSide == QCPAxis::lsOutside) {
6220
    selTickLabelSize =
6221
        (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height()
6222
                                                      : tickLabelsSize.width());
6223
    selTickLabelOffset =
6224
        qMax(tickLengthOut, subTickLengthOut) + tickLabelPadding;
6225
  } else {
6226
    selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal
6227
                             ? tickLabelsSize.height()
6228
                             : tickLabelsSize.width());
6229
    selTickLabelOffset =
6230
        -(qMax(tickLengthIn, subTickLengthIn) + tickLabelPadding);
6231
  }
6232
  int selLabelSize = labelBounds.height();
6233
  int selLabelOffset =
6234
      qMax(tickLengthOut, subTickLengthOut) +
6235
      (!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside
6236
           ? tickLabelPadding + selTickLabelSize
6237
           : 0) +
6238
      labelPadding;
6239
  if (type == QCPAxis::atLeft) {
6240
    mAxisSelectionBox.setCoords(origin.x() - selAxisOutSize, axisRect.top(),
6241
                                origin.x() + selAxisInSize, axisRect.bottom());
6242
    mTickLabelsSelectionBox.setCoords(
6243
        origin.x() - selTickLabelOffset - selTickLabelSize, axisRect.top(),
6244
        origin.x() - selTickLabelOffset, axisRect.bottom());
6245
    mLabelSelectionBox.setCoords(origin.x() - selLabelOffset - selLabelSize,
6246
                                 axisRect.top(), origin.x() - selLabelOffset,
6247
                                 axisRect.bottom());
6248
  } else if (type == QCPAxis::atRight) {
6249
    mAxisSelectionBox.setCoords(origin.x() - selAxisInSize, axisRect.top(),
6250
                                origin.x() + selAxisOutSize, axisRect.bottom());
6251
    mTickLabelsSelectionBox.setCoords(
6252
        origin.x() + selTickLabelOffset + selTickLabelSize, axisRect.top(),
6253
        origin.x() + selTickLabelOffset, axisRect.bottom());
6254
    mLabelSelectionBox.setCoords(origin.x() + selLabelOffset + selLabelSize,
6255
                                 axisRect.top(), origin.x() + selLabelOffset,
6256
                                 axisRect.bottom());
6257
  } else if (type == QCPAxis::atTop) {
6258
    mAxisSelectionBox.setCoords(axisRect.left(), origin.y() - selAxisOutSize,
6259
                                axisRect.right(), origin.y() + selAxisInSize);
6260
    mTickLabelsSelectionBox.setCoords(
6261
        axisRect.left(), origin.y() - selTickLabelOffset - selTickLabelSize,
6262
        axisRect.right(), origin.y() - selTickLabelOffset);
6263
    mLabelSelectionBox.setCoords(axisRect.left(),
6264
                                 origin.y() - selLabelOffset - selLabelSize,
6265
                                 axisRect.right(), origin.y() - selLabelOffset);
6266
  } else if (type == QCPAxis::atBottom) {
6267
    mAxisSelectionBox.setCoords(axisRect.left(), origin.y() - selAxisInSize,
6268
                                axisRect.right(), origin.y() + selAxisOutSize);
6269
    mTickLabelsSelectionBox.setCoords(
6270
        axisRect.left(), origin.y() + selTickLabelOffset + selTickLabelSize,
6271
        axisRect.right(), origin.y() + selTickLabelOffset);
6272
    mLabelSelectionBox.setCoords(axisRect.left(),
6273
                                 origin.y() + selLabelOffset + selLabelSize,
6274
                                 axisRect.right(), origin.y() + selLabelOffset);
6275
  }
6276
  mAxisSelectionBox = mAxisSelectionBox.normalized();
6277
  mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
6278
  mLabelSelectionBox = mLabelSelectionBox.normalized();
6279
  // draw hitboxes for debug purposes:
6280
  // painter->setBrush(Qt::NoBrush);
6281
  // painter->drawRects(QVector<QRect>() << mAxisSelectionBox <<
6282
  // mTickLabelsSelectionBox << mLabelSelectionBox);
6283
}
6284
6285
/*! \internal
6286
6287
  Returns the size ("margin" in QCPAxisRect context, so measured perpendicular
6288
  to the axis backbone direction) needed to fit the axis.
6289
*/
6290
int QCPAxisPainterPrivate::size() const {
6291
  int result = 0;
6292
6293
  // get length of tick marks pointing outwards:
6294
  if (!tickPositions.isEmpty())
6295
    result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6296
6297
  // calculate size of tick labels:
6298
  if (tickLabelSide == QCPAxis::lsOutside) {
6299
    QSize tickLabelsSize(0, 0);
6300
    if (!tickLabels.isEmpty()) {
6301
      for (int i = 0; i < tickLabels.size(); ++i)
6302
        getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6303
      result += QCPAxis::orientation(type) == Qt::Horizontal
6304
                    ? tickLabelsSize.height()
6305
                    : tickLabelsSize.width();
6306
      result += tickLabelPadding;
6307
    }
6308
  }
6309
6310
  // calculate size of axis label (only height needed, because left/right labels
6311
  // are rotated by 90 degrees):
6312
  if (!label.isEmpty()) {
6313
    QFontMetrics fontMetrics(labelFont);
6314
    QRect bounds;
6315
    bounds = fontMetrics.boundingRect(
6316
        0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter,
6317
        label);
6318
    result += bounds.height() + labelPadding;
6319
  }
6320
6321
  return result;
6322
}
6323
6324
/*! \internal
6325
6326
  Clears the internal label cache. Upon the next \ref draw, all labels will be
6327
  created new. This method is called automatically in \ref draw, if any
6328
  parameters have changed that invalidate the cached labels, such as font,
6329
  color, etc.
6330
*/
6331
void QCPAxisPainterPrivate::clearCache() { mLabelCache.clear(); }
6332
6333
/*! \internal
6334
6335
  Returns a hash that allows uniquely identifying whether the label parameters
6336
  have changed such that the cached labels must be refreshed (\ref clearCache).
6337
  It is used in \ref draw. If the return value of this method hasn't changed
6338
  since the last redraw, the respective label parameters haven't changed and
6339
  cached labels may be used.
6340
*/
6341
QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const {
6342
  QByteArray result;
6343
  result.append(QByteArray::number(tickLabelRotation));
6344
  result.append(QByteArray::number((int)tickLabelSide));
6345
  result.append(QByteArray::number((int)substituteExponent));
6346
  result.append(QByteArray::number((int)numberMultiplyCross));
6347
  result.append(tickLabelColor.name().toLatin1() +
6348
                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
6356
  label cache to significantly speed up drawing of labels that were drawn in
6357
  previous calls. The tick label is always bound to an axis, the distance to the
6358
  axis is controllable via \a distanceToAxis in pixels. The pixel position in
6359
  the axis direction is passed in the \a position parameter. Hence for the
6360
  bottom axis, \a position would indicate the horizontal pixel position (not
6361
  coordinate), at which the label should be drawn.
6362
6363
  In order to later draw the axis label in a place that doesn't overlap with the
6364
  tick labels, the largest tick label size is needed. This is acquired by
6365
  passing a \a tickLabelsSize to the \ref drawTickLabel calls during the process
6366
  of drawing all tick labels of one axis. In every call, \a tickLabelsSize is
6367
  expanded, if the drawn label exceeds the value \a tickLabelsSize currently
6368
  holds.
6369
6370
  The label is drawn with the font and pen that are currently set on the \a
6371
  painter. To draw superscripted powers, the font is temporarily made smaller by
6372
  a fixed factor (see \ref getTickLabelData).
6373
*/
6374
void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position,
6375
                                           int distanceToAxis,
6376
                                           const QString &text,
6377
                                           QSize *tickLabelsSize) {
6378
  // warning: if you change anything here, also adapt getMaxTickLabelSize()
6379
  // accordingly!
6380
  if (text.isEmpty()) return;
6381
  QSize finalSize;
6382
  QPointF labelAnchor;
6383
  switch (type) {
6384
    case QCPAxis::atLeft:
6385
      labelAnchor =
6386
          QPointF(axisRect.left() - distanceToAxis - offset, position);
6387
      break;
6388
    case QCPAxis::atRight:
6389
      labelAnchor =
6390
          QPointF(axisRect.right() + distanceToAxis + offset, position);
6391
      break;
6392
    case QCPAxis::atTop:
6393
      labelAnchor = QPointF(position, axisRect.top() - distanceToAxis - offset);
6394
      break;
6395
    case QCPAxis::atBottom:
6396
      labelAnchor =
6397
          QPointF(position, axisRect.bottom() + distanceToAxis + offset);
6398
      break;
6399
  }
6400
  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) &&
6401
      !painter->modes().testFlag(
6402
          QCPPainter::pmNoCaching))  // label caching enabled
6403
  {
6404
    CachedLabel *cachedLabel =
6405
        mLabelCache.take(text);  // attempt to get label from cache
6406
    if (!cachedLabel)            // no cached label existed, create it
6407
    {
6408
      cachedLabel = new CachedLabel;
6409
      TickLabelData labelData = getTickLabelData(painter->font(), text);
6410
      cachedLabel->offset = getTickLabelDrawOffset(labelData) +
6411
                            labelData.rotatedTotalBounds.topLeft();
6412
      cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6413
      cachedLabel->pixmap.fill(Qt::transparent);
6414
      QCPPainter cachePainter(&cachedLabel->pixmap);
6415
      cachePainter.setPen(painter->pen());
6416
      drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(),
6417
                    -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6418
    }
6419
    // if label would be partly clipped by widget border on sides, don't draw it
6420
    // (only for outside tick labels):
6421
    bool labelClippedByBorder = false;
6422
    if (tickLabelSide == QCPAxis::lsOutside) {
6423
      if (QCPAxis::orientation(type) == Qt::Horizontal)
6424
        labelClippedByBorder =
6425
            labelAnchor.x() + cachedLabel->offset.x() +
6426
                    cachedLabel->pixmap.width() >
6427
                viewportRect.right() ||
6428
            labelAnchor.x() + cachedLabel->offset.x() < viewportRect.left();
6429
      else
6430
        labelClippedByBorder =
6431
            labelAnchor.y() + cachedLabel->offset.y() +
6432
                    cachedLabel->pixmap.height() >
6433
                viewportRect.bottom() ||
6434
            labelAnchor.y() + cachedLabel->offset.y() < viewportRect.top();
6435
    }
6436
    if (!labelClippedByBorder) {
6437
      painter->drawPixmap(labelAnchor + cachedLabel->offset,
6438
                          cachedLabel->pixmap);
6439
      finalSize = cachedLabel->pixmap.size();
6440
    }
6441
    mLabelCache.insert(text,
6442
                       cachedLabel);  // return label to cache or insert for the
6443
                                      // first time if newly created
6444
  } else  // label caching disabled, draw text directly on surface:
6445
  {
6446
    TickLabelData labelData = getTickLabelData(painter->font(), text);
6447
    QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6448
    // if label would be partly clipped by widget border on sides, don't draw it
6449
    // (only for outside tick labels):
6450
    bool labelClippedByBorder = false;
6451
    if (tickLabelSide == QCPAxis::lsOutside) {
6452
      if (QCPAxis::orientation(type) == Qt::Horizontal)
6453
        labelClippedByBorder =
6454
            finalPosition.x() + (labelData.rotatedTotalBounds.width() +
6455
                                 labelData.rotatedTotalBounds.left()) >
6456
                viewportRect.right() ||
6457
            finalPosition.x() + labelData.rotatedTotalBounds.left() <
6458
                viewportRect.left();
6459
      else
6460
        labelClippedByBorder =
6461
            finalPosition.y() + (labelData.rotatedTotalBounds.height() +
6462
                                 labelData.rotatedTotalBounds.top()) >
6463
                viewportRect.bottom() ||
6464
            finalPosition.y() + labelData.rotatedTotalBounds.top() <
6465
                viewportRect.top();
6466
    }
6467
    if (!labelClippedByBorder) {
6468
      drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6469
      finalSize = labelData.rotatedTotalBounds.size();
6470
    }
6471
  }
6472
6473
  // expand passed tickLabelsSize if current tick label is larger:
6474
  if (finalSize.width() > tickLabelsSize->width())
6475
    tickLabelsSize->setWidth(finalSize.width());
6476
  if (finalSize.height() > tickLabelsSize->height())
6477
    tickLabelsSize->setHeight(finalSize.height());
6478
}
6479
6480
/*! \internal
6481
6482
  This is a \ref placeTickLabel helper function.
6483
6484
  Draws the tick label specified in \a labelData with \a painter at the pixel
6485
  positions \a x and \a y. This function is used by \ref placeTickLabel to
6486
  create new tick labels for the cache, or to directly draw the labels on the
6487
  QCustomPlot surface when label caching is disabled, i.e. when
6488
  QCP::phCacheLabels plotting hint is not set.
6489
*/
6490
void QCPAxisPainterPrivate::drawTickLabel(
6491
    QCPPainter *painter, double x, double y,
6492
    const TickLabelData &labelData) const {
6493
  // backup painter settings that we're about to change:
6494
  QTransform oldTransform = painter->transform();
6495
  QFont oldFont = painter->font();
6496
6497
  // transform painter to position/rotation:
6498
  painter->translate(x, y);
6499
  if (!qFuzzyIsNull(tickLabelRotation)) painter->rotate(tickLabelRotation);
6500
6501
  // draw text:
6502
  if (!labelData.expPart
6503
           .isEmpty())  // indicator that beautiful powers must be used
6504
  {
6505
    painter->setFont(labelData.baseFont);
6506
    painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6507
    painter->setFont(labelData.expFont);
6508
    painter->drawText(labelData.baseBounds.width() + 1, 0,
6509
                      labelData.expBounds.width(), labelData.expBounds.height(),
6510
                      Qt::TextDontClip, labelData.expPart);
6511
  } else {
6512
    painter->setFont(labelData.baseFont);
6513
    painter->drawText(0, 0, labelData.totalBounds.width(),
6514
                      labelData.totalBounds.height(),
6515
                      Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6516
  }
6517
6518
  // reset painter settings to what it was before:
6519
  painter->setTransform(oldTransform);
6520
  painter->setFont(oldFont);
6521
}
6522
6523
/*! \internal
6524
6525
  This is a \ref placeTickLabel helper function.
6526
6527
  Transforms the passed \a text and \a font to a tickLabelData structure that
6528
  can then be further processed by \ref getTickLabelDrawOffset and \ref
6529
  drawTickLabel. It splits the text into base and exponent if necessary (member
6530
  substituteExponent) and calculates appropriate bounding boxes.
6531
*/
6532
QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(
6533
    const QFont &font, const QString &text) const {
6534
  TickLabelData result;
6535
6536
  // determine whether beautiful decimal powers should be used
6537
  bool useBeautifulPowers = false;
6538
  int ePos = -1;   // first index of exponent part, text before that will be
6539
                   // basePart, text until eLast will be expPart
6540
  int eLast = -1;  // last index of exponent part, rest of text after this will
6541
                   // be suffixPart
6542
  if (substituteExponent) {
6543
    ePos = text.indexOf(QLatin1Char('e'));
6544
    if (ePos > 0 && text.at(ePos - 1).isDigit()) {
6545
      eLast = ePos;
6546
      while (eLast + 1 < text.size() &&
6547
             (text.at(eLast + 1) == QLatin1Char('+') ||
6548
              text.at(eLast + 1) == QLatin1Char('-') ||
6549
              text.at(eLast + 1).isDigit()))
6550
        ++eLast;
6551
      if (eLast > ePos)  // only if also to right of 'e' is a digit/+/-
6552
                         // interpret it as beautifiable power
6553
        useBeautifulPowers = true;
6554
    }
6555
  }
6556
6557
  // calculate text bounding rects and do string preparation for beautiful
6558
  // decimal powers:
6559
  result.baseFont = font;
6560
  if (result.baseFont.pointSizeF() >
6561
      0)  // might return -1 if specified with setPixelSize, in that case we
6562
          // can't do correction in next line
6563
    result.baseFont.setPointSizeF(
6564
        result.baseFont.pointSizeF() +
6565
        0.05);  // QFontMetrics.boundingRect has a bug for exact point sizes
6566
                // that make the results oscillate due to internal rounding
6567
  if (useBeautifulPowers) {
6568
    // split text into parts of number/symbol that will be drawn normally and
6569
    // part that will be drawn as exponent:
6570
    result.basePart = text.left(ePos);
6571
    // in log scaling, we want to turn "1*10^n" into "10^n", else add
6572
    // multiplication sign and decimal base:
6573
    if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6574
      result.basePart = QLatin1String("10");
6575
    else
6576
      result.basePart +=
6577
          (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) +
6578
          QLatin1String("10");
6579
    result.expPart = text.mid(ePos + 1);
6580
    // clip "+" and leading zeros off expPart:
6581
    while (result.expPart.length() > 2 &&
6582
           result.expPart.at(1) ==
6583
               QLatin1Char('0'))  // length > 2 so we leave one zero when
6584
                                  // numberFormatChar is 'e'
6585
      result.expPart.remove(1, 1);
6586
    if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
6587
      result.expPart.remove(0, 1);
6588
    // prepare smaller font for exponent:
6589
    result.expFont = font;
6590
    if (result.expFont.pointSize() > 0)
6591
      result.expFont.setPointSize(result.expFont.pointSize() * 0.75);
6592
    else
6593
      result.expFont.setPixelSize(result.expFont.pixelSize() * 0.75);
6594
    // calculate bounding rects of base part, exponent part and total one:
6595
    result.baseBounds =
6596
        QFontMetrics(result.baseFont)
6597
            .boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6598
    result.expBounds =
6599
        QFontMetrics(result.expFont)
6600
            .boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6601
    result.totalBounds = result.baseBounds.adjusted(
6602
        0, 0, result.expBounds.width() + 2,
6603
        0);  // +2 consists of the 1 pixel spacing between base and exponent
6604
             // (see drawTickLabel) and an extra pixel to include AA
6605
  } else     // useBeautifulPowers == false
6606
  {
6607
    result.basePart = text;
6608
    result.totalBounds =
6609
        QFontMetrics(result.baseFont)
6610
            .boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter,
6611
                          result.basePart);
6612
  }
6613
  result.totalBounds.moveTopLeft(QPoint(
6614
      0, 0));  // want bounding box aligned top left at origin, independent of
6615
               // how it was created, to make further processing simpler
6616
6617
  // calculate possibly different bounding rect after rotation:
6618
  result.rotatedTotalBounds = result.totalBounds;
6619
  if (!qFuzzyIsNull(tickLabelRotation)) {
6620
    QTransform transform;
6621
    transform.rotate(tickLabelRotation);
6622
    result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6623
  }
6624
6625
  return result;
6626
}
6627
6628
/*! \internal
6629
6630
  This is a \ref placeTickLabel helper function.
6631
6632
  Calculates the offset at which the top left corner of the specified tick label
6633
  shall be drawn. The offset is relative to a point right next to the tick the
6634
  label belongs to.
6635
6636
  This function is thus responsible for e.g. centering tick labels under ticks
6637
  and positioning them appropriately when they are rotated.
6638
*/
6639
QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(
6640
    const TickLabelData &labelData) const {
6641
  /*
6642
    calculate label offset from base point at tick (non-trivial, for best visual
6643
    appearance): short explanation for bottom axis: The anchor, i.e. the point
6644
    in the label that is placed horizontally under the corresponding tick is
6645
    always on the label side that is closer to the axis (e.g. the left side of
6646
    the text when we're rotating clockwise). On that side, the height is halved
6647
    and the resulting point is defined the anchor. This way, a 90 degree rotated
6648
    text will be centered under the tick (i.e. displaced horizontally by half
6649
    its height). At the same time, a 45 degree rotated text will "point toward"
6650
    its tick, as is typical for rotated tick labels.
6651
  */
6652
  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6653
  bool flip =
6654
      qFuzzyCompare(qAbs(tickLabelRotation),
6655
                    90.0);  // perfect +/-90 degree flip. Indicates vertical
6656
                            // label centering on vertical axes.
6657
  double radians = tickLabelRotation / 180.0 * M_PI;
6658
  int x = 0, y = 0;
6659
  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) ||
6660
      (type == QCPAxis::atRight &&
6661
       tickLabelSide ==
6662
           QCPAxis::lsInside))  // Anchor at right side of tick label
6663
  {
6664
    if (doRotation) {
6665
      if (tickLabelRotation > 0) {
6666
        x = -qCos(radians) * labelData.totalBounds.width();
6667
        y = flip ? -labelData.totalBounds.width() / 2.0
6668
                 : -qSin(radians) * labelData.totalBounds.width() -
6669
                       qCos(radians) * labelData.totalBounds.height() / 2.0;
6670
      } else {
6671
        x = -qCos(-radians) * labelData.totalBounds.width() -
6672
            qSin(-radians) * labelData.totalBounds.height();
6673
        y = flip ? +labelData.totalBounds.width() / 2.0
6674
                 : +qSin(-radians) * labelData.totalBounds.width() -
6675
                       qCos(-radians) * labelData.totalBounds.height() / 2.0;
6676
      }
6677
    } else {
6678
      x = -labelData.totalBounds.width();
6679
      y = -labelData.totalBounds.height() / 2.0;
6680
    }
6681
  } else if ((type == QCPAxis::atRight &&
6682
              tickLabelSide == QCPAxis::lsOutside) ||
6683
             (type == QCPAxis::atLeft &&
6684
              tickLabelSide ==
6685
                  QCPAxis::lsInside))  // Anchor at left side of tick label
6686
  {
6687
    if (doRotation) {
6688
      if (tickLabelRotation > 0) {
6689
        x = +qSin(radians) * labelData.totalBounds.height();
6690
        y = flip ? -labelData.totalBounds.width() / 2.0
6691
                 : -qCos(radians) * labelData.totalBounds.height() / 2.0;
6692
      } else {
6693
        x = 0;
6694
        y = flip ? +labelData.totalBounds.width() / 2.0
6695
                 : -qCos(-radians) * labelData.totalBounds.height() / 2.0;
6696
      }
6697
    } else {
6698
      x = 0;
6699
      y = -labelData.totalBounds.height() / 2.0;
6700
    }
6701
  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) ||
6702
             (type == QCPAxis::atBottom &&
6703
              tickLabelSide ==
6704
                  QCPAxis::lsInside))  // Anchor at bottom side of tick label
6705
  {
6706
    if (doRotation) {
6707
      if (tickLabelRotation > 0) {
6708
        x = -qCos(radians) * labelData.totalBounds.width() +
6709
            qSin(radians) * labelData.totalBounds.height() / 2.0;
6710
        y = -qSin(radians) * labelData.totalBounds.width() -
6711
            qCos(radians) * labelData.totalBounds.height();
6712
      } else {
6713
        x = -qSin(-radians) * labelData.totalBounds.height() / 2.0;
6714
        y = -qCos(-radians) * labelData.totalBounds.height();
6715
      }
6716
    } else {
6717
      x = -labelData.totalBounds.width() / 2.0;
6718
      y = -labelData.totalBounds.height();
6719
    }
6720
  } else if ((type == QCPAxis::atBottom &&
6721
              tickLabelSide == QCPAxis::lsOutside) ||
6722
             (type == QCPAxis::atTop &&
6723
              tickLabelSide ==
6724
                  QCPAxis::lsInside))  // Anchor at top side of tick label
6725
  {
6726
    if (doRotation) {
6727
      if (tickLabelRotation > 0) {
6728
        x = +qSin(radians) * labelData.totalBounds.height() / 2.0;
6729
        y = 0;
6730
      } else {
6731
        x = -qCos(-radians) * labelData.totalBounds.width() -
6732
            qSin(-radians) * labelData.totalBounds.height() / 2.0;
6733
        y = +qSin(-radians) * labelData.totalBounds.width();
6734
      }
6735
    } else {
6736
      x = -labelData.totalBounds.width() / 2.0;
6737
      y = 0;
6738
    }
6739
  }
6740
6741
  return QPointF(x, y);
6742
}
6743
6744
/*! \internal
6745
6746
  Simulates the steps done by \ref placeTickLabel by calculating bounding boxes
6747
  of the text label to be drawn, depending on number format etc. Since only the
6748
  largest tick label is wanted for the margin calculation, the passed \a
6749
  tickLabelsSize is only expanded, if it's currently set to a smaller
6750
  width/height.
6751
*/
6752
void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font,
6753
                                                const QString &text,
6754
                                                QSize *tickLabelsSize) const {
6755
  // note: this function must return the same tick label sizes as the
6756
  // placeTickLabel function.
6757
  QSize finalSize;
6758
  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) &&
6759
      mLabelCache.contains(
6760
          text))  // label caching enabled and have cached label
6761
  {
6762
    const CachedLabel *cachedLabel = mLabelCache.object(text);
6763
    finalSize = cachedLabel->pixmap.size();
6764
  } else  // label caching disabled or no label with this text cached:
6765
  {
6766
    TickLabelData labelData = getTickLabelData(font, text);
6767
    finalSize = labelData.rotatedTotalBounds.size();
6768
  }
6769
6770
  // expand passed tickLabelsSize if current tick label is larger:
6771
  if (finalSize.width() > tickLabelsSize->width())
6772
    tickLabelsSize->setWidth(finalSize.width());
6773
  if (finalSize.height() > tickLabelsSize->height())
6774
    tickLabelsSize->setHeight(finalSize.height());
6775
}
6776
6777
////////////////////////////////////////////////////////////////////////////////////////////////////
6778
//////////////////// QCPAbstractPlottable
6779
////////////////////////////////////////////////////////////////////////////////////////////////////
6780
6781
/*! \class QCPAbstractPlottable
6782
  \brief The abstract base class for all data representing objects in a plot.
6783
6784
  It defines a very basic interface like name, pen, brush, visibility etc. Since
6785
  this class is abstract, it can't be instantiated. Use one of the subclasses or
6786
  create a subclass yourself to create new ways of displaying data (see
6787
  "Creating own plottables" below).
6788
6789
  All further specifics are in the subclasses, for example:
6790
  \li A normal graph with possibly a line, scatter points and error bars: \ref
6791
  QCPGraph (typically created with \ref QCustomPlot::addGraph) \li A parametric
6792
  curve: \ref QCPCurve \li A bar chart: \ref QCPBars \li A statistical box plot:
6793
  \ref QCPStatisticalBox \li A color encoded two-dimensional map: \ref
6794
  QCPColorMap \li An OHLC/Candlestick chart: \ref QCPFinancial
6795
6796
  \section plottables-subclassing Creating own plottables
6797
6798
  To create an own plottable, you implement a subclass of QCPAbstractPlottable.
6799
  These are the pure virtual functions, you must implement: \li \ref clearData
6800
  \li \ref selectTest
6801
  \li \ref draw
6802
  \li \ref drawLegendIcon
6803
  \li \ref getKeyRange
6804
  \li \ref getValueRange
6805
6806
  See the documentation of those functions for what they need to do.
6807
6808
  For drawing your plot, you can use the \ref coordsToPixels functions to
6809
  translate a point in plot coordinates to pixel coordinates. This function is
6810
  quite convenient, because it takes the orientation of the key and value axes
6811
  into account for you (x and y are swapped when the key axis is vertical and
6812
  the value axis horizontal). If you are worried about performance (i.e. you
6813
  need to translate many points in a loop like QCPGraph), you can directly use
6814
  \ref QCPAxis::coordToPixel. However, you must then take care about the
6815
  orientation of the axis yourself.
6816
6817
  Here are some important members you inherit from QCPAbstractPlottable:
6818
  <table>
6819
  <tr>
6820
    <td>QCustomPlot *\b mParentPlot</td>
6821
    <td>A pointer to the parent QCustomPlot instance. The parent plot is
6822
  inferred from the axes that are passed in the constructor.</td>
6823
  </tr><tr>
6824
    <td>QString \b mName</td>
6825
    <td>The name of the plottable.</td>
6826
  </tr><tr>
6827
    <td>QPen \b mPen</td>
6828
    <td>The generic pen of the plottable. You should use this pen for the most
6829
  prominent data representing lines in the plottable (e.g QCPGraph uses this pen
6830
  for its graph lines and scatters)</td>
6831
  </tr><tr>
6832
    <td>QPen \b mSelectedPen</td>
6833
    <td>The generic pen that should be used when the plottable is selected
6834
  (hint: \ref mainPen gives you the right pen, depending on selection
6835
  state).</td>
6836
  </tr><tr>
6837
    <td>QBrush \b mBrush</td>
6838
    <td>The generic brush of the plottable. You should use this brush for the
6839
  most prominent fillable structures in the plottable (e.g. QCPGraph uses this
6840
  brush to control filling under the graph)</td>
6841
  </tr><tr>
6842
    <td>QBrush \b mSelectedBrush</td>
6843
    <td>The generic brush that should be used when the plottable is selected
6844
  (hint: \ref mainBrush gives you the right brush, depending on selection
6845
  state).</td>
6846
  </tr><tr>
6847
    <td>QPointer<QCPAxis>\b mKeyAxis, \b mValueAxis</td>
6848
    <td>The key and value axes this plottable is attached to. Call their
6849
  QCPAxis::coordToPixel functions to translate coordinates to pixels in either
6850
  the key or value dimension. Make sure to check whether the pointer is null
6851
  before using it. If one of the axes is null, don't draw the plottable.</td>
6852
  </tr><tr>
6853
    <td>bool \b mSelected</td>
6854
    <td>indicates whether the plottable is selected or not.</td>
6855
  </tr>
6856
  </table>
6857
*/
6858
6859
/* start of documentation of pure virtual functions */
6860
6861
/*! \fn void QCPAbstractPlottable::clearData() = 0
6862
  Clears all data in the plottable.
6863
*/
6864
6865
/*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const
6866
  QRect &rect) const = 0 \internal
6867
6868
  called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a
6869
  graphical representation of this plottable inside \a rect, next to the
6870
  plottable name.
6871
6872
  The passed \a painter has its cliprect set to \a rect, so painting outside of
6873
  \a rect won't appear outside the legend icon border.
6874
*/
6875
6876
/*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain
6877
  inSignDomain) const = 0 \internal
6878
6879
  called by rescaleAxes functions to get the full data key bounds. For
6880
  logarithmic plots, one can set \a inSignDomain to either \ref sdNegative or
6881
  \ref sdPositive in order to restrict the returned range to that sign domain.
6882
  E.g. when only negative range is wanted, set \a inSignDomain to \ref
6883
  sdNegative and all positive points will be ignored for range calculation. For
6884
  no restriction, just set \a inSignDomain to \ref sdBoth (default). \a
6885
  foundRange is an output parameter that indicates whether a range could be
6886
  found or not. If this is false, you shouldn't use the returned range (e.g. no
6887
  points in data).
6888
6889
  Note that \a foundRange is not the same as \ref QCPRange::validRange, since
6890
  the range returned by this function may have size zero, which wouldn't count
6891
  as a valid range.
6892
6893
  \see rescaleAxes, getValueRange
6894
*/
6895
6896
/*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange,
6897
  SignDomain inSignDomain) const = 0 \internal
6898
6899
  called by rescaleAxes functions to get the full data value bounds. For
6900
  logarithmic plots, one can set \a inSignDomain to either \ref sdNegative or
6901
  \ref sdPositive in order to restrict the returned range to that sign domain.
6902
  E.g. when only negative range is wanted, set \a inSignDomain to \ref
6903
  sdNegative and all positive points will be ignored for range calculation. For
6904
  no restriction, just set \a inSignDomain to \ref sdBoth (default). \a
6905
  foundRange is an output parameter that indicates whether a range could be
6906
  found or not. If this is false, you shouldn't use the returned range (e.g. no
6907
  points in data).
6908
6909
  Note that \a foundRange is not the same as \ref QCPRange::validRange, since
6910
  the range returned by this function may have size zero, which wouldn't count
6911
  as a valid range.
6912
6913
  \see rescaleAxes, getKeyRange
6914
*/
6915
6916
/* end of documentation of pure virtual functions */
6917
/* start of documentation of signals */
6918
6919
/*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
6920
6921
  This signal is emitted when the selection state of this plottable has changed,
6922
  either by user interaction or by a direct call to \ref setSelected.
6923
*/
6924
6925
/*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable);
6926
6927
  This signal is emitted when the selectability of this plottable has changed.
6928
6929
  \see setSelectable
6930
*/
6931
6932
/* end of documentation of signals */
6933
6934
/*!
6935
  Constructs an abstract plottable which uses \a keyAxis as its key axis ("x")
6936
  and \a valueAxis as its value axis ("y"). \a keyAxis and \a valueAxis must
6937
  reside in the same QCustomPlot instance and have perpendicular orientations.
6938
  If either of these restrictions is violated, a corresponding message is
6939
  printed to the debug output (qDebug), the construction is not aborted, though.
6940
6941
  Since QCPAbstractPlottable is an abstract class that defines the basic
6942
  interface to plottables, it can't be directly instantiated.
6943
6944
  You probably want one of the subclasses like \ref QCPGraph or \ref QCPCurve
6945
  instead.
6946
*/
6947
QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
6948
    : QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
6949
      mName(),
6950
      mAntialiasedFill(true),
6951
      mAntialiasedScatters(true),
6952
      mAntialiasedErrorBars(false),
6953
      mPen(Qt::black),
6954
      mSelectedPen(Qt::black),
6955
      mBrush(Qt::NoBrush),
6956
      mSelectedBrush(Qt::NoBrush),
6957
      mKeyAxis(keyAxis),
6958
      mValueAxis(valueAxis),
6959
      mSelectable(true),
6960
      mSelected(false) {
6961
  if (keyAxis->parentPlot() != valueAxis->parentPlot())
6962
    qDebug() << Q_FUNC_INFO
6963
             << "Parent plot of keyAxis is not the same as that of valueAxis.";
6964
  if (keyAxis->orientation() == valueAxis->orientation())
6965
    qDebug() << Q_FUNC_INFO
6966
             << "keyAxis and valueAxis must be orthogonal to each other.";
6967
}
6968
6969
/*!
6970
   The name is the textual representation of this plottable as it is displayed
6971
   in the legend
6972
   (\ref QCPLegend). It may contain any UTF-8 characters, including newlines.
6973
*/
6974
void QCPAbstractPlottable::setName(const QString &name) { mName = name; }
6975
6976
/*!
6977
  Sets whether fills of this plottable are drawn antialiased or not.
6978
6979
  Note that this setting may be overridden by \ref
6980
  QCustomPlot::setAntialiasedElements and \ref
6981
  QCustomPlot::setNotAntialiasedElements.
6982
*/
6983
void QCPAbstractPlottable::setAntialiasedFill(bool enabled) {
6984
  mAntialiasedFill = enabled;
6985
}
6986
6987
/*!
6988
  Sets whether the scatter symbols of this plottable are drawn antialiased or
6989
  not.
6990
6991
  Note that this setting may be overridden by \ref
6992
  QCustomPlot::setAntialiasedElements and \ref
6993
  QCustomPlot::setNotAntialiasedElements.
6994
*/
6995
void QCPAbstractPlottable::setAntialiasedScatters(bool enabled) {
6996
  mAntialiasedScatters = enabled;
6997
}
6998
6999
/*!
7000
  Sets whether the error bars of this plottable are drawn antialiased or not.
7001
7002
  Note that this setting may be overridden by \ref
7003
  QCustomPlot::setAntialiasedElements and \ref
7004
  QCustomPlot::setNotAntialiasedElements.
7005
*/
7006
void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled) {
7007
  mAntialiasedErrorBars = enabled;
7008
}
7009
7010
/*!
7011
  The pen is used to draw basic lines that make up the plottable representation
7012
  in the plot.
7013
7014
  For example, the \ref QCPGraph subclass draws its graph lines with this pen.
7015
7016
  \see setBrush
7017
*/
7018
void QCPAbstractPlottable::setPen(const QPen &pen) { mPen = pen; }
7019
7020
/*!
7021
  When the plottable is selected, this pen is used to draw basic lines instead
7022
  of the normal pen set via \ref setPen.
7023
7024
  \see setSelected, setSelectable, setSelectedBrush, selectTest
7025
*/
7026
void QCPAbstractPlottable::setSelectedPen(const QPen &pen) {
7027
  mSelectedPen = pen;
7028
}
7029
7030
/*!
7031
  The brush is used to draw basic fills of the plottable representation in the
7032
  plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
7033
7034
  For example, the \ref QCPGraph subclass draws the fill under the graph with
7035
  this brush, when it's not set to Qt::NoBrush.
7036
7037
  \see setPen
7038
*/
7039
void QCPAbstractPlottable::setBrush(const QBrush &brush) { mBrush = brush; }
7040
7041
/*!
7042
  When the plottable is selected, this brush is used to draw fills instead of
7043
  the normal brush set via \ref setBrush.
7044
7045
  \see setSelected, setSelectable, setSelectedPen, selectTest
7046
*/
7047
void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush) {
7048
  mSelectedBrush = brush;
7049
}
7050
7051
/*!
7052
  The key axis of a plottable can be set to any axis of a QCustomPlot, as long
7053
  as it is orthogonal to the plottable's value axis. This function performs no
7054
  checks to make sure this is the case. The typical mathematical choice is to
7055
  use the x-axis (QCustomPlot::xAxis) as key axis and the y-axis
7056
  (QCustomPlot::yAxis) as value axis.
7057
7058
  Normally, the key and value axes are set in the constructor of the plottable
7059
  (or \ref QCustomPlot::addGraph when working with QCPGraphs through the
7060
  dedicated graph interface).
7061
7062
  \see setValueAxis
7063
*/
7064
void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis) { mKeyAxis = axis; }
7065
7066
/*!
7067
  The value axis of a plottable can be set to any axis of a QCustomPlot, as long
7068
  as it is orthogonal to the plottable's key axis. This function performs no
7069
  checks to make sure this is the case. The typical mathematical choice is to
7070
  use the x-axis (QCustomPlot::xAxis) as key axis and the y-axis
7071
  (QCustomPlot::yAxis) as value axis.
7072
7073
  Normally, the key and value axes are set in the constructor of the plottable
7074
  (or \ref QCustomPlot::addGraph when working with QCPGraphs through the
7075
  dedicated graph interface).
7076
7077
  \see setKeyAxis
7078
*/
7079
void QCPAbstractPlottable::setValueAxis(QCPAxis *axis) { mValueAxis = axis; }
7080
7081
/*!
7082
  Sets whether the user can (de-)select this plottable by clicking on the
7083
  QCustomPlot surface. (When \ref QCustomPlot::setInteractions contains
7084
  iSelectPlottables.)
7085
7086
  However, even when \a selectable was set to false, it is possible to set the
7087
  selection manually, by calling \ref setSelected directly.
7088
7089
  \see setSelected
7090
*/
7091
void QCPAbstractPlottable::setSelectable(bool selectable) {
7092
  if (mSelectable != selectable) {
7093
    mSelectable = selectable;
7094
    emit selectableChanged(mSelectable);
7095
  }
7096
}
7097
7098
/*!
7099
  Sets whether this plottable is selected or not. When selected, it uses a
7100
  different pen and brush to draw its lines and fills, see \ref setSelectedPen
7101
  and \ref setSelectedBrush.
7102
7103
  The entire selection mechanism for plottables is handled automatically when
7104
  \ref QCustomPlot::setInteractions contains iSelectPlottables. You only need to
7105
  call this function when you wish to change the selection state manually.
7106
7107
  This function can change the selection state even when \ref setSelectable was
7108
  set to false.
7109
7110
  emits the \ref selectionChanged signal when \a selected is different from the
7111
  previous selection state.
7112
7113
  \see setSelectable, selectTest
7114
*/
7115
void QCPAbstractPlottable::setSelected(bool selected) {
7116
  if (mSelected != selected) {
7117
    mSelected = selected;
7118
    emit selectionChanged(mSelected);
7119
  }
7120
}
7121
7122
/*!
7123
  Rescales the key and value axes associated with this plottable to contain all
7124
  displayed data, so the whole plottable is visible. If the scaling of an axis
7125
  is logarithmic, rescaleAxes will make sure not to rescale to an illegal range
7126
  i.e. a range containing different signs and/or zero. Instead it will stay in
7127
  the current sign domain and ignore all parts of the plottable that lie outside
7128
  of that domain.
7129
7130
  \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's
7131
  possible to show multiple plottables in their entirety by multiple calls to
7132
  rescaleAxes where the first call has \a onlyEnlarge set to false (the
7133
  default), and all subsequent set to true.
7134
7135
  \see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes,
7136
  QCPAxis::rescale
7137
*/
7138
void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const {
7139
  rescaleKeyAxis(onlyEnlarge);
7140
  rescaleValueAxis(onlyEnlarge);
7141
}
7142
7143
/*!
7144
  Rescales the key axis of the plottable so the whole plottable is visible.
7145
7146
  See \ref rescaleAxes for detailed behaviour.
7147
*/
7148
void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const {
7149
  QCPAxis *keyAxis = mKeyAxis.data();
7150
  if (!keyAxis) {
7151
    qDebug() << Q_FUNC_INFO << "invalid key axis";
7152
    return;
7153
  }
7154
7155
  SignDomain signDomain = sdBoth;
7156
  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7157
    signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7158
7159
  bool foundRange;
7160
  QCPRange newRange = getKeyRange(foundRange, signDomain);
7161
  if (foundRange) {
7162
    if (onlyEnlarge) newRange.expand(keyAxis->range());
7163
    if (!QCPRange::validRange(
7164
            newRange))  // likely due to range being zero (plottable has only
7165
                        // constant data in this axis dimension), shift current
7166
                        // range to at least center the plottable
7167
    {
7168
      double center =
7169
          (newRange.lower + newRange.upper) *
7170
          0.5;  // upper and lower should be equal anyway, but just to make
7171
                // sure, incase validRange returned false for other reason
7172
      if (keyAxis->scaleType() == QCPAxis::stLinear) {
7173
        newRange.lower = center - keyAxis->range().size() / 2.0;
7174
        newRange.upper = center + keyAxis->range().size() / 2.0;
7175
      } else  // scaleType() == stLogarithmic
7176
      {
7177
        newRange.lower =
7178
            center / qSqrt(keyAxis->range().upper / keyAxis->range().lower);
7179
        newRange.upper =
7180
            center * qSqrt(keyAxis->range().upper / keyAxis->range().lower);
7181
      }
7182
    }
7183
    keyAxis->setRange(newRange);
7184
  }
7185
}
7186
7187
/*!
7188
  Rescales the value axis of the plottable so the whole plottable is visible.
7189
7190
  Returns true if the axis was actually scaled. This might not be the case if
7191
  this plottable has an invalid range, e.g. because it has no data points.
7192
7193
  See \ref rescaleAxes for detailed behaviour.
7194
*/
7195
void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const {
7196
  QCPAxis *valueAxis = mValueAxis.data();
7197
  if (!valueAxis) {
7198
    qDebug() << Q_FUNC_INFO << "invalid value axis";
7199
    return;
7200
  }
7201
7202
  SignDomain signDomain = sdBoth;
7203
  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7204
    signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7205
7206
  bool foundRange;
7207
  QCPRange newRange = getValueRange(foundRange, signDomain);
7208
  if (foundRange) {
7209
    if (onlyEnlarge) newRange.expand(valueAxis->range());
7210
    if (!QCPRange::validRange(
7211
            newRange))  // likely due to range being zero (plottable has only
7212
                        // constant data in this axis dimension), shift current
7213
                        // range to at least center the plottable
7214
    {
7215
      double center =
7216
          (newRange.lower + newRange.upper) *
7217
          0.5;  // upper and lower should be equal anyway, but just to make
7218
                // sure, incase validRange returned false for other reason
7219
      if (valueAxis->scaleType() == QCPAxis::stLinear) {
7220
        newRange.lower = center - valueAxis->range().size() / 2.0;
7221
        newRange.upper = center + valueAxis->range().size() / 2.0;
7222
      } else  // scaleType() == stLogarithmic
7223
      {
7224
        newRange.lower =
7225
            center / qSqrt(valueAxis->range().upper / valueAxis->range().lower);
7226
        newRange.upper =
7227
            center * qSqrt(valueAxis->range().upper / valueAxis->range().lower);
7228
      }
7229
    }
7230
    valueAxis->setRange(newRange);
7231
  }
7232
}
7233
7234
/*!
7235
  Adds this plottable to the legend of the parent QCustomPlot
7236
  (QCustomPlot::legend).
7237
7238
  Normally, a QCPPlottableLegendItem is created and inserted into the legend. If
7239
  the plottable needs a more specialized representation in the legend, this
7240
  function will take this into account and instead create the specialized
7241
  subclass of QCPAbstractLegendItem.
7242
7243
  Returns true on success, i.e. when the legend exists and a legend item
7244
  associated with this plottable isn't already in the legend.
7245
7246
  \see removeFromLegend, QCPLegend::addItem
7247
*/
7248
bool QCPAbstractPlottable::addToLegend() {
7249
  if (!mParentPlot || !mParentPlot->legend) return false;
7250
7251
  if (!mParentPlot->legend->hasItemWithPlottable(this)) {
7252
    mParentPlot->legend->addItem(
7253
        new QCPPlottableLegendItem(mParentPlot->legend, this));
7254
    return true;
7255
  } else
7256
    return false;
7257
}
7258
7259
/*!
7260
  Removes the plottable from the legend of the parent QCustomPlot. This means
7261
  the QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is
7262
  associated with this plottable is removed.
7263
7264
  Returns true on success, i.e. if the legend exists and a legend item
7265
  associated with this plottable was found and removed.
7266
7267
  \see addToLegend, QCPLegend::removeItem
7268
*/
7269
bool QCPAbstractPlottable::removeFromLegend() const {
7270
  if (!mParentPlot->legend) return false;
7271
7272
  if (QCPPlottableLegendItem *lip =
7273
          mParentPlot->legend->itemWithPlottable(this))
7274
    return mParentPlot->legend->removeItem(lip);
7275
  else
7276
    return false;
7277
}
7278
7279
/* inherits documentation from base class */
7280
QRect QCPAbstractPlottable::clipRect() const {
7281
  if (mKeyAxis && mValueAxis)
7282
    return mKeyAxis.data()->axisRect()->rect() &
7283
           mValueAxis.data()->axisRect()->rect();
7284
  else
7285
    return QRect();
7286
}
7287
7288
/* inherits documentation from base class */
7289
QCP::Interaction QCPAbstractPlottable::selectionCategory() const {
7290
  return QCP::iSelectPlottables;
7291
}
7292
7293
/*! \internal
7294
7295
  Convenience function for transforming a key/value pair to pixels on the
7296
  QCustomPlot surface, taking the orientations of the axes associated with this
7297
  plottable into account (e.g. whether key represents x or y).
7298
7299
  \a key and \a value are transformed to the coodinates in pixels and are
7300
  written to \a x and \a y.
7301
7302
  \see pixelsToCoords, QCPAxis::coordToPixel
7303
*/
7304
void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x,
7305
                                          double &y) const {
7306
  QCPAxis *keyAxis = mKeyAxis.data();
7307
  QCPAxis *valueAxis = mValueAxis.data();
7308
  if (!keyAxis || !valueAxis) {
7309
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
7310
    return;
7311
  }
7312
7313
  if (keyAxis->orientation() == Qt::Horizontal) {
7314
    x = keyAxis->coordToPixel(key);
7315
    y = valueAxis->coordToPixel(value);
7316
  } else {
7317
    y = keyAxis->coordToPixel(key);
7318
    x = valueAxis->coordToPixel(value);
7319
  }
7320
}
7321
7322
/*! \internal
7323
  \overload
7324
7325
  Returns the input as pixel coordinates in a QPointF.
7326
*/
7327
const QPointF QCPAbstractPlottable::coordsToPixels(double key,
7328
                                                   double value) const {
7329
  QCPAxis *keyAxis = mKeyAxis.data();
7330
  QCPAxis *valueAxis = mValueAxis.data();
7331
  if (!keyAxis || !valueAxis) {
7332
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
7333
    return QPointF();
7334
  }
7335
7336
  if (keyAxis->orientation() == Qt::Horizontal)
7337
    return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7338
  else
7339
    return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7340
}
7341
7342
/*! \internal
7343
7344
  Convenience function for transforming a x/y pixel pair on the QCustomPlot
7345
  surface to plot coordinates, taking the orientations of the axes associated
7346
  with this plottable into account (e.g. whether key represents x or y).
7347
7348
  \a x and \a y are transformed to the plot coodinates and are written to \a key
7349
  and \a value.
7350
7351
  \see coordsToPixels, QCPAxis::coordToPixel
7352
*/
7353
void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key,
7354
                                          double &value) const {
7355
  QCPAxis *keyAxis = mKeyAxis.data();
7356
  QCPAxis *valueAxis = mValueAxis.data();
7357
  if (!keyAxis || !valueAxis) {
7358
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
7359
    return;
7360
  }
7361
7362
  if (keyAxis->orientation() == Qt::Horizontal) {
7363
    key = keyAxis->pixelToCoord(x);
7364
    value = valueAxis->pixelToCoord(y);
7365
  } else {
7366
    key = keyAxis->pixelToCoord(y);
7367
    value = valueAxis->pixelToCoord(x);
7368
  }
7369
}
7370
7371
/*! \internal
7372
  \overload
7373
7374
  Returns the pixel input \a pixelPos as plot coordinates \a key and \a value.
7375
*/
7376
void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key,
7377
                                          double &value) const {
7378
  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7379
}
7380
7381
/*! \internal
7382
7383
  Returns the pen that should be used for drawing lines of the plottable.
7384
  Returns mPen when the graph is not selected and mSelectedPen when it is.
7385
*/
7386
QPen QCPAbstractPlottable::mainPen() const {
7387
  return mSelected ? mSelectedPen : mPen;
7388
}
7389
7390
/*! \internal
7391
7392
  Returns the brush that should be used for drawing fills of the plottable.
7393
  Returns mBrush when the graph is not selected and mSelectedBrush when it is.
7394
*/
7395
QBrush QCPAbstractPlottable::mainBrush() const {
7396
  return mSelected ? mSelectedBrush : mBrush;
7397
}
7398
7399
/*! \internal
7400
7401
  A convenience function to easily set the QPainter::Antialiased hint on the
7402
  provided \a painter before drawing plottable lines.
7403
7404
  This is the antialiasing state the painter passed to the \ref draw method is
7405
  in by default.
7406
7407
  This function takes into account the local setting of the antialiasing flag as
7408
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
7409
  \ref QCustomPlot::setNotAntialiasedElements.
7410
7411
  \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint,
7412
  applyErrorBarsAntialiasingHint
7413
*/
7414
void QCPAbstractPlottable::applyDefaultAntialiasingHint(
7415
    QCPPainter *painter) const {
7416
  applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
7417
}
7418
7419
/*! \internal
7420
7421
  A convenience function to easily set the QPainter::Antialiased hint on the
7422
  provided \a painter before drawing plottable fills.
7423
7424
  This function takes into account the local setting of the antialiasing flag as
7425
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
7426
  \ref QCustomPlot::setNotAntialiasedElements.
7427
7428
  \see setAntialiased, applyDefaultAntialiasingHint,
7429
  applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7430
*/
7431
void QCPAbstractPlottable::applyFillAntialiasingHint(
7432
    QCPPainter *painter) const {
7433
  applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
7434
}
7435
7436
/*! \internal
7437
7438
  A convenience function to easily set the QPainter::Antialiased hint on the
7439
  provided \a painter before drawing plottable scatter points.
7440
7441
  This function takes into account the local setting of the antialiasing flag as
7442
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
7443
  \ref QCustomPlot::setNotAntialiasedElements.
7444
7445
  \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint,
7446
  applyErrorBarsAntialiasingHint
7447
*/
7448
void QCPAbstractPlottable::applyScattersAntialiasingHint(
7449
    QCPPainter *painter) const {
7450
  applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
7451
}
7452
7453
/*! \internal
7454
7455
  A convenience function to easily set the QPainter::Antialiased hint on the
7456
  provided \a painter before drawing plottable error bars.
7457
7458
  This function takes into account the local setting of the antialiasing flag as
7459
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
7460
  \ref QCustomPlot::setNotAntialiasedElements.
7461
7462
  \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint,
7463
  applyDefaultAntialiasingHint
7464
*/
7465
void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(
7466
    QCPPainter *painter) const {
7467
  applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars);
7468
}
7469
7470
/*! \internal
7471
7472
  Finds the shortest squared distance of \a point to the line segment defined by
7473
  \a start and \a end.
7474
7475
  This function may be used to help with the implementation of the \ref
7476
  selectTest function for specific plottables.
7477
7478
  \note This function is identical to QCPAbstractItem::distSqrToLine
7479
*/
7480
double QCPAbstractPlottable::distSqrToLine(const QPointF &start,
7481
                                           const QPointF &end,
7482
                                           const QPointF &point) const {
7483
  QVector2D a(start);
7484
  QVector2D b(end);
7485
  QVector2D p(point);
7486
  QVector2D v(b - a);
7487
7488
  double vLengthSqr = v.lengthSquared();
7489
  if (!qFuzzyIsNull(vLengthSqr)) {
7490
    double mu = QVector2D::dotProduct(p - a, v) / vLengthSqr;
7491
    if (mu < 0)
7492
      return (a - p).lengthSquared();
7493
    else if (mu > 1)
7494
      return (b - p).lengthSquared();
7495
    else
7496
      return ((a + mu * v) - p).lengthSquared();
7497
  } else
7498
    return (a - p).lengthSquared();
7499
}
7500
7501
/* inherits documentation from base class */
7502
void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive,
7503
                                       const QVariant &details,
7504
                                       bool *selectionStateChanged) {
7505
  Q_UNUSED(event)
7506
  Q_UNUSED(details)
7507
  if (mSelectable) {
7508
    bool selBefore = mSelected;
7509
    setSelected(additive ? !mSelected : true);
7510
    if (selectionStateChanged) *selectionStateChanged = mSelected != selBefore;
7511
  }
7512
}
7513
7514
/* inherits documentation from base class */
7515
void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) {
7516
  if (mSelectable) {
7517
    bool selBefore = mSelected;
7518
    setSelected(false);
7519
    if (selectionStateChanged) *selectionStateChanged = mSelected != selBefore;
7520
  }
7521
}
7522
7523
////////////////////////////////////////////////////////////////////////////////////////////////////
7524
//////////////////// QCPItemAnchor
7525
////////////////////////////////////////////////////////////////////////////////////////////////////
7526
7527
/*! \class QCPItemAnchor
7528
  \brief An anchor of an item to which positions can be attached to.
7529
7530
  An item (QCPAbstractItem) may have one or more anchors. Unlike
7531
  QCPItemPosition, an anchor doesn't control anything on its item, but provides
7532
  a way to tie other items via their positions to the anchor.
7533
7534
  For example, a QCPItemRect is defined by its positions \a topLeft and \a
7535
  bottomRight. Additionally it has various anchors like \a top, \a topRight or
7536
  \a bottomLeft etc. So you can attach the \a start (which is a QCPItemPosition)
7537
  of a QCPItemLine to one of the anchors by calling
7538
  QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the
7539
  QCPItemRect. This way the start of the line will now always follow the
7540
  respective anchor location on the rect item.
7541
7542
  Note that QCPItemPosition derives from QCPItemAnchor, so every position can
7543
  also serve as an anchor to other positions.
7544
7545
  To learn how to provide anchors in your own item subclasses, see the
7546
  subclassing section of the QCPAbstractItem documentation.
7547
*/
7548
7549
/* start documentation of inline functions */
7550
7551
/*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition()
7552
7553
  Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of
7554
  type QCPItemPosition* if it actually is a QCPItemPosition (which is a subclass
7555
  of QCPItemAnchor).
7556
7557
  This safe downcast functionality could also be achieved with a dynamic_cast.
7558
  However, QCustomPlot avoids dynamic_cast to work with projects that don't have
7559
  RTTI support enabled (e.g. -fno-rtti flag with gcc compiler).
7560
*/
7561
7562
/* end documentation of inline functions */
7563
7564
/*!
7565
  Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances
7566
  directly, even if you want to make a new item subclass. Use \ref
7567
  QCPAbstractItem::createAnchor instead, as explained in the subclassing section
7568
  of the QCPAbstractItem documentation.
7569
*/
7570
QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot,
7571
                             QCPAbstractItem *parentItem, const QString name,
7572
                             int anchorId)
7573
    : mName(name),
7574
      mParentPlot(parentPlot),
7575
      mParentItem(parentItem),
7576
      mAnchorId(anchorId) {}
7577
7578
QCPItemAnchor::~QCPItemAnchor() {
7579
  // unregister as parent at children:
7580
  foreach (QCPItemPosition *child, mChildrenX.toList()) {
7581
    if (child->parentAnchorX() == this)
7582
      child->setParentAnchorX(0);  // this acts back on this anchor and child
7583
                                   // removes itself from mChildrenX
7584
  }
7585
  foreach (QCPItemPosition *child, mChildrenY.toList()) {
7586
    if (child->parentAnchorY() == this)
7587
      child->setParentAnchorY(0);  // this acts back on this anchor and child
7588
                                   // removes itself from mChildrenY
7589
  }
7590
}
7591
7592
/*!
7593
  Returns the final absolute pixel position of the QCPItemAnchor on the
7594
  QCustomPlot surface.
7595
7596
  The pixel information is internally retrieved via
7597
  QCPAbstractItem::anchorPixelPosition of the parent item, QCPItemAnchor is just
7598
  an intermediary.
7599
*/
7600
QPointF QCPItemAnchor::pixelPoint() const {
7601
  if (mParentItem) {
7602
    if (mAnchorId > -1) {
7603
      return mParentItem->anchorPixelPoint(mAnchorId);
7604
    } else {
7605
      qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7606
      return QPointF();
7607
    }
7608
  } else {
7609
    qDebug() << Q_FUNC_INFO << "no parent item set";
7610
    return QPointF();
7611
  }
7612
}
7613
7614
/*! \internal
7615
7616
  Adds \a pos to the childX list of this anchor, which keeps track of which
7617
  children use this anchor as parent anchor for the respective coordinate. This
7618
  is necessary to notify the children prior to destruction of the anchor.
7619
7620
  Note that this function does not change the parent setting in \a pos.
7621
*/
7622
void QCPItemAnchor::addChildX(QCPItemPosition *pos) {
7623
  if (!mChildrenX.contains(pos))
7624
    mChildrenX.insert(pos);
7625
  else
7626
    qDebug() << Q_FUNC_INFO << "provided pos is child already"
7627
             << reinterpret_cast<quintptr>(pos);
7628
}
7629
7630
/*! \internal
7631
7632
  Removes \a pos from the childX list of this anchor.
7633
7634
  Note that this function does not change the parent setting in \a pos.
7635
*/
7636
void QCPItemAnchor::removeChildX(QCPItemPosition *pos) {
7637
  if (!mChildrenX.remove(pos))
7638
    qDebug() << Q_FUNC_INFO << "provided pos isn't child"
7639
             << reinterpret_cast<quintptr>(pos);
7640
}
7641
7642
/*! \internal
7643
7644
  Adds \a pos to the childY list of this anchor, which keeps track of which
7645
  children use this anchor as parent anchor for the respective coordinate. This
7646
  is necessary to notify the children prior to destruction of the anchor.
7647
7648
  Note that this function does not change the parent setting in \a pos.
7649
*/
7650
void QCPItemAnchor::addChildY(QCPItemPosition *pos) {
7651
  if (!mChildrenY.contains(pos))
7652
    mChildrenY.insert(pos);
7653
  else
7654
    qDebug() << Q_FUNC_INFO << "provided pos is child already"
7655
             << reinterpret_cast<quintptr>(pos);
7656
}
7657
7658
/*! \internal
7659
7660
  Removes \a pos from the childY list of this anchor.
7661
7662
  Note that this function does not change the parent setting in \a pos.
7663
*/
7664
void QCPItemAnchor::removeChildY(QCPItemPosition *pos) {
7665
  if (!mChildrenY.remove(pos))
7666
    qDebug() << Q_FUNC_INFO << "provided pos isn't child"
7667
             << reinterpret_cast<quintptr>(pos);
7668
}
7669
7670
////////////////////////////////////////////////////////////////////////////////////////////////////
7671
//////////////////// QCPItemPosition
7672
////////////////////////////////////////////////////////////////////////////////////////////////////
7673
7674
/*! \class QCPItemPosition
7675
  \brief Manages the position of an item.
7676
7677
  Every item has at least one public QCPItemPosition member pointer which
7678
  provides ways to position the item on the QCustomPlot surface. Some items have
7679
  multiple positions, for example QCPItemRect has two: \a topLeft and \a
7680
  bottomRight.
7681
7682
  QCPItemPosition has a type (\ref PositionType) that can be set with \ref
7683
  setType. This type defines how coordinates passed to \ref setCoords are to be
7684
  interpreted, e.g. as absolute pixel coordinates, as plot coordinates of
7685
  certain axes, etc. For more advanced plots it is also possible to assign
7686
  different types per X/Y coordinate of the position (see \ref setTypeX, \ref
7687
  setTypeY). This way an item could be positioned at a fixed pixel distance from
7688
  the top in the Y direction, while following a plot coordinate in the X
7689
  direction.
7690
7691
  A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor.
7692
  This way you can tie multiple items together. If the QCPItemPosition has a
7693
  parent, its coordinates (\ref setCoords) are considered to be absolute pixels
7694
  in the reference frame of the parent anchor, where (0, 0) means directly ontop
7695
  of the parent anchor. For example, You could attach the \a start position of
7696
  a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting
7697
  point of the line always be centered under the text label, no matter where the
7698
  text is moved to. For more advanced plots, it is possible to assign different
7699
  parent anchors per X/Y coordinate of the position, see \ref setParentAnchorX,
7700
  \ref setParentAnchorY. This way an item could follow another item in the X
7701
  direction but stay at a fixed position in the Y direction. Or even follow item
7702
  A in X, and item B in Y.
7703
7704
  Note that every QCPItemPosition inherits from QCPItemAnchor and thus can
7705
  itself be used as parent anchor for other positions.
7706
7707
  To set the apparent pixel position on the QCustomPlot surface directly, use
7708
  \ref setPixelPoint. This works no matter what type this QCPItemPosition is or
7709
  what parent-child situation it is in, as \ref setPixelPoint transforms the
7710
  coordinates appropriately, to make the position appear at the specified pixel
7711
  values.
7712
*/
7713
7714
/* start documentation of inline functions */
7715
7716
/*! \fn QCPItemPosition::PositionType *QCPItemPosition::type() const
7717
7718
  Returns the current position type.
7719
7720
  If different types were set for X and Y (\ref setTypeX, \ref setTypeY), this
7721
  method returns the type of the X coordinate. In that case rather use \a
7722
  typeX() and \a typeY().
7723
7724
  \see setType
7725
*/
7726
7727
/*! \fn QCPItemAnchor *QCPItemPosition::parentAnchor() const
7728
7729
  Returns the current parent anchor.
7730
7731
  If different parent anchors were set for X and Y (\ref setParentAnchorX, \ref
7732
  setParentAnchorY), this method returns the parent anchor of the Y coordinate.
7733
  In that case rather use \a parentAnchorX() and \a parentAnchorY().
7734
7735
  \see setParentAnchor
7736
*/
7737
7738
/* end documentation of inline functions */
7739
7740
/*!
7741
  Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances
7742
  directly, even if you want to make a new item subclass. Use \ref
7743
  QCPAbstractItem::createPosition instead, as explained in the subclassing
7744
  section of the QCPAbstractItem documentation.
7745
*/
7746
QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot,
7747
                                 QCPAbstractItem *parentItem,
7748
                                 const QString name)
7749
    : QCPItemAnchor(parentPlot, parentItem, name),
7750
      mPositionTypeX(ptAbsolute),
7751
      mPositionTypeY(ptAbsolute),
7752
      mKey(0),
7753
      mValue(0),
7754
      mParentAnchorX(0),
7755
      mParentAnchorY(0) {}
7756
7757
QCPItemPosition::~QCPItemPosition() {
7758
  // unregister as parent at children:
7759
  // Note: this is done in ~QCPItemAnchor again, but it's important
7760
  // QCPItemPosition does it itself, because only then
7761
  //       the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint
7762
  //       function instead of QCPItemAnchor::pixelPoint
7763
  foreach (QCPItemPosition *child, mChildrenX.toList()) {
7764
    if (child->parentAnchorX() == this)
7765
      child->setParentAnchorX(0);  // this acts back on this anchor and child
7766
                                   // removes itself from mChildrenX
7767
  }
7768
  foreach (QCPItemPosition *child, mChildrenY.toList()) {
7769
    if (child->parentAnchorY() == this)
7770
      child->setParentAnchorY(0);  // this acts back on this anchor and child
7771
                                   // removes itself from mChildrenY
7772
  }
7773
  // unregister as child in parent:
7774
  if (mParentAnchorX) mParentAnchorX->removeChildX(this);
7775
  if (mParentAnchorY) mParentAnchorY->removeChildY(this);
7776
}
7777
7778
/* can't make this a header inline function, because QPointer breaks with
7779
 * forward declared types, see QTBUG-29588 */
7780
QCPAxisRect *QCPItemPosition::axisRect() const { return mAxisRect.data(); }
7781
7782
/*!
7783
  Sets the type of the position. The type defines how the coordinates passed to
7784
  \ref setCoords should be handled and how the QCPItemPosition should behave in
7785
  the plot.
7786
7787
  The possible values for \a type can be separated in two main categories:
7788
7789
  \li The position is regarded as a point in plot coordinates. This corresponds
7790
  to \ref ptPlotCoords and requires two axes that define the plot coordinate
7791
  system. They can be specified with \ref setAxes. By default, the QCustomPlot's
7792
  x- and yAxis are used.
7793
7794
  \li The position is fixed on the QCustomPlot surface, i.e. independent of axis
7795
  ranges. This corresponds to all other types, i.e. \ref ptAbsolute, \ref
7796
  ptViewportRatio and \ref ptAxisRectRatio. They differ only in the way the
7797
  absolute position is described, see the documentation of \ref PositionType for
7798
  details. For \ref ptAxisRectRatio, note that you can specify the axis rect
7799
  with \ref setAxisRect. By default this is set to the main axis rect.
7800
7801
  Note that the position type \ref ptPlotCoords is only available (and sensible)
7802
  when the position has no parent anchor (\ref setParentAnchor).
7803
7804
  If the type is changed, the apparent pixel position on the plot is preserved.
7805
  This means the coordinates as retrieved with coords() and set with \ref
7806
  setCoords may change in the process.
7807
7808
  This method sets the type for both X and Y directions. It is also possible to
7809
  set different types for X and Y, see \ref setTypeX, \ref setTypeY.
7810
*/
7811
void QCPItemPosition::setType(QCPItemPosition::PositionType type) {
7812
  setTypeX(type);
7813
  setTypeY(type);
7814
}
7815
7816
/*!
7817
  This method sets the position type of the X coordinate to \a type.
7818
7819
  For a detailed description of what a position type is, see the documentation
7820
  of \ref setType.
7821
7822
  \see setType, setTypeY
7823
*/
7824
void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type) {
7825
  if (mPositionTypeX != type) {
7826
    // if switching from or to coordinate type that isn't valid (e.g. because
7827
    // axes or axis rect were deleted), don't try to recover the pixelPoint()
7828
    // because it would output a qDebug warning.
7829
    bool retainPixelPosition = true;
7830
    if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) &&
7831
        (!mKeyAxis || !mValueAxis))
7832
      retainPixelPosition = false;
7833
    if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) &&
7834
        (!mAxisRect))
7835
      retainPixelPosition = false;
7836
7837
    QPointF pixel;
7838
    if (retainPixelPosition) pixel = pixelPoint();
7839
7840
    mPositionTypeX = type;
7841
7842
    if (retainPixelPosition) setPixelPoint(pixel);
7843
  }
7844
}
7845
7846
/*!
7847
  This method sets the position type of the Y coordinate to \a type.
7848
7849
  For a detailed description of what a position type is, see the documentation
7850
  of \ref setType.
7851
7852
  \see setType, setTypeX
7853
*/
7854
void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type) {
7855
  if (mPositionTypeY != type) {
7856
    // if switching from or to coordinate type that isn't valid (e.g. because
7857
    // axes or axis rect were deleted), don't try to recover the pixelPoint()
7858
    // because it would output a qDebug warning.
7859
    bool retainPixelPosition = true;
7860
    if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) &&
7861
        (!mKeyAxis || !mValueAxis))
7862
      retainPixelPosition = false;
7863
    if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) &&
7864
        (!mAxisRect))
7865
      retainPixelPosition = false;
7866
7867
    QPointF pixel;
7868
    if (retainPixelPosition) pixel = pixelPoint();
7869
7870
    mPositionTypeY = type;
7871
7872
    if (retainPixelPosition) setPixelPoint(pixel);
7873
  }
7874
}
7875
7876
/*!
7877
  Sets the parent of this QCPItemPosition to \a parentAnchor. This means the
7878
  position will now follow any position changes of the anchor. The local
7879
  coordinate system of positions with a parent anchor always is absolute pixels,
7880
  with (0, 0) being exactly on top of the parent anchor. (Hence the type
7881
  shouldn't be set to \ref ptPlotCoords for positions with parent anchors.)
7882
7883
  if \a keepPixelPosition is true, the current pixel position of the
7884
  QCPItemPosition is preserved during reparenting. If it's set to false, the
7885
  coordinates are set to (0, 0), i.e. the position will be exactly on top of the
7886
  parent anchor.
7887
7888
  To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to
7889
  0.
7890
7891
  If the QCPItemPosition previously had no parent and the type is \ref
7892
  ptPlotCoords, the type is set to \ref ptAbsolute, to keep the position in a
7893
  valid state.
7894
7895
  This method sets the parent anchor for both X and Y directions. It is also
7896
  possible to set different parents for X and Y, see \ref setParentAnchorX, \ref
7897
  setParentAnchorY.
7898
*/
7899
bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor,
7900
                                      bool keepPixelPosition) {
7901
  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
7902
  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
7903
  return successX && successY;
7904
}
7905
7906
/*!
7907
  This method sets the parent anchor of the X coordinate to \a parentAnchor.
7908
7909
  For a detailed description of what a parent anchor is, see the documentation
7910
  of \ref setParentAnchor.
7911
7912
  \see setParentAnchor, setParentAnchorY
7913
*/
7914
bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor,
7915
                                       bool keepPixelPosition) {
7916
  // make sure self is not assigned as parent:
7917
  if (parentAnchor == this) {
7918
    qDebug() << Q_FUNC_INFO << "can't set self as parent anchor"
7919
             << reinterpret_cast<quintptr>(parentAnchor);
7920
    return false;
7921
  }
7922
  // make sure no recursive parent-child-relationships are created:
7923
  QCPItemAnchor *currentParent = parentAnchor;
7924
  while (currentParent) {
7925
    if (QCPItemPosition *currentParentPos =
7926
            currentParent->toQCPItemPosition()) {
7927
      // is a QCPItemPosition, might have further parent, so keep iterating
7928
      if (currentParentPos == this) {
7929
        qDebug() << Q_FUNC_INFO
7930
                 << "can't create recursive parent-child-relationship"
7931
                 << reinterpret_cast<quintptr>(parentAnchor);
7932
        return false;
7933
      }
7934
      currentParent = currentParentPos->parentAnchorX();
7935
    } else {
7936
      // is a QCPItemAnchor, can't have further parent. Now make sure the parent
7937
      // items aren't the same, to prevent a position being child of an anchor
7938
      // which itself depends on the position, because they're both on the same
7939
      // item:
7940
      if (currentParent->mParentItem == mParentItem) {
7941
        qDebug() << Q_FUNC_INFO
7942
                 << "can't set parent to be an anchor which itself depends on "
7943
                    "this position"
7944
                 << reinterpret_cast<quintptr>(parentAnchor);
7945
        return false;
7946
      }
7947
      break;
7948
    }
7949
  }
7950
7951
  // if previously no parent set and PosType is still ptPlotCoords, set to
7952
  // ptAbsolute:
7953
  if (!mParentAnchorX && mPositionTypeX == ptPlotCoords) setTypeX(ptAbsolute);
7954
7955
  // save pixel position:
7956
  QPointF pixelP;
7957
  if (keepPixelPosition) pixelP = pixelPoint();
7958
  // unregister at current parent anchor:
7959
  if (mParentAnchorX) mParentAnchorX->removeChildX(this);
7960
  // register at new parent anchor:
7961
  if (parentAnchor) parentAnchor->addChildX(this);
7962
  mParentAnchorX = parentAnchor;
7963
  // restore pixel position under new parent:
7964
  if (keepPixelPosition)
7965
    setPixelPoint(pixelP);
7966
  else
7967
    setCoords(0, coords().y());
7968
  return true;
7969
}
7970
7971
/*!
7972
  This method sets the parent anchor of the Y coordinate to \a parentAnchor.
7973
7974
  For a detailed description of what a parent anchor is, see the documentation
7975
  of \ref setParentAnchor.
7976
7977
  \see setParentAnchor, setParentAnchorX
7978
*/
7979
bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor,
7980
                                       bool keepPixelPosition) {
7981
  // make sure self is not assigned as parent:
7982
  if (parentAnchor == this) {
7983
    qDebug() << Q_FUNC_INFO << "can't set self as parent anchor"
7984
             << reinterpret_cast<quintptr>(parentAnchor);
7985
    return false;
7986
  }
7987
  // make sure no recursive parent-child-relationships are created:
7988
  QCPItemAnchor *currentParent = parentAnchor;
7989
  while (currentParent) {
7990
    if (QCPItemPosition *currentParentPos =
7991
            currentParent->toQCPItemPosition()) {
7992
      // is a QCPItemPosition, might have further parent, so keep iterating
7993
      if (currentParentPos == this) {
7994
        qDebug() << Q_FUNC_INFO
7995
                 << "can't create recursive parent-child-relationship"
7996
                 << reinterpret_cast<quintptr>(parentAnchor);
7997
        return false;
7998
      }
7999
      currentParent = currentParentPos->parentAnchorY();
8000
    } else {
8001
      // is a QCPItemAnchor, can't have further parent. Now make sure the parent
8002
      // items aren't the same, to prevent a position being child of an anchor
8003
      // which itself depends on the position, because they're both on the same
8004
      // item:
8005
      if (currentParent->mParentItem == mParentItem) {
8006
        qDebug() << Q_FUNC_INFO
8007
                 << "can't set parent to be an anchor which itself depends on "
8008
                    "this position"
8009
                 << reinterpret_cast<quintptr>(parentAnchor);
8010
        return false;
8011
      }
8012
      break;
8013
    }
8014
  }
8015
8016
  // if previously no parent set and PosType is still ptPlotCoords, set to
8017
  // ptAbsolute:
8018
  if (!mParentAnchorY && mPositionTypeY == ptPlotCoords) setTypeY(ptAbsolute);
8019
8020
  // save pixel position:
8021
  QPointF pixelP;
8022
  if (keepPixelPosition) pixelP = pixelPoint();
8023
  // unregister at current parent anchor:
8024
  if (mParentAnchorY) mParentAnchorY->removeChildY(this);
8025
  // register at new parent anchor:
8026
  if (parentAnchor) parentAnchor->addChildY(this);
8027
  mParentAnchorY = parentAnchor;
8028
  // restore pixel position under new parent:
8029
  if (keepPixelPosition)
8030
    setPixelPoint(pixelP);
8031
  else
8032
    setCoords(coords().x(), 0);
8033
  return true;
8034
}
8035
8036
/*!
8037
  Sets the coordinates of this QCPItemPosition. What the coordinates mean, is
8038
  defined by the type
8039
  (\ref setType, \ref setTypeX, \ref setTypeY).
8040
8041
  For example, if the type is \ref ptAbsolute, \a key and \a value mean the x
8042
  and y pixel position on the QCustomPlot surface. In that case the origin (0,
8043
  0) is in the top left corner of the QCustomPlot viewport. If the type is \ref
8044
  ptPlotCoords, \a key and \a value mean a point in the plot coordinate system
8045
  defined by the axes set by \ref setAxes. By default those are the
8046
  QCustomPlot's xAxis and yAxis. See the documentation of \ref setType for other
8047
  available coordinate types and their meaning.
8048
8049
  If different types were configured for X and Y (\ref setTypeX, \ref setTypeY),
8050
  \a key and \a value must also be provided in the different coordinate systems.
8051
  Here, the X type refers to \a key, and the Y type refers to \a value.
8052
8053
  \see setPixelPoint
8054
*/
8055
void QCPItemPosition::setCoords(double key, double value) {
8056
  mKey = key;
8057
  mValue = value;
8058
}
8059
8060
/*! \overload
8061
8062
  Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key
8063
  and pos.y the meaning of \a value of the \ref setCoords(double key, double
8064
  value) method.
8065
*/
8066
void QCPItemPosition::setCoords(const QPointF &pos) {
8067
  setCoords(pos.x(), pos.y());
8068
}
8069
8070
/*!
8071
  Returns the final absolute pixel position of the QCPItemPosition on the
8072
  QCustomPlot surface. It includes all effects of type (\ref setType) and
8073
  possible parent anchors (\ref setParentAnchor).
8074
8075
  \see setPixelPoint
8076
*/
8077
QPointF QCPItemPosition::pixelPoint() const {
8078
  QPointF result;
8079
8080
  // determine X:
8081
  switch (mPositionTypeX) {
8082
    case ptAbsolute: {
8083
      result.rx() = mKey;
8084
      if (mParentAnchorX) result.rx() += mParentAnchorX->pixelPoint().x();
8085
      break;
8086
    }
8087
    case ptViewportRatio: {
8088
      result.rx() = mKey * mParentPlot->viewport().width();
8089
      if (mParentAnchorX)
8090
        result.rx() += mParentAnchorX->pixelPoint().x();
8091
      else
8092
        result.rx() += mParentPlot->viewport().left();
8093
      break;
8094
    }
8095
    case ptAxisRectRatio: {
8096
      if (mAxisRect) {
8097
        result.rx() = mKey * mAxisRect.data()->width();
8098
        if (mParentAnchorX)
8099
          result.rx() += mParentAnchorX->pixelPoint().x();
8100
        else
8101
          result.rx() += mAxisRect.data()->left();
8102
      } else
8103
        qDebug() << Q_FUNC_INFO
8104
                 << "Item position type x is ptAxisRectRatio, but no axis rect "
8105
                    "was defined";
8106
      break;
8107
    }
8108
    case ptPlotCoords: {
8109
      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8110
        result.rx() = mKeyAxis.data()->coordToPixel(mKey);
8111
      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8112
        result.rx() = mValueAxis.data()->coordToPixel(mValue);
8113
      else
8114
        qDebug()
8115
            << Q_FUNC_INFO
8116
            << "Item position type x is ptPlotCoords, but no axes were defined";
8117
      break;
8118
    }
8119
  }
8120
8121
  // determine Y:
8122
  switch (mPositionTypeY) {
8123
    case ptAbsolute: {
8124
      result.ry() = mValue;
8125
      if (mParentAnchorY) result.ry() += mParentAnchorY->pixelPoint().y();
8126
      break;
8127
    }
8128
    case ptViewportRatio: {
8129
      result.ry() = mValue * mParentPlot->viewport().height();
8130
      if (mParentAnchorY)
8131
        result.ry() += mParentAnchorY->pixelPoint().y();
8132
      else
8133
        result.ry() += mParentPlot->viewport().top();
8134
      break;
8135
    }
8136
    case ptAxisRectRatio: {
8137
      if (mAxisRect) {
8138
        result.ry() = mValue * mAxisRect.data()->height();
8139
        if (mParentAnchorY)
8140
          result.ry() += mParentAnchorY->pixelPoint().y();
8141
        else
8142
          result.ry() += mAxisRect.data()->top();
8143
      } else
8144
        qDebug() << Q_FUNC_INFO
8145
                 << "Item position type y is ptAxisRectRatio, but no axis rect "
8146
                    "was defined";
8147
      break;
8148
    }
8149
    case ptPlotCoords: {
8150
      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8151
        result.ry() = mKeyAxis.data()->coordToPixel(mKey);
8152
      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8153
        result.ry() = mValueAxis.data()->coordToPixel(mValue);
8154
      else
8155
        qDebug()
8156
            << Q_FUNC_INFO
8157
            << "Item position type y is ptPlotCoords, but no axes were defined";
8158
      break;
8159
    }
8160
  }
8161
8162
  return result;
8163
}
8164
8165
/*!
8166
  When \ref setType is \ref ptPlotCoords, this function may be used to specify
8167
  the axes the coordinates set with \ref setCoords relate to. By default they
8168
  are set to the initial xAxis and yAxis of the QCustomPlot.
8169
*/
8170
void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis) {
8171
  mKeyAxis = keyAxis;
8172
  mValueAxis = valueAxis;
8173
}
8174
8175
/*!
8176
  When \ref setType is \ref ptAxisRectRatio, this function may be used to
8177
  specify the axis rect the coordinates set with \ref setCoords relate to. By
8178
  default this is set to the main axis rect of the QCustomPlot.
8179
*/
8180
void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect) {
8181
  mAxisRect = axisRect;
8182
}
8183
8184
/*!
8185
  Sets the apparent pixel position. This works no matter what type (\ref
8186
  setType) this QCPItemPosition is or what parent-child situation it is in, as
8187
  coordinates are transformed appropriately, to make the position finally appear
8188
  at the specified pixel values.
8189
8190
  Only if the type is \ref ptAbsolute and no parent anchor is set, this
8191
  function's effect is identical to that of \ref setCoords.
8192
8193
  \see pixelPoint, setCoords
8194
*/
8195
void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint) {
8196
  double x = pixelPoint.x();
8197
  double y = pixelPoint.y();
8198
8199
  switch (mPositionTypeX) {
8200
    case ptAbsolute: {
8201
      if (mParentAnchorX) x -= mParentAnchorX->pixelPoint().x();
8202
      break;
8203
    }
8204
    case ptViewportRatio: {
8205
      if (mParentAnchorX)
8206
        x -= mParentAnchorX->pixelPoint().x();
8207
      else
8208
        x -= mParentPlot->viewport().left();
8209
      x /= (double)mParentPlot->viewport().width();
8210
      break;
8211
    }
8212
    case ptAxisRectRatio: {
8213
      if (mAxisRect) {
8214
        if (mParentAnchorX)
8215
          x -= mParentAnchorX->pixelPoint().x();
8216
        else
8217
          x -= mAxisRect.data()->left();
8218
        x /= (double)mAxisRect.data()->width();
8219
      } else
8220
        qDebug() << Q_FUNC_INFO
8221
                 << "Item position type x is ptAxisRectRatio, but no axis rect "
8222
                    "was defined";
8223
      break;
8224
    }
8225
    case ptPlotCoords: {
8226
      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8227
        x = mKeyAxis.data()->pixelToCoord(x);
8228
      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8229
        y = mValueAxis.data()->pixelToCoord(x);
8230
      else
8231
        qDebug()
8232
            << Q_FUNC_INFO
8233
            << "Item position type x is ptPlotCoords, but no axes were defined";
8234
      break;
8235
    }
8236
  }
8237
8238
  switch (mPositionTypeY) {
8239
    case ptAbsolute: {
8240
      if (mParentAnchorY) y -= mParentAnchorY->pixelPoint().y();
8241
      break;
8242
    }
8243
    case ptViewportRatio: {
8244
      if (mParentAnchorY)
8245
        y -= mParentAnchorY->pixelPoint().y();
8246
      else
8247
        y -= mParentPlot->viewport().top();
8248
      y /= (double)mParentPlot->viewport().height();
8249
      break;
8250
    }
8251
    case ptAxisRectRatio: {
8252
      if (mAxisRect) {
8253
        if (mParentAnchorY)
8254
          y -= mParentAnchorY->pixelPoint().y();
8255
        else
8256
          y -= mAxisRect.data()->top();
8257
        y /= (double)mAxisRect.data()->height();
8258
      } else
8259
        qDebug() << Q_FUNC_INFO
8260
                 << "Item position type y is ptAxisRectRatio, but no axis rect "
8261
                    "was defined";
8262
      break;
8263
    }
8264
    case ptPlotCoords: {
8265
      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8266
        x = mKeyAxis.data()->pixelToCoord(y);
8267
      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8268
        y = mValueAxis.data()->pixelToCoord(y);
8269
      else
8270
        qDebug()
8271
            << Q_FUNC_INFO
8272
            << "Item position type y is ptPlotCoords, but no axes were defined";
8273
      break;
8274
    }
8275
  }
8276
8277
  setCoords(x, y);
8278
}
8279
8280
////////////////////////////////////////////////////////////////////////////////////////////////////
8281
//////////////////// QCPAbstractItem
8282
////////////////////////////////////////////////////////////////////////////////////////////////////
8283
8284
/*! \class QCPAbstractItem
8285
  \brief The abstract base class for all items in a plot.
8286
8287
  In QCustomPlot, items are supplemental graphical elements that are neither
8288
  plottables (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are
8289
  always tied to two axes and thus plot coordinates, items can also be placed in
8290
  absolute coordinates independent of any axes. Each specific item has at least
8291
  one QCPItemPosition member which controls the positioning. Some items are
8292
  defined by more than one coordinate and thus have two or more QCPItemPosition
8293
  members (For example, QCPItemRect has \a topLeft and \a bottomRight).
8294
8295
  This abstract base class defines a very basic interface like visibility and
8296
  clipping. Since this class is abstract, it can't be instantiated. Use one of
8297
  the subclasses or create a subclass yourself to create new items.
8298
8299
  The built-in items are:
8300
  <table>
8301
  <tr><td>QCPItemLine</td><td>A line defined by a start and an end point. May
8302
  have different ending styles on each side (e.g. arrows).</td></tr>
8303
  <tr><td>QCPItemStraightLine</td><td>A straight line defined by a start and a
8304
  direction point. Unlike QCPItemLine, the straight line is infinitely long and
8305
  has no endings.</td></tr> <tr><td>QCPItemCurve</td><td>A curve defined by
8306
  start, end and two intermediate control points. May have different ending
8307
  styles on each side (e.g. arrows).</td></tr> <tr><td>QCPItemRect</td><td>A
8308
  rectangle</td></tr> <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>
8309
  <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>
8310
  <tr><td>QCPItemText</td><td>A text label</td></tr>
8311
  <tr><td>QCPItemBracket</td><td>A bracket which may be used to
8312
  reference/highlight certain parts in the plot.</td></tr>
8313
  <tr><td>QCPItemTracer</td><td>An item that can be attached to a QCPGraph and
8314
  sticks to its data points, given a key coordinate.</td></tr>
8315
  </table>
8316
8317
  \section items-clipping Clipping
8318
8319
  Items are by default clipped to the main axis rect (they are only visible
8320
  inside the axis rect). To make an item visible outside that axis rect, disable
8321
  clipping via \ref setClipToAxisRect "setClipToAxisRect(false)".
8322
8323
  On the other hand if you want the item to be clipped to a different axis rect,
8324
  specify it via \ref setClipAxisRect. This clipAxisRect property of an item is
8325
  only used for clipping behaviour, and in principle is independent of the
8326
  coordinate axes the item might be tied to via its position members (\ref
8327
  QCPItemPosition::setAxes). However, it is common that the axis rect for
8328
  clipping also contains the axes used for the item positions.
8329
8330
  \section items-using Using items
8331
8332
  First you instantiate the item you want to use and add it to the plot:
8333
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1
8334
  by default, the positions of the item are bound to the x- and y-Axis of the
8335
  plot. So we can just set the plot coordinates where the line should start/end:
8336
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2
8337
  If we don't want the line to be positioned in plot coordinates but a different
8338
  coordinate system, e.g. absolute pixel positions on the QCustomPlot surface,
8339
  we need to change the position type like this: \snippet
8340
  documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3 Then we
8341
  can set the coordinates, this time in pixels: \snippet
8342
  documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4 and make
8343
  the line visible on the entire QCustomPlot, by disabling clipping to the axis
8344
  rect: \snippet documentation/doc-code-snippets/mainwindow.cpp
8345
  qcpitemline-creation-5
8346
8347
  For more advanced plots, it is even possible to set different types and parent
8348
  anchors per X/Y coordinate of an item position, using for example \ref
8349
  QCPItemPosition::setTypeX or \ref QCPItemPosition::setParentAnchorX. For
8350
  details, see the documentation of \ref QCPItemPosition.
8351
8352
  \section items-subclassing Creating own items
8353
8354
  To create an own item, you implement a subclass of QCPAbstractItem. These are
8355
  the pure virtual functions, you must implement: \li \ref selectTest \li \ref
8356
  draw
8357
8358
  See the documentation of those functions for what they need to do.
8359
8360
  \subsection items-positioning Allowing the item to be positioned
8361
8362
  As mentioned, item positions are represented by QCPItemPosition members. Let's
8363
  assume the new item shall have only one point as its position (as opposed to
8364
  two like a rect or multiple like a polygon). You then add a public member of
8365
  type QCPItemPosition like so:
8366
8367
  \code QCPItemPosition * const myPosition;\endcode
8368
8369
  the const makes sure the pointer itself can't be modified from the user of
8370
  your new item (the QCPItemPosition instance it points to, can be modified, of
8371
  course). The initialization of this pointer is made easy with the \ref
8372
  createPosition function. Just assign the return value of this function to each
8373
  QCPItemPosition in the constructor of your item. \ref createPosition takes a
8374
  string which is the name of the position, typically this is identical to the
8375
  variable name. For example, the constructor of QCPItemExample could look like
8376
  this:
8377
8378
  \code
8379
  QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :
8380
    QCPAbstractItem(parentPlot),
8381
    myPosition(createPosition("myPosition"))
8382
  {
8383
    // other constructor code
8384
  }
8385
  \endcode
8386
8387
  \subsection items-drawing The draw function
8388
8389
  To give your item a visual representation, reimplement the \ref draw function
8390
  and use the passed QCPPainter to draw the item. You can retrieve the item
8391
  position in pixel coordinates from the position member(s) via \ref
8392
  QCPItemPosition::pixelPoint.
8393
8394
  To optimize performance you should calculate a bounding rect first (don't
8395
  forget to take the pen width into account), check whether it intersects the
8396
  \ref clipRect, and only draw the item at all if this is the case.
8397
8398
  \subsection items-selection The selectTest function
8399
8400
  Your implementation of the \ref selectTest function may use the helpers \ref
8401
  distSqrToLine and \ref rectSelectTest. With these, the implementation of the
8402
  selection test becomes significantly simpler for most items. See the
8403
  documentation of \ref selectTest for what the function parameters mean and
8404
  what the function should return.
8405
8406
  \subsection anchors Providing anchors
8407
8408
  Providing anchors (QCPItemAnchor) starts off like adding a position. First you
8409
  create a public member, e.g.
8410
8411
  \code QCPItemAnchor * const bottom;\endcode
8412
8413
  and create it in the constructor with the \ref createAnchor function,
8414
  assigning it a name and an anchor id (an integer enumerating all anchors on
8415
  the item, you may create an own enum for this). Since anchors can be placed
8416
  anywhere, relative to the item's position(s), your item needs to provide the
8417
  position of every anchor with the reimplementation of the \ref
8418
  anchorPixelPoint(int anchorId) function.
8419
8420
  In essence the QCPItemAnchor is merely an intermediary that itself asks your
8421
  item for the pixel position when anything attached to the anchor needs to know
8422
  the coordinates.
8423
*/
8424
8425
/* start of documentation of inline functions */
8426
8427
/*! \fn QList<QCPItemPosition*> QCPAbstractItem::positions() const
8428
8429
  Returns all positions of the item in a list.
8430
8431
  \see anchors, position
8432
*/
8433
8434
/*! \fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const
8435
8436
  Returns all anchors of the item in a list. Note that since a position
8437
  (QCPItemPosition) is always also an anchor, the list will also contain the
8438
  positions of this item.
8439
8440
  \see positions, anchor
8441
*/
8442
8443
/* end of documentation of inline functions */
8444
/* start documentation of pure virtual functions */
8445
8446
/*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0
8447
  \internal
8448
8449
  Draws this item with the provided \a painter.
8450
8451
  The cliprect of the provided painter is set to the rect returned by \ref
8452
  clipRect before this function is called. The clipRect depends on the clipping
8453
  settings defined by \ref setClipToAxisRect and \ref setClipAxisRect.
8454
*/
8455
8456
/* end documentation of pure virtual functions */
8457
/* start documentation of signals */
8458
8459
/*! \fn void QCPAbstractItem::selectionChanged(bool selected)
8460
  This signal is emitted when the selection state of this item has changed,
8461
  either by user interaction or by a direct call to \ref setSelected.
8462
*/
8463
8464
/* end documentation of signals */
8465
8466
/*!
8467
  Base class constructor which initializes base class members.
8468
*/
8469
QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot)
8470
    : QCPLayerable(parentPlot),
8471
      mClipToAxisRect(false),
8472
      mSelectable(true),
8473
      mSelected(false) {
8474
  QList<QCPAxisRect *> rects = parentPlot->axisRects();
8475
  if (rects.size() > 0) {
8476
    setClipToAxisRect(true);
8477
    setClipAxisRect(rects.first());
8478
  }
8479
}
8480
8481
QCPAbstractItem::~QCPAbstractItem() {
8482
  // don't delete mPositions because every position is also an anchor and thus
8483
  // in mAnchors
8484
  qDeleteAll(mAnchors);
8485
}
8486
8487
/* can't make this a header inline function, because QPointer breaks with
8488
 * forward declared types, see QTBUG-29588 */
8489
QCPAxisRect *QCPAbstractItem::clipAxisRect() const {
8490
  return mClipAxisRect.data();
8491
}
8492
8493
/*!
8494
  Sets whether the item shall be clipped to an axis rect or whether it shall be
8495
  visible on the entire QCustomPlot. The axis rect can be set with \ref
8496
  setClipAxisRect.
8497
8498
  \see setClipAxisRect
8499
*/
8500
void QCPAbstractItem::setClipToAxisRect(bool clip) {
8501
  mClipToAxisRect = clip;
8502
  if (mClipToAxisRect) setParentLayerable(mClipAxisRect.data());
8503
}
8504
8505
/*!
8506
  Sets the clip axis rect. It defines the rect that will be used to clip the
8507
  item when \ref setClipToAxisRect is set to true.
8508
8509
  \see setClipToAxisRect
8510
*/
8511
void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect) {
8512
  mClipAxisRect = rect;
8513
  if (mClipToAxisRect) setParentLayerable(mClipAxisRect.data());
8514
}
8515
8516
/*!
8517
  Sets whether the user can (de-)select this item by clicking on the QCustomPlot
8518
  surface. (When \ref QCustomPlot::setInteractions contains
8519
  QCustomPlot::iSelectItems.)
8520
8521
  However, even when \a selectable was set to false, it is possible to set the
8522
  selection manually, by calling \ref setSelected.
8523
8524
  \see QCustomPlot::setInteractions, setSelected
8525
*/
8526
void QCPAbstractItem::setSelectable(bool selectable) {
8527
  if (mSelectable != selectable) {
8528
    mSelectable = selectable;
8529
    emit selectableChanged(mSelectable);
8530
  }
8531
}
8532
8533
/*!
8534
  Sets whether this item is selected or not. When selected, it might use a
8535
  different visual appearance (e.g. pen and brush), this depends on the specific
8536
  item though.
8537
8538
  The entire selection mechanism for items is handled automatically when \ref
8539
  QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need
8540
  to call this function when you wish to change the selection state manually.
8541
8542
  This function can change the selection state even when \ref setSelectable was
8543
  set to false.
8544
8545
  emits the \ref selectionChanged signal when \a selected is different from the
8546
  previous selection state.
8547
8548
  \see setSelectable, selectTest
8549
*/
8550
void QCPAbstractItem::setSelected(bool selected) {
8551
  if (mSelected != selected) {
8552
    mSelected = selected;
8553
    emit selectionChanged(mSelected);
8554
  }
8555
}
8556
8557
/*!
8558
  Returns the QCPItemPosition with the specified \a name. If this item doesn't
8559
  have a position by that name, returns 0.
8560
8561
  This function provides an alternative way to access item positions. Normally,
8562
  you access positions direcly by their member pointers (which typically have
8563
  the same variable name as \a name).
8564
8565
  \see positions, anchor
8566
*/
8567
QCPItemPosition *QCPAbstractItem::position(const QString &name) const {
8568
  for (int i = 0; i < mPositions.size(); ++i) {
8569
    if (mPositions.at(i)->name() == name) return mPositions.at(i);
8570
  }
8571
  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8572
  return 0;
8573
}
8574
8575
/*!
8576
  Returns the QCPItemAnchor with the specified \a name. If this item doesn't
8577
  have an anchor by that name, returns 0.
8578
8579
  This function provides an alternative way to access item anchors. Normally,
8580
  you access anchors direcly by their member pointers (which typically have the
8581
  same variable name as \a name).
8582
8583
  \see anchors, position
8584
*/
8585
QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const {
8586
  for (int i = 0; i < mAnchors.size(); ++i) {
8587
    if (mAnchors.at(i)->name() == name) return mAnchors.at(i);
8588
  }
8589
  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8590
  return 0;
8591
}
8592
8593
/*!
8594
  Returns whether this item has an anchor with the specified \a name.
8595
8596
  Note that you can check for positions with this function, too. This is because
8597
  every position is also an anchor (QCPItemPosition inherits from
8598
  QCPItemAnchor).
8599
8600
  \see anchor, position
8601
*/
8602
bool QCPAbstractItem::hasAnchor(const QString &name) const {
8603
  for (int i = 0; i < mAnchors.size(); ++i) {
8604
    if (mAnchors.at(i)->name() == name) return true;
8605
  }
8606
  return false;
8607
}
8608
8609
/*! \internal
8610
8611
  Returns the rect the visual representation of this item is clipped to. This
8612
  depends on the current setting of \ref setClipToAxisRect as well as the axis
8613
  rect set with \ref setClipAxisRect.
8614
8615
  If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport
8616
  rect is returned.
8617
8618
  \see draw
8619
*/
8620
QRect QCPAbstractItem::clipRect() const {
8621
  if (mClipToAxisRect && mClipAxisRect)
8622
    return mClipAxisRect.data()->rect();
8623
  else
8624
    return mParentPlot->viewport();
8625
}
8626
8627
/*! \internal
8628
8629
  A convenience function to easily set the QPainter::Antialiased hint on the
8630
  provided \a painter before drawing item lines.
8631
8632
  This is the antialiasing state the painter passed to the \ref draw method is
8633
  in by default.
8634
8635
  This function takes into account the local setting of the antialiasing flag as
8636
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
8637
  \ref QCustomPlot::setNotAntialiasedElements.
8638
8639
  \see setAntialiased
8640
*/
8641
void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const {
8642
  applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
8643
}
8644
8645
/*! \internal
8646
8647
  Finds the shortest squared distance of \a point to the line segment defined by
8648
  \a start and \a end.
8649
8650
  This function may be used to help with the implementation of the \ref
8651
  selectTest function for specific items.
8652
8653
  \note This function is identical to QCPAbstractPlottable::distSqrToLine
8654
8655
  \see rectSelectTest
8656
*/
8657
double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end,
8658
                                      const QPointF &point) const {
8659
  QVector2D a(start);
8660
  QVector2D b(end);
8661
  QVector2D p(point);
8662
  QVector2D v(b - a);
8663
8664
  double vLengthSqr = v.lengthSquared();
8665
  if (!qFuzzyIsNull(vLengthSqr)) {
8666
    double mu = QVector2D::dotProduct(p - a, v) / vLengthSqr;
8667
    if (mu < 0)
8668
      return (a - p).lengthSquared();
8669
    else if (mu > 1)
8670
      return (b - p).lengthSquared();
8671
    else
8672
      return ((a + mu * v) - p).lengthSquared();
8673
  } else
8674
    return (a - p).lengthSquared();
8675
}
8676
8677
/*! \internal
8678
8679
  A convenience function which returns the selectTest value for a specified \a
8680
  rect and a specified click position \a pos. \a filledRect defines whether a
8681
  click inside the rect should also be considered a hit or whether only the rect
8682
  border is sensitive to hits.
8683
8684
  This function may be used to help with the implementation of the \ref
8685
  selectTest function for specific items.
8686
8687
  For example, if your item consists of four rects, call this function four
8688
  times, once for each rect, in your \ref selectTest reimplementation. Finally,
8689
  return the minimum of all four returned values.
8690
8691
  \see distSqrToLine
8692
*/
8693
double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos,
8694
                                       bool filledRect) const {
8695
  double result = -1;
8696
8697
  // distance to border:
8698
  QList<QLineF> lines;
8699
  lines << QLineF(rect.topLeft(), rect.topRight())
8700
        << QLineF(rect.bottomLeft(), rect.bottomRight())
8701
        << QLineF(rect.topLeft(), rect.bottomLeft())
8702
        << QLineF(rect.topRight(), rect.bottomRight());
8703
  double minDistSqr = std::numeric_limits<double>::max();
8704
  for (int i = 0; i < lines.size(); ++i) {
8705
    double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8706
    if (distSqr < minDistSqr) minDistSqr = distSqr;
8707
  }
8708
  result = qSqrt(minDistSqr);
8709
8710
  // filled rect, allow click inside to count as hit:
8711
  if (filledRect && result > mParentPlot->selectionTolerance() * 0.99) {
8712
    if (rect.contains(pos)) result = mParentPlot->selectionTolerance() * 0.99;
8713
  }
8714
  return result;
8715
}
8716
8717
/*! \internal
8718
8719
  Returns the pixel position of the anchor with Id \a anchorId. This function
8720
  must be reimplemented in item subclasses if they want to provide anchors
8721
  (QCPItemAnchor).
8722
8723
  For example, if the item has two anchors with id 0 and 1, this function takes
8724
  one of these anchor ids and returns the respective pixel points of the
8725
  specified anchor.
8726
8727
  \see createAnchor
8728
*/
8729
QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const {
8730
  qDebug() << Q_FUNC_INFO
8731
           << "called on item which shouldn't have any anchors (this method "
8732
              "not reimplemented). anchorId"
8733
           << anchorId;
8734
  return QPointF();
8735
}
8736
8737
/*! \internal
8738
8739
  Creates a QCPItemPosition, registers it with this item and returns a pointer
8740
  to it. The specified \a name must be a unique string that is usually identical
8741
  to the variable name of the position member (This is needed to provide the
8742
  name-based \ref position access to positions).
8743
8744
  Don't delete positions created by this function manually, as the item will
8745
  take care of it.
8746
8747
  Use this function in the constructor (initialization list) of the specific
8748
  item subclass to create each position member. Don't create QCPItemPositions
8749
  with \b new yourself, because they won't be registered with the item properly.
8750
8751
  \see createAnchor
8752
*/
8753
QCPItemPosition *QCPAbstractItem::createPosition(const QString &name) {
8754
  if (hasAnchor(name))
8755
    qDebug() << Q_FUNC_INFO
8756
             << "anchor/position with name exists already:" << name;
8757
  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8758
  mPositions.append(newPosition);
8759
  mAnchors.append(newPosition);  // every position is also an anchor
8760
  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8761
  newPosition->setType(QCPItemPosition::ptPlotCoords);
8762
  if (mParentPlot->axisRect())
8763
    newPosition->setAxisRect(mParentPlot->axisRect());
8764
  newPosition->setCoords(0, 0);
8765
  return newPosition;
8766
}
8767
8768
/*! \internal
8769
8770
  Creates a QCPItemAnchor, registers it with this item and returns a pointer to
8771
  it. The specified \a name must be a unique string that is usually identical to
8772
  the variable name of the anchor member (This is needed to provide the name
8773
  based \ref anchor access to anchors).
8774
8775
  The \a anchorId must be a number identifying the created anchor. It is
8776
  recommended to create an enum (e.g. "AnchorIndex") for this on each item that
8777
  uses anchors. This id is used by the anchor to identify itself when it calls
8778
  QCPAbstractItem::anchorPixelPoint. That function then returns the correct
8779
  pixel coordinates for the passed anchor id.
8780
8781
  Don't delete anchors created by this function manually, as the item will take
8782
  care of it.
8783
8784
  Use this function in the constructor (initialization list) of the specific
8785
  item subclass to create each anchor member. Don't create QCPItemAnchors with
8786
  \b new yourself, because then they won't be registered with the item properly.
8787
8788
  \see createPosition
8789
*/
8790
QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name,
8791
                                             int anchorId) {
8792
  if (hasAnchor(name))
8793
    qDebug() << Q_FUNC_INFO
8794
             << "anchor/position with name exists already:" << name;
8795
  QCPItemAnchor *newAnchor =
8796
      new QCPItemAnchor(mParentPlot, this, name, anchorId);
8797
  mAnchors.append(newAnchor);
8798
  return newAnchor;
8799
}
8800
8801
/* inherits documentation from base class */
8802
void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive,
8803
                                  const QVariant &details,
8804
                                  bool *selectionStateChanged) {
8805
  Q_UNUSED(event)
8806
  Q_UNUSED(details)
8807
  if (mSelectable) {
8808
    bool selBefore = mSelected;
8809
    setSelected(additive ? !mSelected : true);
8810
    if (selectionStateChanged) *selectionStateChanged = mSelected != selBefore;
8811
  }
8812
}
8813
8814
/* inherits documentation from base class */
8815
void QCPAbstractItem::deselectEvent(bool *selectionStateChanged) {
8816
  if (mSelectable) {
8817
    bool selBefore = mSelected;
8818
    setSelected(false);
8819
    if (selectionStateChanged) *selectionStateChanged = mSelected != selBefore;
8820
  }
8821
}
8822
8823
/* inherits documentation from base class */
8824
QCP::Interaction QCPAbstractItem::selectionCategory() const {
8825
  return QCP::iSelectItems;
8826
}
8827
8828
/*! \file */
8829
8830
////////////////////////////////////////////////////////////////////////////////////////////////////
8831
//////////////////// QCustomPlot
8832
////////////////////////////////////////////////////////////////////////////////////////////////////
8833
8834
/*! \class QCustomPlot
8835
8836
  \brief The central class of the library. This is the QWidget which displays
8837
  the plot and interacts with the user.
8838
8839
  For tutorials on how to use QCustomPlot, see the website\n
8840
  http://www.qcustomplot.com/
8841
*/
8842
8843
/* start of documentation of inline functions */
8844
8845
/*! \fn QRect QCustomPlot::viewport() const
8846
8847
  Returns the viewport rect of this QCustomPlot instance. The viewport is the
8848
  area the plot is drawn in, all mechanisms, e.g. margin caluclation take the
8849
  viewport to be the outer border of the plot. The viewport normally is the
8850
  rect() of the QCustomPlot widget, i.e. a rect with top left (0, 0) and size of
8851
  the QCustomPlot widget.
8852
8853
  Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis
8854
  rect is typically an area enclosed by four axes, where the graphs/plottables
8855
  are drawn in. The viewport is larger and contains also the axes themselves,
8856
  their tick numbers, their labels, the plot title etc.
8857
8858
  Only when saving to a file (see \ref savePng, \ref savePdf etc.) the viewport
8859
  is temporarily modified to allow saving plots with sizes independent of the
8860
  current widget size.
8861
*/
8862
8863
/*! \fn QCPLayoutGrid *QCustomPlot::plotLayout() const
8864
8865
  Returns the top level layout of this QCustomPlot instance. It is a \ref
8866
  QCPLayoutGrid, initially containing just one cell with the main QCPAxisRect
8867
  inside.
8868
*/
8869
8870
/* end of documentation of inline functions */
8871
/* start of documentation of signals */
8872
8873
/*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)
8874
8875
  This signal is emitted when the QCustomPlot receives a mouse double click
8876
  event.
8877
*/
8878
8879
/*! \fn void QCustomPlot::mousePress(QMouseEvent *event)
8880
8881
  This signal is emitted when the QCustomPlot receives a mouse press event.
8882
8883
  It is emitted before QCustomPlot handles any other mechanism like range
8884
  dragging. So a slot connected to this signal can still influence the behaviour
8885
  e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8886
  QCPAxisRect::setRangeDragAxes.
8887
*/
8888
8889
/*! \fn void QCustomPlot::mouseMove(QMouseEvent *event)
8890
8891
  This signal is emitted when the QCustomPlot receives a mouse move event.
8892
8893
  It is emitted before QCustomPlot handles any other mechanism like range
8894
  dragging. So a slot connected to this signal can still influence the behaviour
8895
  e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8896
  QCPAxisRect::setRangeDragAxes.
8897
8898
  \warning It is discouraged to change the drag-axes with \ref
8899
  QCPAxisRect::setRangeDragAxes here, because the dragging starting point was
8900
  saved the moment the mouse was pressed. Thus it only has a meaning for the
8901
  range drag axes that were set at that moment. If you want to change the drag
8902
  axes, consider doing this in the \ref mousePress signal instead.
8903
*/
8904
8905
/*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event)
8906
8907
  This signal is emitted when the QCustomPlot receives a mouse release event.
8908
8909
  It is emitted before QCustomPlot handles any other mechanisms like object
8910
  selection. So a slot connected to this signal can still influence the
8911
  behaviour e.g. with \ref setInteractions or \ref
8912
  QCPAbstractPlottable::setSelectable.
8913
*/
8914
8915
/*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event)
8916
8917
  This signal is emitted when the QCustomPlot receives a mouse wheel event.
8918
8919
  It is emitted before QCustomPlot handles any other mechanisms like range
8920
  zooming. So a slot connected to this signal can still influence the behaviour
8921
  e.g. with \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeZoomAxes
8922
  or \ref QCPAxisRect::setRangeZoomFactor.
8923
*/
8924
8925
/*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable,
8926
  QMouseEvent *event)
8927
8928
  This signal is emitted when a plottable is clicked.
8929
8930
  \a event is the mouse event that caused the click and \a plottable is the
8931
  plottable that received the click.
8932
8933
  \see plottableDoubleClick
8934
*/
8935
8936
/*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable,
8937
  QMouseEvent *event)
8938
8939
  This signal is emitted when a plottable is double clicked.
8940
8941
  \a event is the mouse event that caused the click and \a plottable is the
8942
  plottable that received the click.
8943
8944
  \see plottableClick
8945
*/
8946
8947
/*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)
8948
8949
  This signal is emitted when an item is clicked.
8950
8951
  \a event is the mouse event that caused the click and \a item is the item that
8952
  received the click.
8953
8954
  \see itemDoubleClick
8955
*/
8956
8957
/*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent
8958
  *event)
8959
8960
  This signal is emitted when an item is double clicked.
8961
8962
  \a event is the mouse event that caused the click and \a item is the item that
8963
  received the click.
8964
8965
  \see itemClick
8966
*/
8967
8968
/*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part,
8969
  QMouseEvent *event)
8970
8971
  This signal is emitted when an axis is clicked.
8972
8973
  \a event is the mouse event that caused the click, \a axis is the axis that
8974
  received the click and \a part indicates the part of the axis that was
8975
  clicked.
8976
8977
  \see axisDoubleClick
8978
*/
8979
8980
/*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart
8981
  part, QMouseEvent *event)
8982
8983
  This signal is emitted when an axis is double clicked.
8984
8985
  \a event is the mouse event that caused the click, \a axis is the axis that
8986
  received the click and \a part indicates the part of the axis that was
8987
  clicked.
8988
8989
  \see axisClick
8990
*/
8991
8992
/*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem
8993
  *item, QMouseEvent *event)
8994
8995
  This signal is emitted when a legend (item) is clicked.
8996
8997
  \a event is the mouse event that caused the click, \a legend is the legend
8998
  that received the click and \a item is the legend item that received the
8999
  click. If only the legend and no item is clicked, \a item is 0. This happens
9000
  for a click inside the legend padding or the space between two items.
9001
9002
  \see legendDoubleClick
9003
*/
9004
9005
/*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend,
9006
  QCPAbstractLegendItem *item, QMouseEvent *event)
9007
9008
  This signal is emitted when a legend (item) is double clicked.
9009
9010
  \a event is the mouse event that caused the click, \a legend is the legend
9011
  that received the click and \a item is the legend item that received the
9012
  click. If only the legend and no item is clicked, \a item is 0. This happens
9013
  for a click inside the legend padding or the space between two items.
9014
9015
  \see legendClick
9016
*/
9017
9018
/*! \fn void QCustomPlot:: titleClick(QMouseEvent *event, QCPPlotTitle *title)
9019
9020
  This signal is emitted when a plot title is clicked.
9021
9022
  \a event is the mouse event that caused the click and \a title is the plot
9023
  title that received the click.
9024
9025
  \see titleDoubleClick
9026
*/
9027
9028
/*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event, QCPPlotTitle
9029
  *title)
9030
9031
  This signal is emitted when a plot title is double clicked.
9032
9033
  \a event is the mouse event that caused the click and \a title is the plot
9034
  title that received the click.
9035
9036
  \see titleClick
9037
*/
9038
9039
/*! \fn void QCustomPlot::selectionChangedByUser()
9040
9041
  This signal is emitted after the user has changed the selection in the
9042
  QCustomPlot, e.g. by clicking. It is not emitted when the selection state of
9043
  an object has changed programmatically by a direct call to setSelected() on an
9044
  object or by calling \ref deselectAll.
9045
9046
  In addition to this signal, selectable objects also provide individual
9047
  signals, for example QCPAxis::selectionChanged or
9048
  QCPAbstractPlottable::selectionChanged. Note that those signals are emitted
9049
  even if the selection state is changed programmatically.
9050
9051
  See the documentation of \ref setInteractions for details about the selection
9052
  mechanism.
9053
9054
  \see selectedPlottables, selectedGraphs, selectedItems, selectedAxes,
9055
  selectedLegends
9056
*/
9057
9058
/*! \fn void QCustomPlot::beforeReplot()
9059
9060
  This signal is emitted immediately before a replot takes place (caused by a
9061
  call to the slot \ref replot).
9062
9063
  It is safe to mutually connect the replot slot with this signal on two
9064
  QCustomPlots to make them replot synchronously, it won't cause an infinite
9065
  recursion.
9066
9067
  \see replot, afterReplot
9068
*/
9069
9070
/*! \fn void QCustomPlot::afterReplot()
9071
9072
  This signal is emitted immediately after a replot has taken place (caused by a
9073
  call to the slot \ref replot).
9074
9075
  It is safe to mutually connect the replot slot with this signal on two
9076
  QCustomPlots to make them replot synchronously, it won't cause an infinite
9077
  recursion.
9078
9079
  \see replot, beforeReplot
9080
*/
9081
9082
/* end of documentation of signals */
9083
/* start of documentation of public members */
9084
9085
/*! \var QCPAxis *QCustomPlot::xAxis
9086
9087
  A pointer to the primary x Axis (bottom) of the main axis rect of the plot.
9088
9089
  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis,
9090
  \ref xAxis2, \ref yAxis2) and the \ref legend. They make it very easy working
9091
  with plots that only have a single axis rect and at most one axis at each axis
9092
  rect side. If you use \link thelayoutsystem the layout system\endlink to add
9093
  multiple axis rects or multiple axes to one side, use the \ref
9094
  QCPAxisRect::axis interface to access the new axes. If one of the four default
9095
  axes or the default legend is removed due to manipulation of the layout system
9096
  (e.g. by removing the main axis rect), the corresponding pointers become 0.
9097
*/
9098
9099
/*! \var QCPAxis *QCustomPlot::yAxis
9100
9101
  A pointer to the primary y Axis (left) of the main axis rect of the plot.
9102
9103
  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis,
9104
  \ref xAxis2, \ref yAxis2) and the \ref legend. They make it very easy working
9105
  with plots that only have a single axis rect and at most one axis at each axis
9106
  rect side. If you use \link thelayoutsystem the layout system\endlink to add
9107
  multiple axis rects or multiple axes to one side, use the \ref
9108
  QCPAxisRect::axis interface to access the new axes. If one of the four default
9109
  axes or the default legend is removed due to manipulation of the layout system
9110
  (e.g. by removing the main axis rect), the corresponding pointers become 0.
9111
*/
9112
9113
/*! \var QCPAxis *QCustomPlot::xAxis2
9114
9115
  A pointer to the secondary x Axis (top) of the main axis rect of the plot.
9116
  Secondary axes are invisible by default. Use QCPAxis::setVisible to change
9117
  this (or use \ref QCPAxisRect::setupFullAxesBox).
9118
9119
  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis,
9120
  \ref xAxis2, \ref yAxis2) and the \ref legend. They make it very easy working
9121
  with plots that only have a single axis rect and at most one axis at each axis
9122
  rect side. If you use \link thelayoutsystem the layout system\endlink to add
9123
  multiple axis rects or multiple axes to one side, use the \ref
9124
  QCPAxisRect::axis interface to access the new axes. If one of the four default
9125
  axes or the default legend is removed due to manipulation of the layout system
9126
  (e.g. by removing the main axis rect), the corresponding pointers become 0.
9127
*/
9128
9129
/*! \var QCPAxis *QCustomPlot::yAxis2
9130
9131
  A pointer to the secondary y Axis (right) of the main axis rect of the plot.
9132
  Secondary axes are invisible by default. Use QCPAxis::setVisible to change
9133
  this (or use \ref QCPAxisRect::setupFullAxesBox).
9134
9135
  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis,
9136
  \ref xAxis2, \ref yAxis2) and the \ref legend. They make it very easy working
9137
  with plots that only have a single axis rect and at most one axis at each axis
9138
  rect side. If you use \link thelayoutsystem the layout system\endlink to add
9139
  multiple axis rects or multiple axes to one side, use the \ref
9140
  QCPAxisRect::axis interface to access the new axes. If one of the four default
9141
  axes or the default legend is removed due to manipulation of the layout system
9142
  (e.g. by removing the main axis rect), the corresponding pointers become 0.
9143
*/
9144
9145
/*! \var QCPLegend *QCustomPlot::legend
9146
9147
  A pointer to the default legend of the main axis rect. The legend is invisible
9148
  by default. Use QCPLegend::setVisible to change this.
9149
9150
  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis,
9151
  \ref xAxis2, \ref yAxis2) and the \ref legend. They make it very easy working
9152
  with plots that only have a single axis rect and at most one axis at each axis
9153
  rect side. If you use \link thelayoutsystem the layout system\endlink to add
9154
  multiple legends to the plot, use the layout system interface to access the
9155
  new legend. For example, legends can be placed inside an axis rect's \ref
9156
  QCPAxisRect::insetLayout "inset layout", and must then also be accessed via
9157
  the inset layout. If the default legend is removed due to manipulation of the
9158
  layout system (e.g. by removing the main axis rect), the corresponding pointer
9159
  becomes 0.
9160
*/
9161
9162
/* end of documentation of public members */
9163
9164
/*!
9165
  Constructs a QCustomPlot and sets reasonable default values.
9166
*/
9167
QCustomPlot::QCustomPlot(QWidget *parent)
9168
    : QWidget(parent),
9169
      xAxis(0),
9170
      yAxis(0),
9171
      xAxis2(0),
9172
      yAxis2(0),
9173
      legend(0),
9174
      mPlotLayout(0),
9175
      mAutoAddPlottableToLegend(true),
9176
      mAntialiasedElements(QCP::aeNone),
9177
      mNotAntialiasedElements(QCP::aeNone),
9178
      mInteractions(0),
9179
      mSelectionTolerance(8),
9180
      mNoAntialiasingOnDrag(false),
9181
      mBackgroundBrush(Qt::white, Qt::SolidPattern),
9182
      mBackgroundScaled(true),
9183
      mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9184
      mCurrentLayer(0),
9185
      mPlottingHints(QCP::phCacheLabels | QCP::phForceRepaint),
9186
      mMultiSelectModifier(Qt::ControlModifier),
9187
      mPaintBuffer(size()),
9188
      mMouseEventElement(0),
9189
      mReplotting(false) {
9190
  setAttribute(Qt::WA_NoMousePropagation);
9191
  setAttribute(Qt::WA_OpaquePaintEvent);
9192
  setMouseTracking(true);
9193
  QLocale currentLocale = locale();
9194
  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9195
  setLocale(currentLocale);
9196
9197
  // create initial layers:
9198
  mLayers.append(new QCPLayer(this, QLatin1String("background")));
9199
  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9200
  mLayers.append(new QCPLayer(this, QLatin1String("main")));
9201
  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9202
  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9203
  updateLayerIndices();
9204
  setCurrentLayer(QLatin1String("main"));
9205
9206
  // create initial layout, axis rect and legend:
9207
  mPlotLayout = new QCPLayoutGrid;
9208
  mPlotLayout->initializeParentPlot(this);
9209
  mPlotLayout->setParent(this);  // important because if parent is QWidget,
9210
                                 // QCPLayout::sizeConstraintsChanged will call
9211
                                 // QWidget::updateGeometry
9212
  mPlotLayout->setLayer(QLatin1String("main"));
9213
  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9214
  mPlotLayout->addElement(0, 0, defaultAxisRect);
9215
  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9216
  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9217
  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9218
  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9219
  legend = new QCPLegend;
9220
  legend->setVisible(false);
9221
  defaultAxisRect->insetLayout()->addElement(legend,
9222
                                             Qt::AlignRight | Qt::AlignTop);
9223
  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9224
9225
  defaultAxisRect->setLayer(QLatin1String("background"));
9226
  xAxis->setLayer(QLatin1String("axes"));
9227
  yAxis->setLayer(QLatin1String("axes"));
9228
  xAxis2->setLayer(QLatin1String("axes"));
9229
  yAxis2->setLayer(QLatin1String("axes"));
9230
  xAxis->grid()->setLayer(QLatin1String("grid"));
9231
  yAxis->grid()->setLayer(QLatin1String("grid"));
9232
  xAxis2->grid()->setLayer(QLatin1String("grid"));
9233
  yAxis2->grid()->setLayer(QLatin1String("grid"));
9234
  legend->setLayer(QLatin1String("legend"));
9235
9236
  setViewport(rect());  // needs to be called after mPlotLayout has been created
9237
9238
  replot();
9239
}
9240
9241
QCustomPlot::~QCustomPlot() {
9242
  clearPlottables();
9243
  clearItems();
9244
9245
  if (mPlotLayout) {
9246
    delete mPlotLayout;
9247
    mPlotLayout = 0;
9248
  }
9249
9250
  mCurrentLayer = 0;
9251
  qDeleteAll(mLayers);  // don't use removeLayer, because it would prevent the
9252
                        // last layer to be removed
9253
  mLayers.clear();
9254
}
9255
9256
/*!
9257
  Sets which elements are forcibly drawn antialiased as an \a or combination of
9258
  QCP::AntialiasedElement.
9259
9260
  This overrides the antialiasing settings for whole element groups, normally
9261
  controlled with the \a setAntialiasing function on the individual elements. If
9262
  an element is neither specified in \ref setAntialiasedElements nor in \ref
9263
  setNotAntialiasedElements, the antialiasing setting on each individual element
9264
  instance is used.
9265
9266
  For example, if \a antialiasedElements contains \ref QCP::aePlottables, all
9267
  plottables will be drawn antialiased, no matter what the specific
9268
  QCPAbstractPlottable::setAntialiased value was set to.
9269
9270
  if an element in \a antialiasedElements is already set in \ref
9271
  setNotAntialiasedElements, it is removed from there.
9272
9273
  \see setNotAntialiasedElements
9274
*/
9275
void QCustomPlot::setAntialiasedElements(
9276
    const QCP::AntialiasedElements &antialiasedElements) {
9277
  mAntialiasedElements = antialiasedElements;
9278
9279
  // make sure elements aren't in mNotAntialiasedElements and
9280
  // mAntialiasedElements simultaneously:
9281
  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9282
    mNotAntialiasedElements |= ~mAntialiasedElements;
9283
}
9284
9285
/*!
9286
  Sets whether the specified \a antialiasedElement is forcibly drawn
9287
  antialiased.
9288
9289
  See \ref setAntialiasedElements for details.
9290
9291
  \see setNotAntialiasedElement
9292
*/
9293
void QCustomPlot::setAntialiasedElement(
9294
    QCP::AntialiasedElement antialiasedElement, bool enabled) {
9295
  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9296
    mAntialiasedElements &= ~antialiasedElement;
9297
  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9298
    mAntialiasedElements |= antialiasedElement;
9299
9300
  // make sure elements aren't in mNotAntialiasedElements and
9301
  // mAntialiasedElements simultaneously:
9302
  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9303
    mNotAntialiasedElements |= ~mAntialiasedElements;
9304
}
9305
9306
/*!
9307
  Sets which elements are forcibly drawn not antialiased as an \a or combination
9308
  of QCP::AntialiasedElement.
9309
9310
  This overrides the antialiasing settings for whole element groups, normally
9311
  controlled with the \a setAntialiasing function on the individual elements. If
9312
  an element is neither specified in \ref setAntialiasedElements nor in \ref
9313
  setNotAntialiasedElements, the antialiasing setting on each individual element
9314
  instance is used.
9315
9316
  For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no
9317
  plottables will be drawn antialiased, no matter what the specific
9318
  QCPAbstractPlottable::setAntialiased value was set to.
9319
9320
  if an element in \a notAntialiasedElements is already set in \ref
9321
  setAntialiasedElements, it is removed from there.
9322
9323
  \see setAntialiasedElements
9324
*/
9325
void QCustomPlot::setNotAntialiasedElements(
9326
    const QCP::AntialiasedElements &notAntialiasedElements) {
9327
  mNotAntialiasedElements = notAntialiasedElements;
9328
9329
  // make sure elements aren't in mNotAntialiasedElements and
9330
  // mAntialiasedElements simultaneously:
9331
  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9332
    mAntialiasedElements |= ~mNotAntialiasedElements;
9333
}
9334
9335
/*!
9336
  Sets whether the specified \a notAntialiasedElement is forcibly drawn not
9337
  antialiased.
9338
9339
  See \ref setNotAntialiasedElements for details.
9340
9341
  \see setAntialiasedElement
9342
*/
9343
void QCustomPlot::setNotAntialiasedElement(
9344
    QCP::AntialiasedElement notAntialiasedElement, bool enabled) {
9345
  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9346
    mNotAntialiasedElements &= ~notAntialiasedElement;
9347
  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9348
    mNotAntialiasedElements |= notAntialiasedElement;
9349
9350
  // make sure elements aren't in mNotAntialiasedElements and
9351
  // mAntialiasedElements simultaneously:
9352
  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9353
    mAntialiasedElements |= ~mNotAntialiasedElements;
9354
}
9355
9356
/*!
9357
  If set to true, adding a plottable (e.g. a graph) to the QCustomPlot
9358
  automatically also adds the plottable to the legend (QCustomPlot::legend).
9359
9360
  \see addPlottable, addGraph, QCPLegend::addItem
9361
*/
9362
void QCustomPlot::setAutoAddPlottableToLegend(bool on) {
9363
  mAutoAddPlottableToLegend = on;
9364
}
9365
9366
/*!
9367
  Sets the possible interactions of this QCustomPlot as an or-combination of
9368
  \ref QCP::Interaction enums. There are the following types of interactions:
9369
9370
  <b>Axis range manipulation</b> is controlled via \ref QCP::iRangeDrag and \ref
9371
  QCP::iRangeZoom. When the respective interaction is enabled, the user may drag
9372
  axes ranges and zoom with the mouse wheel. For details how to control which
9373
  axes the user may drag/zoom and in what orientations, see \ref
9374
  QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeZoom, \ref
9375
  QCPAxisRect::setRangeDragAxes, \ref QCPAxisRect::setRangeZoomAxes.
9376
9377
  <b>Plottable selection</b> is controlled by \ref QCP::iSelectPlottables. If
9378
  \ref QCP::iSelectPlottables is set, the user may select plottables (graphs,
9379
  curves, bars,...) by clicking on them or in their vicinity (\ref
9380
  setSelectionTolerance). Whether the user can actually select a plottable can
9381
  further be restricted with the \ref QCPAbstractPlottable::setSelectable
9382
  function on the specific plottable. To find out whether a specific plottable
9383
  is selected, call QCPAbstractPlottable::selected(). To retrieve a list of all
9384
  currently selected plottables, call \ref selectedPlottables. If you're only
9385
  interested in QCPGraphs, you may use the convenience function \ref
9386
  selectedGraphs.
9387
9388
  <b>Item selection</b> is controlled by \ref QCP::iSelectItems. If \ref
9389
  QCP::iSelectItems is set, the user may select items (QCPItemLine,
9390
  QCPItemText,...) by clicking on them or in their vicinity. To find out whether
9391
  a specific item is selected, call QCPAbstractItem::selected(). To retrieve a
9392
  list of all currently selected items, call \ref selectedItems.
9393
9394
  <b>Axis selection</b> is controlled with \ref QCP::iSelectAxes. If \ref
9395
  QCP::iSelectAxes is set, the user may select parts of the axes by clicking on
9396
  them. What parts exactly (e.g. Axis base line, tick labels, axis label) are
9397
  selectable can be controlled via \ref QCPAxis::setSelectableParts for each
9398
  axis. To retrieve a list of all axes that currently contain selected parts,
9399
  call \ref selectedAxes. Which parts of an axis are selected, can be retrieved
9400
  with QCPAxis::selectedParts().
9401
9402
  <b>Legend selection</b> is controlled with \ref QCP::iSelectLegend. If this is
9403
  set, the user may select the legend itself or individual items by clicking on
9404
  them. What parts exactly are selectable can be controlled via \ref
9405
  QCPLegend::setSelectableParts. To find out whether the legend or any of its
9406
  child items are selected, check the value of QCPLegend::selectedParts. To find
9407
  out which child items are selected, call \ref QCPLegend::selectedItems.
9408
9409
  <b>All other selectable elements</b> The selection of all other selectable
9410
  objects (e.g. QCPPlotTitle, or your own layerable subclasses) is controlled
9411
  with \ref QCP::iSelectOther. If set, the user may select those objects by
9412
  clicking on them. To find out which are currently selected, you need to check
9413
  their selected state explicitly.
9414
9415
  If the selection state has changed by user interaction, the \ref
9416
  selectionChangedByUser signal is emitted. Each selectable object additionally
9417
  emits an individual selectionChanged signal whenever their selection state has
9418
  changed, i.e. not only by user interaction.
9419
9420
  To allow multiple objects to be selected by holding the selection modifier
9421
  (\ref setMultiSelectModifier), set the flag \ref QCP::iMultiSelect.
9422
9423
  \note In addition to the selection mechanism presented here, QCustomPlot
9424
  always emits corresponding signals, when an object is clicked or double
9425
  clicked. see \ref plottableClick and \ref plottableDoubleClick for example.
9426
9427
  \see setInteraction, setSelectionTolerance
9428
*/
9429
void QCustomPlot::setInteractions(const QCP::Interactions &interactions) {
9430
  mInteractions = interactions;
9431
}
9432
9433
/*!
9434
  Sets the single \a interaction of this QCustomPlot to \a enabled.
9435
9436
  For details about the interaction system, see \ref setInteractions.
9437
9438
  \see setInteractions
9439
*/
9440
void QCustomPlot::setInteraction(const QCP::Interaction &interaction,
9441
                                 bool enabled) {
9442
  if (!enabled && mInteractions.testFlag(interaction))
9443
    mInteractions &= ~interaction;
9444
  else if (enabled && !mInteractions.testFlag(interaction))
9445
    mInteractions |= interaction;
9446
}
9447
9448
/*!
9449
  Sets the tolerance that is used to decide whether a click selects an object
9450
  (e.g. a plottable) or not.
9451
9452
  If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only
9453
  regarded as a potential selection when the minimum distance between the click
9454
  position and the graph line is smaller than \a pixels. Objects that are
9455
  defined by an area (e.g. QCPBars) only react to clicks directly inside the
9456
  area and ignore this selection tolerance. In other words, it only has meaning
9457
  for parts of objects that are too thin to exactly hit with a click and thus
9458
  need such a tolerance.
9459
9460
  \see setInteractions, QCPLayerable::selectTest
9461
*/
9462
void QCustomPlot::setSelectionTolerance(int pixels) {
9463
  mSelectionTolerance = pixels;
9464
}
9465
9466
/*!
9467
  Sets whether antialiasing is disabled for this QCustomPlot while the user is
9468
  dragging axes ranges. If many objects, especially plottables, are drawn
9469
  antialiased, this greatly improves performance during dragging. Thus it
9470
  creates a more responsive user experience. As soon as the user stops dragging,
9471
  the last replot is done with normal antialiasing, to restore high image
9472
  quality.
9473
9474
  \see setAntialiasedElements, setNotAntialiasedElements
9475
*/
9476
void QCustomPlot::setNoAntialiasingOnDrag(bool enabled) {
9477
  mNoAntialiasingOnDrag = enabled;
9478
}
9479
9480
/*!
9481
  Sets the plotting hints for this QCustomPlot instance as an \a or combination
9482
  of QCP::PlottingHint.
9483
9484
  \see setPlottingHint
9485
*/
9486
void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints) {
9487
  mPlottingHints = hints;
9488
}
9489
9490
/*!
9491
  Sets the specified plotting \a hint to \a enabled.
9492
9493
  \see setPlottingHints
9494
*/
9495
void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled) {
9496
  QCP::PlottingHints newHints = mPlottingHints;
9497
  if (!enabled)
9498
    newHints &= ~hint;
9499
  else
9500
    newHints |= hint;
9501
9502
  if (newHints != mPlottingHints) setPlottingHints(newHints);
9503
}
9504
9505
/*!
9506
  Sets the keyboard modifier that will be recognized as multi-select-modifier.
9507
9508
  If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may
9509
  select multiple objects by clicking on them one after the other while holding
9510
  down \a modifier.
9511
9512
  By default the multi-select-modifier is set to Qt::ControlModifier.
9513
9514
  \see setInteractions
9515
*/
9516
void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier) {
9517
  mMultiSelectModifier = modifier;
9518
}
9519
9520
/*!
9521
  Sets the viewport of this QCustomPlot. The Viewport is the area that the top
9522
  level layout (QCustomPlot::plotLayout()) uses as its rect. Normally, the
9523
  viewport is the entire widget rect.
9524
9525
  This function is used to allow arbitrary size exports with \ref toPixmap, \ref
9526
  savePng, \ref savePdf, etc. by temporarily changing the viewport size.
9527
*/
9528
void QCustomPlot::setViewport(const QRect &rect) {
9529
  mViewport = rect;
9530
  if (mPlotLayout) mPlotLayout->setOuterRect(mViewport);
9531
}
9532
9533
/*!
9534
  Sets \a pm as the viewport background pixmap (see \ref setViewport). The
9535
  pixmap is always drawn below all other objects in the plot.
9536
9537
  For cases where the provided pixmap doesn't have the same size as the
9538
  viewport, scaling can be enabled with \ref setBackgroundScaled and the scaling
9539
  mode (whether and how the aspect ratio is preserved) can be set with \ref
9540
  setBackgroundScaledMode. To set all these options in one call, consider using
9541
  the overloaded version of this function.
9542
9543
  If a background brush was set with \ref setBackground(const QBrush &brush),
9544
  the viewport will first be filled with that brush, before drawing the
9545
  background pixmap. This can be useful for background pixmaps with translucent
9546
  areas.
9547
9548
  \see setBackgroundScaled, setBackgroundScaledMode
9549
*/
9550
void QCustomPlot::setBackground(const QPixmap &pm) {
9551
  mBackgroundPixmap = pm;
9552
  mScaledBackgroundPixmap = QPixmap();
9553
}
9554
9555
/*!
9556
  Sets the background brush of the viewport (see \ref setViewport).
9557
9558
  Before drawing everything else, the background is filled with \a brush. If a
9559
  background pixmap was set with \ref setBackground(const QPixmap &pm), this
9560
  brush will be used to fill the viewport before the background pixmap is drawn.
9561
  This can be useful for background pixmaps with translucent areas.
9562
9563
  Set \a brush to Qt::NoBrush or Qt::Transparent to leave background
9564
  transparent. This can be useful for exporting to image formats which support
9565
  transparency, e.g. \ref savePng.
9566
9567
  \see setBackgroundScaled, setBackgroundScaledMode
9568
*/
9569
void QCustomPlot::setBackground(const QBrush &brush) {
9570
  mBackgroundBrush = brush;
9571
}
9572
9573
/*! \overload
9574
9575
  Allows setting the background pixmap of the viewport, whether it shall be
9576
  scaled and how it shall be scaled in one call.
9577
9578
  \see setBackground(const QPixmap &pm), setBackgroundScaled,
9579
  setBackgroundScaledMode
9580
*/
9581
void QCustomPlot::setBackground(const QPixmap &pm, bool scaled,
9582
                                Qt::AspectRatioMode mode) {
9583
  mBackgroundPixmap = pm;
9584
  mScaledBackgroundPixmap = QPixmap();
9585
  mBackgroundScaled = scaled;
9586
  mBackgroundScaledMode = mode;
9587
}
9588
9589
/*!
9590
  Sets whether the viewport background pixmap shall be scaled to fit the
9591
  viewport. If \a scaled is set to true, control whether and how the aspect
9592
  ratio of the original pixmap is preserved with \ref setBackgroundScaledMode.
9593
9594
  Note that the scaled version of the original pixmap is buffered, so there is
9595
  no performance penalty on replots. (Except when the viewport dimensions are
9596
  changed continuously.)
9597
9598
  \see setBackground, setBackgroundScaledMode
9599
*/
9600
void QCustomPlot::setBackgroundScaled(bool scaled) {
9601
  mBackgroundScaled = scaled;
9602
}
9603
9604
/*!
9605
  If scaling of the viewport background pixmap is enabled (\ref
9606
  setBackgroundScaled), use this function to define whether and how the aspect
9607
  ratio of the original pixmap is preserved.
9608
9609
  \see setBackground, setBackgroundScaled
9610
*/
9611
void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode) {
9612
  mBackgroundScaledMode = mode;
9613
}
9614
9615
/*!
9616
  Returns the plottable with \a index. If the index is invalid, returns 0.
9617
9618
  There is an overloaded version of this function with no parameter which
9619
  returns the last added plottable, see QCustomPlot::plottable()
9620
9621
  \see plottableCount, addPlottable
9622
*/
9623
QCPAbstractPlottable *QCustomPlot::plottable(int index) {
9624
  if (index >= 0 && index < mPlottables.size()) {
9625
    return mPlottables.at(index);
9626
  } else {
9627
    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9628
    return 0;
9629
  }
9630
}
9631
9632
/*! \overload
9633
9634
  Returns the last plottable that was added with \ref addPlottable. If there are
9635
  no plottables in the plot, returns 0.
9636
9637
  \see plottableCount, addPlottable
9638
*/
9639
QCPAbstractPlottable *QCustomPlot::plottable() {
9640
  if (!mPlottables.isEmpty()) {
9641
    return mPlottables.last();
9642
  } else
9643
    return 0;
9644
}
9645
9646
/*!
9647
  Adds the specified plottable to the plot and, if \ref
9648
  setAutoAddPlottableToLegend is enabled, to the legend (QCustomPlot::legend).
9649
  QCustomPlot takes ownership of the plottable.
9650
9651
  Returns true on success, i.e. when \a plottable isn't already in the plot and
9652
  the parent plot of \a plottable is this QCustomPlot (the latter is controlled
9653
  by what axes were passed in the plottable's constructor).
9654
9655
  \see plottable, plottableCount, removePlottable, clearPlottables
9656
*/
9657
bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable) {
9658
  if (mPlottables.contains(plottable)) {
9659
    qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:"
9660
             << reinterpret_cast<quintptr>(plottable);
9661
    return false;
9662
  }
9663
  if (plottable->parentPlot() != this) {
9664
    qDebug() << Q_FUNC_INFO
9665
             << "plottable not created with this QCustomPlot as parent:"
9666
             << reinterpret_cast<quintptr>(plottable);
9667
    return false;
9668
  }
9669
9670
  mPlottables.append(plottable);
9671
  // possibly add plottable to legend:
9672
  if (mAutoAddPlottableToLegend) plottable->addToLegend();
9673
  // special handling for QCPGraphs to maintain the simple graph interface:
9674
  if (QCPGraph *graph = qobject_cast<QCPGraph *>(plottable))
9675
    mGraphs.append(graph);
9676
  if (!plottable
9677
           ->layer())  // usually the layer is already set in the constructor of
9678
                       // the plottable (via QCPLayerable constructor)
9679
    plottable->setLayer(currentLayer());
9680
  return true;
9681
}
9682
9683
/*!
9684
  Removes the specified plottable from the plot and, if necessary, from the
9685
  legend (QCustomPlot::legend).
9686
9687
  Returns true on success.
9688
9689
  \see addPlottable, clearPlottables
9690
*/
9691
bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable) {
9692
  if (!mPlottables.contains(plottable)) {
9693
    qDebug() << Q_FUNC_INFO << "plottable not in list:"
9694
             << reinterpret_cast<quintptr>(plottable);
9695
    return false;
9696
  }
9697
9698
  // remove plottable from legend:
9699
  plottable->removeFromLegend();
9700
  // special handling for QCPGraphs to maintain the simple graph interface:
9701
  if (QCPGraph *graph = qobject_cast<QCPGraph *>(plottable))
9702
    mGraphs.removeOne(graph);
9703
  // remove plottable:
9704
  delete plottable;
9705
  mPlottables.removeOne(plottable);
9706
  return true;
9707
}
9708
9709
/*! \overload
9710
9711
  Removes the plottable by its \a index.
9712
*/
9713
bool QCustomPlot::removePlottable(int index) {
9714
  if (index >= 0 && index < mPlottables.size())
9715
    return removePlottable(mPlottables[index]);
9716
  else {
9717
    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9718
    return false;
9719
  }
9720
}
9721
9722
/*!
9723
  Removes all plottables from the plot (and the QCustomPlot::legend, if
9724
  necessary).
9725
9726
  Returns the number of plottables removed.
9727
9728
  \see removePlottable
9729
*/
9730
int QCustomPlot::clearPlottables() {
9731
  int c = mPlottables.size();
9732
  for (int i = c - 1; i >= 0; --i) removePlottable(mPlottables[i]);
9733
  return c;
9734
}
9735
9736
/*!
9737
  Returns the number of currently existing plottables in the plot
9738
9739
  \see plottable, addPlottable
9740
*/
9741
int QCustomPlot::plottableCount() const { return mPlottables.size(); }
9742
9743
/*!
9744
  Returns a list of the selected plottables. If no plottables are currently
9745
  selected, the list is empty.
9746
9747
  There is a convenience function if you're only interested in selected graphs,
9748
  see \ref selectedGraphs.
9749
9750
  \see setInteractions, QCPAbstractPlottable::setSelectable,
9751
  QCPAbstractPlottable::setSelected
9752
*/
9753
QList<QCPAbstractPlottable *> QCustomPlot::selectedPlottables() const {
9754
  QList<QCPAbstractPlottable *> result;
9755
  foreach (QCPAbstractPlottable *plottable, mPlottables) {
9756
    if (plottable->selected()) result.append(plottable);
9757
  }
9758
  return result;
9759
}
9760
9761
/*!
9762
  Returns the plottable at the pixel position \a pos. Plottables that only
9763
  consist of single lines (like graphs) have a tolerance band around them, see
9764
  \ref setSelectionTolerance. If multiple plottables come into consideration,
9765
  the one closest to \a pos is returned.
9766
9767
  If \a onlySelectable is true, only plottables that are selectable
9768
  (QCPAbstractPlottable::setSelectable) are considered.
9769
9770
  If there is no plottable at \a pos, the return value is 0.
9771
9772
  \see itemAt, layoutElementAt
9773
*/
9774
QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos,
9775
                                               bool onlySelectable) const {
9776
  QCPAbstractPlottable *resultPlottable = 0;
9777
  double resultDistance =
9778
      mSelectionTolerance;  // only regard clicks with distances smaller than
9779
                            // mSelectionTolerance as selections, so initialize
9780
                            // with that value
9781
9782
  foreach (QCPAbstractPlottable *plottable, mPlottables) {
9783
    if (onlySelectable &&
9784
        !plottable->selectable())  // we could have also passed onlySelectable
9785
                                   // to the selectTest function, but checking
9786
                                   // here is faster, because we have access to
9787
                                   // QCPabstractPlottable::selectable
9788
      continue;
9789
    if ((plottable->keyAxis()->axisRect()->rect() &
9790
         plottable->valueAxis()->axisRect()->rect())
9791
            .contains(
9792
                pos.toPoint()))  // only consider clicks inside the rect that is
9793
                                 // spanned by the plottable's key/value axes
9794
    {
9795
      double currentDistance = plottable->selectTest(pos, false);
9796
      if (currentDistance >= 0 && currentDistance < resultDistance) {
9797
        resultPlottable = plottable;
9798
        resultDistance = currentDistance;
9799
      }
9800
    }
9801
  }
9802
9803
  return resultPlottable;
9804
}
9805
9806
/*!
9807
  Returns whether this QCustomPlot instance contains the \a plottable.
9808
9809
  \see addPlottable
9810
*/
9811
bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const {
9812
  return mPlottables.contains(plottable);
9813
}
9814
9815
/*!
9816
  Returns the graph with \a index. If the index is invalid, returns 0.
9817
9818
  There is an overloaded version of this function with no parameter which
9819
  returns the last created graph, see QCustomPlot::graph()
9820
9821
  \see graphCount, addGraph
9822
*/
9823
QCPGraph *QCustomPlot::graph(int index) const {
9824
  if (index >= 0 && index < mGraphs.size()) {
9825
    return mGraphs.at(index);
9826
  } else {
9827
    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9828
    return 0;
9829
  }
9830
}
9831
9832
/*! \overload
9833
9834
  Returns the last graph, that was created with \ref addGraph. If there are no
9835
  graphs in the plot, returns 0.
9836
9837
  \see graphCount, addGraph
9838
*/
9839
QCPGraph *QCustomPlot::graph() const {
9840
  if (!mGraphs.isEmpty()) {
9841
    return mGraphs.last();
9842
  } else
9843
    return 0;
9844
}
9845
9846
/*!
9847
  Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left
9848
  unspecified (0), the bottom (xAxis) is used as key and the left (yAxis) is
9849
  used as value axis. If specified, \a keyAxis and \a valueAxis must reside in
9850
  this QCustomPlot.
9851
9852
  \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value
9853
  axis (typically "y") for the graph.
9854
9855
  Returns a pointer to the newly created graph, or 0 if adding the graph failed.
9856
9857
  \see graph, graphCount, removeGraph, clearGraphs
9858
*/
9859
QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) {
9860
  if (!keyAxis) keyAxis = xAxis;
9861
  if (!valueAxis) valueAxis = yAxis;
9862
  if (!keyAxis || !valueAxis) {
9863
    qDebug() << Q_FUNC_INFO
9864
             << "can't use default QCustomPlot xAxis or yAxis, because at "
9865
                "least one is invalid (has been deleted)";
9866
    return 0;
9867
  }
9868
  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this) {
9869
    qDebug() << Q_FUNC_INFO
9870
             << "passed keyAxis or valueAxis doesn't have this QCustomPlot as "
9871
                "parent";
9872
    return 0;
9873
  }
9874
9875
  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9876
  if (addPlottable(newGraph)) {
9877
    newGraph->setName(QLatin1String("Graph ") +
9878
                      QString::number(mGraphs.size()));
9879
    return newGraph;
9880
  } else {
9881
    delete newGraph;
9882
    return 0;
9883
  }
9884
}
9885
9886
/*!
9887
  Removes the specified \a graph from the plot and, if necessary, from the
9888
  QCustomPlot::legend. If any other graphs in the plot have a channel fill set
9889
  towards the removed graph, the channel fill property of those graphs is reset
9890
  to zero (no channel fill).
9891
9892
  Returns true on success.
9893
9894
  \see clearGraphs
9895
*/
9896
bool QCustomPlot::removeGraph(QCPGraph *graph) {
9897
  return removePlottable(graph);
9898
}
9899
9900
/*! \overload
9901
9902
  Removes the graph by its \a index.
9903
*/
9904
bool QCustomPlot::removeGraph(int index) {
9905
  if (index >= 0 && index < mGraphs.size())
9906
    return removeGraph(mGraphs[index]);
9907
  else
9908
    return false;
9909
}
9910
9911
/*!
9912
  Removes all graphs from the plot (and the QCustomPlot::legend, if necessary).
9913
9914
  Returns the number of graphs removed.
9915
9916
  \see removeGraph
9917
*/
9918
int QCustomPlot::clearGraphs() {
9919
  int c = mGraphs.size();
9920
  for (int i = c - 1; i >= 0; --i) removeGraph(mGraphs[i]);
9921
  return c;
9922
}
9923
9924
/*!
9925
  Returns the number of currently existing graphs in the plot
9926
9927
  \see graph, addGraph
9928
*/
9929
int QCustomPlot::graphCount() const { return mGraphs.size(); }
9930
9931
/*!
9932
  Returns a list of the selected graphs. If no graphs are currently selected,
9933
  the list is empty.
9934
9935
  If you are not only interested in selected graphs but other plottables like
9936
  QCPCurve, QCPBars, etc., use \ref selectedPlottables.
9937
9938
  \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable,
9939
  QCPAbstractPlottable::setSelected
9940
*/
9941
QList<QCPGraph *> QCustomPlot::selectedGraphs() const {
9942
  QList<QCPGraph *> result;
9943
  foreach (QCPGraph *graph, mGraphs) {
9944
    if (graph->selected()) result.append(graph);
9945
  }
9946
  return result;
9947
}
9948
9949
/*!
9950
  Returns the item with \a index. If the index is invalid, returns 0.
9951
9952
  There is an overloaded version of this function with no parameter which
9953
  returns the last added item, see QCustomPlot::item()
9954
9955
  \see itemCount, addItem
9956
*/
9957
QCPAbstractItem *QCustomPlot::item(int index) const {
9958
  if (index >= 0 && index < mItems.size()) {
9959
    return mItems.at(index);
9960
  } else {
9961
    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9962
    return 0;
9963
  }
9964
}
9965
9966
/*! \overload
9967
9968
  Returns the last item, that was added with \ref addItem. If there are no items
9969
  in the plot, returns 0.
9970
9971
  \see itemCount, addItem
9972
*/
9973
QCPAbstractItem *QCustomPlot::item() const {
9974
  if (!mItems.isEmpty()) {
9975
    return mItems.last();
9976
  } else
9977
    return 0;
9978
}
9979
9980
/*!
9981
  Adds the specified item to the plot. QCustomPlot takes ownership of the item.
9982
9983
  Returns true on success, i.e. when \a item wasn't already in the plot and the
9984
  parent plot of \a item is this QCustomPlot.
9985
9986
  \see item, itemCount, removeItem, clearItems
9987
*/
9988
bool QCustomPlot::addItem(QCPAbstractItem *item) {
9989
  if (!mItems.contains(item) && item->parentPlot() == this) {
9990
    mItems.append(item);
9991
    return true;
9992
  } else {
9993
    qDebug() << Q_FUNC_INFO
9994
             << "item either already in list or not created with this "
9995
                "QCustomPlot as parent:"
9996
             << reinterpret_cast<quintptr>(item);
9997
    return false;
9998
  }
9999
}
10000
10001
/*!
10002
  Removes the specified item from the plot.
10003
10004
  Returns true on success.
10005
10006
  \see addItem, clearItems
10007
*/
10008
bool QCustomPlot::removeItem(QCPAbstractItem *item) {
10009
  if (mItems.contains(item)) {
10010
    delete item;
10011
    mItems.removeOne(item);
10012
    return true;
10013
  } else {
10014
    qDebug() << Q_FUNC_INFO
10015
             << "item not in list:" << reinterpret_cast<quintptr>(item);
10016
    return false;
10017
  }
10018
}
10019
10020
/*! \overload
10021
10022
  Removes the item by its \a index.
10023
*/
10024
bool QCustomPlot::removeItem(int index) {
10025
  if (index >= 0 && index < mItems.size())
10026
    return removeItem(mItems[index]);
10027
  else {
10028
    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10029
    return false;
10030
  }
10031
}
10032
10033
/*!
10034
  Removes all items from the plot.
10035
10036
  Returns the number of items removed.
10037
10038
  \see removeItem
10039
*/
10040
int QCustomPlot::clearItems() {
10041
  int c = mItems.size();
10042
  for (int i = c - 1; i >= 0; --i) removeItem(mItems[i]);
10043
  return c;
10044
}
10045
10046
/*!
10047
  Returns the number of currently existing items in the plot
10048
10049
  \see item, addItem
10050
*/
10051
int QCustomPlot::itemCount() const { return mItems.size(); }
10052
10053
/*!
10054
  Returns a list of the selected items. If no items are currently selected, the
10055
  list is empty.
10056
10057
  \see setInteractions, QCPAbstractItem::setSelectable,
10058
  QCPAbstractItem::setSelected
10059
*/
10060
QList<QCPAbstractItem *> QCustomPlot::selectedItems() const {
10061
  QList<QCPAbstractItem *> result;
10062
  foreach (QCPAbstractItem *item, mItems) {
10063
    if (item->selected()) result.append(item);
10064
  }
10065
  return result;
10066
}
10067
10068
/*!
10069
  Returns the item at the pixel position \a pos. Items that only consist of
10070
  single lines (e.g. \ref QCPItemLine or \ref QCPItemCurve) have a tolerance
10071
  band around them, see \ref setSelectionTolerance. If multiple items come into
10072
  consideration, the one closest to \a pos is returned.
10073
10074
  If \a onlySelectable is true, only items that are selectable
10075
  (QCPAbstractItem::setSelectable) are considered.
10076
10077
  If there is no item at \a pos, the return value is 0.
10078
10079
  \see plottableAt, layoutElementAt
10080
*/
10081
QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos,
10082
                                     bool onlySelectable) const {
10083
  QCPAbstractItem *resultItem = 0;
10084
  double resultDistance =
10085
      mSelectionTolerance;  // only regard clicks with distances smaller than
10086
                            // mSelectionTolerance as selections, so initialize
10087
                            // with that value
10088
10089
  foreach (QCPAbstractItem *item, mItems) {
10090
    if (onlySelectable &&
10091
        !item->selectable())  // we could have also passed onlySelectable to the
10092
                              // selectTest function, but checking here is
10093
                              // faster, because we have access to
10094
                              // QCPAbstractItem::selectable
10095
      continue;
10096
    if (!item->clipToAxisRect() ||
10097
        item->clipRect().contains(
10098
            pos.toPoint()))  // only consider clicks inside axis cliprect of the
10099
                             // item if actually clipped to it
10100
    {
10101
      double currentDistance = item->selectTest(pos, false);
10102
      if (currentDistance >= 0 && currentDistance < resultDistance) {
10103
        resultItem = item;
10104
        resultDistance = currentDistance;
10105
      }
10106
    }
10107
  }
10108
10109
  return resultItem;
10110
}
10111
10112
/*!
10113
  Returns whether this QCustomPlot contains the \a item.
10114
10115
  \see addItem
10116
*/
10117
bool QCustomPlot::hasItem(QCPAbstractItem *item) const {
10118
  return mItems.contains(item);
10119
}
10120
10121
/*!
10122
  Returns the layer with the specified \a name. If there is no layer with the
10123
  specified name, 0 is returned.
10124
10125
  Layer names are case-sensitive.
10126
10127
  \see addLayer, moveLayer, removeLayer
10128
*/
10129
QCPLayer *QCustomPlot::layer(const QString &name) const {
10130
  foreach (QCPLayer *layer, mLayers) {
10131
    if (layer->name() == name) return layer;
10132
  }
10133
  return 0;
10134
}
10135
10136
/*! \overload
10137
10138
  Returns the layer by \a index. If the index is invalid, 0 is returned.
10139
10140
  \see addLayer, moveLayer, removeLayer
10141
*/
10142
QCPLayer *QCustomPlot::layer(int index) const {
10143
  if (index >= 0 && index < mLayers.size()) {
10144
    return mLayers.at(index);
10145
  } else {
10146
    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10147
    return 0;
10148
  }
10149
}
10150
10151
/*!
10152
  Returns the layer that is set as current layer (see \ref setCurrentLayer).
10153
*/
10154
QCPLayer *QCustomPlot::currentLayer() const { return mCurrentLayer; }
10155
10156
/*!
10157
  Sets the layer with the specified \a name to be the current layer. All
10158
  layerables (\ref QCPLayerable), e.g. plottables and items, are created on the
10159
  current layer.
10160
10161
  Returns true on success, i.e. if there is a layer with the specified \a name
10162
  in the QCustomPlot.
10163
10164
  Layer names are case-sensitive.
10165
10166
  \see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer
10167
*/
10168
bool QCustomPlot::setCurrentLayer(const QString &name) {
10169
  if (QCPLayer *newCurrentLayer = layer(name)) {
10170
    return setCurrentLayer(newCurrentLayer);
10171
  } else {
10172
    qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10173
    return false;
10174
  }
10175
}
10176
10177
/*! \overload
10178
10179
  Sets the provided \a layer to be the current layer.
10180
10181
  Returns true on success, i.e. when \a layer is a valid layer in the
10182
  QCustomPlot.
10183
10184
  \see addLayer, moveLayer, removeLayer
10185
*/
10186
bool QCustomPlot::setCurrentLayer(QCPLayer *layer) {
10187
  if (!mLayers.contains(layer)) {
10188
    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
10189
             << reinterpret_cast<quintptr>(layer);
10190
    return false;
10191
  }
10192
10193
  mCurrentLayer = layer;
10194
  return true;
10195
}
10196
10197
/*!
10198
  Returns the number of currently existing layers in the plot
10199
10200
  \see layer, addLayer
10201
*/
10202
int QCustomPlot::layerCount() const { return mLayers.size(); }
10203
10204
/*!
10205
  Adds a new layer to this QCustomPlot instance. The new layer will have the
10206
  name \a name, which must be unique. Depending on \a insertMode, it is
10207
  positioned either below or above \a otherLayer.
10208
10209
  Returns true on success, i.e. if there is no other layer named \a name and \a
10210
  otherLayer is a valid layer inside this QCustomPlot.
10211
10212
  If \a otherLayer is 0, the highest layer in the QCustomPlot will be used.
10213
10214
  For an explanation of what layers are in QCustomPlot, see the documentation of
10215
  \ref QCPLayer.
10216
10217
  \see layer, moveLayer, removeLayer
10218
*/
10219
bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer,
10220
                           QCustomPlot::LayerInsertMode insertMode) {
10221
  if (!otherLayer) otherLayer = mLayers.last();
10222
  if (!mLayers.contains(otherLayer)) {
10223
    qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:"
10224
             << reinterpret_cast<quintptr>(otherLayer);
10225
    return false;
10226
  }
10227
  if (layer(name)) {
10228
    qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10229
    return false;
10230
  }
10231
10232
  QCPLayer *newLayer = new QCPLayer(this, name);
10233
  mLayers.insert(otherLayer->index() + (insertMode == limAbove ? 1 : 0),
10234
                 newLayer);
10235
  updateLayerIndices();
10236
  return true;
10237
}
10238
10239
/*!
10240
  Removes the specified \a layer and returns true on success.
10241
10242
  All layerables (e.g. plottables and items) on the removed layer will be moved
10243
  to the layer below \a layer. If \a layer is the bottom layer, the layerables
10244
  are moved to the layer above. In both cases, the total rendering order of all
10245
  layerables in the QCustomPlot is preserved.
10246
10247
  If \a layer is the current layer (\ref setCurrentLayer), the layer below (or
10248
  above, if bottom layer) becomes the new current layer.
10249
10250
  It is not possible to remove the last layer of the plot.
10251
10252
  \see layer, addLayer, moveLayer
10253
*/
10254
bool QCustomPlot::removeLayer(QCPLayer *layer) {
10255
  if (!mLayers.contains(layer)) {
10256
    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
10257
             << reinterpret_cast<quintptr>(layer);
10258
    return false;
10259
  }
10260
  if (mLayers.size() < 2) {
10261
    qDebug() << Q_FUNC_INFO << "can't remove last layer";
10262
    return false;
10263
  }
10264
10265
  // append all children of this layer to layer below (if this is lowest layer,
10266
  // prepend to layer above)
10267
  int removedIndex = layer->index();
10268
  bool isFirstLayer = removedIndex == 0;
10269
  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex + 1)
10270
                                       : mLayers.at(removedIndex - 1);
10271
  QList<QCPLayerable *> children = layer->children();
10272
  if (isFirstLayer)  // prepend in reverse order (so order relative to each
10273
                     // other stays the same)
10274
  {
10275
    for (int i = children.size() - 1; i >= 0; --i)
10276
      children.at(i)->moveToLayer(targetLayer, true);
10277
  } else  // append normally
10278
  {
10279
    for (int i = 0; i < children.size(); ++i)
10280
      children.at(i)->moveToLayer(targetLayer, false);
10281
  }
10282
  // if removed layer is current layer, change current layer to layer
10283
  // below/above:
10284
  if (layer == mCurrentLayer) setCurrentLayer(targetLayer);
10285
  // remove layer:
10286
  delete layer;
10287
  mLayers.removeOne(layer);
10288
  updateLayerIndices();
10289
  return true;
10290
}
10291
10292
/*!
10293
  Moves the specified \a layer either above or below \a otherLayer. Whether it's
10294
  placed above or below is controlled with \a insertMode.
10295
10296
  Returns true on success, i.e. when both \a layer and \a otherLayer are valid
10297
  layers in the QCustomPlot.
10298
10299
  \see layer, addLayer, moveLayer
10300
*/
10301
bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer,
10302
                            QCustomPlot::LayerInsertMode insertMode) {
10303
  if (!mLayers.contains(layer)) {
10304
    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
10305
             << reinterpret_cast<quintptr>(layer);
10306
    return false;
10307
  }
10308
  if (!mLayers.contains(otherLayer)) {
10309
    qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:"
10310
             << reinterpret_cast<quintptr>(otherLayer);
10311
    return false;
10312
  }
10313
10314
  if (layer->index() > otherLayer->index())
10315
    mLayers.move(layer->index(),
10316
                 otherLayer->index() + (insertMode == limAbove ? 1 : 0));
10317
  else if (layer->index() < otherLayer->index())
10318
    mLayers.move(layer->index(),
10319
                 otherLayer->index() + (insertMode == limAbove ? 0 : -1));
10320
10321
  updateLayerIndices();
10322
  return true;
10323
}
10324
10325
/*!
10326
  Returns the number of axis rects in the plot.
10327
10328
  All axis rects can be accessed via QCustomPlot::axisRect().
10329
10330
  Initially, only one axis rect exists in the plot.
10331
10332
  \see axisRect, axisRects
10333
*/
10334
int QCustomPlot::axisRectCount() const { return axisRects().size(); }
10335
10336
/*!
10337
  Returns the axis rect with \a index.
10338
10339
  Initially, only one axis rect (with index 0) exists in the plot. If multiple
10340
  axis rects were added, all of them may be accessed with this function in a
10341
  linear fashion (even when they are nested in a layout hierarchy or inside
10342
  other axis rects via QCPAxisRect::insetLayout).
10343
10344
  \see axisRectCount, axisRects
10345
*/
10346
QCPAxisRect *QCustomPlot::axisRect(int index) const {
10347
  const QList<QCPAxisRect *> rectList = axisRects();
10348
  if (index >= 0 && index < rectList.size()) {
10349
    return rectList.at(index);
10350
  } else {
10351
    qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10352
    return 0;
10353
  }
10354
}
10355
10356
/*!
10357
  Returns all axis rects in the plot.
10358
10359
  \see axisRectCount, axisRect
10360
*/
10361
QList<QCPAxisRect *> QCustomPlot::axisRects() const {
10362
  QList<QCPAxisRect *> result;
10363
  QStack<QCPLayoutElement *> elementStack;
10364
  if (mPlotLayout) elementStack.push(mPlotLayout);
10365
10366
  while (!elementStack.isEmpty()) {
10367
    foreach (QCPLayoutElement *element, elementStack.pop()->elements(false)) {
10368
      if (element) {
10369
        elementStack.push(element);
10370
        if (QCPAxisRect *ar = qobject_cast<QCPAxisRect *>(element))
10371
          result.append(ar);
10372
      }
10373
    }
10374
  }
10375
10376
  return result;
10377
}
10378
10379
/*!
10380
  Returns the layout element at pixel position \a pos. If there is no element at
10381
  that position, returns 0.
10382
10383
  Only visible elements are used. If \ref QCPLayoutElement::setVisible on the
10384
  element itself or on any of its parent elements is set to false, it will not
10385
  be considered.
10386
10387
  \see itemAt, plottableAt
10388
*/
10389
QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const {
10390
  QCPLayoutElement *currentElement = mPlotLayout;
10391
  bool searchSubElements = true;
10392
  while (searchSubElements && currentElement) {
10393
    searchSubElements = false;
10394
    foreach (QCPLayoutElement *subElement, currentElement->elements(false)) {
10395
      if (subElement && subElement->realVisibility() &&
10396
          subElement->selectTest(pos, false) >= 0) {
10397
        currentElement = subElement;
10398
        searchSubElements = true;
10399
        break;
10400
      }
10401
    }
10402
  }
10403
  return currentElement;
10404
}
10405
10406
/*!
10407
  Returns the axes that currently have selected parts, i.e. whose selection
10408
  state is not \ref QCPAxis::spNone.
10409
10410
  \see selectedPlottables, selectedLegends, setInteractions,
10411
  QCPAxis::setSelectedParts, QCPAxis::setSelectableParts
10412
*/
10413
QList<QCPAxis *> QCustomPlot::selectedAxes() const {
10414
  QList<QCPAxis *> result, allAxes;
10415
  foreach (QCPAxisRect *rect, axisRects()) allAxes << rect->axes();
10416
10417
  foreach (QCPAxis *axis, allAxes) {
10418
    if (axis->selectedParts() != QCPAxis::spNone) result.append(axis);
10419
  }
10420
10421
  return result;
10422
}
10423
10424
/*!
10425
  Returns the legends that currently have selected parts, i.e. whose selection
10426
  state is not \ref QCPLegend::spNone.
10427
10428
  \see selectedPlottables, selectedAxes, setInteractions,
10429
  QCPLegend::setSelectedParts, QCPLegend::setSelectableParts,
10430
  QCPLegend::selectedItems
10431
*/
10432
QList<QCPLegend *> QCustomPlot::selectedLegends() const {
10433
  QList<QCPLegend *> result;
10434
10435
  QStack<QCPLayoutElement *> elementStack;
10436
  if (mPlotLayout) elementStack.push(mPlotLayout);
10437
10438
  while (!elementStack.isEmpty()) {
10439
    foreach (QCPLayoutElement *subElement,
10440
             elementStack.pop()->elements(false)) {
10441
      if (subElement) {
10442
        elementStack.push(subElement);
10443
        if (QCPLegend *leg = qobject_cast<QCPLegend *>(subElement)) {
10444
          if (leg->selectedParts() != QCPLegend::spNone) result.append(leg);
10445
        }
10446
      }
10447
    }
10448
  }
10449
10450
  return result;
10451
}
10452
10453
/*!
10454
  Deselects all layerables (plottables, items, axes, legends,...) of the
10455
  QCustomPlot.
10456
10457
  Since calling this function is not a user interaction, this does not emit the
10458
  \ref selectionChangedByUser signal. The individual selectionChanged signals
10459
  are emitted though, if the objects were previously selected.
10460
10461
  \see setInteractions, selectedPlottables, selectedItems, selectedAxes,
10462
  selectedLegends
10463
*/
10464
void QCustomPlot::deselectAll() {
10465
  foreach (QCPLayer *layer, mLayers) {
10466
    foreach (QCPLayerable *layerable, layer->children())
10467
      layerable->deselectEvent(0);
10468
  }
10469
}
10470
10471
/*!
10472
  Causes a complete replot into the internal buffer. Finally, update() is
10473
  called, to redraw the buffer on the QCustomPlot widget surface. This is the
10474
  method that must be called to make changes, for example on the axis ranges or
10475
  data points of graphs, visible.
10476
10477
  Under a few circumstances, QCustomPlot causes a replot by itself. Those are
10478
  resize events of the QCustomPlot widget and user interactions (object
10479
  selection and range dragging/zooming).
10480
10481
  Before the replot happens, the signal \ref beforeReplot is emitted. After the
10482
  replot, \ref afterReplot is emitted. It is safe to mutually connect the replot
10483
  slot with any of those two signals on two QCustomPlots to make them replot
10484
  synchronously, it won't cause an infinite recursion.
10485
*/
10486
void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority) {
10487
  if (mReplotting)  // incase signals loop back to replot slot
10488
    return;
10489
  mReplotting = true;
10490
  emit beforeReplot();
10491
10492
  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern
10493
                        ? mBackgroundBrush.color()
10494
                        : Qt::transparent);
10495
  QCPPainter painter;
10496
  painter.begin(&mPaintBuffer);
10497
  if (painter.isActive()) {
10498
    painter.setRenderHint(
10499
        QPainter::HighQualityAntialiasing);  // to make Antialiasing look good
10500
                                             // if using the OpenGL
10501
                                             // graphicssystem
10502
    if (mBackgroundBrush.style() != Qt::SolidPattern &&
10503
        mBackgroundBrush.style() != Qt::NoBrush)
10504
      painter.fillRect(mViewport, mBackgroundBrush);
10505
    draw(&painter);
10506
    painter.end();
10507
    if ((refreshPriority == rpHint &&
10508
         mPlottingHints.testFlag(QCP::phForceRepaint)) ||
10509
        refreshPriority == rpImmediate)
10510
      repaint();
10511
    else
10512
      update();
10513
  } else  // might happen if QCustomPlot has width or height zero
10514
    qDebug() << Q_FUNC_INFO
10515
             << "Couldn't activate painter on buffer. This usually happens "
10516
                "because QCustomPlot has width or height zero.";
10517
10518
  emit afterReplot();
10519
  mReplotting = false;
10520
}
10521
10522
/*!
10523
  Rescales the axes such that all plottables (like graphs) in the plot are fully
10524
  visible.
10525
10526
  if \a onlyVisiblePlottables is set to true, only the plottables that have
10527
  their visibility set to true (QCPLayerable::setVisible), will be used to
10528
  rescale the axes.
10529
10530
  \see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale
10531
*/
10532
void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables) {
10533
  QList<QCPAxis *> allAxes;
10534
  foreach (QCPAxisRect *rect, axisRects()) allAxes << rect->axes();
10535
10536
  foreach (QCPAxis *axis, allAxes) axis->rescale(onlyVisiblePlottables);
10537
}
10538
10539
/*!
10540
  Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio
10541
  as well as the scale of texts and lines will be derived from the specified \a
10542
  width and \a height. This means, the output will look like the normal
10543
  on-screen output of a QCustomPlot widget with the corresponding pixel width
10544
  and height. If either \a width or \a height is zero, the exported image will
10545
  have the same dimensions as the QCustomPlot widget currently has.
10546
10547
  \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF
10548
  file. Cosmetic pens are pens with numerical width 0, which are always drawn as
10549
  a one pixel wide line, no matter what zoom factor is set in the PDF-Viewer.
10550
  For more information about cosmetic pens, see the QPainter and QPen
10551
  documentation.
10552
10553
  The objects of the plot will appear in the current selection state. If you
10554
  don't want any selected objects to be painted in their selected look, deselect
10555
  everything with \ref deselectAll before calling this function.
10556
10557
  Returns true on success.
10558
10559
  \warning
10560
  \li If you plan on editing the exported PDF file with a vector graphics editor
10561
  like Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing
10562
  those cosmetic lines (which might be quite many, because cosmetic pens are the
10563
  default for e.g. axes and tick marks). \li If calling this function inside the
10564
  constructor of the parent of the QCustomPlot widget (i.e. the MainWindow
10565
  constructor, if QCustomPlot is inside the MainWindow), always provide explicit
10566
  non-zero widths and heights. If you leave \a width or \a height as 0
10567
  (default), this function uses the current width and height of the QCustomPlot
10568
  widget. However, in Qt, these aren't defined yet inside the constructor, so
10569
  you would get an image that has strange widths/heights.
10570
10571
  \a pdfCreator and \a pdfTitle may be used to set the according metadata fields
10572
  in the resulting PDF file.
10573
10574
  \note On Android systems, this method does nothing and issues an according
10575
  qDebug warning message. This is also the case if for other reasons the define
10576
  flag QT_NO_PRINTER is set.
10577
10578
  \see savePng, saveBmp, saveJpg, saveRastered
10579
*/
10580
bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen,
10581
                          int width, int height, const QString &pdfCreator,
10582
                          const QString &pdfTitle) {
10583
  bool success = false;
10584
#ifdef QT_NO_PRINTER
10585
  Q_UNUSED(fileName)
10586
  Q_UNUSED(noCosmeticPen)
10587
  Q_UNUSED(width)
10588
  Q_UNUSED(height)
10589
  Q_UNUSED(pdfCreator)
10590
  Q_UNUSED(pdfTitle)
10591
  qDebug() << Q_FUNC_INFO
10592
           << "Qt was built without printer support (QT_NO_PRINTER). PDF not "
10593
              "created.";
10594
#else
10595
  int newWidth, newHeight;
10596
  if (width == 0 || height == 0) {
10597
    newWidth = this->width();
10598
    newHeight = this->height();
10599
  } else {
10600
    newWidth = width;
10601
    newHeight = height;
10602
  }
10603
10604
  QPrinter printer(QPrinter::ScreenResolution);
10605
  printer.setOutputFileName(fileName);
10606
  printer.setOutputFormat(QPrinter::PdfFormat);
10607
  printer.setColorMode(QPrinter::Color);
10608
  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10609
  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10610
  QRect oldViewport = viewport();
10611
  setViewport(QRect(0, 0, newWidth, newHeight));
10612
#if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10613
  printer.setFullPage(true);
10614
  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10615
#else
10616
  QPageLayout pageLayout;
10617
  pageLayout.setMode(QPageLayout::FullPageMode);
10618
  pageLayout.setOrientation(QPageLayout::Portrait);
10619
  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10620
  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point,
10621
                                   QString(), QPageSize::ExactMatch));
10622
  printer.setPageLayout(pageLayout);
10623
#endif
10624
  QCPPainter printpainter;
10625
  if (printpainter.begin(&printer)) {
10626
    printpainter.setMode(QCPPainter::pmVectorized);
10627
    printpainter.setMode(QCPPainter::pmNoCaching);
10628
    printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10629
    printpainter.setWindow(mViewport);
10630
    if (mBackgroundBrush.style() != Qt::NoBrush &&
10631
        mBackgroundBrush.color() != Qt::white &&
10632
        mBackgroundBrush.color() != Qt::transparent &&
10633
        mBackgroundBrush.color().alpha() >
10634
            0)  // draw pdf background color if not white/transparent
10635
      printpainter.fillRect(viewport(), mBackgroundBrush);
10636
    draw(&printpainter);
10637
    printpainter.end();
10638
    success = true;
10639
  }
10640
  setViewport(oldViewport);
10641
#endif  // QT_NO_PRINTER
10642
  return success;
10643
}
10644
10645
/*!
10646
  Saves a PNG image file to \a fileName on disc. The output plot will have the
10647
  dimensions \a width and \a height in pixels. If either \a width or \a height
10648
  is zero, the exported image will have the same dimensions as the QCustomPlot
10649
  widget currently has. Line widths and texts etc. are not scaled up when larger
10650
  widths/heights are used. If you want that effect, use the \a scale parameter.
10651
10652
  For example, if you set both \a width and \a height to 100 and \a scale to 2,
10653
  you will end up with an image file of size 200*200 in which all graphical
10654
  elements are scaled up by factor 2 (line widths, texts, etc.). This scaling is
10655
  not done by stretching a 100*100 image, the result will have full 200*200
10656
  pixel resolution.
10657
10658
  If you use a high scaling factor, it is recommended to enable antialiasing for
10659
  all elements via temporarily setting \ref QCustomPlot::setAntialiasedElements
10660
  to \ref QCP::aeAll as this allows QCustomPlot to place objects with sub-pixel
10661
  accuracy.
10662
10663
  \warning If calling this function inside the constructor of the parent of the
10664
  QCustomPlot widget (i.e. the MainWindow constructor, if QCustomPlot is inside
10665
  the MainWindow), always provide explicit non-zero widths and heights. If you
10666
  leave \a width or \a height as 0 (default), this function uses the current
10667
  width and height of the QCustomPlot widget. However, in Qt, these aren't
10668
  defined yet inside the constructor, so you would get an image that has strange
10669
  widths/heights.
10670
10671
  The objects of the plot will appear in the current selection state. If you
10672
  don't want any selected objects to be painted in their selected look, deselect
10673
  everything with \ref deselectAll before calling this function.
10674
10675
  If you want the PNG to have a transparent background, call \ref
10676
  setBackground(const QBrush &brush) with no brush (Qt::NoBrush) or a
10677
  transparent color (Qt::transparent), before saving.
10678
10679
  PNG compression can be controlled with the \a quality parameter which must be
10680
  between 0 and 100 or -1 to use the default setting.
10681
10682
  Returns true on success. If this function fails, most likely the PNG format
10683
  isn't supported by the system, see Qt docs about
10684
  QImageWriter::supportedImageFormats().
10685
10686
  \see savePdf, saveBmp, saveJpg, saveRastered
10687
*/
10688
bool QCustomPlot::savePng(const QString &fileName, int width, int height,
10689
                          double scale, int quality) {
10690
  return saveRastered(fileName, width, height, scale, "PNG", quality);
10691
}
10692
10693
/*!
10694
  Saves a JPG image file to \a fileName on disc. The output plot will have the
10695
  dimensions \a width and \a height in pixels. If either \a width or \a height
10696
  is zero, the exported image will have the same dimensions as the QCustomPlot
10697
  widget currently has. Line widths and texts etc. are not scaled up when larger
10698
  widths/heights are used. If you want that effect, use the \a scale parameter.
10699
10700
  For example, if you set both \a width and \a height to 100 and \a scale to 2,
10701
  you will end up with an image file of size 200*200 in which all graphical
10702
  elements are scaled up by factor 2 (line widths, texts, etc.). This scaling is
10703
  not done by stretching a 100*100 image, the result will have full 200*200
10704
  pixel resolution.
10705
10706
  If you use a high scaling factor, it is recommended to enable antialiasing for
10707
  all elements via temporarily setting \ref QCustomPlot::setAntialiasedElements
10708
  to \ref QCP::aeAll as this allows QCustomPlot to place objects with sub-pixel
10709
  accuracy.
10710
10711
  \warning If calling this function inside the constructor of the parent of the
10712
  QCustomPlot widget (i.e. the MainWindow constructor, if QCustomPlot is inside
10713
  the MainWindow), always provide explicit non-zero widths and heights. If you
10714
  leave \a width or \a height as 0 (default), this function uses the current
10715
  width and height of the QCustomPlot widget. However, in Qt, these aren't
10716
  defined yet inside the constructor, so you would get an image that has strange
10717
  widths/heights.
10718
10719
  The objects of the plot will appear in the current selection state. If you
10720
  don't want any selected objects to be painted in their selected look, deselect
10721
  everything with \ref deselectAll before calling this function.
10722
10723
  JPG compression can be controlled with the \a quality parameter which must be
10724
  between 0 and 100 or -1 to use the default setting.
10725
10726
  Returns true on success. If this function fails, most likely the JPG format
10727
  isn't supported by the system, see Qt docs about
10728
  QImageWriter::supportedImageFormats().
10729
10730
  \see savePdf, savePng, saveBmp, saveRastered
10731
*/
10732
bool QCustomPlot::saveJpg(const QString &fileName, int width, int height,
10733
                          double scale, int quality) {
10734
  return saveRastered(fileName, width, height, scale, "JPG", quality);
10735
}
10736
10737
/*!
10738
  Saves a BMP image file to \a fileName on disc. The output plot will have the
10739
  dimensions \a width and \a height in pixels. If either \a width or \a height
10740
  is zero, the exported image will have the same dimensions as the QCustomPlot
10741
  widget currently has. Line widths and texts etc. are not scaled up when larger
10742
  widths/heights are used. If you want that effect, use the \a scale parameter.
10743
10744
  For example, if you set both \a width and \a height to 100 and \a scale to 2,
10745
  you will end up with an image file of size 200*200 in which all graphical
10746
  elements are scaled up by factor 2 (line widths, texts, etc.). This scaling is
10747
  not done by stretching a 100*100 image, the result will have full 200*200
10748
  pixel resolution.
10749
10750
  If you use a high scaling factor, it is recommended to enable antialiasing for
10751
  all elements via temporarily setting \ref QCustomPlot::setAntialiasedElements
10752
  to \ref QCP::aeAll as this allows QCustomPlot to place objects with sub-pixel
10753
  accuracy.
10754
10755
  \warning If calling this function inside the constructor of the parent of the
10756
  QCustomPlot widget (i.e. the MainWindow constructor, if QCustomPlot is inside
10757
  the MainWindow), always provide explicit non-zero widths and heights. If you
10758
  leave \a width or \a height as 0 (default), this function uses the current
10759
  width and height of the QCustomPlot widget. However, in Qt, these aren't
10760
  defined yet inside the constructor, so you would get an image that has strange
10761
  widths/heights.
10762
10763
  The objects of the plot will appear in the current selection state. If you
10764
  don't want any selected objects to be painted in their selected look, deselect
10765
  everything with \ref deselectAll before calling this function.
10766
10767
  Returns true on success. If this function fails, most likely the BMP format
10768
  isn't supported by the system, see Qt docs about
10769
  QImageWriter::supportedImageFormats().
10770
10771
  \see savePdf, savePng, saveJpg, saveRastered
10772
*/
10773
bool QCustomPlot::saveBmp(const QString &fileName, int width, int height,
10774
                          double scale) {
10775
  return saveRastered(fileName, width, height, scale, "BMP");
10776
}
10777
10778
/*! \internal
10779
10780
  Returns a minimum size hint that corresponds to the minimum size of the top
10781
  level layout
10782
  (\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width
10783
  zero, set a minimum size (setMinimumSize) either on the whole QCustomPlot or
10784
  on any layout elements inside the plot. This is especially important, when
10785
  placed in a QLayout where other components try to take in as much space as
10786
  possible (e.g. QMdiArea).
10787
*/
10788
QSize QCustomPlot::minimumSizeHint() const {
10789
  return mPlotLayout->minimumSizeHint();
10790
}
10791
10792
/*! \internal
10793
10794
  Returns a size hint that is the same as \ref minimumSizeHint.
10795
10796
*/
10797
QSize QCustomPlot::sizeHint() const { return mPlotLayout->minimumSizeHint(); }
10798
10799
/*! \internal
10800
10801
  Event handler for when the QCustomPlot widget needs repainting. This does not
10802
  cause a \ref replot, but draws the internal buffer on the widget surface.
10803
*/
10804
void QCustomPlot::paintEvent(QPaintEvent *event) {
10805
  Q_UNUSED(event);
10806
  QPainter painter(this);
10807
  painter.drawPixmap(0, 0, mPaintBuffer);
10808
}
10809
10810
/*! \internal
10811
10812
  Event handler for a resize of the QCustomPlot widget. Causes the internal
10813
  buffer to be resized to the new size. The viewport (which becomes the outer
10814
  rect of mPlotLayout) is resized appropriately. Finally a \ref replot is
10815
  performed.
10816
*/
10817
void QCustomPlot::resizeEvent(QResizeEvent *event) {
10818
  // resize and repaint the buffer:
10819
  mPaintBuffer = QPixmap(event->size());
10820
  setViewport(rect());
10821
  replot(rpQueued);  // queued update is important here, to prevent painting
10822
                     // issues in some contexts
10823
}
10824
10825
/*! \internal
10826
10827
 Event handler for when a double click occurs. Emits the \ref mouseDoubleClick
10828
 signal, then emits the specialized signals when certain objecs are clicked
10829
 (e.g. \ref plottableDoubleClick, \ref axisDoubleClick, etc.). Finally
10830
 determines the affected layout element and forwards the event to it.
10831
10832
 \see mousePressEvent, mouseReleaseEvent
10833
*/
10834
void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) {
10835
  emit mouseDoubleClick(event);
10836
10837
  QVariant details;
10838
  QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10839
10840
  // emit specialized object double click signals:
10841
  if (QCPAbstractPlottable *ap =
10842
          qobject_cast<QCPAbstractPlottable *>(clickedLayerable))
10843
    emit plottableDoubleClick(ap, event);
10844
  else if (QCPAxis *ax = qobject_cast<QCPAxis *>(clickedLayerable))
10845
    emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10846
  else if (QCPAbstractItem *ai =
10847
               qobject_cast<QCPAbstractItem *>(clickedLayerable))
10848
    emit itemDoubleClick(ai, event);
10849
  else if (QCPLegend *lg = qobject_cast<QCPLegend *>(clickedLayerable))
10850
    emit legendDoubleClick(lg, 0, event);
10851
  else if (QCPAbstractLegendItem *li =
10852
               qobject_cast<QCPAbstractLegendItem *>(clickedLayerable))
10853
    emit legendDoubleClick(li->parentLegend(), li, event);
10854
  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle *>(clickedLayerable))
10855
    emit titleDoubleClick(event, pt);
10856
10857
  // call double click event of affected layout element:
10858
  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10859
    el->mouseDoubleClickEvent(event);
10860
10861
  // call release event of affected layout element (as in mouseReleaseEvent,
10862
  // since the mouseDoubleClick replaces the second release event in double
10863
  // click case):
10864
  if (mMouseEventElement) {
10865
    mMouseEventElement->mouseReleaseEvent(event);
10866
    mMouseEventElement = 0;
10867
  }
10868
10869
  // QWidget::mouseDoubleClickEvent(event); don't call base class implementation
10870
  // because it would just cause a mousePress/ReleaseEvent, which we don't want.
10871
}
10872
10873
/*! \internal
10874
10875
  Event handler for when a mouse button is pressed. Emits the mousePress signal.
10876
  Then determines the affected layout element and forwards the event to it.
10877
10878
  \see mouseMoveEvent, mouseReleaseEvent
10879
*/
10880
void QCustomPlot::mousePressEvent(QMouseEvent *event) {
10881
  emit mousePress(event);
10882
  mMousePressPos =
10883
      event->pos();  // need this to determine in releaseEvent whether it was a
10884
                     // click (no position change between press and release)
10885
10886
  // call event of affected layout element:
10887
  mMouseEventElement = layoutElementAt(event->pos());
10888
  if (mMouseEventElement) mMouseEventElement->mousePressEvent(event);
10889
10890
  QWidget::mousePressEvent(event);
10891
}
10892
10893
/*! \internal
10894
10895
  Event handler for when the cursor is moved. Emits the \ref mouseMove signal.
10896
10897
  If a layout element has mouse capture focus (a mousePressEvent happened on top
10898
  of the layout element before), the mouseMoveEvent is forwarded to that
10899
  element.
10900
10901
  \see mousePressEvent, mouseReleaseEvent
10902
*/
10903
void QCustomPlot::mouseMoveEvent(QMouseEvent *event) {
10904
  emit mouseMove(event);
10905
10906
  // call event of affected layout element:
10907
  if (mMouseEventElement) mMouseEventElement->mouseMoveEvent(event);
10908
10909
  QWidget::mouseMoveEvent(event);
10910
}
10911
10912
/*! \internal
10913
10914
  Event handler for when a mouse button is released. Emits the \ref mouseRelease
10915
  signal.
10916
10917
  If the mouse was moved less than a certain threshold in any direction since
10918
  the \ref mousePressEvent, it is considered a click which causes the selection
10919
  mechanism (if activated via \ref setInteractions) to possibly change selection
10920
  states accordingly. Further, specialized mouse click signals are emitted (e.g.
10921
  \ref plottableClick, \ref axisClick, etc.)
10922
10923
  If a layout element has mouse capture focus (a \ref mousePressEvent happened
10924
  on top of the layout element before), the \ref mouseReleaseEvent is forwarded
10925
  to that element.
10926
10927
  \see mousePressEvent, mouseMoveEvent
10928
*/
10929
void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) {
10930
  emit mouseRelease(event);
10931
  bool doReplot = false;
10932
10933
  if ((mMousePressPos - event->pos()).manhattanLength() <
10934
      5)  // determine whether it was a click operation
10935
  {
10936
    if (event->button() == Qt::LeftButton) {
10937
      // handle selection mechanism:
10938
      QVariant details;
10939
      QCPLayerable *clickedLayerable =
10940
          layerableAt(event->pos(), true, &details);
10941
      bool selectionStateChanged = false;
10942
      bool additive = mInteractions.testFlag(QCP::iMultiSelect) &&
10943
                      event->modifiers().testFlag(mMultiSelectModifier);
10944
      // deselect all other layerables if not additive selection:
10945
      if (!additive) {
10946
        foreach (QCPLayer *layer, mLayers) {
10947
          foreach (QCPLayerable *layerable, layer->children()) {
10948
            if (layerable != clickedLayerable &&
10949
                mInteractions.testFlag(layerable->selectionCategory())) {
10950
              bool selChanged = false;
10951
              layerable->deselectEvent(&selChanged);
10952
              selectionStateChanged |= selChanged;
10953
            }
10954
          }
10955
        }
10956
      }
10957
      if (clickedLayerable &&
10958
          mInteractions.testFlag(clickedLayerable->selectionCategory())) {
10959
        // a layerable was actually clicked, call its selectEvent:
10960
        bool selChanged = false;
10961
        clickedLayerable->selectEvent(event, additive, details, &selChanged);
10962
        selectionStateChanged |= selChanged;
10963
      }
10964
      if (selectionStateChanged) {
10965
        doReplot = true;
10966
        emit selectionChangedByUser();
10967
      }
10968
    }
10969
10970
    // emit specialized object click signals:
10971
    QVariant details;
10972
    QCPLayerable *clickedLayerable = layerableAt(
10973
        event->pos(), false,
10974
        &details);  // for these signals, selectability is ignored, that's why
10975
                    // we call this again with onlySelectable set to false
10976
    if (QCPAbstractPlottable *ap =
10977
            qobject_cast<QCPAbstractPlottable *>(clickedLayerable))
10978
      emit plottableClick(ap, event);
10979
    else if (QCPAxis *ax = qobject_cast<QCPAxis *>(clickedLayerable))
10980
      emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10981
    else if (QCPAbstractItem *ai =
10982
                 qobject_cast<QCPAbstractItem *>(clickedLayerable))
10983
      emit itemClick(ai, event);
10984
    else if (QCPLegend *lg = qobject_cast<QCPLegend *>(clickedLayerable))
10985
      emit legendClick(lg, 0, event);
10986
    else if (QCPAbstractLegendItem *li =
10987
                 qobject_cast<QCPAbstractLegendItem *>(clickedLayerable))
10988
      emit legendClick(li->parentLegend(), li, event);
10989
    else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle *>(clickedLayerable))
10990
      emit titleClick(event, pt);
10991
  }
10992
10993
  // call event of affected layout element:
10994
  if (mMouseEventElement) {
10995
    mMouseEventElement->mouseReleaseEvent(event);
10996
    mMouseEventElement = 0;
10997
  }
10998
10999
  if (doReplot || noAntialiasingOnDrag()) replot();
11000
11001
  QWidget::mouseReleaseEvent(event);
11002
}
11003
11004
/*! \internal
11005
11006
  Event handler for mouse wheel events. First, the \ref mouseWheel signal is
11007
  emitted. Then determines the affected layout element and forwards the event to
11008
  it.
11009
11010
*/
11011
void QCustomPlot::wheelEvent(QWheelEvent *event) {
11012
  emit mouseWheel(event);
11013
11014
  // call event of affected layout element:
11015
  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
11016
    el->wheelEvent(event);
11017
11018
  QWidget::wheelEvent(event);
11019
}
11020
11021
/*! \internal
11022
11023
  This is the main draw function. It draws the entire plot, including background
11024
  pixmap, with the specified \a painter. Note that it does not fill the
11025
  background with the background brush (as the user may specify with \ref
11026
  setBackground(const QBrush &brush)), this is up to the respective functions
11027
  calling this method (e.g. \ref replot, \ref toPixmap and \ref toPainter).
11028
*/
11029
void QCustomPlot::draw(QCPPainter *painter) {
11030
  // run through layout phases:
11031
  mPlotLayout->update(QCPLayoutElement::upPreparation);
11032
  mPlotLayout->update(QCPLayoutElement::upMargins);
11033
  mPlotLayout->update(QCPLayoutElement::upLayout);
11034
11035
  // draw viewport background pixmap:
11036
  drawBackground(painter);
11037
11038
  // draw all layered objects (grid, axes, plottables, items, legend,...):
11039
  foreach (QCPLayer *layer, mLayers) {
11040
    foreach (QCPLayerable *child, layer->children()) {
11041
      if (child->realVisibility()) {
11042
        painter->save();
11043
        painter->setClipRect(child->clipRect().translated(0, -1));
11044
        child->applyDefaultAntialiasingHint(painter);
11045
        child->draw(painter);
11046
        painter->restore();
11047
      }
11048
    }
11049
  }
11050
11051
  /* Debug code to draw all layout element rects
11052
  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
11053
  {
11054
    painter->setBrush(Qt::NoBrush);
11055
    painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
11056
    painter->drawRect(el->rect());
11057
    painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
11058
    painter->drawRect(el->outerRect());
11059
  }
11060
  */
11061
}
11062
11063
/*! \internal
11064
11065
  Draws the viewport background pixmap of the plot.
11066
11067
  If a pixmap was provided via \ref setBackground, this function buffers the
11068
  scaled version depending on \ref setBackgroundScaled and \ref
11069
  setBackgroundScaledMode and then draws it inside the viewport with the
11070
  provided \a painter. The scaled version is buffered in mScaledBackgroundPixmap
11071
  to prevent expensive rescaling at every redraw. It is only updated, when the
11072
  axis rect has changed in a way that requires a rescale of the background
11073
  pixmap (this is dependent on the \ref setBackgroundScaledMode), or when a
11074
  differend axis background pixmap was set.
11075
11076
  Note that this function does not draw a fill with the background brush (\ref
11077
  setBackground(const QBrush &brush)) beneath the pixmap.
11078
11079
  \see setBackground, setBackgroundScaled, setBackgroundScaledMode
11080
*/
11081
void QCustomPlot::drawBackground(QCPPainter *painter) {
11082
  // Note: background color is handled in individual replot/save functions
11083
11084
  // draw background pixmap (on top of fill, if brush specified):
11085
  if (!mBackgroundPixmap.isNull()) {
11086
    if (mBackgroundScaled) {
11087
      // check whether mScaledBackground needs to be updated:
11088
      QSize scaledSize(mBackgroundPixmap.size());
11089
      scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
11090
      if (mScaledBackgroundPixmap.size() != scaledSize)
11091
        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(
11092
            mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
11093
      painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap,
11094
                          QRect(0, 0, mViewport.width(), mViewport.height()) &
11095
                              mScaledBackgroundPixmap.rect());
11096
    } else {
11097
      painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap,
11098
                          QRect(0, 0, mViewport.width(), mViewport.height()));
11099
    }
11100
  }
11101
}
11102
11103
/*! \internal
11104
11105
  This method is used by \ref QCPAxisRect::removeAxis to report removed axes to
11106
  the QCustomPlot so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and
11107
  yAxis2 members accordingly.
11108
*/
11109
void QCustomPlot::axisRemoved(QCPAxis *axis) {
11110
  if (xAxis == axis) xAxis = 0;
11111
  if (xAxis2 == axis) xAxis2 = 0;
11112
  if (yAxis == axis) yAxis = 0;
11113
  if (yAxis2 == axis) yAxis2 = 0;
11114
11115
  // Note: No need to take care of range drag axes and range zoom axes, because
11116
  // they are stored in smart pointers
11117
}
11118
11119
/*! \internal
11120
11121
  This method is used by the QCPLegend destructor to report legend removal to
11122
  the QCustomPlot so it may clear its QCustomPlot::legend member accordingly.
11123
*/
11124
void QCustomPlot::legendRemoved(QCPLegend *legend) {
11125
  if (this->legend == legend) this->legend = 0;
11126
}
11127
11128
/*! \internal
11129
11130
  Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This
11131
  method is thus called after every operation that changes the layer indices,
11132
  like layer removal, layer creation, layer moving.
11133
*/
11134
void QCustomPlot::updateLayerIndices() const {
11135
  for (int i = 0; i < mLayers.size(); ++i) mLayers.at(i)->mIndex = i;
11136
}
11137
11138
/*! \internal
11139
11140
  Returns the layerable at pixel position \a pos. If \a onlySelectable is set to
11141
  true, only those layerables that are selectable will be considered. (Layerable
11142
  subclasses communicate their selectability via the QCPLayerable::selectTest
11143
  method, by returning -1.)
11144
11145
  \a selectionDetails is an output parameter that contains selection specifics
11146
  of the affected layerable. This is useful if the respective layerable shall be
11147
  given a subsequent QCPLayerable::selectEvent (like in \ref mouseReleaseEvent).
11148
  \a selectionDetails usually contains information about which part of the
11149
  layerable was hit, in multi-part layerables (e.g. QCPAxis::SelectablePart).
11150
*/
11151
QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable,
11152
                                       QVariant *selectionDetails) const {
11153
  for (int layerIndex = mLayers.size() - 1; layerIndex >= 0; --layerIndex) {
11154
    const QList<QCPLayerable *> layerables = mLayers.at(layerIndex)->children();
11155
    double minimumDistance = selectionTolerance() * 1.1;
11156
    QCPLayerable *minimumDistanceLayerable = 0;
11157
    for (int i = layerables.size() - 1; i >= 0; --i) {
11158
      if (!layerables.at(i)->realVisibility()) continue;
11159
      QVariant details;
11160
      double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11161
      if (dist >= 0 && dist < minimumDistance) {
11162
        minimumDistance = dist;
11163
        minimumDistanceLayerable = layerables.at(i);
11164
        if (selectionDetails) *selectionDetails = details;
11165
      }
11166
    }
11167
    if (minimumDistance < selectionTolerance()) return minimumDistanceLayerable;
11168
  }
11169
  return 0;
11170
}
11171
11172
/*!
11173
  Saves the plot to a rastered image file \a fileName in the image format \a
11174
  format. The plot is sized to \a width and \a height in pixels and scaled with
11175
  \a scale. (width 100 and scale 2.0 lead to a full resolution file with width
11176
  200.) If the \a format supports compression, \a quality may be between 0 and
11177
  100 to control it.
11178
11179
  Returns true on success. If this function fails, most likely the given \a
11180
  format isn't supported by the system, see Qt docs about
11181
  QImageWriter::supportedImageFormats().
11182
11183
  \see saveBmp, saveJpg, savePng, savePdf
11184
*/
11185
bool QCustomPlot::saveRastered(const QString &fileName, int width, int height,
11186
                               double scale, const char *format, int quality) {
11187
  QPixmap buffer = toPixmap(width, height, scale);
11188
  if (!buffer.isNull())
11189
    return buffer.save(fileName, format, quality);
11190
  else
11191
    return false;
11192
}
11193
11194
/*!
11195
  Renders the plot to a pixmap and returns it.
11196
11197
  The plot is sized to \a width and \a height in pixels and scaled with \a
11198
  scale. (width 100 and scale 2.0 lead to a full resolution pixmap with width
11199
  200.)
11200
11201
  \see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf
11202
*/
11203
QPixmap QCustomPlot::toPixmap(int width, int height, double scale) {
11204
  // this method is somewhat similar to toPainter. Change something here, and a
11205
  // change in toPainter might be necessary, too.
11206
  int newWidth, newHeight;
11207
  if (width == 0 || height == 0) {
11208
    newWidth = this->width();
11209
    newHeight = this->height();
11210
  } else {
11211
    newWidth = width;
11212
    newHeight = height;
11213
  }
11214
  int scaledWidth = qRound(scale * newWidth);
11215
  int scaledHeight = qRound(scale * newHeight);
11216
11217
  QPixmap result(scaledWidth, scaledHeight);
11218
  result.fill(
11219
      mBackgroundBrush.style() == Qt::SolidPattern
11220
          ? mBackgroundBrush.color()
11221
          : Qt::transparent);  // if using non-solid pattern, make transparent
11222
                               // now and draw brush pattern later
11223
  QCPPainter painter;
11224
  painter.begin(&result);
11225
  if (painter.isActive()) {
11226
    QRect oldViewport = viewport();
11227
    setViewport(QRect(0, 0, newWidth, newHeight));
11228
    painter.setMode(QCPPainter::pmNoCaching);
11229
    if (!qFuzzyCompare(scale, 1.0)) {
11230
      if (scale >
11231
          1.0)  // for scale < 1 we always want cosmetic pens where possible,
11232
                // because else lines might disappear for very small scales
11233
        painter.setMode(QCPPainter::pmNonCosmetic);
11234
      painter.scale(scale, scale);
11235
    }
11236
    if (mBackgroundBrush.style() != Qt::SolidPattern &&
11237
        mBackgroundBrush.style() !=
11238
            Qt::NoBrush)  // solid fills were done a few lines above with
11239
                          // QPixmap::fill
11240
      painter.fillRect(mViewport, mBackgroundBrush);
11241
    draw(&painter);
11242
    setViewport(oldViewport);
11243
    painter.end();
11244
  } else  // might happen if pixmap has width or height zero
11245
  {
11246
    qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11247
    return QPixmap();
11248
  }
11249
  return result;
11250
}
11251
11252
/*!
11253
  Renders the plot using the passed \a painter.
11254
11255
  The plot is sized to \a width and \a height in pixels. If the \a painter's
11256
  scale is not 1.0, the resulting plot will appear scaled accordingly.
11257
11258
  \note If you are restricted to using a QPainter (instead of QCPPainter),
11259
  create a temporary QPicture and open a QCPPainter on it. Then call \ref
11260
  toPainter with this QCPPainter. After ending the paint operation on the
11261
  picture, draw it with the QPainter. This will reproduce the painter actions
11262
  the QCPPainter took, with a QPainter.
11263
11264
  \see toPixmap
11265
*/
11266
void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) {
11267
  // this method is somewhat similar to toPixmap. Change something here, and a
11268
  // change in toPixmap might be necessary, too.
11269
  int newWidth, newHeight;
11270
  if (width == 0 || height == 0) {
11271
    newWidth = this->width();
11272
    newHeight = this->height();
11273
  } else {
11274
    newWidth = width;
11275
    newHeight = height;
11276
  }
11277
11278
  if (painter->isActive()) {
11279
    QRect oldViewport = viewport();
11280
    setViewport(QRect(0, 0, newWidth, newHeight));
11281
    painter->setMode(QCPPainter::pmNoCaching);
11282
    if (mBackgroundBrush.style() !=
11283
        Qt::NoBrush)  // unlike in toPixmap, we can't do QPixmap::fill for
11284
                      // Qt::SolidPattern brush style, so we also draw solid
11285
                      // fills with fillRect here
11286
      painter->fillRect(mViewport, mBackgroundBrush);
11287
    draw(painter);
11288
    setViewport(oldViewport);
11289
  } else
11290
    qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11291
}
11292
11293
////////////////////////////////////////////////////////////////////////////////////////////////////
11294
//////////////////// QCPColorGradient
11295
////////////////////////////////////////////////////////////////////////////////////////////////////
11296
11297
/*! \class QCPColorGradient
11298
  \brief Defines a color gradient for use with e.g. \ref QCPColorMap
11299
11300
  This class describes a color gradient which can be used to encode data with
11301
  color. For example, QCPColorMap and QCPColorScale have \ref
11302
  QCPColorMap::setGradient "setGradient" methods which take an instance of this
11303
  class. Colors are set with \ref setColorStopAt(double position, const QColor
11304
  &color) with a \a position from 0 to 1. In between these defined color
11305
  positions, the color will be interpolated linearly either in RGB or HSV space,
11306
  see \ref setColorInterpolation.
11307
11308
  Alternatively, load one of the preset color gradients shown in the image
11309
  below, with \ref loadPreset, or by directly specifying the preset in the
11310
  constructor.
11311
11312
  \image html QCPColorGradient.png
11313
11314
  The fact that the \ref QCPColorGradient(GradientPreset preset) constructor
11315
  allows directly converting a \ref GradientPreset to a QCPColorGradient, you
11316
  can also directly pass \ref GradientPreset to all the \a setGradient methods,
11317
  e.g.: \snippet documentation/doc-code-snippets/mainwindow.cpp
11318
  qcpcolorgradient-setgradient
11319
11320
  The total number of levels used in the gradient can be set with \ref
11321
  setLevelCount. Whether the color gradient shall be applied periodically
11322
  (wrapping around) to data values that lie outside the data range specified on
11323
  the plottable instance can be controlled with \ref setPeriodic.
11324
*/
11325
11326
/*!
11327
  Constructs a new QCPColorGradient initialized with the colors and color
11328
  interpolation according to \a preset.
11329
11330
  The color level count is initialized to 350.
11331
*/
11332
QCPColorGradient::QCPColorGradient(GradientPreset preset)
11333
    : mLevelCount(350),
11334
      mColorInterpolation(ciRGB),
11335
      mPeriodic(false),
11336
      mColorBufferInvalidated(true) {
11337
  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11338
  loadPreset(preset);
11339
}
11340
11341
/* undocumented operator */
11342
bool QCPColorGradient::operator==(const QCPColorGradient &other) const {
11343
  return ((other.mLevelCount == this->mLevelCount) &&
11344
          (other.mColorInterpolation == this->mColorInterpolation) &&
11345
          (other.mPeriodic == this->mPeriodic) &&
11346
          (other.mColorStops == this->mColorStops));
11347
}
11348
11349
/*!
11350
  Sets the number of discretization levels of the color gradient to \a n. The
11351
  default is 350 which is typically enough to create a smooth appearance.
11352
11353
  \image html QCPColorGradient-levelcount.png
11354
*/
11355
void QCPColorGradient::setLevelCount(int n) {
11356
  if (n < 2) {
11357
    qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11358
    n = 2;
11359
  }
11360
  if (n != mLevelCount) {
11361
    mLevelCount = n;
11362
    mColorBufferInvalidated = true;
11363
  }
11364
}
11365
11366
/*!
11367
  Sets at which positions from 0 to 1 which color shall occur. The positions are
11368
  the keys, the colors are the values of the passed QMap \a colorStops. In
11369
  between these color stops, the color is interpolated according to \ref
11370
  setColorInterpolation.
11371
11372
  A more convenient way to create a custom gradient may be to clear all color
11373
  stops with \ref clearColorStops and then adding them one by one with \ref
11374
  setColorStopAt.
11375
11376
  \see clearColorStops
11377
*/
11378
void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops) {
11379
  mColorStops = colorStops;
11380
  mColorBufferInvalidated = true;
11381
}
11382
11383
/*!
11384
  Sets the \a color the gradient will have at the specified \a position (from 0
11385
  to 1). In between these color stops, the color is interpolated according to
11386
  \ref setColorInterpolation.
11387
11388
  \see setColorStops, clearColorStops
11389
*/
11390
void QCPColorGradient::setColorStopAt(double position, const QColor &color) {
11391
  mColorStops.insert(position, color);
11392
  mColorBufferInvalidated = true;
11393
}
11394
11395
/*!
11396
  Sets whether the colors in between the configured color stops (see \ref
11397
  setColorStopAt) shall be interpolated linearly in RGB or in HSV color space.
11398
11399
  For example, a sweep in RGB space from red to green will have a muddy brown
11400
  intermediate color, whereas in HSV space the intermediate color is yellow.
11401
*/
11402
void QCPColorGradient::setColorInterpolation(
11403
    QCPColorGradient::ColorInterpolation interpolation) {
11404
  if (interpolation != mColorInterpolation) {
11405
    mColorInterpolation = interpolation;
11406
    mColorBufferInvalidated = true;
11407
  }
11408
}
11409
11410
/*!
11411
  Sets whether data points that are outside the configured data range (e.g. \ref
11412
  QCPColorMap::setDataRange) are colored by periodically repeating the color
11413
  gradient or whether they all have the same color, corresponding to the
11414
  respective gradient boundary color.
11415
11416
  \image html QCPColorGradient-periodic.png
11417
11418
  As shown in the image above, gradients that have the same start and end color
11419
  are especially suitable for a periodic gradient mapping, since they produce
11420
  smooth color transitions throughout the color map. A preset that has this
11421
  property is \ref gpHues.
11422
11423
  In practice, using periodic color gradients makes sense when the data
11424
  corresponds to a periodic dimension, such as an angle or a phase. If this is
11425
  not the case, the color encoding might become ambiguous, because multiple
11426
  different data values are shown as the same color.
11427
*/
11428
void QCPColorGradient::setPeriodic(bool enabled) { mPeriodic = enabled; }
11429
11430
/*!
11431
  This method is used to quickly convert a \a data array to colors. The colors
11432
  will be output in the array \a scanLine. Both \a data and \a scanLine must
11433
  have the length \a n when passed to this function. The data range that shall
11434
  be used for mapping the data value to the gradient is passed in \a range. \a
11435
  logarithmic indicates whether the data values shall be mapped to colors
11436
  logarithmically.
11437
11438
  if \a data actually contains 2D-data linearized via <tt>[row*columnCount +
11439
  column]</tt>, you can set \a dataIndexFactor to <tt>columnCount</tt> to
11440
  convert a column instead of a row of the data array, in \a scanLine. \a
11441
  scanLine will remain a regular (1D) array. This works because \a data is
11442
  addressed <tt>data[i*dataIndexFactor]</tt>.
11443
*/
11444
void QCPColorGradient::colorize(const double *data, const QCPRange &range,
11445
                                QRgb *scanLine, int n, int dataIndexFactor,
11446
                                bool logarithmic) {
11447
  // If you change something here, make sure to also adapt ::color()
11448
  if (!data) {
11449
    qDebug() << Q_FUNC_INFO << "null pointer given as data";
11450
    return;
11451
  }
11452
  if (!scanLine) {
11453
    qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11454
    return;
11455
  }
11456
  if (mColorBufferInvalidated) updateColorBuffer();
11457
11458
  if (!logarithmic) {
11459
    const double posToIndexFactor = (mLevelCount - 1) / range.size();
11460
    if (mPeriodic) {
11461
      for (int i = 0; i < n; ++i) {
11462
        int index = (int)((data[dataIndexFactor * i] - range.lower) *
11463
                          posToIndexFactor) %
11464
                    mLevelCount;
11465
        if (index < 0) index += mLevelCount;
11466
        scanLine[i] = mColorBuffer.at(index);
11467
      }
11468
    } else {
11469
      for (int i = 0; i < n; ++i) {
11470
        int index =
11471
            (data[dataIndexFactor * i] - range.lower) * posToIndexFactor;
11472
        if (index < 0)
11473
          index = 0;
11474
        else if (index >= mLevelCount)
11475
          index = mLevelCount - 1;
11476
        scanLine[i] = mColorBuffer.at(index);
11477
      }
11478
    }
11479
  } else  // logarithmic == true
11480
  {
11481
    if (mPeriodic) {
11482
      for (int i = 0; i < n; ++i) {
11483
        int index = (int)(qLn(data[dataIndexFactor * i] / range.lower) /
11484
                          qLn(range.upper / range.lower) * (mLevelCount - 1)) %
11485
                    mLevelCount;
11486
        if (index < 0) index += mLevelCount;
11487
        scanLine[i] = mColorBuffer.at(index);
11488
      }
11489
    } else {
11490
      for (int i = 0; i < n; ++i) {
11491
        int index = qLn(data[dataIndexFactor * i] / range.lower) /
11492
                    qLn(range.upper / range.lower) * (mLevelCount - 1);
11493
        if (index < 0)
11494
          index = 0;
11495
        else if (index >= mLevelCount)
11496
          index = mLevelCount - 1;
11497
        scanLine[i] = mColorBuffer.at(index);
11498
      }
11499
    }
11500
  }
11501
}
11502
11503
/*! \internal
11504
11505
  This method is used to colorize a single data value given in \a position, to
11506
  colors. The data range that shall be used for mapping the data value to the
11507
  gradient is passed in \a range. \a logarithmic indicates whether the data
11508
  value shall be mapped to a color logarithmically.
11509
11510
  If an entire array of data values shall be converted, rather use \ref
11511
  colorize, for better performance.
11512
*/
11513
QRgb QCPColorGradient::color(double position, const QCPRange &range,
11514
                             bool logarithmic) {
11515
  // If you change something here, make sure to also adapt ::colorize()
11516
  if (mColorBufferInvalidated) updateColorBuffer();
11517
  int index = 0;
11518
  if (!logarithmic)
11519
    index = (position - range.lower) * (mLevelCount - 1) / range.size();
11520
  else
11521
    index = qLn(position / range.lower) / qLn(range.upper / range.lower) *
11522
            (mLevelCount - 1);
11523
  if (mPeriodic) {
11524
    index = index % mLevelCount;
11525
    if (index < 0) index += mLevelCount;
11526
  } else {
11527
    if (index < 0)
11528
      index = 0;
11529
    else if (index >= mLevelCount)
11530
      index = mLevelCount - 1;
11531
  }
11532
  return mColorBuffer.at(index);
11533
}
11534
11535
/*!
11536
  Clears the current color stops and loads the specified \a preset. A preset
11537
  consists of predefined color stops and the corresponding color interpolation
11538
  method.
11539
11540
  The available presets are:
11541
  \image html QCPColorGradient.png
11542
*/
11543
void QCPColorGradient::loadPreset(GradientPreset preset) {
11544
  clearColorStops();
11545
  switch (preset) {
11546
    case gpGrayscale:
11547
      setColorInterpolation(ciRGB);
11548
      setColorStopAt(0, Qt::black);
11549
      setColorStopAt(1, Qt::white);
11550
      break;
11551
    case gpHot:
11552
      setColorInterpolation(ciRGB);
11553
      setColorStopAt(0, QColor(50, 0, 0));
11554
      setColorStopAt(0.2, QColor(180, 10, 0));
11555
      setColorStopAt(0.4, QColor(245, 50, 0));
11556
      setColorStopAt(0.6, QColor(255, 150, 10));
11557
      setColorStopAt(0.8, QColor(255, 255, 50));
11558
      setColorStopAt(1, QColor(255, 255, 255));
11559
      break;
11560
    case gpCold:
11561
      setColorInterpolation(ciRGB);
11562
      setColorStopAt(0, QColor(0, 0, 50));
11563
      setColorStopAt(0.2, QColor(0, 10, 180));
11564
      setColorStopAt(0.4, QColor(0, 50, 245));
11565
      setColorStopAt(0.6, QColor(10, 150, 255));
11566
      setColorStopAt(0.8, QColor(50, 255, 255));
11567
      setColorStopAt(1, QColor(255, 255, 255));
11568
      break;
11569
    case gpNight:
11570
      setColorInterpolation(ciHSV);
11571
      setColorStopAt(0, QColor(10, 20, 30));
11572
      setColorStopAt(1, QColor(250, 255, 250));
11573
      break;
11574
    case gpCandy:
11575
      setColorInterpolation(ciHSV);
11576
      setColorStopAt(0, QColor(0, 0, 255));
11577
      setColorStopAt(1, QColor(255, 250, 250));
11578
      break;
11579
    case gpGeography:
11580
      setColorInterpolation(ciRGB);
11581
      setColorStopAt(0, QColor(70, 170, 210));
11582
      setColorStopAt(0.20, QColor(90, 160, 180));
11583
      setColorStopAt(0.25, QColor(45, 130, 175));
11584
      setColorStopAt(0.30, QColor(100, 140, 125));
11585
      setColorStopAt(0.5, QColor(100, 140, 100));
11586
      setColorStopAt(0.6, QColor(130, 145, 120));
11587
      setColorStopAt(0.7, QColor(140, 130, 120));
11588
      setColorStopAt(0.9, QColor(180, 190, 190));
11589
      setColorStopAt(1, QColor(210, 210, 230));
11590
      break;
11591
    case gpIon:
11592
      setColorInterpolation(ciHSV);
11593
      setColorStopAt(0, QColor(50, 10, 10));
11594
      setColorStopAt(0.45, QColor(0, 0, 255));
11595
      setColorStopAt(0.8, QColor(0, 255, 255));
11596
      setColorStopAt(1, QColor(0, 255, 0));
11597
      break;
11598
    case gpThermal:
11599
      setColorInterpolation(ciRGB);
11600
      setColorStopAt(0, QColor(0, 0, 50));
11601
      setColorStopAt(0.15, QColor(20, 0, 120));
11602
      setColorStopAt(0.33, QColor(200, 30, 140));
11603
      setColorStopAt(0.6, QColor(255, 100, 0));
11604
      setColorStopAt(0.85, QColor(255, 255, 40));
11605
      setColorStopAt(1, QColor(255, 255, 255));
11606
      break;
11607
    case gpPolar:
11608
      setColorInterpolation(ciRGB);
11609
      setColorStopAt(0, QColor(50, 255, 255));
11610
      setColorStopAt(0.18, QColor(10, 70, 255));
11611
      setColorStopAt(0.28, QColor(10, 10, 190));
11612
      setColorStopAt(0.5, QColor(0, 0, 0));
11613
      setColorStopAt(0.72, QColor(190, 10, 10));
11614
      setColorStopAt(0.82, QColor(255, 70, 10));
11615
      setColorStopAt(1, QColor(255, 255, 50));
11616
      break;
11617
    case gpSpectrum:
11618
      setColorInterpolation(ciHSV);
11619
      setColorStopAt(0, QColor(50, 0, 50));
11620
      setColorStopAt(0.15, QColor(0, 0, 255));
11621
      setColorStopAt(0.35, QColor(0, 255, 255));
11622
      setColorStopAt(0.6, QColor(255, 255, 0));
11623
      setColorStopAt(0.75, QColor(255, 30, 0));
11624
      setColorStopAt(1, QColor(50, 0, 0));
11625
      break;
11626
    case gpJet:
11627
      setColorInterpolation(ciRGB);
11628
      setColorStopAt(0, QColor(0, 0, 100));
11629
      setColorStopAt(0.15, QColor(0, 50, 255));
11630
      setColorStopAt(0.35, QColor(0, 255, 255));
11631
      setColorStopAt(0.65, QColor(255, 255, 0));
11632
      setColorStopAt(0.85, QColor(255, 30, 0));
11633
      setColorStopAt(1, QColor(100, 0, 0));
11634
      break;
11635
    case gpHues:
11636
      setColorInterpolation(ciHSV);
11637
      setColorStopAt(0, QColor(255, 0, 0));
11638
      setColorStopAt(1.0 / 3.0, QColor(0, 0, 255));
11639
      setColorStopAt(2.0 / 3.0, QColor(0, 255, 0));
11640
      setColorStopAt(1, QColor(255, 0, 0));
11641
      break;
11642
  }
11643
}
11644
11645
/*!
11646
  Clears all color stops.
11647
11648
  \see setColorStops, setColorStopAt
11649
*/
11650
void QCPColorGradient::clearColorStops() {
11651
  mColorStops.clear();
11652
  mColorBufferInvalidated = true;
11653
}
11654
11655
/*!
11656
  Returns an inverted gradient. The inverted gradient has all properties as this
11657
  \ref QCPColorGradient, but the order of the color stops is inverted.
11658
11659
  \see setColorStops, setColorStopAt
11660
*/
11661
QCPColorGradient QCPColorGradient::inverted() const {
11662
  QCPColorGradient result(*this);
11663
  result.clearColorStops();
11664
  for (QMap<double, QColor>::const_iterator it = mColorStops.constBegin();
11665
       it != mColorStops.constEnd(); ++it)
11666
    result.setColorStopAt(1.0 - it.key(), it.value());
11667
  return result;
11668
}
11669
11670
/*! \internal
11671
11672
  Updates the internal color buffer which will be used by \ref colorize and \ref
11673
  color, to quickly convert positions to colors. This is where the interpolation
11674
  between color stops is calculated.
11675
*/
11676
void QCPColorGradient::updateColorBuffer() {
11677
  if (mColorBuffer.size() != mLevelCount) mColorBuffer.resize(mLevelCount);
11678
  if (mColorStops.size() > 1) {
11679
    double indexToPosFactor = 1.0 / (double)(mLevelCount - 1);
11680
    for (int i = 0; i < mLevelCount; ++i) {
11681
      double position = i * indexToPosFactor;
11682
      QMap<double, QColor>::const_iterator it =
11683
          mColorStops.lowerBound(position);
11684
      if (it == mColorStops.constEnd())  // position is on or after last stop,
11685
                                         // use color of last stop
11686
      {
11687
        mColorBuffer[i] = (it - 1).value().rgb();
11688
      } else if (it ==
11689
                 mColorStops.constBegin())  // position is on or before first
11690
                                            // stop, use color of first stop
11691
      {
11692
        mColorBuffer[i] = it.value().rgb();
11693
      } else  // position is in between stops (or on an intermediate stop),
11694
              // interpolate color
11695
      {
11696
        QMap<double, QColor>::const_iterator high = it;
11697
        QMap<double, QColor>::const_iterator low = it - 1;
11698
        double t = (position - low.key()) /
11699
                   (high.key() - low.key());  // interpolation factor 0..1
11700
        switch (mColorInterpolation) {
11701
          case ciRGB: {
11702
            mColorBuffer[i] =
11703
                qRgb((1 - t) * low.value().red() + t * high.value().red(),
11704
                     (1 - t) * low.value().green() + t * high.value().green(),
11705
                     (1 - t) * low.value().blue() + t * high.value().blue());
11706
            break;
11707
          }
11708
          case ciHSV: {
11709
            QColor lowHsv = low.value().toHsv();
11710
            QColor highHsv = high.value().toHsv();
11711
            double hue = 0;
11712
            double hueDiff = highHsv.hueF() - lowHsv.hueF();
11713
            if (hueDiff > 0.5)
11714
              hue = lowHsv.hueF() - t * (1.0 - hueDiff);
11715
            else if (hueDiff < -0.5)
11716
              hue = lowHsv.hueF() + t * (1.0 + hueDiff);
11717
            else
11718
              hue = lowHsv.hueF() + t * hueDiff;
11719
            if (hue < 0)
11720
              hue += 1.0;
11721
            else if (hue >= 1.0)
11722
              hue -= 1.0;
11723
            mColorBuffer[i] =
11724
                QColor::fromHsvF(
11725
                    hue,
11726
                    (1 - t) * lowHsv.saturationF() + t * highHsv.saturationF(),
11727
                    (1 - t) * lowHsv.valueF() + t * highHsv.valueF())
11728
                    .rgb();
11729
            break;
11730
          }
11731
        }
11732
      }
11733
    }
11734
  } else if (mColorStops.size() == 1) {
11735
    mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11736
  } else  // mColorStops is empty, fill color buffer with black
11737
  {
11738
    mColorBuffer.fill(qRgb(0, 0, 0));
11739
  }
11740
  mColorBufferInvalidated = false;
11741
}
11742
11743
////////////////////////////////////////////////////////////////////////////////////////////////////
11744
//////////////////// QCPAxisRect
11745
////////////////////////////////////////////////////////////////////////////////////////////////////
11746
11747
/*! \class QCPAxisRect
11748
  \brief Holds multiple axes and arranges them in a rectangular shape.
11749
11750
  This class represents an axis rect, a rectangular area that is bounded on all
11751
  sides with an arbitrary number of axes.
11752
11753
  Initially QCustomPlot has one axis rect, accessible via
11754
  QCustomPlot::axisRect(). However, the layout system allows to have multiple
11755
  axis rects, e.g. arranged in a grid layout (QCustomPlot::plotLayout).
11756
11757
  By default, QCPAxisRect comes with four axes, at bottom, top, left and right.
11758
  They can be accessed via \ref axis by providing the respective axis type (\ref
11759
  QCPAxis::AxisType) and index. If you need all axes in the axis rect, use \ref
11760
  axes. The top and right axes are set to be invisible initially
11761
  (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
11762
  addAxes. To remove an axis, use \ref removeAxis.
11763
11764
  The axis rect layerable itself only draws a background pixmap or color, if
11765
  specified (\ref setBackground). It is placed on the "background" layer
11766
  initially (see \ref QCPLayer for an explanation of the QCustomPlot layer
11767
  system). The axes that are held by the axis rect can be placed on other
11768
  layers, independently of the axis rect.
11769
11770
  Every axis rect has a child layout of type \ref QCPLayoutInset. It is
11771
  accessible via \ref insetLayout and can be used to have other layout elements
11772
  (or even other layouts with multiple elements) hovering inside the axis rect.
11773
11774
  If an axis rect is clicked and dragged, it processes this by moving certain
11775
  axis ranges. The behaviour can be controlled with \ref setRangeDrag and \ref
11776
  setRangeDragAxes. If the mouse wheel is scrolled while the cursor is on the
11777
  axis rect, certain axes are scaled. This is controllable via \ref
11778
  setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These
11779
  interactions are only enabled if \ref QCustomPlot::setInteractions contains
11780
  \ref QCP::iRangeDrag and \ref QCP::iRangeZoom.
11781
11782
  \image html AxisRectSpacingOverview.png
11783
  <center>Overview of the spacings and paddings that define the geometry of an
11784
  axis. The dashed line on the far left indicates the viewport/widget
11785
  border.</center>
11786
*/
11787
11788
/* start documentation of inline functions */
11789
11790
/*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
11791
11792
  Returns the inset layout of this axis rect. It can be used to place other
11793
  layout elements (or even layouts with multiple other elements) inside/on top
11794
  of an axis rect.
11795
11796
  \see QCPLayoutInset
11797
*/
11798
11799
/*! \fn int QCPAxisRect::left() const
11800
11801
  Returns the pixel position of the left border of this axis rect. Margins are
11802
  not taken into account here, so the returned value is with respect to the
11803
  inner \ref rect.
11804
*/
11805
11806
/*! \fn int QCPAxisRect::right() const
11807
11808
  Returns the pixel position of the right border of this axis rect. Margins are
11809
  not taken into account here, so the returned value is with respect to the
11810
  inner \ref rect.
11811
*/
11812
11813
/*! \fn int QCPAxisRect::top() const
11814
11815
  Returns the pixel position of the top border of this axis rect. Margins are
11816
  not taken into account here, so the returned value is with respect to the
11817
  inner \ref rect.
11818
*/
11819
11820
/*! \fn int QCPAxisRect::bottom() const
11821
11822
  Returns the pixel position of the bottom border of this axis rect. Margins are
11823
  not taken into account here, so the returned value is with respect to the
11824
  inner \ref rect.
11825
*/
11826
11827
/*! \fn int QCPAxisRect::width() const
11828
11829
  Returns the pixel width of this axis rect. Margins are not taken into account
11830
  here, so the returned value is with respect to the inner \ref rect.
11831
*/
11832
11833
/*! \fn int QCPAxisRect::height() const
11834
11835
  Returns the pixel height of this axis rect. Margins are not taken into account
11836
  here, so the returned value is with respect to the inner \ref rect.
11837
*/
11838
11839
/*! \fn QSize QCPAxisRect::size() const
11840
11841
  Returns the pixel size of this axis rect. Margins are not taken into account
11842
  here, so the returned value is with respect to the inner \ref rect.
11843
*/
11844
11845
/*! \fn QPoint QCPAxisRect::topLeft() const
11846
11847
  Returns the top left corner of this axis rect in pixels. Margins are not taken
11848
  into account here, so the returned value is with respect to the inner \ref
11849
  rect.
11850
*/
11851
11852
/*! \fn QPoint QCPAxisRect::topRight() const
11853
11854
  Returns the top right corner of this axis rect in pixels. Margins are not
11855
  taken into account here, so the returned value is with respect to the inner
11856
  \ref rect.
11857
*/
11858
11859
/*! \fn QPoint QCPAxisRect::bottomLeft() const
11860
11861
  Returns the bottom left corner of this axis rect in pixels. Margins are not
11862
  taken into account here, so the returned value is with respect to the inner
11863
  \ref rect.
11864
*/
11865
11866
/*! \fn QPoint QCPAxisRect::bottomRight() const
11867
11868
  Returns the bottom right corner of this axis rect in pixels. Margins are not
11869
  taken into account here, so the returned value is with respect to the inner
11870
  \ref rect.
11871
*/
11872
11873
/*! \fn QPoint QCPAxisRect::center() const
11874
11875
  Returns the center of this axis rect in pixels. Margins are not taken into
11876
  account here, so the returned value is with respect to the inner \ref rect.
11877
*/
11878
11879
/* end documentation of inline functions */
11880
11881
/*!
11882
  Creates a QCPAxisRect instance and sets default values. An axis is added for
11883
  each of the four sides, the top and right axes are set invisible initially.
11884
*/
11885
QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes)
11886
    : QCPLayoutElement(parentPlot),
11887
      mBackgroundBrush(Qt::NoBrush),
11888
      mBackgroundScaled(true),
11889
      mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11890
      mInsetLayout(new QCPLayoutInset),
11891
      mRangeDrag(Qt::Horizontal | Qt::Vertical),
11892
      mRangeZoom(Qt::Horizontal | Qt::Vertical),
11893
      mRangeZoomFactorHorz(0.85),
11894
      mRangeZoomFactorVert(0.85),
11895
      mDragging(false) {
11896
  mInsetLayout->initializeParentPlot(mParentPlot);
11897
  mInsetLayout->setParentLayerable(this);
11898
  mInsetLayout->setParent(this);
11899
11900
  setMinimumSize(50, 50);
11901
  setMinimumMargins(QMargins(15, 15, 15, 15));
11902
  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis *>());
11903
  mAxes.insert(QCPAxis::atRight, QList<QCPAxis *>());
11904
  mAxes.insert(QCPAxis::atTop, QList<QCPAxis *>());
11905
  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis *>());
11906
11907
  if (setupDefaultAxes) {
11908
    QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11909
    QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
11910
    QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11911
    QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11912
    setRangeDragAxes(xAxis, yAxis);
11913
    setRangeZoomAxes(xAxis, yAxis);
11914
    xAxis2->setVisible(false);
11915
    yAxis2->setVisible(false);
11916
    xAxis->grid()->setVisible(true);
11917
    yAxis->grid()->setVisible(true);
11918
    xAxis2->grid()->setVisible(false);
11919
    yAxis2->grid()->setVisible(false);
11920
    xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11921
    yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11922
    xAxis2->grid()->setVisible(false);
11923
    yAxis2->grid()->setVisible(false);
11924
  }
11925
}
11926
11927
QCPAxisRect::~QCPAxisRect() {
11928
  delete mInsetLayout;
11929
  mInsetLayout = 0;
11930
11931
  QList<QCPAxis *> axesList = axes();
11932
  for (int i = 0; i < axesList.size(); ++i) removeAxis(axesList.at(i));
11933
}
11934
11935
/*!
11936
  Returns the number of axes on the axis rect side specified with \a type.
11937
11938
  \see axis
11939
*/
11940
int QCPAxisRect::axisCount(QCPAxis::AxisType type) const {
11941
  return mAxes.value(type).size();
11942
}
11943
11944
/*!
11945
  Returns the axis with the given \a index on the axis rect side specified with
11946
  \a type.
11947
11948
  \see axisCount, axes
11949
*/
11950
QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const {
11951
  QList<QCPAxis *> ax(mAxes.value(type));
11952
  if (index >= 0 && index < ax.size()) {
11953
    return ax.at(index);
11954
  } else {
11955
    qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11956
    return 0;
11957
  }
11958
}
11959
11960
/*!
11961
  Returns all axes on the axis rect sides specified with \a types.
11962
11963
  \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination,
11964
  to get the axes of multiple sides.
11965
11966
  \see axis
11967
*/
11968
QList<QCPAxis *> QCPAxisRect::axes(QCPAxis::AxisTypes types) const {
11969
  QList<QCPAxis *> result;
11970
  if (types.testFlag(QCPAxis::atLeft)) result << mAxes.value(QCPAxis::atLeft);
11971
  if (types.testFlag(QCPAxis::atRight)) result << mAxes.value(QCPAxis::atRight);
11972
  if (types.testFlag(QCPAxis::atTop)) result << mAxes.value(QCPAxis::atTop);
11973
  if (types.testFlag(QCPAxis::atBottom))
11974
    result << mAxes.value(QCPAxis::atBottom);
11975
  return result;
11976
}
11977
11978
/*! \overload
11979
11980
  Returns all axes of this axis rect.
11981
*/
11982
QList<QCPAxis *> QCPAxisRect::axes() const {
11983
  QList<QCPAxis *> result;
11984
  QHashIterator<QCPAxis::AxisType, QList<QCPAxis *> > it(mAxes);
11985
  while (it.hasNext()) {
11986
    it.next();
11987
    result << it.value();
11988
  }
11989
  return result;
11990
}
11991
11992
/*!
11993
  Adds a new axis to the axis rect side specified with \a type, and returns it.
11994
  If \a axis is 0, a new QCPAxis instance is created internally.
11995
11996
  You may inject QCPAxis instances (or sublasses of QCPAxis) by setting \a axis
11997
  to an axis that was previously created outside QCustomPlot. It is important to
11998
  note that QCustomPlot takes ownership of the axis, so you may not delete it
11999
  afterwards. Further, the \a axis must have been created with this axis rect as
12000
  parent and with the same axis type as specified in \a type. If this is not the
12001
  case, a debug output is generated, the axis is not added, and the method
12002
  returns 0.
12003
12004
  This method can not be used to move \a axis between axis rects. The same \a
12005
  axis instance must not be added multiple times to the same or different axis
12006
  rects.
12007
12008
  If an axis rect side already contains one or more axes, the lower and upper
12009
  endings of the new axis (\ref QCPAxis::setLowerEnding, \ref
12010
  QCPAxis::setUpperEnding) are set to \ref QCPLineEnding::esHalfBar.
12011
12012
  \see addAxes, setupFullAxesBox
12013
*/
12014
QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis) {
12015
  QCPAxis *newAxis = axis;
12016
  if (!newAxis) {
12017
    newAxis = new QCPAxis(this, type);
12018
  } else  // user provided existing axis instance, do some sanity checks
12019
  {
12020
    if (newAxis->axisType() != type) {
12021
      qDebug() << Q_FUNC_INFO
12022
               << "passed axis has different axis type than specified in type "
12023
                  "parameter";
12024
      return 0;
12025
    }
12026
    if (newAxis->axisRect() != this) {
12027
      qDebug() << Q_FUNC_INFO
12028
               << "passed axis doesn't have this axis rect as parent axis rect";
12029
      return 0;
12030
    }
12031
    if (axes().contains(newAxis)) {
12032
      qDebug() << Q_FUNC_INFO
12033
               << "passed axis is already owned by this axis rect";
12034
      return 0;
12035
    }
12036
  }
12037
  if (mAxes[type].size() > 0)  // multiple axes on one side, add half-bar axis
12038
                               // ending to additional axes with offset
12039
  {
12040
    bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
12041
    newAxis->setLowerEnding(
12042
        QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
12043
    newAxis->setUpperEnding(
12044
        QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
12045
  }
12046
  mAxes[type].append(newAxis);
12047
  return newAxis;
12048
}
12049
12050
/*!
12051
  Adds a new axis with \ref addAxis to each axis rect side specified in \a
12052
  types. This may be an <tt>or</tt>-combination of QCPAxis::AxisType, so axes
12053
  can be added to multiple sides at once.
12054
12055
  Returns a list of the added axes.
12056
12057
  \see addAxis, setupFullAxesBox
12058
*/
12059
QList<QCPAxis *> QCPAxisRect::addAxes(QCPAxis::AxisTypes types) {
12060
  QList<QCPAxis *> result;
12061
  if (types.testFlag(QCPAxis::atLeft)) result << addAxis(QCPAxis::atLeft);
12062
  if (types.testFlag(QCPAxis::atRight)) result << addAxis(QCPAxis::atRight);
12063
  if (types.testFlag(QCPAxis::atTop)) result << addAxis(QCPAxis::atTop);
12064
  if (types.testFlag(QCPAxis::atBottom)) result << addAxis(QCPAxis::atBottom);
12065
  return result;
12066
}
12067
12068
/*!
12069
  Removes the specified \a axis from the axis rect and deletes it.
12070
12071
  Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
12072
12073
  \see addAxis
12074
*/
12075
bool QCPAxisRect::removeAxis(QCPAxis *axis) {
12076
  // don't access axis->axisType() to provide safety when axis is an invalid
12077
  // pointer, rather go through all axis containers:
12078
  QHashIterator<QCPAxis::AxisType, QList<QCPAxis *> > it(mAxes);
12079
  while (it.hasNext()) {
12080
    it.next();
12081
    if (it.value().contains(axis)) {
12082
      mAxes[it.key()].removeOne(axis);
12083
      if (qobject_cast<QCustomPlot *>(
12084
              parentPlot()))  // make sure this isn't called from QObject dtor
12085
                              // when QCustomPlot is already destructed (happens
12086
                              // when the axis rect is not in any layout and
12087
                              // thus QObject-child of QCustomPlot)
12088
        parentPlot()->axisRemoved(axis);
12089
      delete axis;
12090
      return true;
12091
    }
12092
  }
12093
  qDebug() << Q_FUNC_INFO
12094
           << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
12095
  return false;
12096
}
12097
12098
/*!
12099
  Convenience function to create an axis on each side that doesn't have any axes
12100
  yet and set their visibility to true. Further, the top/right axes are assigned
12101
  the following properties of the bottom/left axes:
12102
12103
  \li range (\ref QCPAxis::setRange)
12104
  \li range reversed (\ref QCPAxis::setRangeReversed)
12105
  \li scale type (\ref QCPAxis::setScaleType)
12106
  \li scale log base  (\ref QCPAxis::setScaleLogBase)
12107
  \li ticks (\ref QCPAxis::setTicks)
12108
  \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
12109
  \li sub tick count (\ref QCPAxis::setSubTickCount)
12110
  \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
12111
  \li tick step (\ref QCPAxis::setTickStep)
12112
  \li auto tick step (\ref QCPAxis::setAutoTickStep)
12113
  \li number format (\ref QCPAxis::setNumberFormat)
12114
  \li number precision (\ref QCPAxis::setNumberPrecision)
12115
  \li tick label type (\ref QCPAxis::setTickLabelType)
12116
  \li date time format (\ref QCPAxis::setDateTimeFormat)
12117
  \li date time spec (\ref QCPAxis::setDateTimeSpec)
12118
12119
  Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to
12120
  false.
12121
12122
  If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged"
12123
  signals of the bottom and left axes are connected to the \ref
12124
  QCPAxis::setRange slots of the top and right axes.
12125
*/
12126
void QCPAxisRect::setupFullAxesBox(bool connectRanges) {
12127
  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
12128
  if (axisCount(QCPAxis::atBottom) == 0)
12129
    xAxis = addAxis(QCPAxis::atBottom);
12130
  else
12131
    xAxis = axis(QCPAxis::atBottom);
12132
12133
  if (axisCount(QCPAxis::atLeft) == 0)
12134
    yAxis = addAxis(QCPAxis::atLeft);
12135
  else
12136
    yAxis = axis(QCPAxis::atLeft);
12137
12138
  if (axisCount(QCPAxis::atTop) == 0)
12139
    xAxis2 = addAxis(QCPAxis::atTop);
12140
  else
12141
    xAxis2 = axis(QCPAxis::atTop);
12142
12143
  if (axisCount(QCPAxis::atRight) == 0)
12144
    yAxis2 = addAxis(QCPAxis::atRight);
12145
  else
12146
    yAxis2 = axis(QCPAxis::atRight);
12147
12148
  xAxis->setVisible(true);
12149
  yAxis->setVisible(true);
12150
  xAxis2->setVisible(true);
12151
  yAxis2->setVisible(true);
12152
  xAxis2->setTickLabels(false);
12153
  yAxis2->setTickLabels(false);
12154
12155
  xAxis2->setRange(xAxis->range());
12156
  xAxis2->setRangeReversed(xAxis->rangeReversed());
12157
  xAxis2->setScaleType(xAxis->scaleType());
12158
  xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12159
  xAxis2->setTicks(xAxis->ticks());
12160
  xAxis2->setAutoTickCount(xAxis->autoTickCount());
12161
  xAxis2->setSubTickCount(xAxis->subTickCount());
12162
  xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12163
  xAxis2->setTickStep(xAxis->tickStep());
12164
  xAxis2->setAutoTickStep(xAxis->autoTickStep());
12165
  xAxis2->setNumberFormat(xAxis->numberFormat());
12166
  xAxis2->setNumberPrecision(xAxis->numberPrecision());
12167
  xAxis2->setTickLabelType(xAxis->tickLabelType());
12168
  xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12169
  xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12170
12171
  yAxis2->setRange(yAxis->range());
12172
  yAxis2->setRangeReversed(yAxis->rangeReversed());
12173
  yAxis2->setScaleType(yAxis->scaleType());
12174
  yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12175
  yAxis2->setTicks(yAxis->ticks());
12176
  yAxis2->setAutoTickCount(yAxis->autoTickCount());
12177
  yAxis2->setSubTickCount(yAxis->subTickCount());
12178
  yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12179
  yAxis2->setTickStep(yAxis->tickStep());
12180
  yAxis2->setAutoTickStep(yAxis->autoTickStep());
12181
  yAxis2->setNumberFormat(yAxis->numberFormat());
12182
  yAxis2->setNumberPrecision(yAxis->numberPrecision());
12183
  yAxis2->setTickLabelType(yAxis->tickLabelType());
12184
  yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12185
  yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12186
12187
  if (connectRanges) {
12188
    connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2,
12189
            SLOT(setRange(QCPRange)));
12190
    connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2,
12191
            SLOT(setRange(QCPRange)));
12192
  }
12193
}
12194
12195
/*!
12196
  Returns a list of all the plottables that are associated with this axis rect.
12197
12198
  A plottable is considered associated with an axis rect if its key or value
12199
  axis (or both) is in this axis rect.
12200
12201
  \see graphs, items
12202
*/
12203
QList<QCPAbstractPlottable *> QCPAxisRect::plottables() const {
12204
  // Note: don't append all QCPAxis::plottables() into a list, because we might
12205
  // get duplicate entries
12206
  QList<QCPAbstractPlottable *> result;
12207
  for (int i = 0; i < mParentPlot->mPlottables.size(); ++i) {
12208
    if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||
12209
        mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12210
      result.append(mParentPlot->mPlottables.at(i));
12211
  }
12212
  return result;
12213
}
12214
12215
/*!
12216
  Returns a list of all the graphs that are associated with this axis rect.
12217
12218
  A graph is considered associated with an axis rect if its key or value axis
12219
  (or both) is in this axis rect.
12220
12221
  \see plottables, items
12222
*/
12223
QList<QCPGraph *> QCPAxisRect::graphs() const {
12224
  // Note: don't append all QCPAxis::graphs() into a list, because we might get
12225
  // duplicate entries
12226
  QList<QCPGraph *> result;
12227
  for (int i = 0; i < mParentPlot->mGraphs.size(); ++i) {
12228
    if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this ||
12229
        mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12230
      result.append(mParentPlot->mGraphs.at(i));
12231
  }
12232
  return result;
12233
}
12234
12235
/*!
12236
  Returns a list of all the items that are associated with this axis rect.
12237
12238
  An item is considered associated with an axis rect if any of its positions has
12239
  key or value axis set to an axis that is in this axis rect, or if any of its
12240
  positions has \ref QCPItemPosition::setAxisRect set to the axis rect, or if
12241
  the clip axis rect (\ref QCPAbstractItem::setClipAxisRect) is set to this axis
12242
  rect.
12243
12244
  \see plottables, graphs
12245
*/
12246
QList<QCPAbstractItem *> QCPAxisRect::items() const {
12247
  // Note: don't just append all QCPAxis::items() into a list, because we might
12248
  // get duplicate entries
12249
  //       and miss those items that have this axis rect as clipAxisRect.
12250
  QList<QCPAbstractItem *> result;
12251
  for (int itemId = 0; itemId < mParentPlot->mItems.size(); ++itemId) {
12252
    if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this) {
12253
      result.append(mParentPlot->mItems.at(itemId));
12254
      continue;
12255
    }
12256
    QList<QCPItemPosition *> positions =
12257
        mParentPlot->mItems.at(itemId)->positions();
12258
    for (int posId = 0; posId < positions.size(); ++posId) {
12259
      if (positions.at(posId)->axisRect() == this ||
12260
          positions.at(posId)->keyAxis()->axisRect() == this ||
12261
          positions.at(posId)->valueAxis()->axisRect() == this) {
12262
        result.append(mParentPlot->mItems.at(itemId));
12263
        break;
12264
      }
12265
    }
12266
  }
12267
  return result;
12268
}
12269
12270
/*!
12271
  This method is called automatically upon replot and doesn't need to be called
12272
  by users of QCPAxisRect.
12273
12274
  Calls the base class implementation to update the margins (see \ref
12275
  QCPLayoutElement::update), and finally passes the \ref rect to the inset
12276
  layout (\ref insetLayout) and calls its QCPInsetLayout::update function.
12277
*/
12278
void QCPAxisRect::update(UpdatePhase phase) {
12279
  QCPLayoutElement::update(phase);
12280
12281
  switch (phase) {
12282
    case upPreparation: {
12283
      QList<QCPAxis *> allAxes = axes();
12284
      for (int i = 0; i < allAxes.size(); ++i)
12285
        allAxes.at(i)->setupTickVectors();
12286
      break;
12287
    }
12288
    case upLayout: {
12289
      mInsetLayout->setOuterRect(rect());
12290
      break;
12291
    }
12292
    default:
12293
      break;
12294
  }
12295
12296
  // pass update call on to inset layout (doesn't happen automatically, because
12297
  // QCPAxisRect doesn't derive from QCPLayout):
12298
  mInsetLayout->update(phase);
12299
}
12300
12301
/* inherits documentation from base class */
12302
QList<QCPLayoutElement *> QCPAxisRect::elements(bool recursive) const {
12303
  QList<QCPLayoutElement *> result;
12304
  if (mInsetLayout) {
12305
    result << mInsetLayout;
12306
    if (recursive) result << mInsetLayout->elements(recursive);
12307
  }
12308
  return result;
12309
}
12310
12311
/* inherits documentation from base class */
12312
void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const {
12313
  painter->setAntialiasing(false);
12314
}
12315
12316
/* inherits documentation from base class */
12317
void QCPAxisRect::draw(QCPPainter *painter) { drawBackground(painter); }
12318
12319
/*!
12320
  Sets \a pm as the axis background pixmap. The axis background pixmap will be
12321
  drawn inside the axis rect. Since axis rects place themselves on the
12322
  "background" layer by default, the axis rect backgrounds are usually drawn
12323
  below everything else.
12324
12325
  For cases where the provided pixmap doesn't have the same size as the axis
12326
  rect, scaling can be enabled with \ref setBackgroundScaled and the scaling
12327
  mode (i.e. whether and how the aspect ratio is preserved) can be set with \ref
12328
  setBackgroundScaledMode. To set all these options in one call, consider using
12329
  the overloaded version of this function.
12330
12331
  Below the pixmap, the axis rect may be optionally filled with a brush, if
12332
  specified with \ref setBackground(const QBrush &brush).
12333
12334
  \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush
12335
  &brush)
12336
*/
12337
void QCPAxisRect::setBackground(const QPixmap &pm) {
12338
  mBackgroundPixmap = pm;
12339
  mScaledBackgroundPixmap = QPixmap();
12340
}
12341
12342
/*! \overload
12343
12344
  Sets \a brush as the background brush. The axis rect background will be filled
12345
  with this brush. Since axis rects place themselves on the "background" layer
12346
  by default, the axis rect backgrounds are usually drawn below everything else.
12347
12348
  The brush will be drawn before (under) any background pixmap, which may be
12349
  specified with \ref setBackground(const QPixmap &pm).
12350
12351
  To disable drawing of a background brush, set \a brush to Qt::NoBrush.
12352
12353
  \see setBackground(const QPixmap &pm)
12354
*/
12355
void QCPAxisRect::setBackground(const QBrush &brush) {
12356
  mBackgroundBrush = brush;
12357
}
12358
12359
/*! \overload
12360
12361
  Allows setting the background pixmap of the axis rect, whether it shall be
12362
  scaled and how it shall be scaled in one call.
12363
12364
  \see setBackground(const QPixmap &pm), setBackgroundScaled,
12365
  setBackgroundScaledMode
12366
*/
12367
void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled,
12368
                                Qt::AspectRatioMode mode) {
12369
  mBackgroundPixmap = pm;
12370
  mScaledBackgroundPixmap = QPixmap();
12371
  mBackgroundScaled = scaled;
12372
  mBackgroundScaledMode = mode;
12373
}
12374
12375
/*!
12376
  Sets whether the axis background pixmap shall be scaled to fit the axis rect
12377
  or not. If \a scaled is set to true, you may control whether and how the
12378
  aspect ratio of the original pixmap is preserved with \ref
12379
  setBackgroundScaledMode.
12380
12381
  Note that the scaled version of the original pixmap is buffered, so there is
12382
  no performance penalty on replots. (Except when the axis rect dimensions are
12383
  changed continuously.)
12384
12385
  \see setBackground, setBackgroundScaledMode
12386
*/
12387
void QCPAxisRect::setBackgroundScaled(bool scaled) {
12388
  mBackgroundScaled = scaled;
12389
}
12390
12391
/*!
12392
  If scaling of the axis background pixmap is enabled (\ref
12393
  setBackgroundScaled), use this function to define whether and how the aspect
12394
  ratio of the original pixmap passed to \ref setBackground is preserved. \see
12395
  setBackground, setBackgroundScaled
12396
*/
12397
void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode) {
12398
  mBackgroundScaledMode = mode;
12399
}
12400
12401
/*!
12402
  Returns the range drag axis of the \a orientation provided.
12403
12404
  \see setRangeDragAxes
12405
*/
12406
QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation) {
12407
  return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data()
12408
                                        : mRangeDragVertAxis.data());
12409
}
12410
12411
/*!
12412
  Returns the range zoom axis of the \a orientation provided.
12413
12414
  \see setRangeZoomAxes
12415
*/
12416
QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation) {
12417
  return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data()
12418
                                        : mRangeZoomVertAxis.data());
12419
}
12420
12421
/*!
12422
  Returns the range zoom factor of the \a orientation provided.
12423
12424
  \see setRangeZoomFactor
12425
*/
12426
double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation) {
12427
  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz
12428
                                        : mRangeZoomFactorVert);
12429
}
12430
12431
/*!
12432
  Sets which axis orientation may be range dragged by the user with mouse
12433
  interaction. What orientation corresponds to which specific axis can be set
12434
  with \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
12435
  default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
12436
  is the left axis (yAxis).
12437
12438
  To disable range dragging entirely, pass 0 as \a orientations or remove \ref
12439
  QCP::iRangeDrag from \ref QCustomPlot::setInteractions. To enable range
12440
  dragging for both directions, pass <tt>Qt::Horizontal | Qt::Vertical</tt> as
12441
  \a orientations.
12442
12443
  In addition to setting \a orientations to a non-zero value, make sure \ref
12444
  QCustomPlot::setInteractions contains \ref QCP::iRangeDrag to enable the range
12445
  dragging interaction.
12446
12447
  \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag
12448
*/
12449
void QCPAxisRect::setRangeDrag(Qt::Orientations orientations) {
12450
  mRangeDrag = orientations;
12451
}
12452
12453
/*!
12454
  Sets which axis orientation may be zoomed by the user with the mouse wheel.
12455
  What orientation corresponds to which specific axis can be set with \ref
12456
  setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical). By default, the
12457
  horizontal axis is the bottom axis (xAxis) and the vertical axis is the left
12458
  axis (yAxis).
12459
12460
  To disable range zooming entirely, pass 0 as \a orientations or remove \ref
12461
  QCP::iRangeZoom from \ref QCustomPlot::setInteractions. To enable range
12462
  zooming for both directions, pass <tt>Qt::Horizontal | Qt::Vertical</tt> as \a
12463
  orientations.
12464
12465
  In addition to setting \a orientations to a non-zero value, make sure \ref
12466
  QCustomPlot::setInteractions contains \ref QCP::iRangeZoom to enable the range
12467
  zooming interaction.
12468
12469
  \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
12470
*/
12471
void QCPAxisRect::setRangeZoom(Qt::Orientations orientations) {
12472
  mRangeZoom = orientations;
12473
}
12474
12475
/*!
12476
  Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse
12477
  range dragging on the QCustomPlot widget.
12478
12479
  \see setRangeZoomAxes
12480
*/
12481
void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical) {
12482
  mRangeDragHorzAxis = horizontal;
12483
  mRangeDragVertAxis = vertical;
12484
}
12485
12486
/*!
12487
  Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse
12488
  wheel zooming on the QCustomPlot widget. The two axes can be zoomed with
12489
  different strengths, when different factors are passed to \ref
12490
  setRangeZoomFactor(double horizontalFactor, double verticalFactor).
12491
12492
  \see setRangeDragAxes
12493
*/
12494
void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical) {
12495
  mRangeZoomHorzAxis = horizontal;
12496
  mRangeZoomVertAxis = vertical;
12497
}
12498
12499
/*!
12500
  Sets how strong one rotation step of the mouse wheel zooms, when range zoom
12501
  was activated with \ref setRangeZoom. The two parameters \a horizontalFactor
12502
  and \a verticalFactor provide a way to let the horizontal axis zoom at
12503
  different rates than the vertical axis. Which axis is horizontal and which is
12504
  vertical, can be set with \ref setRangeZoomAxes.
12505
12506
  When the zoom factor is greater than one, scrolling the mouse wheel backwards
12507
  (towards the user) will zoom in (make the currently visible range smaller).
12508
  For zoom factors smaller than one, the same scrolling direction will zoom out.
12509
*/
12510
void QCPAxisRect::setRangeZoomFactor(double horizontalFactor,
12511
                                     double verticalFactor) {
12512
  mRangeZoomFactorHorz = horizontalFactor;
12513
  mRangeZoomFactorVert = verticalFactor;
12514
}
12515
12516
/*! \overload
12517
12518
  Sets both the horizontal and vertical zoom \a factor.
12519
*/
12520
void QCPAxisRect::setRangeZoomFactor(double factor) {
12521
  mRangeZoomFactorHorz = factor;
12522
  mRangeZoomFactorVert = factor;
12523
}
12524
12525
/*! \internal
12526
12527
  Draws the background of this axis rect. It may consist of a background fill (a
12528
  QBrush) and a pixmap.
12529
12530
  If a brush was given via \ref setBackground(const QBrush &brush), this
12531
  function first draws an according filling inside the axis rect with the
12532
  provided \a painter.
12533
12534
  Then, if a pixmap was provided via \ref setBackground, this function buffers
12535
  the scaled version depending on \ref setBackgroundScaled and \ref
12536
  setBackgroundScaledMode and then draws it inside the axis rect with the
12537
  provided \a painter. The scaled version is buffered in mScaledBackgroundPixmap
12538
  to prevent expensive rescaling at every redraw. It is only updated, when the
12539
  axis rect has changed in a way that requires a rescale of the background
12540
  pixmap (this is dependant on the \ref setBackgroundScaledMode), or when a
12541
  differend axis backgroud pixmap was set.
12542
12543
  \see setBackground, setBackgroundScaled, setBackgroundScaledMode
12544
*/
12545
void QCPAxisRect::drawBackground(QCPPainter *painter) {
12546
  // draw background fill:
12547
  if (mBackgroundBrush != Qt::NoBrush)
12548
    painter->fillRect(mRect, mBackgroundBrush);
12549
12550
  // draw background pixmap (on top of fill, if brush specified):
12551
  if (!mBackgroundPixmap.isNull()) {
12552
    if (mBackgroundScaled) {
12553
      // check whether mScaledBackground needs to be updated:
12554
      QSize scaledSize(mBackgroundPixmap.size());
12555
      scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12556
      if (mScaledBackgroundPixmap.size() != scaledSize)
12557
        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(
12558
            mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12559
      painter->drawPixmap(mRect.topLeft() + QPoint(0, -1),
12560
                          mScaledBackgroundPixmap,
12561
                          QRect(0, 0, mRect.width(), mRect.height()) &
12562
                              mScaledBackgroundPixmap.rect());
12563
    } else {
12564
      painter->drawPixmap(mRect.topLeft() + QPoint(0, -1), mBackgroundPixmap,
12565
                          QRect(0, 0, mRect.width(), mRect.height()));
12566
    }
12567
  }
12568
}
12569
12570
/*! \internal
12571
12572
  This function makes sure multiple axes on the side specified with \a type
12573
  don't collide, but are distributed according to their respective space
12574
  requirement (QCPAxis::calculateMargin).
12575
12576
  It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all
12577
  axes except the one with index zero.
12578
12579
  This function is called by \ref calculateAutoMargin.
12580
*/
12581
void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type) {
12582
  const QList<QCPAxis *> axesList = mAxes.value(type);
12583
  if (axesList.isEmpty()) return;
12584
12585
  bool isFirstVisible =
12586
      !axesList.first()
12587
           ->visible();  // if the first axis is visible, the second axis (which
12588
                         // is where the loop starts) isn't the first visible
12589
                         // axis, so initialize with false
12590
  for (int i = 1; i < axesList.size(); ++i) {
12591
    int offset =
12592
        axesList.at(i - 1)->offset() + axesList.at(i - 1)->calculateMargin();
12593
    if (axesList.at(i)
12594
            ->visible())  // only add inner tick length to offset if this axis
12595
                          // is visible and it's not the first visible one
12596
                          // (might happen if true first axis is invisible)
12597
    {
12598
      if (!isFirstVisible) offset += axesList.at(i)->tickLengthIn();
12599
      isFirstVisible = false;
12600
    }
12601
    axesList.at(i)->setOffset(offset);
12602
  }
12603
}
12604
12605
/* inherits documentation from base class */
12606
int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side) {
12607
  if (!mAutoMargins.testFlag(side))
12608
    qDebug() << Q_FUNC_INFO
12609
             << "Called with side that isn't specified as auto margin";
12610
12611
  updateAxesOffset(QCPAxis::marginSideToAxisType(side));
12612
12613
  // note: only need to look at the last (outer most) axis to determine the
12614
  // total margin, due to updateAxisOffset call
12615
  const QList<QCPAxis *> axesList =
12616
      mAxes.value(QCPAxis::marginSideToAxisType(side));
12617
  if (axesList.size() > 0)
12618
    return axesList.last()->offset() + axesList.last()->calculateMargin();
12619
  else
12620
    return 0;
12621
}
12622
12623
/*! \internal
12624
12625
  Event handler for when a mouse button is pressed on the axis rect. If the left
12626
  mouse button is pressed, the range dragging interaction is initialized (the
12627
  actual range manipulation happens in the \ref mouseMoveEvent).
12628
12629
  The mDragging flag is set to true and some anchor points are set that are
12630
  needed to determine the distance the mouse was dragged in the mouse
12631
  move/release events later.
12632
12633
  \see mouseMoveEvent, mouseReleaseEvent
12634
*/
12635
void QCPAxisRect::mousePressEvent(QMouseEvent *event) {
12636
  mDragStart =
12637
      event->pos();  // need this even when not LeftButton is pressed, to
12638
                     // determine in releaseEvent whether it was a full click
12639
                     // (no position change between press and release)
12640
  if (event->buttons() & Qt::LeftButton) {
12641
    mDragging = true;
12642
    // initialize antialiasing backup in case we start dragging:
12643
    if (mParentPlot->noAntialiasingOnDrag()) {
12644
      mAADragBackup = mParentPlot->antialiasedElements();
12645
      mNotAADragBackup = mParentPlot->notAntialiasedElements();
12646
    }
12647
    // Mouse range dragging interaction:
12648
    if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) {
12649
      if (mRangeDragHorzAxis)
12650
        mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12651
      if (mRangeDragVertAxis)
12652
        mDragStartVertRange = mRangeDragVertAxis.data()->range();
12653
    }
12654
  }
12655
}
12656
12657
/*! \internal
12658
12659
  Event handler for when the mouse is moved on the axis rect. If range dragging
12660
  was activated in a preceding \ref mousePressEvent, the range is moved
12661
  accordingly.
12662
12663
  \see mousePressEvent, mouseReleaseEvent
12664
*/
12665
void QCPAxisRect::mouseMoveEvent(QMouseEvent *event) {
12666
  // Mouse range dragging interaction:
12667
  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag)) {
12668
    if (mRangeDrag.testFlag(Qt::Horizontal)) {
12669
      if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data()) {
12670
        if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear) {
12671
          double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) -
12672
                        rangeDragHorzAxis->pixelToCoord(event->pos().x());
12673
          rangeDragHorzAxis->setRange(mDragStartHorzRange.lower + diff,
12674
                                      mDragStartHorzRange.upper + diff);
12675
        } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic) {
12676
          double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) /
12677
                        rangeDragHorzAxis->pixelToCoord(event->pos().x());
12678
          rangeDragHorzAxis->setRange(mDragStartHorzRange.lower * diff,
12679
                                      mDragStartHorzRange.upper * diff);
12680
        }
12681
      }
12682
    }
12683
    if (mRangeDrag.testFlag(Qt::Vertical)) {
12684
      if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data()) {
12685
        if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear) {
12686
          double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) -
12687
                        rangeDragVertAxis->pixelToCoord(event->pos().y());
12688
          rangeDragVertAxis->setRange(mDragStartVertRange.lower + diff,
12689
                                      mDragStartVertRange.upper + diff);
12690
        } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic) {
12691
          double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) /
12692
                        rangeDragVertAxis->pixelToCoord(event->pos().y());
12693
          rangeDragVertAxis->setRange(mDragStartVertRange.lower * diff,
12694
                                      mDragStartVertRange.upper * diff);
12695
        }
12696
      }
12697
    }
12698
    if (mRangeDrag !=
12699
        0)  // if either vertical or horizontal drag was enabled, do a replot
12700
    {
12701
      if (mParentPlot->noAntialiasingOnDrag())
12702
        mParentPlot->setNotAntialiasedElements(QCP::aeAll);
12703
      mParentPlot->replot();
12704
    }
12705
  }
12706
}
12707
12708
/* inherits documentation from base class */
12709
void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event) {
12710
  Q_UNUSED(event)
12711
  mDragging = false;
12712
  if (mParentPlot->noAntialiasingOnDrag()) {
12713
    mParentPlot->setAntialiasedElements(mAADragBackup);
12714
    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
12715
  }
12716
}
12717
12718
/*! \internal
12719
12720
  Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal,
12721
  Qt::Vertical or both, the ranges of the axes defined as rangeZoomHorzAxis and
12722
  rangeZoomVertAxis are scaled. The center of the scaling operation is the
12723
  current cursor position inside the axis rect. The scaling factor is dependant
12724
  on the mouse wheel delta (which direction the wheel was rotated) to provide a
12725
  natural zooming feel. The Strength of the zoom can be controlled via \ref
12726
  setRangeZoomFactor.
12727
12728
  Note, that event->delta() is usually +/-120 for single rotation steps.
12729
  However, if the mouse wheel is turned rapidly, many steps may bunch up to one
12730
  event, so the event->delta() may then be multiples of 120. This is taken into
12731
  account here, by calculating \a wheelSteps and using it as exponent of the
12732
  range zoom factor. This takes care of the wheel direction automatically, by
12733
  inverting the factor, when the wheel step is negative (f^-1 = 1/f).
12734
*/
12735
void QCPAxisRect::wheelEvent(QWheelEvent *event) {
12736
  // Mouse range zooming interaction:
12737
  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) {
12738
    if (mRangeZoom != 0) {
12739
      double factor;
12740
      double wheelSteps =
12741
          event->delta() / 120.0;  // a single step delta is +/-120 usually
12742
      if (mRangeZoom.testFlag(Qt::Horizontal)) {
12743
        factor = qPow(mRangeZoomFactorHorz, wheelSteps);
12744
        if (mRangeZoomHorzAxis.data())
12745
          mRangeZoomHorzAxis.data()->scaleRange(
12746
              factor,
12747
              mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
12748
      }
12749
      if (mRangeZoom.testFlag(Qt::Vertical)) {
12750
        factor = qPow(mRangeZoomFactorVert, wheelSteps);
12751
        if (mRangeZoomVertAxis.data())
12752
          mRangeZoomVertAxis.data()->scaleRange(
12753
              factor,
12754
              mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
12755
      }
12756
      mParentPlot->replot();
12757
    }
12758
  }
12759
}
12760
12761
////////////////////////////////////////////////////////////////////////////////////////////////////
12762
//////////////////// QCPAbstractLegendItem
12763
////////////////////////////////////////////////////////////////////////////////////////////////////
12764
12765
/*! \class QCPAbstractLegendItem
12766
  \brief The abstract base class for all entries in a QCPLegend.
12767
12768
  It defines a very basic interface for entries in a QCPLegend. For representing
12769
  plottables in the legend, the subclass \ref QCPPlottableLegendItem is more
12770
  suitable.
12771
12772
  Only derive directly from this class when you need absolute freedom (e.g. a
12773
  custom legend entry that's not even associated with a plottable).
12774
12775
  You must implement the following pure virtual functions:
12776
  \li \ref draw (from QCPLayerable)
12777
12778
  You inherit the following members you may use:
12779
  <table>
12780
    <tr>
12781
      <td>QCPLegend *\b mParentLegend</td>
12782
      <td>A pointer to the parent QCPLegend.</td>
12783
    </tr><tr>
12784
      <td>QFont \b mFont</td>
12785
      <td>The generic font of the item. You should use this font for all or at
12786
  least the most prominent text of the item.</td>
12787
    </tr>
12788
  </table>
12789
*/
12790
12791
/* start of documentation of signals */
12792
12793
/*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
12794
12795
  This signal is emitted when the selection state of this legend item has
12796
  changed, either by user interaction or by a direct call to \ref setSelected.
12797
*/
12798
12799
/* end of documentation of signals */
12800
12801
/*!
12802
  Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a
12803
  parent. This does not cause the item to be added to \a parent, so \ref
12804
  QCPLegend::addItem must be called separately.
12805
*/
12806
QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent)
12807
    : QCPLayoutElement(parent->parentPlot()),
12808
      mParentLegend(parent),
12809
      mFont(parent->font()),
12810
      mTextColor(parent->textColor()),
12811
      mSelectedFont(parent->selectedFont()),
12812
      mSelectedTextColor(parent->selectedTextColor()),
12813
      mSelectable(true),
12814
      mSelected(false) {
12815
  setLayer(QLatin1String("legend"));
12816
  setMargins(QMargins(8, 2, 8, 2));
12817
}
12818
12819
/*!
12820
  Sets the default font of this specific legend item to \a font.
12821
12822
  \see setTextColor, QCPLegend::setFont
12823
*/
12824
void QCPAbstractLegendItem::setFont(const QFont &font) { mFont = font; }
12825
12826
/*!
12827
  Sets the default text color of this specific legend item to \a color.
12828
12829
  \see setFont, QCPLegend::setTextColor
12830
*/
12831
void QCPAbstractLegendItem::setTextColor(const QColor &color) {
12832
  mTextColor = color;
12833
}
12834
12835
/*!
12836
  When this legend item is selected, \a font is used to draw generic text,
12837
  instead of the normal font set with \ref setFont.
12838
12839
  \see setFont, QCPLegend::setSelectedFont
12840
*/
12841
void QCPAbstractLegendItem::setSelectedFont(const QFont &font) {
12842
  mSelectedFont = font;
12843
}
12844
12845
/*!
12846
  When this legend item is selected, \a color is used to draw generic text,
12847
  instead of the normal color set with \ref setTextColor.
12848
12849
  \see setTextColor, QCPLegend::setSelectedTextColor
12850
*/
12851
void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color) {
12852
  mSelectedTextColor = color;
12853
}
12854
12855
/*!
12856
  Sets whether this specific legend item is selectable.
12857
12858
  \see setSelectedParts, QCustomPlot::setInteractions
12859
*/
12860
void QCPAbstractLegendItem::setSelectable(bool selectable) {
12861
  if (mSelectable != selectable) {
12862
    mSelectable = selectable;
12863
    emit selectableChanged(mSelectable);
12864
  }
12865
}
12866
12867
/*!
12868
  Sets whether this specific legend item is selected.
12869
12870
  It is possible to set the selection state of this item by calling this
12871
  function directly, even if setSelectable is set to false.
12872
12873
  \see setSelectableParts, QCustomPlot::setInteractions
12874
*/
12875
void QCPAbstractLegendItem::setSelected(bool selected) {
12876
  if (mSelected != selected) {
12877
    mSelected = selected;
12878
    emit selectionChanged(mSelected);
12879
  }
12880
}
12881
12882
/* inherits documentation from base class */
12883
double QCPAbstractLegendItem::selectTest(const QPointF &pos,
12884
                                         bool onlySelectable,
12885
                                         QVariant *details) const {
12886
  Q_UNUSED(details)
12887
  if (!mParentPlot) return -1;
12888
  if (onlySelectable &&
12889
      (!mSelectable ||
12890
       !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12891
    return -1;
12892
12893
  if (mRect.contains(pos.toPoint()))
12894
    return mParentPlot->selectionTolerance() * 0.99;
12895
  else
12896
    return -1;
12897
}
12898
12899
/* inherits documentation from base class */
12900
void QCPAbstractLegendItem::applyDefaultAntialiasingHint(
12901
    QCPPainter *painter) const {
12902
  applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
12903
}
12904
12905
/* inherits documentation from base class */
12906
QRect QCPAbstractLegendItem::clipRect() const { return mOuterRect; }
12907
12908
/* inherits documentation from base class */
12909
void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive,
12910
                                        const QVariant &details,
12911
                                        bool *selectionStateChanged) {
12912
  Q_UNUSED(event)
12913
  Q_UNUSED(details)
12914
  if (mSelectable &&
12915
      mParentLegend->selectableParts().testFlag(QCPLegend::spItems)) {
12916
    bool selBefore = mSelected;
12917
    setSelected(additive ? !mSelected : true);
12918
    if (selectionStateChanged) *selectionStateChanged = mSelected != selBefore;
12919
  }
12920
}
12921
12922
/* inherits documentation from base class */
12923
void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged) {
12924
  if (mSelectable &&
12925
      mParentLegend->selectableParts().testFlag(QCPLegend::spItems)) {
12926
    bool selBefore = mSelected;
12927
    setSelected(false);
12928
    if (selectionStateChanged) *selectionStateChanged = mSelected != selBefore;
12929
  }
12930
}
12931
12932
////////////////////////////////////////////////////////////////////////////////////////////////////
12933
//////////////////// QCPPlottableLegendItem
12934
////////////////////////////////////////////////////////////////////////////////////////////////////
12935
12936
/*! \class QCPPlottableLegendItem
12937
  \brief A legend item representing a plottable with an icon and the plottable
12938
  name.
12939
12940
  This is the standard legend item for plottables. It displays an icon of the
12941
  plottable next to the plottable name. The icon is drawn by the respective
12942
  plottable itself (\ref QCPAbstractPlottable::drawLegendIcon), and tries to
12943
  give an intuitive symbol for the plottable. For example, the QCPGraph draws a
12944
  centered horizontal line and/or a single scatter point in the middle.
12945
12946
  Legend items of this type are always associated with one plottable
12947
  (retrievable via the plottable() function and settable with the constructor).
12948
  You may change the font of the plottable name with \ref setFont. Icon padding
12949
  and border pen is taken from the parent QCPLegend, see \ref
12950
  QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
12951
12952
  The function \ref QCPAbstractPlottable::addToLegend/\ref
12953
  QCPAbstractPlottable::removeFromLegend creates/removes legend items of this
12954
  type in the default implementation. However, these functions may be
12955
  reimplemented such that a different kind of legend item (e.g a direct subclass
12956
  of QCPAbstractLegendItem) is used for that plottable.
12957
12958
  Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a
12959
  subclass of QCPLayoutElement. While it could be added to a legend (or any
12960
  other layout) via the normal layout interface, QCPLegend has specialized
12961
  functions for handling legend items conveniently, see the documentation of
12962
  \ref QCPLegend.
12963
*/
12964
12965
/*!
12966
  Creates a new legend item associated with \a plottable.
12967
12968
  Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
12969
12970
  A more convenient way of adding/removing a plottable to/from the legend is via
12971
  the functions \ref QCPAbstractPlottable::addToLegend and \ref
12972
  QCPAbstractPlottable::removeFromLegend.
12973
*/
12974
QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent,
12975
                                               QCPAbstractPlottable *plottable)
12976
    : QCPAbstractLegendItem(parent), mPlottable(plottable) {}
12977
12978
/*! \internal
12979
12980
  Returns the pen that shall be used to draw the icon border, taking into
12981
  account the selection state of this item.
12982
*/
12983
QPen QCPPlottableLegendItem::getIconBorderPen() const {
12984
  return mSelected ? mParentLegend->selectedIconBorderPen()
12985
                   : mParentLegend->iconBorderPen();
12986
}
12987
12988
/*! \internal
12989
12990
  Returns the text color that shall be used to draw text, taking into account
12991
  the selection state of this item.
12992
*/
12993
QColor QCPPlottableLegendItem::getTextColor() const {
12994
  return mSelected ? mSelectedTextColor : mTextColor;
12995
}
12996
12997
/*! \internal
12998
12999
  Returns the font that shall be used to draw text, taking into account the
13000
  selection state of this item.
13001
*/
13002
QFont QCPPlottableLegendItem::getFont() const {
13003
  return mSelected ? mSelectedFont : mFont;
13004
}
13005
13006
/*! \internal
13007
13008
  Draws the item with \a painter. The size and position of the drawn legend item
13009
  is defined by the parent layout (typically a \ref QCPLegend) and the \ref
13010
  minimumSizeHint and \ref maximumSizeHint of this legend item.
13011
*/
13012
void QCPPlottableLegendItem::draw(QCPPainter *painter) {
13013
  if (!mPlottable) return;
13014
  painter->setFont(getFont());
13015
  painter->setPen(QPen(getTextColor()));
13016
  QSizeF iconSize = mParentLegend->iconSize();
13017
  QRectF textRect = painter->fontMetrics().boundingRect(
13018
      0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
13019
  QRectF iconRect(mRect.topLeft(), iconSize);
13020
  int textHeight = qMax(
13021
      textRect.height(),
13022
      iconSize.height());  // if text has smaller height than icon, center text
13023
                           // vertically in icon height, else align tops
13024
  painter->drawText(
13025
      mRect.x() + iconSize.width() + mParentLegend->iconTextPadding(),
13026
      mRect.y(), textRect.width(), textHeight, Qt::TextDontClip,
13027
      mPlottable->name());
13028
  // draw icon:
13029
  painter->save();
13030
  painter->setClipRect(iconRect, Qt::IntersectClip);
13031
  mPlottable->drawLegendIcon(painter, iconRect);
13032
  painter->restore();
13033
  // draw icon border:
13034
  if (getIconBorderPen().style() != Qt::NoPen) {
13035
    painter->setPen(getIconBorderPen());
13036
    painter->setBrush(Qt::NoBrush);
13037
    painter->drawRect(iconRect);
13038
  }
13039
}
13040
13041
/*! \internal
13042
13043
  Calculates and returns the size of this item. This includes the icon, the text
13044
  and the padding in between.
13045
*/
13046
QSize QCPPlottableLegendItem::minimumSizeHint() const {
13047
  if (!mPlottable) return QSize();
13048
  QSize result(0, 0);
13049
  QRect textRect;
13050
  QFontMetrics fontMetrics(getFont());
13051
  QSize iconSize = mParentLegend->iconSize();
13052
  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(),
13053
                                      Qt::TextDontClip, mPlottable->name());
13054
  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() +
13055
                  textRect.width() + mMargins.left() + mMargins.right());
13056
  result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() +
13057
                   mMargins.bottom());
13058
  return result;
13059
}
13060
13061
////////////////////////////////////////////////////////////////////////////////////////////////////
13062
//////////////////// QCPLegend
13063
////////////////////////////////////////////////////////////////////////////////////////////////////
13064
13065
/*! \class QCPLegend
13066
  \brief Manages a legend inside a QCustomPlot.
13067
13068
  A legend is a small box somewhere in the plot which lists plottables with
13069
  their name and icon.
13070
13071
  Normally, the legend is populated by calling \ref
13072
  QCPAbstractPlottable::addToLegend. The respective legend item can be removed
13073
  with \ref QCPAbstractPlottable::removeFromLegend. However, QCPLegend also
13074
  offers an interface to add and manipulate legend items directly: \ref item,
13075
  \ref itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
13076
13077
  The QCPLegend derives from QCPLayoutGrid and as such can be placed in any
13078
  position a QCPLayoutElement may be positioned. The legend items are themselves
13079
  QCPLayoutElements which are placed in the grid layout of the legend. QCPLegend
13080
  only adds an interface specialized for handling child elements of type
13081
  QCPAbstractLegendItem, as mentioned above. In principle, any other layout
13082
  elements may also be added to a legend via the normal \ref QCPLayoutGrid
13083
  interface. However, the QCPAbstractLegendItem-Interface will ignore those
13084
  elements (e.g. \ref itemCount will only return the number of items with
13085
  QCPAbstractLegendItems type).
13086
13087
  By default, every QCustomPlot has one legend (QCustomPlot::legend) which is
13088
  placed in the inset layout of the main axis rect (\ref
13089
  QCPAxisRect::insetLayout). To move the legend to another position inside the
13090
  axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
13091
  outside of the axis rect, place it anywhere else with the
13092
  QCPLayout/QCPLayoutElement interface.
13093
*/
13094
13095
/* start of documentation of signals */
13096
13097
/*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
13098
13099
  This signal is emitted when the selection state of this legend has changed.
13100
13101
  \see setSelectedParts, setSelectableParts
13102
*/
13103
13104
/* end of documentation of signals */
13105
13106
/*!
13107
  Constructs a new QCPLegend instance with \a parentPlot as the containing plot
13108
  and default values.
13109
13110
  Note that by default, QCustomPlot already contains a legend ready to be used
13111
  as QCustomPlot::legend
13112
*/
13113
QCPLegend::QCPLegend() {
13114
  setRowSpacing(0);
13115
  setColumnSpacing(10);
13116
  setMargins(QMargins(2, 3, 2, 2));
13117
  setAntialiased(false);
13118
  setIconSize(32, 18);
13119
13120
  setIconTextPadding(7);
13121
13122
  setSelectableParts(spLegendBox | spItems);
13123
  setSelectedParts(spNone);
13124
13125
  setBorderPen(QPen(Qt::black));
13126
  setSelectedBorderPen(QPen(Qt::blue, 2));
13127
  setIconBorderPen(Qt::NoPen);
13128
  setSelectedIconBorderPen(QPen(Qt::blue, 2));
13129
  setBrush(Qt::white);
13130
  setSelectedBrush(Qt::white);
13131
  setTextColor(Qt::black);
13132
  setSelectedTextColor(Qt::blue);
13133
}
13134
13135
QCPLegend::~QCPLegend() {
13136
  clearItems();
13137
  if (qobject_cast<QCustomPlot *>(
13138
          mParentPlot))  // make sure this isn't called from QObject dtor when
13139
                         // QCustomPlot is already destructed (happens when the
13140
                         // legend is not in any layout and thus QObject-child
13141
                         // of QCustomPlot)
13142
    mParentPlot->legendRemoved(this);
13143
}
13144
13145
/* no doc for getter, see setSelectedParts */
13146
QCPLegend::SelectableParts QCPLegend::selectedParts() const {
13147
  // check whether any legend elements selected, if yes, add spItems to return
13148
  // value
13149
  bool hasSelectedItems = false;
13150
  for (int i = 0; i < itemCount(); ++i) {
13151
    if (item(i) && item(i)->selected()) {
13152
      hasSelectedItems = true;
13153
      break;
13154
    }
13155
  }
13156
  if (hasSelectedItems)
13157
    return mSelectedParts | spItems;
13158
  else
13159
    return mSelectedParts & ~spItems;
13160
}
13161
13162
/*!
13163
  Sets the pen, the border of the entire legend is drawn with.
13164
*/
13165
void QCPLegend::setBorderPen(const QPen &pen) { mBorderPen = pen; }
13166
13167
/*!
13168
  Sets the brush of the legend background.
13169
*/
13170
void QCPLegend::setBrush(const QBrush &brush) { mBrush = brush; }
13171
13172
/*!
13173
  Sets the default font of legend text. Legend items that draw text (e.g. the
13174
  name of a graph) will use this font by default. However, a different font can
13175
  be specified on a per-item-basis by accessing the specific legend item.
13176
13177
  This function will also set \a font on all already existing legend items.
13178
13179
  \see QCPAbstractLegendItem::setFont
13180
*/
13181
void QCPLegend::setFont(const QFont &font) {
13182
  mFont = font;
13183
  for (int i = 0; i < itemCount(); ++i) {
13184
    if (item(i)) item(i)->setFont(mFont);
13185
  }
13186
}
13187
13188
/*!
13189
  Sets the default color of legend text. Legend items that draw text (e.g. the
13190
  name of a graph) will use this color by default. However, a different colors
13191
  can be specified on a per-item-basis by accessing the specific legend item.
13192
13193
  This function will also set \a color on all already existing legend items.
13194
13195
  \see QCPAbstractLegendItem::setTextColor
13196
*/
13197
void QCPLegend::setTextColor(const QColor &color) {
13198
  mTextColor = color;
13199
  for (int i = 0; i < itemCount(); ++i) {
13200
    if (item(i)) item(i)->setTextColor(color);
13201
  }
13202
}
13203
13204
/*!
13205
  Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
13206
  representation of the graph) will use this size by default.
13207
*/
13208
void QCPLegend::setIconSize(const QSize &size) { mIconSize = size; }
13209
13210
/*! \overload
13211
 */
13212
void QCPLegend::setIconSize(int width, int height) {
13213
  mIconSize.setWidth(width);
13214
  mIconSize.setHeight(height);
13215
}
13216
13217
/*!
13218
  Sets the horizontal space in pixels between the legend icon and the text next
13219
  to it. Legend items that draw an icon (e.g. a visual representation of the
13220
  graph) and text (e.g. the name of the graph) will use this space by default.
13221
*/
13222
void QCPLegend::setIconTextPadding(int padding) { mIconTextPadding = padding; }
13223
13224
/*!
13225
  Sets the pen used to draw a border around each legend icon. Legend items that
13226
  draw an icon (e.g. a visual representation of the graph) will use this pen by
13227
  default.
13228
13229
  If no border is wanted, set this to \a Qt::NoPen.
13230
*/
13231
void QCPLegend::setIconBorderPen(const QPen &pen) { mIconBorderPen = pen; }
13232
13233
/*!
13234
  Sets whether the user can (de-)select the parts in \a selectable by clicking
13235
  on the QCustomPlot surface. (When \ref QCustomPlot::setInteractions contains
13236
  \ref QCP::iSelectLegend.)
13237
13238
  However, even when \a selectable is set to a value not allowing the selection
13239
  of a specific part, it is still possible to set the selection of this part
13240
  manually, by calling \ref setSelectedParts directly.
13241
13242
  \see SelectablePart, setSelectedParts
13243
*/
13244
void QCPLegend::setSelectableParts(const SelectableParts &selectable) {
13245
  if (mSelectableParts != selectable) {
13246
    mSelectableParts = selectable;
13247
    emit selectableChanged(mSelectableParts);
13248
  }
13249
}
13250
13251
/*!
13252
  Sets the selected state of the respective legend parts described by \ref
13253
  SelectablePart. When a part is selected, it uses a different pen/font and
13254
  brush. If some legend items are selected and \a selected doesn't contain \ref
13255
  spItems, those items become deselected.
13256
13257
  The entire selection mechanism is handled automatically when \ref
13258
  QCustomPlot::setInteractions contains iSelectLegend. You only need to call
13259
  this function when you wish to change the selection state manually.
13260
13261
  This function can change the selection state of a part even when \ref
13262
  setSelectableParts was set to a value that actually excludes the part.
13263
13264
  emits the \ref selectionChanged signal when \a selected is different from the
13265
  previous selection state.
13266
13267
  Note that it doesn't make sense to set the selected state \ref spItems here
13268
  when it wasn't set before, because there's no way to specify which exact items
13269
  to newly select. Do this by calling \ref QCPAbstractLegendItem::setSelected
13270
  directly on the legend item you wish to select.
13271
13272
  \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen,
13273
  setSelectedIconBorderPen, setSelectedBrush, setSelectedFont
13274
*/
13275
void QCPLegend::setSelectedParts(const SelectableParts &selected) {
13276
  SelectableParts newSelected = selected;
13277
  mSelectedParts = this->selectedParts();  // update mSelectedParts in case item
13278
                                           // selection changed
13279
13280
  if (mSelectedParts != newSelected) {
13281
    if (!mSelectedParts.testFlag(spItems) &&
13282
        newSelected.testFlag(
13283
            spItems))  // attempt to set spItems flag (can't do that)
13284
    {
13285
      qDebug() << Q_FUNC_INFO
13286
               << "spItems flag can not be set, it can only be unset with this "
13287
                  "function";
13288
      newSelected &= ~spItems;
13289
    }
13290
    if (mSelectedParts.testFlag(spItems) &&
13291
        !newSelected.testFlag(
13292
            spItems))  // spItems flag was unset, so clear item selection
13293
    {
13294
      for (int i = 0; i < itemCount(); ++i) {
13295
        if (item(i)) item(i)->setSelected(false);
13296
      }
13297
    }
13298
    mSelectedParts = newSelected;
13299
    emit selectionChanged(mSelectedParts);
13300
  }
13301
}
13302
13303
/*!
13304
  When the legend box is selected, this pen is used to draw the border instead
13305
  of the normal pen set via \ref setBorderPen.
13306
13307
  \see setSelectedParts, setSelectableParts, setSelectedBrush
13308
*/
13309
void QCPLegend::setSelectedBorderPen(const QPen &pen) {
13310
  mSelectedBorderPen = pen;
13311
}
13312
13313
/*!
13314
  Sets the pen legend items will use to draw their icon borders, when they are
13315
  selected.
13316
13317
  \see setSelectedParts, setSelectableParts, setSelectedFont
13318
*/
13319
void QCPLegend::setSelectedIconBorderPen(const QPen &pen) {
13320
  mSelectedIconBorderPen = pen;
13321
}
13322
13323
/*!
13324
  When the legend box is selected, this brush is used to draw the legend
13325
  background instead of the normal brush set via \ref setBrush.
13326
13327
  \see setSelectedParts, setSelectableParts, setSelectedBorderPen
13328
*/
13329
void QCPLegend::setSelectedBrush(const QBrush &brush) {
13330
  mSelectedBrush = brush;
13331
}
13332
13333
/*!
13334
  Sets the default font that is used by legend items when they are selected.
13335
13336
  This function will also set \a font on all already existing legend items.
13337
13338
  \see setFont, QCPAbstractLegendItem::setSelectedFont
13339
*/
13340
void QCPLegend::setSelectedFont(const QFont &font) {
13341
  mSelectedFont = font;
13342
  for (int i = 0; i < itemCount(); ++i) {
13343
    if (item(i)) item(i)->setSelectedFont(font);
13344
  }
13345
}
13346
13347
/*!
13348
  Sets the default text color that is used by legend items when they are
13349
  selected.
13350
13351
  This function will also set \a color on all already existing legend items.
13352
13353
  \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
13354
*/
13355
void QCPLegend::setSelectedTextColor(const QColor &color) {
13356
  mSelectedTextColor = color;
13357
  for (int i = 0; i < itemCount(); ++i) {
13358
    if (item(i)) item(i)->setSelectedTextColor(color);
13359
  }
13360
}
13361
13362
/*!
13363
  Returns the item with index \a i.
13364
13365
  \see itemCount
13366
*/
13367
QCPAbstractLegendItem *QCPLegend::item(int index) const {
13368
  return qobject_cast<QCPAbstractLegendItem *>(elementAt(index));
13369
}
13370
13371
/*!
13372
  Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g.
13373
  a \ref QCPGraph*). If such an item isn't in the legend, returns 0.
13374
13375
  \see hasItemWithPlottable
13376
*/
13377
QCPPlottableLegendItem *QCPLegend::itemWithPlottable(
13378
    const QCPAbstractPlottable *plottable) const {
13379
  for (int i = 0; i < itemCount(); ++i) {
13380
    if (QCPPlottableLegendItem *pli =
13381
            qobject_cast<QCPPlottableLegendItem *>(item(i))) {
13382
      if (pli->plottable() == plottable) return pli;
13383
    }
13384
  }
13385
  return 0;
13386
}
13387
13388
/*!
13389
  Returns the number of items currently in the legend.
13390
  \see item
13391
*/
13392
int QCPLegend::itemCount() const { return elementCount(); }
13393
13394
/*!
13395
  Returns whether the legend contains \a itm.
13396
*/
13397
bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const {
13398
  for (int i = 0; i < itemCount(); ++i) {
13399
    if (item == this->item(i)) return true;
13400
  }
13401
  return false;
13402
}
13403
13404
/*!
13405
  Returns whether the legend contains a QCPPlottableLegendItem which is
13406
  associated with \a plottable (e.g. a \ref QCPGraph*). If such an item isn't in
13407
  the legend, returns false.
13408
13409
  \see itemWithPlottable
13410
*/
13411
bool QCPLegend::hasItemWithPlottable(
13412
    const QCPAbstractPlottable *plottable) const {
13413
  return itemWithPlottable(plottable);
13414
}
13415
13416
/*!
13417
  Adds \a item to the legend, if it's not present already.
13418
13419
  Returns true on sucess, i.e. if the item wasn't in the list already and has
13420
  been successfuly added.
13421
13422
  The legend takes ownership of the item.
13423
*/
13424
bool QCPLegend::addItem(QCPAbstractLegendItem *item) {
13425
  if (!hasItem(item)) {
13426
    return addElement(rowCount(), 0, item);
13427
  } else
13428
    return false;
13429
}
13430
13431
/*!
13432
  Removes the item with index \a index from the legend.
13433
13434
  Returns true, if successful.
13435
13436
  \see itemCount, clearItems
13437
*/
13438
bool QCPLegend::removeItem(int index) {
13439
  if (QCPAbstractLegendItem *ali = item(index)) {
13440
    bool success = remove(ali);
13441
    simplify();
13442
    return success;
13443
  } else
13444
    return false;
13445
}
13446
13447
/*! \overload
13448
13449
  Removes \a item from the legend.
13450
13451
  Returns true, if successful.
13452
13453
  \see clearItems
13454
*/
13455
bool QCPLegend::removeItem(QCPAbstractLegendItem *item) {
13456
  bool success = remove(item);
13457
  simplify();
13458
  return success;
13459
}
13460
13461
/*!
13462
  Removes all items from the legend.
13463
*/
13464
void QCPLegend::clearItems() {
13465
  for (int i = itemCount() - 1; i >= 0; --i) removeItem(i);
13466
}
13467
13468
/*!
13469
  Returns the legend items that are currently selected. If no items are
13470
  selected, the list is empty.
13471
13472
  \see QCPAbstractLegendItem::setSelected, setSelectable
13473
*/
13474
QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const {
13475
  QList<QCPAbstractLegendItem *> result;
13476
  for (int i = 0; i < itemCount(); ++i) {
13477
    if (QCPAbstractLegendItem *ali = item(i)) {
13478
      if (ali->selected()) result.append(ali);
13479
    }
13480
  }
13481
  return result;
13482
}
13483
13484
/*! \internal
13485
13486
  A convenience function to easily set the QPainter::Antialiased hint on the
13487
  provided \a painter before drawing main legend elements.
13488
13489
  This is the antialiasing state the painter passed to the \ref draw method is
13490
  in by default.
13491
13492
  This function takes into account the local setting of the antialiasing flag as
13493
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
13494
  \ref QCustomPlot::setNotAntialiasedElements.
13495
13496
  \see setAntialiased
13497
*/
13498
void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const {
13499
  applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
13500
}
13501
13502
/*! \internal
13503
13504
  Returns the pen used to paint the border of the legend, taking into account
13505
  the selection state of the legend box.
13506
*/
13507
QPen QCPLegend::getBorderPen() const {
13508
  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
13509
}
13510
13511
/*! \internal
13512
13513
  Returns the brush used to paint the background of the legend, taking into
13514
  account the selection state of the legend box.
13515
*/
13516
QBrush QCPLegend::getBrush() const {
13517
  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13518
}
13519
13520
/*! \internal
13521
13522
  Draws the legend box with the provided \a painter. The individual legend items
13523
  are layerables themselves, thus are drawn independently.
13524
*/
13525
void QCPLegend::draw(QCPPainter *painter) {
13526
  // draw background rect:
13527
  painter->setBrush(getBrush());
13528
  painter->setPen(getBorderPen());
13529
  painter->drawRect(mOuterRect);
13530
}
13531
13532
/* inherits documentation from base class */
13533
double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable,
13534
                             QVariant *details) const {
13535
  if (!mParentPlot) return -1;
13536
  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox)) return -1;
13537
13538
  if (mOuterRect.contains(pos.toPoint())) {
13539
    if (details) details->setValue(spLegendBox);
13540
    return mParentPlot->selectionTolerance() * 0.99;
13541
  }
13542
  return -1;
13543
}
13544
13545
/* inherits documentation from base class */
13546
void QCPLegend::selectEvent(QMouseEvent *event, bool additive,
13547
                            const QVariant &details,
13548
                            bool *selectionStateChanged) {
13549
  Q_UNUSED(event)
13550
  mSelectedParts = selectedParts();  // in case item selection has changed
13551
  if (details.value<SelectablePart>() == spLegendBox &&
13552
      mSelectableParts.testFlag(spLegendBox)) {
13553
    SelectableParts selBefore = mSelectedParts;
13554
    setSelectedParts(
13555
        additive ? mSelectedParts ^ spLegendBox
13556
                 : mSelectedParts |
13557
                       spLegendBox);  // no need to unset spItems in !additive
13558
                                      // case, because they will be deselected
13559
                                      // by QCustomPlot (they're normal
13560
                                      // QCPLayerables with own deselectEvent)
13561
    if (selectionStateChanged)
13562
      *selectionStateChanged = mSelectedParts != selBefore;
13563
  }
13564
}
13565
13566
/* inherits documentation from base class */
13567
void QCPLegend::deselectEvent(bool *selectionStateChanged) {
13568
  mSelectedParts = selectedParts();  // in case item selection has changed
13569
  if (mSelectableParts.testFlag(spLegendBox)) {
13570
    SelectableParts selBefore = mSelectedParts;
13571
    setSelectedParts(selectedParts() & ~spLegendBox);
13572
    if (selectionStateChanged)
13573
      *selectionStateChanged = mSelectedParts != selBefore;
13574
  }
13575
}
13576
13577
/* inherits documentation from base class */
13578
QCP::Interaction QCPLegend::selectionCategory() const {
13579
  return QCP::iSelectLegend;
13580
}
13581
13582
/* inherits documentation from base class */
13583
QCP::Interaction QCPAbstractLegendItem::selectionCategory() const {
13584
  return QCP::iSelectLegend;
13585
}
13586
13587
/* inherits documentation from base class */
13588
void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) {
13589
  Q_UNUSED(parentPlot)
13590
}
13591
13592
////////////////////////////////////////////////////////////////////////////////////////////////////
13593
//////////////////// QCPPlotTitle
13594
////////////////////////////////////////////////////////////////////////////////////////////////////
13595
13596
/*! \class QCPPlotTitle
13597
  \brief A layout element displaying a plot title text
13598
13599
  The text may be specified with \ref setText, theformatting can be controlled
13600
  with \ref setFont and \ref setTextColor.
13601
13602
  A plot title can be added as follows:
13603
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpplottitle-creation
13604
13605
  Since a plot title is a common requirement, QCustomPlot offers specialized
13606
  selection signals for easy interaction with QCPPlotTitle. If a layout element
13607
  of type QCPPlotTitle is clicked, the signal \ref QCustomPlot::titleClick is
13608
  emitted. A double click emits the \ref QCustomPlot::titleDoubleClick signal.
13609
*/
13610
13611
/* start documentation of signals */
13612
13613
/*! \fn void QCPPlotTitle::selectionChanged(bool selected)
13614
13615
  This signal is emitted when the selection state has changed to \a selected,
13616
  either by user interaction or by a direct call to \ref setSelected.
13617
13618
  \see setSelected, setSelectable
13619
*/
13620
13621
/* end documentation of signals */
13622
13623
/*!
13624
  Creates a new QCPPlotTitle instance and sets default values. The initial text
13625
  is empty (\ref setText).
13626
13627
  To set the title text in the constructor, rather use \ref
13628
  QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
13629
*/
13630
QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot)
13631
    : QCPLayoutElement(parentPlot),
13632
      mFont(QFont(QLatin1String("sans serif"), 13 * 1.5, QFont::Bold)),
13633
      mTextColor(Qt::black),
13634
      mSelectedFont(QFont(QLatin1String("sans serif"), 13 * 1.6, QFont::Bold)),
13635
      mSelectedTextColor(Qt::blue),
13636
      mSelectable(false),
13637
      mSelected(false) {
13638
  if (parentPlot) {
13639
    setLayer(parentPlot->currentLayer());
13640
    mFont = QFont(parentPlot->font().family(),
13641
                  parentPlot->font().pointSize() * 1.5, QFont::Bold);
13642
    mSelectedFont = QFont(parentPlot->font().family(),
13643
                          parentPlot->font().pointSize() * 1.6, QFont::Bold);
13644
  }
13645
  setMargins(QMargins(5, 5, 5, 0));
13646
}
13647
13648
/*! \overload
13649
13650
  Creates a new QCPPlotTitle instance and sets default values. The initial text
13651
  is set to \a text.
13652
*/
13653
QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text)
13654
    : QCPLayoutElement(parentPlot),
13655
      mText(text),
13656
      mFont(QFont(parentPlot->font().family(),
13657
                  parentPlot->font().pointSize() * 1.5, QFont::Bold)),
13658
      mTextColor(Qt::black),
13659
      mSelectedFont(QFont(parentPlot->font().family(),
13660
                          parentPlot->font().pointSize() * 1.6, QFont::Bold)),
13661
      mSelectedTextColor(Qt::blue),
13662
      mSelectable(false),
13663
      mSelected(false) {
13664
  setLayer(QLatin1String("axes"));
13665
  setMargins(QMargins(5, 5, 5, 0));
13666
}
13667
13668
/*!
13669
  Sets the text that will be displayed to \a text. Multiple lines can be created
13670
  by insertion of "\n".
13671
13672
  \see setFont, setTextColor
13673
*/
13674
void QCPPlotTitle::setText(const QString &text) { mText = text; }
13675
13676
/*!
13677
  Sets the \a font of the title text.
13678
13679
  \see setTextColor, setSelectedFont
13680
*/
13681
void QCPPlotTitle::setFont(const QFont &font) { mFont = font; }
13682
13683
/*!
13684
  Sets the \a color of the title text.
13685
13686
  \see setFont, setSelectedTextColor
13687
*/
13688
void QCPPlotTitle::setTextColor(const QColor &color) { mTextColor = color; }
13689
13690
/*!
13691
  Sets the \a font of the title text that will be used if the plot title is
13692
  selected (\ref setSelected).
13693
13694
  \see setFont
13695
*/
13696
void QCPPlotTitle::setSelectedFont(const QFont &font) { mSelectedFont = font; }
13697
13698
/*!
13699
  Sets the \a color of the title text that will be used if the plot title is
13700
  selected (\ref setSelected).
13701
13702
  \see setTextColor
13703
*/
13704
void QCPPlotTitle::setSelectedTextColor(const QColor &color) {
13705
  mSelectedTextColor = color;
13706
}
13707
13708
/*!
13709
  Sets whether the user may select this plot title to \a selectable.
13710
13711
  Note that even when \a selectable is set to <tt>false</tt>, the selection
13712
  state may be changed programmatically via \ref setSelected.
13713
*/
13714
void QCPPlotTitle::setSelectable(bool selectable) {
13715
  if (mSelectable != selectable) {
13716
    mSelectable = selectable;
13717
    emit selectableChanged(mSelectable);
13718
  }
13719
}
13720
13721
/*!
13722
  Sets the selection state of this plot title to \a selected. If the selection
13723
  has changed, \ref selectionChanged is emitted.
13724
13725
  Note that this function can change the selection state independently of the
13726
  current \ref setSelectable state.
13727
*/
13728
void QCPPlotTitle::setSelected(bool selected) {
13729
  if (mSelected != selected) {
13730
    mSelected = selected;
13731
    emit selectionChanged(mSelected);
13732
  }
13733
}
13734
13735
/* inherits documentation from base class */
13736
void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const {
13737
  applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
13738
}
13739
13740
/* inherits documentation from base class */
13741
void QCPPlotTitle::draw(QCPPainter *painter) {
13742
  painter->setFont(mainFont());
13743
  painter->setPen(QPen(mainTextColor()));
13744
  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13745
}
13746
13747
/* inherits documentation from base class */
13748
QSize QCPPlotTitle::minimumSizeHint() const {
13749
  QFontMetrics metrics(mFont);
13750
  QSize result =
13751
      metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13752
  result.rwidth() += mMargins.left() + mMargins.right();
13753
  result.rheight() += mMargins.top() + mMargins.bottom();
13754
  return result;
13755
}
13756
13757
/* inherits documentation from base class */
13758
QSize QCPPlotTitle::maximumSizeHint() const {
13759
  QFontMetrics metrics(mFont);
13760
  QSize result =
13761
      metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13762
  result.rheight() += mMargins.top() + mMargins.bottom();
13763
  result.setWidth(QWIDGETSIZE_MAX);
13764
  return result;
13765
}
13766
13767
/* inherits documentation from base class */
13768
void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive,
13769
                               const QVariant &details,
13770
                               bool *selectionStateChanged) {
13771
  Q_UNUSED(event)
13772
  Q_UNUSED(details)
13773
  if (mSelectable) {
13774
    bool selBefore = mSelected;
13775
    setSelected(additive ? !mSelected : true);
13776
    if (selectionStateChanged) *selectionStateChanged = mSelected != selBefore;
13777
  }
13778
}
13779
13780
/* inherits documentation from base class */
13781
void QCPPlotTitle::deselectEvent(bool *selectionStateChanged) {
13782
  if (mSelectable) {
13783
    bool selBefore = mSelected;
13784
    setSelected(false);
13785
    if (selectionStateChanged) *selectionStateChanged = mSelected != selBefore;
13786
  }
13787
}
13788
13789
/* inherits documentation from base class */
13790
double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable,
13791
                                QVariant *details) const {
13792
  Q_UNUSED(details)
13793
  if (onlySelectable && !mSelectable) return -1;
13794
13795
  if (mTextBoundingRect.contains(pos.toPoint()))
13796
    return mParentPlot->selectionTolerance() * 0.99;
13797
  else
13798
    return -1;
13799
}
13800
13801
/*! \internal
13802
13803
  Returns the main font to be used. This is mSelectedFont if \ref setSelected is
13804
  set to <tt>true</tt>, else mFont is returned.
13805
*/
13806
QFont QCPPlotTitle::mainFont() const {
13807
  return mSelected ? mSelectedFont : mFont;
13808
}
13809
13810
/*! \internal
13811
13812
  Returns the main color to be used. This is mSelectedTextColor if \ref
13813
  setSelected is set to <tt>true</tt>, else mTextColor is returned.
13814
*/
13815
QColor QCPPlotTitle::mainTextColor() const {
13816
  return mSelected ? mSelectedTextColor : mTextColor;
13817
}
13818
13819
////////////////////////////////////////////////////////////////////////////////////////////////////
13820
//////////////////// QCPColorScale
13821
////////////////////////////////////////////////////////////////////////////////////////////////////
13822
13823
/*! \class QCPColorScale
13824
  \brief A color scale for use with color coding data such as QCPColorMap
13825
13826
  This layout element can be placed on the plot to correlate a color gradient
13827
  with data values. It is usually used in combination with one or multiple \ref
13828
  QCPColorMap "QCPColorMaps".
13829
13830
  \image html QCPColorScale.png
13831
13832
  The color scale can be either horizontal or vertical, as shown in the image
13833
  above. The orientation and the side where the numbers appear is controlled
13834
  with \ref setType.
13835
13836
  Use \ref QCPColorMap::setColorScale to connect a color map with a color scale.
13837
  Once they are connected, they share their gradient, data range and data scale
13838
  type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple
13839
  color maps may be associated with a single color scale, to make them all
13840
  synchronize these properties.
13841
13842
  To have finer control over the number display and axis behaviour, you can
13843
  directly access the \ref axis. See the documentation of QCPAxis for details
13844
  about configuring axes. For example, if you want to change the number of
13845
  automatically generated ticks, call \snippet
13846
  documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-autotickcount
13847
13848
  Placing a color scale next to the main axis rect works like with any other
13849
  layout element: \snippet documentation/doc-code-snippets/mainwindow.cpp
13850
  qcpcolorscale-creation In this case we have placed it to the right of the
13851
  default axis rect, so it wasn't necessary to call \ref setType, since \ref
13852
  QCPAxis::atRight is already the default. The text next to the color scale can
13853
  be set with \ref setLabel.
13854
13855
  For optimum appearance (like in the image above), it may be desirable to line
13856
  up the axis rect and the borders of the color scale. Use a \ref QCPMarginGroup
13857
  to achieve this: \snippet documentation/doc-code-snippets/mainwindow.cpp
13858
  qcpcolorscale-margingroup
13859
13860
  Color scales are initialized with a non-zero minimum top and bottom margin
13861
  (\ref setMinimumMargins), because vertical color scales are most common and
13862
  the minimum top/bottom margin makes sure it keeps some distance to the
13863
  top/bottom widget border. So if you change to a horizontal color scale by
13864
  setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you
13865
  might want to also change the minimum margins accordingly, e.g.
13866
  <tt>setMinimumMargins(QMargins(6, 0, 6, 0))</tt>.
13867
*/
13868
13869
/* start documentation of inline functions */
13870
13871
/*! \fn QCPAxis *QCPColorScale::axis() const
13872
13873
  Returns the internal \ref QCPAxis instance of this color scale. You can access
13874
  it to alter the appearance and behaviour of the axis. \ref QCPColorScale
13875
  duplicates some properties in its interface for convenience. Those are \ref
13876
  setDataRange (\ref QCPAxis::setRange), \ref setDataScaleType (\ref
13877
  QCPAxis::setScaleType), and the method \ref setLabel (\ref QCPAxis::setLabel).
13878
  As they each are connected, it does not matter whether you use the method on
13879
  the QCPColorScale or on its QCPAxis.
13880
13881
  If the type of the color scale is changed with \ref setType, the axis returned
13882
  by this method will change, too, to either the left, right, bottom or top
13883
  axis, depending on which type was set.
13884
*/
13885
13886
/* end documentation of signals */
13887
/* start documentation of signals */
13888
13889
/*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange);
13890
13891
  This signal is emitted when the data range changes.
13892
13893
  \see setDataRange
13894
*/
13895
13896
/*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
13897
13898
  This signal is emitted when the data scale type changes.
13899
13900
  \see setDataScaleType
13901
*/
13902
13903
/*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient);
13904
13905
  This signal is emitted when the gradient changes.
13906
13907
  \see setGradient
13908
*/
13909
13910
/* end documentation of signals */
13911
13912
/*!
13913
  Constructs a new QCPColorScale.
13914
*/
13915
QCPColorScale::QCPColorScale(QCustomPlot *parentPlot)
13916
    : QCPLayoutElement(parentPlot),
13917
      mType(QCPAxis::atTop),  // set to atTop such that
13918
                              // setType(QCPAxis::atRight) below doesn't skip
13919
                              // work because it thinks it's already atRight
13920
      mDataScaleType(QCPAxis::stLinear),
13921
      mBarWidth(20),
13922
      mAxisRect(new QCPColorScaleAxisRectPrivate(this)) {
13923
  setMinimumMargins(QMargins(
13924
      0, 6, 0, 6));  // for default right color scale types, keep some room at
13925
                     // bottom and top (important if no margin group is used)
13926
  setType(QCPAxis::atRight);
13927
  setDataRange(QCPRange(0, 6));
13928
}
13929
13930
QCPColorScale::~QCPColorScale() { delete mAxisRect; }
13931
13932
/* undocumented getter */
13933
QString QCPColorScale::label() const {
13934
  if (!mColorAxis) {
13935
    qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13936
    return QString();
13937
  }
13938
13939
  return mColorAxis.data()->label();
13940
}
13941
13942
/* undocumented getter */
13943
bool QCPColorScale::rangeDrag() const {
13944
  if (!mAxisRect) {
13945
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13946
    return false;
13947
  }
13948
13949
  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13950
         mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
13951
         mAxisRect.data()
13952
                 ->rangeDragAxis(QCPAxis::orientation(mType))
13953
                 ->orientation() == QCPAxis::orientation(mType);
13954
}
13955
13956
/* undocumented getter */
13957
bool QCPColorScale::rangeZoom() const {
13958
  if (!mAxisRect) {
13959
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13960
    return false;
13961
  }
13962
13963
  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13964
         mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13965
         mAxisRect.data()
13966
                 ->rangeZoomAxis(QCPAxis::orientation(mType))
13967
                 ->orientation() == QCPAxis::orientation(mType);
13968
}
13969
13970
/*!
13971
  Sets at which side of the color scale the axis is placed, and thus also its
13972
  orientation.
13973
13974
  Note that after setting \a type to a different value, the axis returned by
13975
  \ref axis() will be a different one. The new axis will adopt the following
13976
  properties from the previous axis: The range, scale type, log base and label.
13977
*/
13978
void QCPColorScale::setType(QCPAxis::AxisType type) {
13979
  if (!mAxisRect) {
13980
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13981
    return;
13982
  }
13983
  if (mType != type) {
13984
    mType = type;
13985
    QCPRange rangeTransfer(0, 6);
13986
    double logBaseTransfer = 10;
13987
    QString labelTransfer;
13988
    // revert some settings on old axis:
13989
    if (mColorAxis) {
13990
      rangeTransfer = mColorAxis.data()->range();
13991
      labelTransfer = mColorAxis.data()->label();
13992
      logBaseTransfer = mColorAxis.data()->scaleLogBase();
13993
      mColorAxis.data()->setLabel(QString());
13994
      disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this,
13995
                 SLOT(setDataRange(QCPRange)));
13996
      disconnect(mColorAxis.data(),
13997
                 SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this,
13998
                 SLOT(setDataScaleType(QCPAxis::ScaleType)));
13999
    }
14000
    QList<QCPAxis::AxisType> allAxisTypes =
14001
        QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight
14002
                                   << QCPAxis::atBottom << QCPAxis::atTop;
14003
    foreach (QCPAxis::AxisType atype, allAxisTypes) {
14004
      mAxisRect.data()->axis(atype)->setTicks(atype == mType);
14005
      mAxisRect.data()->axis(atype)->setTickLabels(atype == mType);
14006
    }
14007
    // set new mColorAxis pointer:
14008
    mColorAxis = mAxisRect.data()->axis(mType);
14009
    // transfer settings to new axis:
14010
    mColorAxis.data()->setRange(
14011
        rangeTransfer);  // transfer range of old axis to new one (necessary if
14012
                         // axis changes from vertical to horizontal or vice
14013
                         // versa)
14014
    mColorAxis.data()->setLabel(labelTransfer);
14015
    mColorAxis.data()->setScaleLogBase(
14016
        logBaseTransfer);  // scaleType is synchronized among axes in realtime
14017
                           // via signals (connected in QCPColorScale ctor), so
14018
                           // we only need to take care of log base here
14019
    connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this,
14020
            SLOT(setDataRange(QCPRange)));
14021
    connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
14022
            this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
14023
    mAxisRect.data()->setRangeDragAxes(
14024
        QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
14025
        QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
14026
  }
14027
}
14028
14029
/*!
14030
  Sets the range spanned by the color gradient and that is shown by the axis in
14031
  the color scale.
14032
14033
  It is equivalent to calling QCPColorMap::setDataRange on any of the connected
14034
  color maps. It is also equivalent to directly accessing the \ref axis and
14035
  setting its range with \ref QCPAxis::setRange.
14036
14037
  \see setDataScaleType, setGradient, rescaleDataRange
14038
*/
14039
void QCPColorScale::setDataRange(const QCPRange &dataRange) {
14040
  if (mDataRange.lower != dataRange.lower ||
14041
      mDataRange.upper != dataRange.upper) {
14042
    mDataRange = dataRange;
14043
    if (mColorAxis) mColorAxis.data()->setRange(mDataRange);
14044
    emit dataRangeChanged(mDataRange);
14045
  }
14046
}
14047
14048
/*!
14049
  Sets the scale type of the color scale, i.e. whether values are linearly
14050
  associated with colors or logarithmically.
14051
14052
  It is equivalent to calling QCPColorMap::setDataScaleType on any of the
14053
  connected color maps. It is also equivalent to directly accessing the \ref
14054
  axis and setting its scale type with \ref QCPAxis::setScaleType.
14055
14056
  \see setDataRange, setGradient
14057
*/
14058
void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType) {
14059
  if (mDataScaleType != scaleType) {
14060
    mDataScaleType = scaleType;
14061
    if (mColorAxis) mColorAxis.data()->setScaleType(mDataScaleType);
14062
    if (mDataScaleType == QCPAxis::stLogarithmic)
14063
      setDataRange(mDataRange.sanitizedForLogScale());
14064
    emit dataScaleTypeChanged(mDataScaleType);
14065
  }
14066
}
14067
14068
/*!
14069
  Sets the color gradient that will be used to represent data values.
14070
14071
  It is equivalent to calling QCPColorMap::setGradient on any of the connected
14072
  color maps.
14073
14074
  \see setDataRange, setDataScaleType
14075
*/
14076
void QCPColorScale::setGradient(const QCPColorGradient &gradient) {
14077
  if (mGradient != gradient) {
14078
    mGradient = gradient;
14079
    if (mAxisRect) mAxisRect.data()->mGradientImageInvalidated = true;
14080
    emit gradientChanged(mGradient);
14081
  }
14082
}
14083
14084
/*!
14085
  Sets the axis label of the color scale. This is equivalent to calling \ref
14086
  QCPAxis::setLabel on the internal \ref axis.
14087
*/
14088
void QCPColorScale::setLabel(const QString &str) {
14089
  if (!mColorAxis) {
14090
    qDebug() << Q_FUNC_INFO << "internal color axis undefined";
14091
    return;
14092
  }
14093
14094
  mColorAxis.data()->setLabel(str);
14095
}
14096
14097
/*!
14098
  Sets the width (or height, for horizontal color scales) the bar where the
14099
  gradient is displayed will have.
14100
*/
14101
void QCPColorScale::setBarWidth(int width) { mBarWidth = width; }
14102
14103
/*!
14104
  Sets whether the user can drag the data range (\ref setDataRange).
14105
14106
  Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref
14107
  QCustomPlot::setInteractions) to allow range dragging.
14108
*/
14109
void QCPColorScale::setRangeDrag(bool enabled) {
14110
  if (!mAxisRect) {
14111
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14112
    return;
14113
  }
14114
14115
  if (enabled)
14116
    mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14117
  else
14118
    mAxisRect.data()->setRangeDrag(0);
14119
}
14120
14121
/*!
14122
  Sets whether the user can zoom the data range (\ref setDataRange) by scrolling
14123
  the mouse wheel.
14124
14125
  Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref
14126
  QCustomPlot::setInteractions) to allow range dragging.
14127
*/
14128
void QCPColorScale::setRangeZoom(bool enabled) {
14129
  if (!mAxisRect) {
14130
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14131
    return;
14132
  }
14133
14134
  if (enabled)
14135
    mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14136
  else
14137
    mAxisRect.data()->setRangeZoom(0);
14138
}
14139
14140
/*!
14141
  Returns a list of all the color maps associated with this color scale.
14142
*/
14143
QList<QCPColorMap *> QCPColorScale::colorMaps() const {
14144
  QList<QCPColorMap *> result;
14145
  for (int i = 0; i < mParentPlot->plottableCount(); ++i) {
14146
    if (QCPColorMap *cm =
14147
            qobject_cast<QCPColorMap *>(mParentPlot->plottable(i)))
14148
      if (cm->colorScale() == this) result.append(cm);
14149
  }
14150
  return result;
14151
}
14152
14153
/*!
14154
  Changes the data range such that all color maps associated with this color
14155
  scale are fully mapped to the gradient in the data dimension.
14156
14157
  \see setDataRange
14158
*/
14159
void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps) {
14160
  QList<QCPColorMap *> maps = colorMaps();
14161
  QCPRange newRange;
14162
  bool haveRange = false;
14163
  int sign = 0;  // TODO: should change this to QCPAbstractPlottable::SignDomain
14164
                 // later (currently is protected, maybe move to QCP namespace)
14165
  if (mDataScaleType == QCPAxis::stLogarithmic)
14166
    sign = (mDataRange.upper < 0 ? -1 : 1);
14167
  for (int i = 0; i < maps.size(); ++i) {
14168
    if (!maps.at(i)->realVisibility() && onlyVisibleMaps) continue;
14169
    QCPRange mapRange;
14170
    if (maps.at(i)->colorScale() == this) {
14171
      bool currentFoundRange = true;
14172
      mapRange = maps.at(i)->data()->dataBounds();
14173
      if (sign == 1) {
14174
        if (mapRange.lower <= 0 && mapRange.upper > 0)
14175
          mapRange.lower = mapRange.upper * 1e-3;
14176
        else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14177
          currentFoundRange = false;
14178
      } else if (sign == -1) {
14179
        if (mapRange.upper >= 0 && mapRange.lower < 0)
14180
          mapRange.upper = mapRange.lower * 1e-3;
14181
        else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14182
          currentFoundRange = false;
14183
      }
14184
      if (currentFoundRange) {
14185
        if (!haveRange)
14186
          newRange = mapRange;
14187
        else
14188
          newRange.expand(mapRange);
14189
        haveRange = true;
14190
      }
14191
    }
14192
  }
14193
  if (haveRange) {
14194
    if (!QCPRange::validRange(
14195
            newRange))  // likely due to range being zero (plottable has only
14196
                        // constant data in this dimension), shift current range
14197
                        // to at least center the data
14198
    {
14199
      double center =
14200
          (newRange.lower + newRange.upper) *
14201
          0.5;  // upper and lower should be equal anyway, but just to make
14202
                // sure, incase validRange returned false for other reason
14203
      if (mDataScaleType == QCPAxis::stLinear) {
14204
        newRange.lower = center - mDataRange.size() / 2.0;
14205
        newRange.upper = center + mDataRange.size() / 2.0;
14206
      } else  // mScaleType == stLogarithmic
14207
      {
14208
        newRange.lower = center / qSqrt(mDataRange.upper / mDataRange.lower);
14209
        newRange.upper = center * qSqrt(mDataRange.upper / mDataRange.lower);
14210
      }
14211
    }
14212
    setDataRange(newRange);
14213
  }
14214
}
14215
14216
/* inherits documentation from base class */
14217
void QCPColorScale::update(UpdatePhase phase) {
14218
  QCPLayoutElement::update(phase);
14219
  if (!mAxisRect) {
14220
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14221
    return;
14222
  }
14223
14224
  mAxisRect.data()->update(phase);
14225
14226
  switch (phase) {
14227
    case upMargins: {
14228
      if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop) {
14229
        setMaximumSize(QWIDGETSIZE_MAX,
14230
                       mBarWidth + mAxisRect.data()->margins().top() +
14231
                           mAxisRect.data()->margins().bottom() +
14232
                           margins().top() + margins().bottom());
14233
        setMinimumSize(0, mBarWidth + mAxisRect.data()->margins().top() +
14234
                              mAxisRect.data()->margins().bottom() +
14235
                              margins().top() + margins().bottom());
14236
      } else {
14237
        setMaximumSize(mBarWidth + mAxisRect.data()->margins().left() +
14238
                           mAxisRect.data()->margins().right() +
14239
                           margins().left() + margins().right(),
14240
                       QWIDGETSIZE_MAX);
14241
        setMinimumSize(mBarWidth + mAxisRect.data()->margins().left() +
14242
                           mAxisRect.data()->margins().right() +
14243
                           margins().left() + margins().right(),
14244
                       0);
14245
      }
14246
      break;
14247
    }
14248
    case upLayout: {
14249
      mAxisRect.data()->setOuterRect(rect());
14250
      break;
14251
    }
14252
    default:
14253
      break;
14254
  }
14255
}
14256
14257
/* inherits documentation from base class */
14258
void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const {
14259
  painter->setAntialiasing(false);
14260
}
14261
14262
/* inherits documentation from base class */
14263
void QCPColorScale::mousePressEvent(QMouseEvent *event) {
14264
  if (!mAxisRect) {
14265
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14266
    return;
14267
  }
14268
  mAxisRect.data()->mousePressEvent(event);
14269
}
14270
14271
/* inherits documentation from base class */
14272
void QCPColorScale::mouseMoveEvent(QMouseEvent *event) {
14273
  if (!mAxisRect) {
14274
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14275
    return;
14276
  }
14277
  mAxisRect.data()->mouseMoveEvent(event);
14278
}
14279
14280
/* inherits documentation from base class */
14281
void QCPColorScale::mouseReleaseEvent(QMouseEvent *event) {
14282
  if (!mAxisRect) {
14283
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14284
    return;
14285
  }
14286
  mAxisRect.data()->mouseReleaseEvent(event);
14287
}
14288
14289
/* inherits documentation from base class */
14290
void QCPColorScale::wheelEvent(QWheelEvent *event) {
14291
  if (!mAxisRect) {
14292
    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14293
    return;
14294
  }
14295
  mAxisRect.data()->wheelEvent(event);
14296
}
14297
14298
////////////////////////////////////////////////////////////////////////////////////////////////////
14299
//////////////////// QCPColorScaleAxisRectPrivate
14300
////////////////////////////////////////////////////////////////////////////////////////////////////
14301
14302
/*! \class QCPColorScaleAxisRectPrivate
14303
14304
  \internal
14305
  \brief An axis rect subclass for use in a QCPColorScale
14306
14307
  This is a private class and not part of the public QCustomPlot interface.
14308
14309
  It provides the axis rect functionality for the QCPColorScale class.
14310
*/
14311
14312
/*!
14313
  Creates a new instance, as a child of \a parentColorScale.
14314
*/
14315
QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(
14316
    QCPColorScale *parentColorScale)
14317
    : QCPAxisRect(parentColorScale->parentPlot(), true),
14318
      mParentColorScale(parentColorScale),
14319
      mGradientImageInvalidated(true) {
14320
  setParentLayerable(parentColorScale);
14321
  setMinimumMargins(QMargins(0, 0, 0, 0));
14322
  QList<QCPAxis::AxisType> allAxisTypes =
14323
      QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
14324
                                 << QCPAxis::atLeft << QCPAxis::atRight;
14325
  foreach (QCPAxis::AxisType type, allAxisTypes) {
14326
    axis(type)->setVisible(true);
14327
    axis(type)->grid()->setVisible(false);
14328
    axis(type)->setPadding(0);
14329
    connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)),
14330
            this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14331
    connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)),
14332
            this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14333
  }
14334
14335
  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)),
14336
          axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14337
  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)),
14338
          axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14339
  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)),
14340
          axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14341
  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)),
14342
          axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14343
  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
14344
          axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14345
  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
14346
          axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14347
  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
14348
          axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14349
  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
14350
          axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14351
14352
  // make layer transfers of color scale transfer to axis rect and axes
14353
  // the axes must be set after axis rect, such that they appear above color
14354
  // gradient drawn by axis rect:
14355
  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer *)), this,
14356
          SLOT(setLayer(QCPLayer *)));
14357
  foreach (QCPAxis::AxisType type, allAxisTypes)
14358
    connect(parentColorScale, SIGNAL(layerChanged(QCPLayer *)), axis(type),
14359
            SLOT(setLayer(QCPLayer *)));
14360
}
14361
14362
/*! \internal
14363
  Updates the color gradient image if necessary, by calling \ref
14364
  updateGradientImage, then draws it. Then the axes are drawn by calling the
14365
  \ref QCPAxisRect::draw base class implementation.
14366
*/
14367
void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter) {
14368
  if (mGradientImageInvalidated) updateGradientImage();
14369
14370
  bool mirrorHorz = false;
14371
  bool mirrorVert = false;
14372
  if (mParentColorScale->mColorAxis) {
14373
    mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() &&
14374
                 (mParentColorScale->type() == QCPAxis::atBottom ||
14375
                  mParentColorScale->type() == QCPAxis::atTop);
14376
    mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() &&
14377
                 (mParentColorScale->type() == QCPAxis::atLeft ||
14378
                  mParentColorScale->type() == QCPAxis::atRight);
14379
  }
14380
14381
  painter->drawImage(rect().adjusted(0, -1, 0, -1),
14382
                     mGradientImage.mirrored(mirrorHorz, mirrorVert));
14383
  QCPAxisRect::draw(painter);
14384
}
14385
14386
/*! \internal
14387
14388
  Uses the current gradient of the parent \ref QCPColorScale (specified in the
14389
  constructor) to generate a gradient image. This gradient image will be used in
14390
  the \ref draw method.
14391
*/
14392
void QCPColorScaleAxisRectPrivate::updateGradientImage() {
14393
  if (rect().isEmpty()) return;
14394
14395
  int n = mParentColorScale->mGradient.levelCount();
14396
  int w, h;
14397
  QVector<double> data(n);
14398
  for (int i = 0; i < n; ++i) data[i] = i;
14399
  if (mParentColorScale->mType == QCPAxis::atBottom ||
14400
      mParentColorScale->mType == QCPAxis::atTop) {
14401
    w = n;
14402
    h = rect().height();
14403
    mGradientImage = QImage(w, h, QImage::Format_RGB32);
14404
    QVector<QRgb *> pixels;
14405
    for (int y = 0; y < h; ++y)
14406
      pixels.append(reinterpret_cast<QRgb *>(mGradientImage.scanLine(y)));
14407
    mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n - 1),
14408
                                          pixels.first(), n);
14409
    for (int y = 1; y < h; ++y)
14410
      memcpy(pixels.at(y), pixels.first(), n * sizeof(QRgb));
14411
  } else {
14412
    w = rect().width();
14413
    h = n;
14414
    mGradientImage = QImage(w, h, QImage::Format_RGB32);
14415
    for (int y = 0; y < h; ++y) {
14416
      QRgb *pixels = reinterpret_cast<QRgb *>(mGradientImage.scanLine(y));
14417
      const QRgb lineColor = mParentColorScale->mGradient.color(
14418
          data[h - 1 - y], QCPRange(0, n - 1));
14419
      for (int x = 0; x < w; ++x) pixels[x] = lineColor;
14420
    }
14421
  }
14422
  mGradientImageInvalidated = false;
14423
}
14424
14425
/*! \internal
14426
14427
  This slot is connected to the selectionChanged signals of the four axes in the
14428
  constructor. It synchronizes the selection state of the axes.
14429
*/
14430
void QCPColorScaleAxisRectPrivate::axisSelectionChanged(
14431
    QCPAxis::SelectableParts selectedParts) {
14432
  // axis bases of four axes shall always (de-)selected synchronously:
14433
  QList<QCPAxis::AxisType> allAxisTypes =
14434
      QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
14435
                                 << QCPAxis::atLeft << QCPAxis::atRight;
14436
  foreach (QCPAxis::AxisType type, allAxisTypes) {
14437
    if (QCPAxis *senderAxis = qobject_cast<QCPAxis *>(sender()))
14438
      if (senderAxis->axisType() == type) continue;
14439
14440
    if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) {
14441
      if (selectedParts.testFlag(QCPAxis::spAxis))
14442
        axis(type)->setSelectedParts(axis(type)->selectedParts() |
14443
                                     QCPAxis::spAxis);
14444
      else
14445
        axis(type)->setSelectedParts(axis(type)->selectedParts() &
14446
                                     ~QCPAxis::spAxis);
14447
    }
14448
  }
14449
}
14450
14451
/*! \internal
14452
14453
  This slot is connected to the selectableChanged signals of the four axes in
14454
  the constructor. It synchronizes the selectability of the axes.
14455
*/
14456
void QCPColorScaleAxisRectPrivate::axisSelectableChanged(
14457
    QCPAxis::SelectableParts selectableParts) {
14458
  // synchronize axis base selectability:
14459
  QList<QCPAxis::AxisType> allAxisTypes =
14460
      QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
14461
                                 << QCPAxis::atLeft << QCPAxis::atRight;
14462
  foreach (QCPAxis::AxisType type, allAxisTypes) {
14463
    if (QCPAxis *senderAxis = qobject_cast<QCPAxis *>(sender()))
14464
      if (senderAxis->axisType() == type) continue;
14465
14466
    if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) {
14467
      if (selectableParts.testFlag(QCPAxis::spAxis))
14468
        axis(type)->setSelectableParts(axis(type)->selectableParts() |
14469
                                       QCPAxis::spAxis);
14470
      else
14471
        axis(type)->setSelectableParts(axis(type)->selectableParts() &
14472
                                       ~QCPAxis::spAxis);
14473
    }
14474
  }
14475
}
14476
14477
////////////////////////////////////////////////////////////////////////////////////////////////////
14478
//////////////////// QCPData
14479
////////////////////////////////////////////////////////////////////////////////////////////////////
14480
14481
/*! \class QCPData
14482
  \brief Holds the data of one single data point for QCPGraph.
14483
14484
  The container for storing multiple data points is \ref QCPDataMap.
14485
14486
  The stored data is:
14487
  \li \a key: coordinate on the key axis of this data point
14488
  \li \a value: coordinate on the value axis of this data point
14489
  \li \a keyErrorMinus: negative error in the key dimension (for error bars)
14490
  \li \a keyErrorPlus: positive error in the key dimension (for error bars)
14491
  \li \a valueErrorMinus: negative error in the value dimension (for error bars)
14492
  \li \a valueErrorPlus: positive error in the value dimension (for error bars)
14493
14494
  \see QCPDataMap
14495
*/
14496
14497
/*!
14498
  Constructs a data point with key, value and all errors set to zero.
14499
*/
14500
QCPData::QCPData()
14501
    : key(0),
14502
      value(0),
14503
      keyErrorPlus(0),
14504
      keyErrorMinus(0),
14505
      valueErrorPlus(0),
14506
      valueErrorMinus(0) {}
14507
14508
/*!
14509
  Constructs a data point with the specified \a key and \a value. All errors are
14510
  set to zero.
14511
*/
14512
QCPData::QCPData(double key, double value)
14513
    : key(key),
14514
      value(value),
14515
      keyErrorPlus(0),
14516
      keyErrorMinus(0),
14517
      valueErrorPlus(0),
14518
      valueErrorMinus(0) {}
14519
14520
////////////////////////////////////////////////////////////////////////////////////////////////////
14521
//////////////////// QCPGraph
14522
////////////////////////////////////////////////////////////////////////////////////////////////////
14523
14524
/*! \class QCPGraph
14525
  \brief A plottable representing a graph in a plot.
14526
14527
  \image html QCPGraph.png
14528
14529
  Usually you create new graphs by calling QCustomPlot::addGraph. The resulting
14530
  instance can be accessed via QCustomPlot::graph.
14531
14532
  To plot data, assign it with the \ref setData or \ref addData functions.
14533
  Alternatively, you can also access and modify the graph's data via the \ref
14534
  data method, which returns a pointer to the internal \ref QCPDataMap.
14535
14536
  Graphs are used to display single-valued data. Single-valued means that there
14537
  should only be one data point per unique key coordinate. In other words, the
14538
  graph can't have \a loops. If you do want to plot non-single-valued curves,
14539
  rather use the QCPCurve plottable.
14540
14541
  Gaps in the graph line can be created by adding data points with NaN as value
14542
  (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in
14543
  between the two data points that shall be separated.
14544
14545
  \section appearance Changing the appearance
14546
14547
  The appearance of the graph is mainly determined by the line style, scatter
14548
  style, brush and pen of the graph (\ref setLineStyle, \ref setScatterStyle,
14549
  \ref setBrush, \ref setPen).
14550
14551
  \subsection filling Filling under or between graphs
14552
14553
  QCPGraph knows two types of fills: Normal graph fills towards the
14554
  zero-value-line parallel to the key axis of the graph, and fills between two
14555
  graphs, called channel fills. To enable a fill, just set a brush with \ref
14556
  setBrush which is neither Qt::NoBrush nor fully transparent.
14557
14558
  By default, a normal fill towards the zero-value-line will be drawn. To set up
14559
  a channel fill between this graph and another one, call \ref
14560
  setChannelFillGraph with the other graph as parameter.
14561
14562
  \see QCustomPlot::addGraph, QCustomPlot::graph
14563
*/
14564
14565
/* start of documentation of inline functions */
14566
14567
/*! \fn QCPDataMap *QCPGraph::data() const
14568
14569
  Returns a pointer to the internal data storage of type \ref QCPDataMap. You
14570
  may use it to directly manipulate the data, which may be more convenient and
14571
  faster than using the regular \ref setData or \ref addData methods, in certain
14572
  situations.
14573
*/
14574
14575
/* end of documentation of inline functions */
14576
14577
/*!
14578
  Constructs a graph which uses \a keyAxis as its key axis ("x") and \a
14579
  valueAxis as its value axis ("y"). \a keyAxis and \a valueAxis must reside in
14580
  the same QCustomPlot instance and not have the same orientation. If either of
14581
  these restrictions is violated, a corresponding message is printed to the
14582
  debug output (qDebug), the construction is not aborted, though.
14583
14584
  The constructed QCPGraph can be added to the plot with
14585
  QCustomPlot::addPlottable, QCustomPlot then takes ownership of the graph.
14586
14587
  To directly create a graph inside a plot, you can also use the simpler
14588
  QCustomPlot::addGraph function.
14589
*/
14590
QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
14591
    : QCPAbstractPlottable(keyAxis, valueAxis) {
14592
  mData = new QCPDataMap;
14593
14594
  setPen(QPen(Qt::blue, 0));
14595
  setErrorPen(QPen(Qt::black));
14596
  setBrush(Qt::NoBrush);
14597
  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14598
  setSelectedBrush(Qt::NoBrush);
14599
14600
  setLineStyle(lsLine);
14601
  setErrorType(etNone);
14602
  setErrorBarSize(6);
14603
  setErrorBarSkipSymbol(true);
14604
  setChannelFillGraph(0);
14605
  setAdaptiveSampling(true);
14606
}
14607
14608
QCPGraph::~QCPGraph() { delete mData; }
14609
14610
/*!
14611
  Replaces the current data with the provided \a data.
14612
14613
  If \a copy is set to true, data points in \a data will only be copied. if
14614
  false, the graph takes ownership of the passed data and replaces the internal
14615
  data pointer with it. This is significantly faster than copying for large
14616
  datasets.
14617
14618
  Alternatively, you can also access and modify the graph's data via the \ref
14619
  data method, which returns a pointer to the internal \ref QCPDataMap.
14620
*/
14621
void QCPGraph::setData(QCPDataMap *data, bool copy) {
14622
  if (mData == data) {
14623
    qDebug() << Q_FUNC_INFO
14624
             << "The data pointer is already in (and owned by) this plottable"
14625
             << reinterpret_cast<quintptr>(data);
14626
    return;
14627
  }
14628
  if (copy) {
14629
    *mData = *data;
14630
  } else {
14631
    delete mData;
14632
    mData = data;
14633
  }
14634
}
14635
14636
/*! \overload
14637
14638
  Replaces the current data with the provided points in \a key and \a value
14639
  pairs. The provided vectors should have equal length. Else, the number of
14640
  added points will be the size of the smallest vector.
14641
*/
14642
void QCPGraph::setData(const QVector<double> &key,
14643
                       const QVector<double> &value) {
14644
  mData->clear();
14645
  int n = key.size();
14646
  n = qMin(n, value.size());
14647
  QCPData newData;
14648
  for (int i = 0; i < n; ++i) {
14649
    newData.key = key[i];
14650
    newData.value = value[i];
14651
    mData->insertMulti(newData.key, newData);
14652
  }
14653
}
14654
14655
/*!
14656
  Replaces the current data with the provided points in \a key and \a value
14657
  pairs. Additionally the symmetrical value error of the data points are set to
14658
  the values in \a valueError. For error bars to show appropriately, see \ref
14659
  setErrorType. The provided vectors should have equal length. Else, the number
14660
  of added points will be the size of the smallest vector.
14661
14662
  For asymmetrical errors (plus different from minus), see the overloaded
14663
  version of this function.
14664
*/
14665
void QCPGraph::setDataValueError(const QVector<double> &key,
14666
                                 const QVector<double> &value,
14667
                                 const QVector<double> &valueError) {
14668
  mData->clear();
14669
  int n = key.size();
14670
  n = qMin(n, value.size());
14671
  n = qMin(n, valueError.size());
14672
  QCPData newData;
14673
  for (int i = 0; i < n; ++i) {
14674
    newData.key = key[i];
14675
    newData.value = value[i];
14676
    newData.valueErrorMinus = valueError[i];
14677
    newData.valueErrorPlus = valueError[i];
14678
    mData->insertMulti(key[i], newData);
14679
  }
14680
}
14681
14682
/*!
14683
  \overload
14684
  Replaces the current data with the provided points in \a key and \a value
14685
  pairs. Additionally the negative value error of the data points are set to the
14686
  values in \a valueErrorMinus, the positive value error to \a valueErrorPlus.
14687
  For error bars to show appropriately, see \ref setErrorType.
14688
  The provided vectors should have equal length. Else, the number of added
14689
  points will be the size of the smallest vector.
14690
*/
14691
void QCPGraph::setDataValueError(const QVector<double> &key,
14692
                                 const QVector<double> &value,
14693
                                 const QVector<double> &valueErrorMinus,
14694
                                 const QVector<double> &valueErrorPlus) {
14695
  mData->clear();
14696
  int n = key.size();
14697
  n = qMin(n, value.size());
14698
  n = qMin(n, valueErrorMinus.size());
14699
  n = qMin(n, valueErrorPlus.size());
14700
  QCPData newData;
14701
  for (int i = 0; i < n; ++i) {
14702
    newData.key = key[i];
14703
    newData.value = value[i];
14704
    newData.valueErrorMinus = valueErrorMinus[i];
14705
    newData.valueErrorPlus = valueErrorPlus[i];
14706
    mData->insertMulti(key[i], newData);
14707
  }
14708
}
14709
14710
/*!
14711
  Replaces the current data with the provided points in \a key and \a value
14712
  pairs. Additionally the symmetrical key error of the data points are set to
14713
  the values in \a keyError. For error bars to show appropriately, see \ref
14714
  setErrorType. The provided vectors should have equal length. Else, the number
14715
  of added points will be the size of the smallest vector.
14716
14717
  For asymmetrical errors (plus different from minus), see the overloaded
14718
  version of this function.
14719
*/
14720
void QCPGraph::setDataKeyError(const QVector<double> &key,
14721
                               const QVector<double> &value,
14722
                               const QVector<double> &keyError) {
14723
  mData->clear();
14724
  int n = key.size();
14725
  n = qMin(n, value.size());
14726
  n = qMin(n, keyError.size());
14727
  QCPData newData;
14728
  for (int i = 0; i < n; ++i) {
14729
    newData.key = key[i];
14730
    newData.value = value[i];
14731
    newData.keyErrorMinus = keyError[i];
14732
    newData.keyErrorPlus = keyError[i];
14733
    mData->insertMulti(key[i], newData);
14734
  }
14735
}
14736
14737
/*!
14738
  \overload
14739
  Replaces the current data with the provided points in \a key and \a value
14740
  pairs. Additionally the negative key error of the data points are set to the
14741
  values in \a keyErrorMinus, the positive key error to \a keyErrorPlus. For
14742
  error bars to show appropriately, see \ref setErrorType. The provided vectors
14743
  should have equal length. Else, the number of added points will be the size of
14744
  the smallest vector.
14745
*/
14746
void QCPGraph::setDataKeyError(const QVector<double> &key,
14747
                               const QVector<double> &value,
14748
                               const QVector<double> &keyErrorMinus,
14749
                               const QVector<double> &keyErrorPlus) {
14750
  mData->clear();
14751
  int n = key.size();
14752
  n = qMin(n, value.size());
14753
  n = qMin(n, keyErrorMinus.size());
14754
  n = qMin(n, keyErrorPlus.size());
14755
  QCPData newData;
14756
  for (int i = 0; i < n; ++i) {
14757
    newData.key = key[i];
14758
    newData.value = value[i];
14759
    newData.keyErrorMinus = keyErrorMinus[i];
14760
    newData.keyErrorPlus = keyErrorPlus[i];
14761
    mData->insertMulti(key[i], newData);
14762
  }
14763
}
14764
14765
/*!
14766
  Replaces the current data with the provided points in \a key and \a value
14767
  pairs. Additionally the symmetrical key and value errors of the data points
14768
  are set to the values in \a keyError and \a valueError. For error bars to show
14769
  appropriately, see \ref setErrorType. The provided vectors should have equal
14770
  length. Else, the number of added points will be the size of the smallest
14771
  vector.
14772
14773
  For asymmetrical errors (plus different from minus), see the overloaded
14774
  version of this function.
14775
*/
14776
void QCPGraph::setDataBothError(const QVector<double> &key,
14777
                                const QVector<double> &value,
14778
                                const QVector<double> &keyError,
14779
                                const QVector<double> &valueError) {
14780
  mData->clear();
14781
  int n = key.size();
14782
  n = qMin(n, value.size());
14783
  n = qMin(n, valueError.size());
14784
  n = qMin(n, keyError.size());
14785
  QCPData newData;
14786
  for (int i = 0; i < n; ++i) {
14787
    newData.key = key[i];
14788
    newData.value = value[i];
14789
    newData.keyErrorMinus = keyError[i];
14790
    newData.keyErrorPlus = keyError[i];
14791
    newData.valueErrorMinus = valueError[i];
14792
    newData.valueErrorPlus = valueError[i];
14793
    mData->insertMulti(key[i], newData);
14794
  }
14795
}
14796
14797
/*!
14798
  \overload
14799
  Replaces the current data with the provided points in \a key and \a value
14800
  pairs. Additionally the negative key and value errors of the data points are
14801
  set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive key
14802
  and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus.
14803
  For error bars to show appropriately, see \ref setErrorType.
14804
  The provided vectors should have equal length. Else, the number of added
14805
  points will be the size of the smallest vector.
14806
*/
14807
void QCPGraph::setDataBothError(const QVector<double> &key,
14808
                                const QVector<double> &value,
14809
                                const QVector<double> &keyErrorMinus,
14810
                                const QVector<double> &keyErrorPlus,
14811
                                const QVector<double> &valueErrorMinus,
14812
                                const QVector<double> &valueErrorPlus) {
14813
  mData->clear();
14814
  int n = key.size();
14815
  n = qMin(n, value.size());
14816
  n = qMin(n, valueErrorMinus.size());
14817
  n = qMin(n, valueErrorPlus.size());
14818
  n = qMin(n, keyErrorMinus.size());
14819
  n = qMin(n, keyErrorPlus.size());
14820
  QCPData newData;
14821
  for (int i = 0; i < n; ++i) {
14822
    newData.key = key[i];
14823
    newData.value = value[i];
14824
    newData.keyErrorMinus = keyErrorMinus[i];
14825
    newData.keyErrorPlus = keyErrorPlus[i];
14826
    newData.valueErrorMinus = valueErrorMinus[i];
14827
    newData.valueErrorPlus = valueErrorPlus[i];
14828
    mData->insertMulti(key[i], newData);
14829
  }
14830
}
14831
14832
/*!
14833
  Sets how the single data points are connected in the plot. For scatter-only
14834
  plots, set \a ls to \ref lsNone and \ref setScatterStyle to the desired
14835
  scatter style.
14836
14837
  \see setScatterStyle
14838
*/
14839
void QCPGraph::setLineStyle(LineStyle ls) { mLineStyle = ls; }
14840
14841
/*!
14842
  Sets the visual appearance of single data points in the plot. If set to \ref
14843
  QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only-plots
14844
  with appropriate line style).
14845
14846
  \see QCPScatterStyle, setLineStyle
14847
*/
14848
void QCPGraph::setScatterStyle(const QCPScatterStyle &style) {
14849
  mScatterStyle = style;
14850
}
14851
14852
/*!
14853
  Sets which kind of error bars (Key Error, Value Error or both) should be drawn
14854
  on each data point. If you set \a errorType to something other than \ref
14855
  etNone, make sure to actually pass error data via the specific setData
14856
  functions along with the data points (e.g. \ref setDataValueError, \ref
14857
  setDataKeyError, \ref setDataBothError).
14858
14859
  \see ErrorType
14860
*/
14861
void QCPGraph::setErrorType(ErrorType errorType) { mErrorType = errorType; }
14862
14863
/*!
14864
  Sets the pen with which the error bars will be drawn.
14865
  \see setErrorBarSize, setErrorType
14866
*/
14867
void QCPGraph::setErrorPen(const QPen &pen) { mErrorPen = pen; }
14868
14869
/*!
14870
  Sets the width of the handles at both ends of an error bar in pixels.
14871
*/
14872
void QCPGraph::setErrorBarSize(double size) { mErrorBarSize = size; }
14873
14874
/*!
14875
  If \a enabled is set to true, the error bar will not be drawn as a solid line
14876
  under the scatter symbol but leave some free space around the symbol.
14877
14878
  This feature uses the current scatter size (\ref QCPScatterStyle::setSize) to
14879
  determine the size of the area to leave blank. So when drawing Pixmaps as
14880
  scatter points (\ref QCPScatterStyle::ssPixmap), the scatter size must be set
14881
  manually to a value corresponding to the size of the Pixmap, if the error bars
14882
  should leave gaps to its boundaries.
14883
14884
  \ref setErrorType, setErrorBarSize, setScatterStyle
14885
*/
14886
void QCPGraph::setErrorBarSkipSymbol(bool enabled) {
14887
  mErrorBarSkipSymbol = enabled;
14888
}
14889
14890
/*!
14891
  Sets the target graph for filling the area between this graph and \a
14892
  targetGraph with the current brush (\ref setBrush).
14893
14894
  When \a targetGraph is set to 0, a normal graph fill to the zero-value-line
14895
  will be shown. To disable any filling, set the brush to Qt::NoBrush.
14896
14897
  \see setBrush
14898
*/
14899
void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph) {
14900
  // prevent setting channel target to this graph itself:
14901
  if (targetGraph == this) {
14902
    qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14903
    mChannelFillGraph = 0;
14904
    return;
14905
  }
14906
  // prevent setting channel target to a graph not in the plot:
14907
  if (targetGraph && targetGraph->mParentPlot != mParentPlot) {
14908
    qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14909
    mChannelFillGraph = 0;
14910
    return;
14911
  }
14912
14913
  mChannelFillGraph = targetGraph;
14914
}
14915
14916
/*!
14917
  Sets whether adaptive sampling shall be used when plotting this graph.
14918
  QCustomPlot's adaptive sampling technique can drastically improve the replot
14919
  performance for graphs with a larger number of points (e.g. above 10,000),
14920
  without notably changing the appearance of the graph.
14921
14922
  By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides
14923
  whether adaptive sampling shall actually be used on a per-graph basis. So
14924
  leaving adaptive sampling enabled has no disadvantage in almost all cases.
14925
14926
  \image html adaptive-sampling-line.png "A line plot of 500,000 points without
14927
  and with adaptive sampling"
14928
14929
  As can be seen, line plots experience no visual degradation from adaptive
14930
  sampling. Outliers are reproduced reliably, as well as the overall shape of
14931
  the data set. The replot time reduces dramatically though. This allows
14932
  QCustomPlot to display large amounts of data in realtime.
14933
14934
  \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points
14935
  without and with adaptive sampling"
14936
14937
  Care must be taken when using high-density scatter plots in combination with
14938
  adaptive sampling. The adaptive sampling algorithm treats scatter plots more
14939
  carefully than line plots which still gives a significant reduction of replot
14940
  times, but not quite as much as for line plots. This is because scatter plots
14941
  inherently need more data points to be preserved in order to still resemble
14942
  the original, non-adaptive-sampling plot. As shown above, the results still
14943
  aren't quite identical, as banding occurs for the outer data points. This is
14944
  in fact intentional, such that the boundaries of the data cloud stay visible
14945
  to the viewer. How strong the banding appears, depends on the point density,
14946
  i.e. the number of points in the plot.
14947
14948
  For some situations with scatter plots it might thus be desirable to manually
14949
  turn adaptive sampling off. For example, when saving the plot to disk. This
14950
  can be achieved by setting \a enabled to false before issuing a command like
14951
  \ref QCustomPlot::savePng, and setting \a enabled back to true afterwards.
14952
*/
14953
void QCPGraph::setAdaptiveSampling(bool enabled) {
14954
  mAdaptiveSampling = enabled;
14955
}
14956
14957
/*!
14958
  Adds the provided data points in \a dataMap to the current data.
14959
14960
  Alternatively, you can also access and modify the graph's data via the \ref
14961
  data method, which returns a pointer to the internal \ref QCPDataMap.
14962
14963
  \see removeData
14964
*/
14965
void QCPGraph::addData(const QCPDataMap &dataMap) { mData->unite(dataMap); }
14966
14967
/*! \overload
14968
  Adds the provided single data point in \a data to the current data.
14969
14970
  Alternatively, you can also access and modify the graph's data via the \ref
14971
  data method, which returns a pointer to the internal \ref QCPDataMap.
14972
14973
  \see removeData
14974
*/
14975
void QCPGraph::addData(const QCPData &data) {
14976
  mData->insertMulti(data.key, data);
14977
}
14978
14979
/*! \overload
14980
  Adds the provided single data point as \a key and \a value pair to the current
14981
  data.
14982
14983
  Alternatively, you can also access and modify the graph's data via the \ref
14984
  data method, which returns a pointer to the internal \ref QCPDataMap.
14985
14986
  \see removeData
14987
*/
14988
void QCPGraph::addData(double key, double value) {
14989
  QCPData newData;
14990
  newData.key = key;
14991
  newData.value = value;
14992
  mData->insertMulti(newData.key, newData);
14993
}
14994
14995
/*! \overload
14996
  Adds the provided data points as \a key and \a value pairs to the current
14997
  data.
14998
14999
  Alternatively, you can also access and modify the graph's data via the \ref
15000
  data method, which returns a pointer to the internal \ref QCPDataMap.
15001
15002
  \see removeData
15003
*/
15004
void QCPGraph::addData(const QVector<double> &keys,
15005
                       const QVector<double> &values) {
15006
  int n = qMin(keys.size(), values.size());
15007
  QCPData newData;
15008
  for (int i = 0; i < n; ++i) {
15009
    newData.key = keys[i];
15010
    newData.value = values[i];
15011
    mData->insertMulti(newData.key, newData);
15012
  }
15013
}
15014
15015
/*!
15016
  Removes all data points with keys smaller than \a key.
15017
  \see addData, clearData
15018
*/
15019
void QCPGraph::removeDataBefore(double key) {
15020
  QCPDataMap::iterator it = mData->begin();
15021
  while (it != mData->end() && it.key() < key) it = mData->erase(it);
15022
}
15023
15024
/*!
15025
  Removes all data points with keys greater than \a key.
15026
  \see addData, clearData
15027
*/
15028
void QCPGraph::removeDataAfter(double key) {
15029
  if (mData->isEmpty()) return;
15030
  QCPDataMap::iterator it = mData->upperBound(key);
15031
  while (it != mData->end()) it = mData->erase(it);
15032
}
15033
15034
/*!
15035
  Removes all data points with keys between \a fromKey and \a toKey.
15036
  if \a fromKey is greater or equal to \a toKey, the function does nothing. To
15037
  remove a single data point with known key, use \ref removeData(double key).
15038
15039
  \see addData, clearData
15040
*/
15041
void QCPGraph::removeData(double fromKey, double toKey) {
15042
  if (fromKey >= toKey || mData->isEmpty()) return;
15043
  QCPDataMap::iterator it = mData->upperBound(fromKey);
15044
  QCPDataMap::iterator itEnd = mData->upperBound(toKey);
15045
  while (it != itEnd) it = mData->erase(it);
15046
}
15047
15048
/*! \overload
15049
15050
  Removes a single data point at \a key. If the position is not known with
15051
  absolute precision, consider using \ref removeData(double fromKey, double
15052
  toKey) with a small fuzziness interval around the suspected position, depeding
15053
  on the precision with which the key is known.
15054
15055
  \see addData, clearData
15056
*/
15057
void QCPGraph::removeData(double key) { mData->remove(key); }
15058
15059
/*!
15060
  Removes all data points.
15061
  \see removeData, removeDataAfter, removeDataBefore
15062
*/
15063
void QCPGraph::clearData() { mData->clear(); }
15064
15065
/* inherits documentation from base class */
15066
double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable,
15067
                            QVariant *details) const {
15068
  Q_UNUSED(details)
15069
  if ((onlySelectable && !mSelectable) || mData->isEmpty()) return -1;
15070
  if (!mKeyAxis || !mValueAxis) {
15071
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15072
    return -1;
15073
  }
15074
15075
  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
15076
    return pointDistance(pos);
15077
  else
15078
    return -1;
15079
}
15080
15081
/*! \overload
15082
15083
  Allows to define whether error bars are taken into consideration when
15084
  determining the new axis range.
15085
15086
  \see rescaleKeyAxis, rescaleValueAxis, QCPAbstractPlottable::rescaleAxes,
15087
  QCustomPlot::rescaleAxes
15088
*/
15089
void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const {
15090
  rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15091
  rescaleValueAxis(onlyEnlarge, includeErrorBars);
15092
}
15093
15094
/*! \overload
15095
15096
  Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken
15097
  into consideration when determining the new axis range.
15098
15099
  \see rescaleAxes, QCPAbstractPlottable::rescaleKeyAxis
15100
*/
15101
void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const {
15102
  // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only
15103
  // change that getKeyRange is passed the includeErrorBars value.
15104
  if (mData->isEmpty()) return;
15105
15106
  QCPAxis *keyAxis = mKeyAxis.data();
15107
  if (!keyAxis) {
15108
    qDebug() << Q_FUNC_INFO << "invalid key axis";
15109
    return;
15110
  }
15111
15112
  SignDomain signDomain = sdBoth;
15113
  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15114
    signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15115
15116
  bool foundRange;
15117
  QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15118
15119
  if (foundRange) {
15120
    if (onlyEnlarge) {
15121
      if (keyAxis->range().lower < newRange.lower)
15122
        newRange.lower = keyAxis->range().lower;
15123
      if (keyAxis->range().upper > newRange.upper)
15124
        newRange.upper = keyAxis->range().upper;
15125
    }
15126
    keyAxis->setRange(newRange);
15127
  }
15128
}
15129
15130
/*! \overload
15131
15132
  Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken
15133
  into consideration when determining the new axis range.
15134
15135
  \see rescaleAxes, QCPAbstractPlottable::rescaleValueAxis
15136
*/
15137
void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const {
15138
  // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only
15139
  // change is that getValueRange is passed the includeErrorBars value.
15140
  if (mData->isEmpty()) return;
15141
15142
  QCPAxis *valueAxis = mValueAxis.data();
15143
  if (!valueAxis) {
15144
    qDebug() << Q_FUNC_INFO << "invalid value axis";
15145
    return;
15146
  }
15147
15148
  SignDomain signDomain = sdBoth;
15149
  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15150
    signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15151
15152
  bool foundRange;
15153
  QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15154
15155
  if (foundRange) {
15156
    if (onlyEnlarge) {
15157
      if (valueAxis->range().lower < newRange.lower)
15158
        newRange.lower = valueAxis->range().lower;
15159
      if (valueAxis->range().upper > newRange.upper)
15160
        newRange.upper = valueAxis->range().upper;
15161
    }
15162
    valueAxis->setRange(newRange);
15163
  }
15164
}
15165
15166
/* inherits documentation from base class */
15167
void QCPGraph::draw(QCPPainter *painter) {
15168
  if (!mKeyAxis || !mValueAxis) {
15169
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15170
    return;
15171
  }
15172
  if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15173
  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15174
15175
  // allocate line and (if necessary) point vectors:
15176
  QVector<QPointF> *lineData = new QVector<QPointF>;
15177
  QVector<QCPData> *scatterData = 0;
15178
  if (!mScatterStyle.isNone()) scatterData = new QVector<QCPData>;
15179
15180
  // fill vectors with data appropriate to plot style:
15181
  getPlotData(lineData, scatterData);
15182
15183
  // check data validity if flag set:
15184
#ifdef QCUSTOMPLOT_CHECK_DATA
15185
  QCPDataMap::const_iterator it;
15186
  for (it = mData->constBegin(); it != mData->constEnd(); ++it) {
15187
    if (QCP::isInvalidData(it.value().key, it.value().value) ||
15188
        QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15189
        QCP::isInvalidData(it.value().valueErrorPlus,
15190
                           it.value().valueErrorPlus))
15191
      qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid."
15192
               << "Plottable name:" << name();
15193
  }
15194
#endif
15195
15196
  // draw fill of graph:
15197
  if (mLineStyle != lsNone) drawFill(painter, lineData);
15198
15199
  // draw line:
15200
  if (mLineStyle == lsImpulse)
15201
    drawImpulsePlot(painter, lineData);
15202
  else if (mLineStyle != lsNone)
15203
    drawLinePlot(painter,
15204
                 lineData);  // also step plots can be drawn as a line plot
15205
15206
  // draw scatters:
15207
  if (scatterData) drawScatterPlot(painter, scatterData);
15208
15209
  // free allocated line and point vectors:
15210
  delete lineData;
15211
  if (scatterData) delete scatterData;
15212
}
15213
15214
/* inherits documentation from base class */
15215
void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
15216
  // draw fill:
15217
  if (mBrush.style() != Qt::NoBrush) {
15218
    applyFillAntialiasingHint(painter);
15219
    painter->fillRect(QRectF(rect.left(), rect.top() + rect.height() / 2.0,
15220
                             rect.width(), rect.height() / 3.0),
15221
                      mBrush);
15222
  }
15223
  // draw line vertically centered:
15224
  if (mLineStyle != lsNone) {
15225
    applyDefaultAntialiasingHint(painter);
15226
    painter->setPen(mPen);
15227
    painter->drawLine(QLineF(
15228
        rect.left(), rect.top() + rect.height() / 2.0, rect.right() + 5,
15229
        rect.top() + rect.height() / 2.0));  // +5 on x2 else last segment is
15230
                                             // missing from dashed/dotted pens
15231
  }
15232
  // draw scatter symbol:
15233
  if (!mScatterStyle.isNone()) {
15234
    applyScattersAntialiasingHint(painter);
15235
    // scale scatter pixmap if it's too large to fit in legend icon rect:
15236
    if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap &&
15237
        (mScatterStyle.pixmap().size().width() > rect.width() ||
15238
         mScatterStyle.pixmap().size().height() > rect.height())) {
15239
      QCPScatterStyle scaledStyle(mScatterStyle);
15240
      scaledStyle.setPixmap(scaledStyle.pixmap().scaled(
15241
          rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15242
      scaledStyle.applyTo(painter, mPen);
15243
      scaledStyle.drawShape(painter, QRectF(rect).center());
15244
    } else {
15245
      mScatterStyle.applyTo(painter, mPen);
15246
      mScatterStyle.drawShape(painter, QRectF(rect).center());
15247
    }
15248
  }
15249
}
15250
15251
/*! \internal
15252
15253
  This function branches out to the line style specific "get(...)PlotData"
15254
  functions, according to the line style of the graph.
15255
15256
  \a lineData will be filled with raw points that will be drawn with the
15257
  according draw functions, e.g. \ref drawLinePlot and \ref drawImpulsePlot.
15258
  These aren't necessarily the original data points, since for step plots for
15259
  example, additional points are needed for drawing lines that make up steps. If
15260
  the line style of the graph is \ref lsNone, the \a lineData vector will be
15261
  left untouched.
15262
15263
  \a scatterData will be filled with the original data points so \ref
15264
  drawScatterPlot can draw the scatter symbols accordingly. If no scatters need
15265
  to be drawn, i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone,
15266
  pass 0 as \a scatterData, and this step will be skipped.
15267
15268
  \see getScatterPlotData, getLinePlotData, getStepLeftPlotData,
15269
  getStepRightPlotData, getStepCenterPlotData, getImpulsePlotData
15270
*/
15271
void QCPGraph::getPlotData(QVector<QPointF> *lineData,
15272
                           QVector<QCPData> *scatterData) const {
15273
  switch (mLineStyle) {
15274
    case lsNone:
15275
      getScatterPlotData(scatterData);
15276
      break;
15277
    case lsLine:
15278
      getLinePlotData(lineData, scatterData);
15279
      break;
15280
    case lsStepLeft:
15281
      getStepLeftPlotData(lineData, scatterData);
15282
      break;
15283
    case lsStepRight:
15284
      getStepRightPlotData(lineData, scatterData);
15285
      break;
15286
    case lsStepCenter:
15287
      getStepCenterPlotData(lineData, scatterData);
15288
      break;
15289
    case lsImpulse:
15290
      getImpulsePlotData(lineData, scatterData);
15291
      break;
15292
  }
15293
}
15294
15295
/*! \internal
15296
15297
  If line style is \ref lsNone and the scatter style's shape is not \ref
15298
  QCPScatterStyle::ssNone, this function serves at providing the visible data
15299
  points in \a scatterData, so the \ref drawScatterPlot function can draw the
15300
  scatter points accordingly.
15301
15302
  If line style is not \ref lsNone, this function is not called and the data for
15303
  the scatter points are (if needed) calculated inside the corresponding other
15304
  "get(...)PlotData" functions.
15305
15306
  \see drawScatterPlot
15307
*/
15308
void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const {
15309
  getPreparedData(0, scatterData);
15310
}
15311
15312
/*! \internal
15313
15314
  Places the raw data points needed for a normal linearly connected graph in \a
15315
  linePixelData.
15316
15317
  As for all plot data retrieval functions, \a scatterData just contains all
15318
  unaltered data (scatter) points that are visible for drawing scatter points,
15319
  if necessary. If drawing scatter points is disabled (i.e. the scatter style's
15320
  shape is \ref QCPScatterStyle::ssNone), pass 0 as \a scatterData, and the
15321
  function will skip filling the vector.
15322
15323
  \see drawLinePlot
15324
*/
15325
void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData,
15326
                               QVector<QCPData> *scatterData) const {
15327
  QCPAxis *keyAxis = mKeyAxis.data();
15328
  QCPAxis *valueAxis = mValueAxis.data();
15329
  if (!keyAxis || !valueAxis) {
15330
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15331
    return;
15332
  }
15333
  if (!linePixelData) {
15334
    qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData";
15335
    return;
15336
  }
15337
15338
  QVector<QCPData> lineData;
15339
  getPreparedData(&lineData, scatterData);
15340
  linePixelData->reserve(lineData.size() +
15341
                         2);  // added 2 to reserve memory for lower/upper fill
15342
                              // base points that might be needed for fill
15343
  linePixelData->resize(lineData.size());
15344
15345
  // transform lineData points to pixels:
15346
  if (keyAxis->orientation() == Qt::Vertical) {
15347
    for (int i = 0; i < lineData.size(); ++i) {
15348
      (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15349
      (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15350
    }
15351
  } else  // key axis is horizontal
15352
  {
15353
    for (int i = 0; i < lineData.size(); ++i) {
15354
      (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15355
      (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15356
    }
15357
  }
15358
}
15359
15360
/*!
15361
  \internal
15362
  Places the raw data points needed for a step plot with left oriented steps in
15363
  \a lineData.
15364
15365
  As for all plot data retrieval functions, \a scatterData just contains all
15366
  unaltered data (scatter) points that are visible for drawing scatter points,
15367
  if necessary. If drawing scatter points is disabled (i.e. the scatter style's
15368
  shape is \ref QCPScatterStyle::ssNone), pass 0 as \a scatterData, and the
15369
  function will skip filling the vector.
15370
15371
  \see drawLinePlot
15372
*/
15373
void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData,
15374
                                   QVector<QCPData> *scatterData) const {
15375
  QCPAxis *keyAxis = mKeyAxis.data();
15376
  QCPAxis *valueAxis = mValueAxis.data();
15377
  if (!keyAxis || !valueAxis) {
15378
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15379
    return;
15380
  }
15381
  if (!linePixelData) {
15382
    qDebug() << Q_FUNC_INFO << "null pointer passed as lineData";
15383
    return;
15384
  }
15385
15386
  QVector<QCPData> lineData;
15387
  getPreparedData(&lineData, scatterData);
15388
  linePixelData->reserve(lineData.size() * 2 +
15389
                         2);  // added 2 to reserve memory for lower/upper fill
15390
                              // base points that might be needed for fill
15391
  linePixelData->resize(lineData.size() * 2);
15392
15393
  // calculate steps from lineData and transform to pixel coordinates:
15394
  if (keyAxis->orientation() == Qt::Vertical) {
15395
    double lastValue = valueAxis->coordToPixel(lineData.first().value);
15396
    double key;
15397
    for (int i = 0; i < lineData.size(); ++i) {
15398
      key = keyAxis->coordToPixel(lineData.at(i).key);
15399
      (*linePixelData)[i * 2 + 0].setX(lastValue);
15400
      (*linePixelData)[i * 2 + 0].setY(key);
15401
      lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15402
      (*linePixelData)[i * 2 + 1].setX(lastValue);
15403
      (*linePixelData)[i * 2 + 1].setY(key);
15404
    }
15405
  } else  // key axis is horizontal
15406
  {
15407
    double lastValue = valueAxis->coordToPixel(lineData.first().value);
15408
    double key;
15409
    for (int i = 0; i < lineData.size(); ++i) {
15410
      key = keyAxis->coordToPixel(lineData.at(i).key);
15411
      (*linePixelData)[i * 2 + 0].setX(key);
15412
      (*linePixelData)[i * 2 + 0].setY(lastValue);
15413
      lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15414
      (*linePixelData)[i * 2 + 1].setX(key);
15415
      (*linePixelData)[i * 2 + 1].setY(lastValue);
15416
    }
15417
  }
15418
}
15419
15420
/*!
15421
  \internal
15422
  Places the raw data points needed for a step plot with right oriented steps in
15423
  \a lineData.
15424
15425
  As for all plot data retrieval functions, \a scatterData just contains all
15426
  unaltered data (scatter) points that are visible for drawing scatter points,
15427
  if necessary. If drawing scatter points is disabled (i.e. the scatter style's
15428
  shape is \ref QCPScatterStyle::ssNone), pass 0 as \a scatterData, and the
15429
  function will skip filling the vector.
15430
15431
  \see drawLinePlot
15432
*/
15433
void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData,
15434
                                    QVector<QCPData> *scatterData) const {
15435
  QCPAxis *keyAxis = mKeyAxis.data();
15436
  QCPAxis *valueAxis = mValueAxis.data();
15437
  if (!keyAxis || !valueAxis) {
15438
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15439
    return;
15440
  }
15441
  if (!linePixelData) {
15442
    qDebug() << Q_FUNC_INFO << "null pointer passed as lineData";
15443
    return;
15444
  }
15445
15446
  QVector<QCPData> lineData;
15447
  getPreparedData(&lineData, scatterData);
15448
  linePixelData->reserve(lineData.size() * 2 +
15449
                         2);  // added 2 to reserve memory for lower/upper fill
15450
                              // base points that might be needed for fill
15451
  linePixelData->resize(lineData.size() * 2);
15452
15453
  // calculate steps from lineData and transform to pixel coordinates:
15454
  if (keyAxis->orientation() == Qt::Vertical) {
15455
    double lastKey = keyAxis->coordToPixel(lineData.first().key);
15456
    double value;
15457
    for (int i = 0; i < lineData.size(); ++i) {
15458
      value = valueAxis->coordToPixel(lineData.at(i).value);
15459
      (*linePixelData)[i * 2 + 0].setX(value);
15460
      (*linePixelData)[i * 2 + 0].setY(lastKey);
15461
      lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15462
      (*linePixelData)[i * 2 + 1].setX(value);
15463
      (*linePixelData)[i * 2 + 1].setY(lastKey);
15464
    }
15465
  } else  // key axis is horizontal
15466
  {
15467
    double lastKey = keyAxis->coordToPixel(lineData.first().key);
15468
    double value;
15469
    for (int i = 0; i < lineData.size(); ++i) {
15470
      value = valueAxis->coordToPixel(lineData.at(i).value);
15471
      (*linePixelData)[i * 2 + 0].setX(lastKey);
15472
      (*linePixelData)[i * 2 + 0].setY(value);
15473
      lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15474
      (*linePixelData)[i * 2 + 1].setX(lastKey);
15475
      (*linePixelData)[i * 2 + 1].setY(value);
15476
    }
15477
  }
15478
}
15479
15480
/*!
15481
  \internal
15482
  Places the raw data points needed for a step plot with centered steps in \a
15483
  lineData.
15484
15485
  As for all plot data retrieval functions, \a scatterData just contains all
15486
  unaltered data (scatter) points that are visible for drawing scatter points,
15487
  if necessary. If drawing scatter points is disabled (i.e. the scatter style's
15488
  shape is \ref QCPScatterStyle::ssNone), pass 0 as \a scatterData, and the
15489
  function will skip filling the vector.
15490
15491
  \see drawLinePlot
15492
*/
15493
void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData,
15494
                                     QVector<QCPData> *scatterData) const {
15495
  QCPAxis *keyAxis = mKeyAxis.data();
15496
  QCPAxis *valueAxis = mValueAxis.data();
15497
  if (!keyAxis || !valueAxis) {
15498
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15499
    return;
15500
  }
15501
  if (!linePixelData) {
15502
    qDebug() << Q_FUNC_INFO << "null pointer passed as lineData";
15503
    return;
15504
  }
15505
15506
  QVector<QCPData> lineData;
15507
  getPreparedData(&lineData, scatterData);
15508
  linePixelData->reserve(lineData.size() * 2 +
15509
                         2);  // added 2 to reserve memory for lower/upper fill
15510
                              // base points that might be needed for fill
15511
  linePixelData->resize(lineData.size() * 2);
15512
  // calculate steps from lineData and transform to pixel coordinates:
15513
  if (keyAxis->orientation() == Qt::Vertical) {
15514
    double lastKey = keyAxis->coordToPixel(lineData.first().key);
15515
    double lastValue = valueAxis->coordToPixel(lineData.first().value);
15516
    double key;
15517
    (*linePixelData)[0].setX(lastValue);
15518
    (*linePixelData)[0].setY(lastKey);
15519
    for (int i = 1; i < lineData.size(); ++i) {
15520
      key = (keyAxis->coordToPixel(lineData.at(i).key) + lastKey) * 0.5;
15521
      (*linePixelData)[i * 2 - 1].setX(lastValue);
15522
      (*linePixelData)[i * 2 - 1].setY(key);
15523
      lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15524
      lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15525
      (*linePixelData)[i * 2 + 0].setX(lastValue);
15526
      (*linePixelData)[i * 2 + 0].setY(key);
15527
    }
15528
    (*linePixelData)[lineData.size() * 2 - 1].setX(lastValue);
15529
    (*linePixelData)[lineData.size() * 2 - 1].setY(lastKey);
15530
  } else  // key axis is horizontal
15531
  {
15532
    double lastKey = keyAxis->coordToPixel(lineData.first().key);
15533
    double lastValue = valueAxis->coordToPixel(lineData.first().value);
15534
    double key;
15535
    (*linePixelData)[0].setX(lastKey);
15536
    (*linePixelData)[0].setY(lastValue);
15537
    for (int i = 1; i < lineData.size(); ++i) {
15538
      key = (keyAxis->coordToPixel(lineData.at(i).key) + lastKey) * 0.5;
15539
      (*linePixelData)[i * 2 - 1].setX(key);
15540
      (*linePixelData)[i * 2 - 1].setY(lastValue);
15541
      lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15542
      lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15543
      (*linePixelData)[i * 2 + 0].setX(key);
15544
      (*linePixelData)[i * 2 + 0].setY(lastValue);
15545
    }
15546
    (*linePixelData)[lineData.size() * 2 - 1].setX(lastKey);
15547
    (*linePixelData)[lineData.size() * 2 - 1].setY(lastValue);
15548
  }
15549
}
15550
15551
/*!
15552
  \internal
15553
  Places the raw data points needed for an impulse plot in \a lineData.
15554
15555
  As for all plot data retrieval functions, \a scatterData just contains all
15556
  unaltered data (scatter) points that are visible for drawing scatter points,
15557
  if necessary. If drawing scatter points is disabled (i.e. the scatter style's
15558
  shape is \ref QCPScatterStyle::ssNone), pass 0 as \a scatterData, and the
15559
  function will skip filling the vector.
15560
15561
  \see drawImpulsePlot
15562
*/
15563
void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData,
15564
                                  QVector<QCPData> *scatterData) const {
15565
  QCPAxis *keyAxis = mKeyAxis.data();
15566
  QCPAxis *valueAxis = mValueAxis.data();
15567
  if (!keyAxis || !valueAxis) {
15568
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15569
    return;
15570
  }
15571
  if (!linePixelData) {
15572
    qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData";
15573
    return;
15574
  }
15575
15576
  QVector<QCPData> lineData;
15577
  getPreparedData(&lineData, scatterData);
15578
  linePixelData->resize(
15579
      lineData.size() *
15580
      2);  // no need to reserve 2 extra points because impulse plot has no fill
15581
15582
  // transform lineData points to pixels:
15583
  if (keyAxis->orientation() == Qt::Vertical) {
15584
    double zeroPointX = valueAxis->coordToPixel(0);
15585
    double key;
15586
    for (int i = 0; i < lineData.size(); ++i) {
15587
      key = keyAxis->coordToPixel(lineData.at(i).key);
15588
      (*linePixelData)[i * 2 + 0].setX(zeroPointX);
15589
      (*linePixelData)[i * 2 + 0].setY(key);
15590
      (*linePixelData)[i * 2 + 1].setX(
15591
          valueAxis->coordToPixel(lineData.at(i).value));
15592
      (*linePixelData)[i * 2 + 1].setY(key);
15593
    }
15594
  } else  // key axis is horizontal
15595
  {
15596
    double zeroPointY = valueAxis->coordToPixel(0);
15597
    double key;
15598
    for (int i = 0; i < lineData.size(); ++i) {
15599
      key = keyAxis->coordToPixel(lineData.at(i).key);
15600
      (*linePixelData)[i * 2 + 0].setX(key);
15601
      (*linePixelData)[i * 2 + 0].setY(zeroPointY);
15602
      (*linePixelData)[i * 2 + 1].setX(key);
15603
      (*linePixelData)[i * 2 + 1].setY(
15604
          valueAxis->coordToPixel(lineData.at(i).value));
15605
    }
15606
  }
15607
}
15608
15609
/*! \internal
15610
15611
  Draws the fill of the graph with the specified brush.
15612
15613
  If the fill is a normal fill towards the zero-value-line, only the \a lineData
15614
  is required (and two extra points at the zero-value-line, which are added by
15615
  \ref addFillBasePoints and removed by \ref removeFillBasePoints after the fill
15616
  drawing is done).
15617
15618
  If the fill is a channel fill between this QCPGraph and another QCPGraph
15619
  (mChannelFillGraph), the more complex polygon is calculated with the \ref
15620
  getChannelFillPolygon function.
15621
15622
  \see drawLinePlot
15623
*/
15624
void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const {
15625
  if (mLineStyle == lsImpulse)
15626
    return;  // fill doesn't make sense for impulse plot
15627
  if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0)
15628
    return;
15629
15630
  applyFillAntialiasingHint(painter);
15631
  if (!mChannelFillGraph) {
15632
    // draw base fill under graph, fill goes all the way to the zero-value-line:
15633
    addFillBasePoints(lineData);
15634
    painter->setPen(Qt::NoPen);
15635
    painter->setBrush(mainBrush());
15636
    painter->drawPolygon(QPolygonF(*lineData));
15637
    removeFillBasePoints(lineData);
15638
  } else {
15639
    // draw channel fill between this graph and mChannelFillGraph:
15640
    painter->setPen(Qt::NoPen);
15641
    painter->setBrush(mainBrush());
15642
    painter->drawPolygon(getChannelFillPolygon(lineData));
15643
  }
15644
}
15645
15646
/*! \internal
15647
15648
  Draws scatter symbols at every data point passed in \a scatterData. scatter
15649
  symbols are independent of the line style and are always drawn if the scatter
15650
  style's shape is not \ref QCPScatterStyle::ssNone. Hence, the \a scatterData
15651
  vector is outputted by all "get(...)PlotData" functions, together with the
15652
  (line style dependent) line data.
15653
15654
  \see drawLinePlot, drawImpulsePlot
15655
*/
15656
void QCPGraph::drawScatterPlot(QCPPainter *painter,
15657
                               QVector<QCPData> *scatterData) const {
15658
  QCPAxis *keyAxis = mKeyAxis.data();
15659
  QCPAxis *valueAxis = mValueAxis.data();
15660
  if (!keyAxis || !valueAxis) {
15661
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15662
    return;
15663
  }
15664
15665
  // draw error bars:
15666
  if (mErrorType != etNone) {
15667
    applyErrorBarsAntialiasingHint(painter);
15668
    painter->setPen(mErrorPen);
15669
    if (keyAxis->orientation() == Qt::Vertical) {
15670
      for (int i = 0; i < scatterData->size(); ++i)
15671
        drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value),
15672
                  keyAxis->coordToPixel(scatterData->at(i).key),
15673
                  scatterData->at(i));
15674
    } else {
15675
      for (int i = 0; i < scatterData->size(); ++i)
15676
        drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key),
15677
                  valueAxis->coordToPixel(scatterData->at(i).value),
15678
                  scatterData->at(i));
15679
    }
15680
  }
15681
15682
  // draw scatter point symbols:
15683
  applyScattersAntialiasingHint(painter);
15684
  mScatterStyle.applyTo(painter, mPen);
15685
  if (keyAxis->orientation() == Qt::Vertical) {
15686
    for (int i = 0; i < scatterData->size(); ++i)
15687
      if (!qIsNaN(scatterData->at(i).value))
15688
        mScatterStyle.drawShape(
15689
            painter, valueAxis->coordToPixel(scatterData->at(i).value),
15690
            keyAxis->coordToPixel(scatterData->at(i).key));
15691
  } else {
15692
    for (int i = 0; i < scatterData->size(); ++i)
15693
      if (!qIsNaN(scatterData->at(i).value))
15694
        mScatterStyle.drawShape(
15695
            painter, keyAxis->coordToPixel(scatterData->at(i).key),
15696
            valueAxis->coordToPixel(scatterData->at(i).value));
15697
  }
15698
}
15699
15700
/*!  \internal
15701
15702
  Draws line graphs from the provided data. It connects all points in \a
15703
  lineData, which was created by one of the "get(...)PlotData" functions for
15704
  line styles that require simple line connections between the point vector they
15705
  create. These are for example \ref getLinePlotData, \ref getStepLeftPlotData,
15706
  \ref getStepRightPlotData and \ref getStepCenterPlotData.
15707
15708
  \see drawScatterPlot, drawImpulsePlot
15709
*/
15710
void QCPGraph::drawLinePlot(QCPPainter *painter,
15711
                            QVector<QPointF> *lineData) const {
15712
  // draw line of graph:
15713
  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) {
15714
    applyDefaultAntialiasingHint(painter);
15715
    painter->setPen(mainPen());
15716
    painter->setBrush(Qt::NoBrush);
15717
15718
    /* Draws polyline in batches, currently not used:
15719
    int p = 0;
15720
    while (p < lineData->size())
15721
    {
15722
      int batch = qMin(25, lineData->size()-p);
15723
      if (p != 0)
15724
      {
15725
        ++batch;
15726
        --p; // to draw the connection lines between two batches
15727
      }
15728
      painter->drawPolyline(lineData->constData()+p, batch);
15729
      p += batch;
15730
    }
15731
    */
15732
15733
    // if drawing solid line and not in PDF, use much faster line drawing
15734
    // instead of polyline:
15735
    if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
15736
        painter->pen().style() == Qt::SolidLine &&
15737
        !painter->modes().testFlag(QCPPainter::pmVectorized) &&
15738
        !painter->modes().testFlag(QCPPainter::pmNoCaching)) {
15739
      int i = 0;
15740
      bool lastIsNan = false;
15741
      const int lineDataSize = lineData->size();
15742
      while (i < lineDataSize &&
15743
             (qIsNaN(lineData->at(i).y()) ||
15744
              qIsNaN(lineData->at(i).x())))  // make sure first point is not NaN
15745
        ++i;
15746
      ++i;  // because drawing works in 1 point retrospect
15747
      while (i < lineDataSize) {
15748
        if (!qIsNaN(lineData->at(i).y()) &&
15749
            !qIsNaN(lineData->at(i).x()))  // NaNs create a gap in the line
15750
        {
15751
          if (!lastIsNan)
15752
            painter->drawLine(lineData->at(i - 1), lineData->at(i));
15753
          else
15754
            lastIsNan = false;
15755
        } else
15756
          lastIsNan = true;
15757
        ++i;
15758
      }
15759
    } else {
15760
      int segmentStart = 0;
15761
      int i = 0;
15762
      const int lineDataSize = lineData->size();
15763
      while (i < lineDataSize) {
15764
        if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()) ||
15765
            qIsInf(lineData->at(i)
15766
                       .y()))  // NaNs create a gap in the line. Also filter
15767
                               // Infs which make drawPolyline block
15768
        {
15769
          painter->drawPolyline(
15770
              lineData->constData() + segmentStart,
15771
              i - segmentStart);  // i, because we don't want to include the
15772
                                  // current NaN point
15773
          segmentStart = i + 1;
15774
        }
15775
        ++i;
15776
      }
15777
      // draw last segment:
15778
      painter->drawPolyline(lineData->constData() + segmentStart,
15779
                            lineDataSize - segmentStart);
15780
    }
15781
  }
15782
}
15783
15784
/*! \internal
15785
15786
  Draws impulses from the provided data, i.e. it connects all line pairs in \a
15787
  lineData, which was created by \ref getImpulsePlotData.
15788
15789
  \see drawScatterPlot, drawLinePlot
15790
*/
15791
void QCPGraph::drawImpulsePlot(QCPPainter *painter,
15792
                               QVector<QPointF> *lineData) const {
15793
  // draw impulses:
15794
  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) {
15795
    applyDefaultAntialiasingHint(painter);
15796
    QPen pen = mainPen();
15797
    pen.setCapStyle(
15798
        Qt::FlatCap);  // so impulse line doesn't reach beyond zero-line
15799
    painter->setPen(pen);
15800
    painter->setBrush(Qt::NoBrush);
15801
    painter->drawLines(*lineData);
15802
  }
15803
}
15804
15805
/*! \internal
15806
15807
  Returns the \a lineData and \a scatterData that need to be plotted for this
15808
  graph taking into consideration the current axis ranges and, if \ref
15809
  setAdaptiveSampling is enabled, local point densities.
15810
15811
  0 may be passed as \a lineData or \a scatterData to indicate that the
15812
  respective dataset isn't needed. For example, if the scatter style (\ref
15813
  setScatterStyle) is \ref QCPScatterStyle::ssNone, \a scatterData should be 0
15814
  to prevent unnecessary calculations.
15815
15816
  This method is used by the various "get(...)PlotData" methods to get the basic
15817
  working set of data.
15818
*/
15819
void QCPGraph::getPreparedData(QVector<QCPData> *lineData,
15820
                               QVector<QCPData> *scatterData) const {
15821
  QCPAxis *keyAxis = mKeyAxis.data();
15822
  QCPAxis *valueAxis = mValueAxis.data();
15823
  if (!keyAxis || !valueAxis) {
15824
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15825
    return;
15826
  }
15827
  // get visible data range:
15828
  QCPDataMap::const_iterator lower,
15829
      upper;  // note that upper is the actual upper point, and not 1 step after
15830
              // the upper point
15831
  getVisibleDataBounds(lower, upper);
15832
  if (lower == mData->constEnd() || upper == mData->constEnd()) return;
15833
15834
  // count points in visible range, taking into account that we only need to
15835
  // count to the limit maxCount if using adaptive sampling:
15836
  int maxCount = std::numeric_limits<int>::max();
15837
  if (mAdaptiveSampling) {
15838
    int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key()) -
15839
                            keyAxis->coordToPixel(upper.key()));
15840
    maxCount = 2 * keyPixelSpan + 2;
15841
  }
15842
  int dataCount = countDataInBounds(lower, upper, maxCount);
15843
15844
  if (mAdaptiveSampling &&
15845
      dataCount >= maxCount)  // use adaptive sampling only if there are at
15846
                              // least two points per pixel on average
15847
  {
15848
    if (lineData) {
15849
      QCPDataMap::const_iterator it = lower;
15850
      QCPDataMap::const_iterator upperEnd = upper + 1;
15851
      double minValue = it.value().value;
15852
      double maxValue = it.value().value;
15853
      QCPDataMap::const_iterator currentIntervalFirstPoint = it;
15854
      int reversedFactor =
15855
          keyAxis->rangeReversed() != (keyAxis->orientation() == Qt::Vertical)
15856
              ? -1
15857
              : 1;  // is used to calculate keyEpsilon pixel into the correct
15858
                    // direction
15859
      int reversedRound =
15860
          keyAxis->rangeReversed() != (keyAxis->orientation() == Qt::Vertical)
15861
              ? 1
15862
              : 0;  // is used to switch between floor (normal) and ceil
15863
                    // (reversed) rounding of currentIntervalStartKey
15864
      double currentIntervalStartKey = keyAxis->pixelToCoord(
15865
          (int)(keyAxis->coordToPixel(lower.key()) + reversedRound));
15866
      double lastIntervalEndKey = currentIntervalStartKey;
15867
      double keyEpsilon = qAbs(
15868
          currentIntervalStartKey -
15869
          keyAxis->pixelToCoord(
15870
              keyAxis->coordToPixel(currentIntervalStartKey) +
15871
              1.0 * reversedFactor));  // interval of one pixel on screen when
15872
                                       // mapped to plot key coordinates
15873
      bool keyEpsilonVariable =
15874
          keyAxis->scaleType() ==
15875
          QCPAxis::stLogarithmic;  // indicates whether keyEpsilon needs to be
15876
                                   // updated after every interval (for log
15877
                                   // axes)
15878
      int intervalDataCount = 1;
15879
      ++it;  // advance iterator to second data point because adaptive sampling
15880
             // works in 1 point retrospect
15881
      while (it != upperEnd) {
15882
        if (it.key() < currentIntervalStartKey +
15883
                           keyEpsilon)  // data point is still within same
15884
                                        // pixel, so skip it and expand value
15885
                                        // span of this cluster if necessary
15886
        {
15887
          if (it.value().value < minValue)
15888
            minValue = it.value().value;
15889
          else if (it.value().value > maxValue)
15890
            maxValue = it.value().value;
15891
          ++intervalDataCount;
15892
        } else  // new pixel interval started
15893
        {
15894
          if (intervalDataCount >= 2)  // last pixel had multiple data points,
15895
                                       // consolidate them to a cluster
15896
          {
15897
            if (lastIntervalEndKey <
15898
                currentIntervalStartKey -
15899
                    keyEpsilon)  // last point is further away, so first point
15900
                                 // of this cluster must be at a real data point
15901
              lineData->append(
15902
                  QCPData(currentIntervalStartKey + keyEpsilon * 0.2,
15903
                          currentIntervalFirstPoint.value().value));
15904
            lineData->append(
15905
                QCPData(currentIntervalStartKey + keyEpsilon * 0.25, minValue));
15906
            lineData->append(
15907
                QCPData(currentIntervalStartKey + keyEpsilon * 0.75, maxValue));
15908
            if (it.key() >
15909
                currentIntervalStartKey +
15910
                    keyEpsilon *
15911
                        2)  // new pixel started further away from previous
15912
                            // cluster, so make sure the last point of the
15913
                            // cluster is at a real data point
15914
              lineData->append(
15915
                  QCPData(currentIntervalStartKey + keyEpsilon * 0.8,
15916
                          (it - 1).value().value));
15917
          } else
15918
            lineData->append(QCPData(currentIntervalFirstPoint.key(),
15919
                                     currentIntervalFirstPoint.value().value));
15920
          lastIntervalEndKey = (it - 1).value().key;
15921
          minValue = it.value().value;
15922
          maxValue = it.value().value;
15923
          currentIntervalFirstPoint = it;
15924
          currentIntervalStartKey = keyAxis->pixelToCoord(
15925
              (int)(keyAxis->coordToPixel(it.key()) + reversedRound));
15926
          if (keyEpsilonVariable)
15927
            keyEpsilon =
15928
                qAbs(currentIntervalStartKey -
15929
                     keyAxis->pixelToCoord(
15930
                         keyAxis->coordToPixel(currentIntervalStartKey) +
15931
                         1.0 * reversedFactor));
15932
          intervalDataCount = 1;
15933
        }
15934
        ++it;
15935
      }
15936
      // handle last interval:
15937
      if (intervalDataCount >= 2)  // last pixel had multiple data points,
15938
                                   // consolidate them to a cluster
15939
      {
15940
        if (lastIntervalEndKey <
15941
            currentIntervalStartKey -
15942
                keyEpsilon)  // last point wasn't a cluster, so first point of
15943
                             // this cluster must be at a real data point
15944
          lineData->append(QCPData(currentIntervalStartKey + keyEpsilon * 0.2,
15945
                                   currentIntervalFirstPoint.value().value));
15946
        lineData->append(
15947
            QCPData(currentIntervalStartKey + keyEpsilon * 0.25, minValue));
15948
        lineData->append(
15949
            QCPData(currentIntervalStartKey + keyEpsilon * 0.75, maxValue));
15950
      } else
15951
        lineData->append(QCPData(currentIntervalFirstPoint.key(),
15952
                                 currentIntervalFirstPoint.value().value));
15953
    }
15954
15955
    if (scatterData) {
15956
      double valueMaxRange = valueAxis->range().upper;
15957
      double valueMinRange = valueAxis->range().lower;
15958
      QCPDataMap::const_iterator it = lower;
15959
      QCPDataMap::const_iterator upperEnd = upper + 1;
15960
      double minValue = it.value().value;
15961
      double maxValue = it.value().value;
15962
      QCPDataMap::const_iterator minValueIt = it;
15963
      QCPDataMap::const_iterator maxValueIt = it;
15964
      QCPDataMap::const_iterator currentIntervalStart = it;
15965
      int reversedFactor = keyAxis->rangeReversed()
15966
                               ? -1
15967
                               : 1;  // is used to calculate keyEpsilon pixel
15968
                                     // into the correct direction
15969
      int reversedRound =
15970
          keyAxis->rangeReversed()
15971
              ? 1
15972
              : 0;  // is used to switch between floor (normal) and ceil
15973
                    // (reversed) rounding of currentIntervalStartKey
15974
      double currentIntervalStartKey = keyAxis->pixelToCoord(
15975
          (int)(keyAxis->coordToPixel(lower.key()) + reversedRound));
15976
      double keyEpsilon = qAbs(
15977
          currentIntervalStartKey -
15978
          keyAxis->pixelToCoord(
15979
              keyAxis->coordToPixel(currentIntervalStartKey) +
15980
              1.0 * reversedFactor));  // interval of one pixel on screen when
15981
                                       // mapped to plot key coordinates
15982
      bool keyEpsilonVariable =
15983
          keyAxis->scaleType() ==
15984
          QCPAxis::stLogarithmic;  // indicates whether keyEpsilon needs to be
15985
                                   // updated after every interval (for log
15986
                                   // axes)
15987
      int intervalDataCount = 1;
15988
      ++it;  // advance iterator to second data point because adaptive sampling
15989
             // works in 1 point retrospect
15990
      while (it != upperEnd) {
15991
        if (it.key() < currentIntervalStartKey +
15992
                           keyEpsilon)  // data point is still within same
15993
                                        // pixel, so skip it and expand value
15994
                                        // span of this pixel if necessary
15995
        {
15996
          if (it.value().value < minValue && it.value().value > valueMinRange &&
15997
              it.value().value < valueMaxRange) {
15998
            minValue = it.value().value;
15999
            minValueIt = it;
16000
          } else if (it.value().value > maxValue &&
16001
                     it.value().value > valueMinRange &&
16002
                     it.value().value < valueMaxRange) {
16003
            maxValue = it.value().value;
16004
            maxValueIt = it;
16005
          }
16006
          ++intervalDataCount;
16007
        } else  // new pixel started
16008
        {
16009
          if (intervalDataCount >=
16010
              2)  // last pixel had multiple data points, consolidate them
16011
          {
16012
            // determine value pixel span and add as many points in interval to
16013
            // maintain certain vertical data density (this is specific to
16014
            // scatter plot):
16015
            double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue) -
16016
                                         valueAxis->coordToPixel(maxValue));
16017
            int dataModulo =
16018
                qMax(1, qRound(intervalDataCount /
16019
                               (valuePixelSpan /
16020
                                4.0)));  // approximately every 4 value pixels
16021
                                         // one data point on average
16022
            QCPDataMap::const_iterator intervalIt = currentIntervalStart;
16023
            int c = 0;
16024
            while (intervalIt != it) {
16025
              if ((c % dataModulo == 0 || intervalIt == minValueIt ||
16026
                   intervalIt == maxValueIt) &&
16027
                  intervalIt.value().value > valueMinRange &&
16028
                  intervalIt.value().value < valueMaxRange)
16029
                scatterData->append(intervalIt.value());
16030
              ++c;
16031
              ++intervalIt;
16032
            }
16033
          } else if (currentIntervalStart.value().value > valueMinRange &&
16034
                     currentIntervalStart.value().value < valueMaxRange)
16035
            scatterData->append(currentIntervalStart.value());
16036
          minValue = it.value().value;
16037
          maxValue = it.value().value;
16038
          currentIntervalStart = it;
16039
          currentIntervalStartKey = keyAxis->pixelToCoord(
16040
              (int)(keyAxis->coordToPixel(it.key()) + reversedRound));
16041
          if (keyEpsilonVariable)
16042
            keyEpsilon =
16043
                qAbs(currentIntervalStartKey -
16044
                     keyAxis->pixelToCoord(
16045
                         keyAxis->coordToPixel(currentIntervalStartKey) +
16046
                         1.0 * reversedFactor));
16047
          intervalDataCount = 1;
16048
        }
16049
        ++it;
16050
      }
16051
      // handle last interval:
16052
      if (intervalDataCount >=
16053
          2)  // last pixel had multiple data points, consolidate them
16054
      {
16055
        // determine value pixel span and add as many points in interval to
16056
        // maintain certain vertical data density (this is specific to scatter
16057
        // plot):
16058
        double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue) -
16059
                                     valueAxis->coordToPixel(maxValue));
16060
        int dataModulo =
16061
            qMax(1, qRound(intervalDataCount /
16062
                           (valuePixelSpan /
16063
                            4.0)));  // approximately every 4 value pixels one
16064
                                     // data point on average
16065
        QCPDataMap::const_iterator intervalIt = currentIntervalStart;
16066
        int c = 0;
16067
        while (intervalIt != it) {
16068
          if ((c % dataModulo == 0 || intervalIt == minValueIt ||
16069
               intervalIt == maxValueIt) &&
16070
              intervalIt.value().value > valueMinRange &&
16071
              intervalIt.value().value < valueMaxRange)
16072
            scatterData->append(intervalIt.value());
16073
          ++c;
16074
          ++intervalIt;
16075
        }
16076
      } else if (currentIntervalStart.value().value > valueMinRange &&
16077
                 currentIntervalStart.value().value < valueMaxRange)
16078
        scatterData->append(currentIntervalStart.value());
16079
    }
16080
  } else  // don't use adaptive sampling algorithm, transfer points one-to-one
16081
          // from the map into the output parameters
16082
  {
16083
    QVector<QCPData> *dataVector = 0;
16084
    if (lineData)
16085
      dataVector = lineData;
16086
    else if (scatterData)
16087
      dataVector = scatterData;
16088
    if (dataVector) {
16089
      QCPDataMap::const_iterator it = lower;
16090
      QCPDataMap::const_iterator upperEnd = upper + 1;
16091
      dataVector->reserve(dataCount + 2);  // +2 for possible fill end points
16092
      while (it != upperEnd) {
16093
        dataVector->append(it.value());
16094
        ++it;
16095
      }
16096
    }
16097
    if (lineData && scatterData) *scatterData = *dataVector;
16098
  }
16099
}
16100
16101
/*!  \internal
16102
16103
  called by the scatter drawing function (\ref drawScatterPlot) to draw the
16104
  error bars on one data point. \a x and \a y pixel positions of the data point
16105
  are passed since they are already known in pixel coordinates in the drawing
16106
  function, so we save some extra coordToPixel transforms here. \a data is
16107
  therefore only used for the errors, not key and value.
16108
*/
16109
void QCPGraph::drawError(QCPPainter *painter, double x, double y,
16110
                         const QCPData &data) const {
16111
  if (qIsNaN(data.value)) return;
16112
  QCPAxis *keyAxis = mKeyAxis.data();
16113
  QCPAxis *valueAxis = mValueAxis.data();
16114
  if (!keyAxis || !valueAxis) {
16115
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16116
    return;
16117
  }
16118
16119
  double a, b;  // positions of error bar bounds in pixels
16120
  double barWidthHalf = mErrorBarSize * 0.5;
16121
  double skipSymbolMargin =
16122
      mScatterStyle.size();  // pixels left blank per side, when
16123
                             // mErrorBarSkipSymbol is true
16124
16125
  if (keyAxis->orientation() == Qt::Vertical) {
16126
    // draw key error vertically and value error horizontally
16127
    if (mErrorType == etKey || mErrorType == etBoth) {
16128
      a = keyAxis->coordToPixel(data.key - data.keyErrorMinus);
16129
      b = keyAxis->coordToPixel(data.key + data.keyErrorPlus);
16130
      if (keyAxis->rangeReversed()) qSwap(a, b);
16131
      // draw spine:
16132
      if (mErrorBarSkipSymbol) {
16133
        if (a - y > skipSymbolMargin)  // don't draw spine if error is so small
16134
                                       // it's within skipSymbolmargin
16135
          painter->drawLine(QLineF(x, a, x, y + skipSymbolMargin));
16136
        if (y - b > skipSymbolMargin)
16137
          painter->drawLine(QLineF(x, y - skipSymbolMargin, x, b));
16138
      } else
16139
        painter->drawLine(QLineF(x, a, x, b));
16140
      // draw handles:
16141
      painter->drawLine(QLineF(x - barWidthHalf, a, x + barWidthHalf, a));
16142
      painter->drawLine(QLineF(x - barWidthHalf, b, x + barWidthHalf, b));
16143
    }
16144
    if (mErrorType == etValue || mErrorType == etBoth) {
16145
      a = valueAxis->coordToPixel(data.value - data.valueErrorMinus);
16146
      b = valueAxis->coordToPixel(data.value + data.valueErrorPlus);
16147
      if (valueAxis->rangeReversed()) qSwap(a, b);
16148
      // draw spine:
16149
      if (mErrorBarSkipSymbol) {
16150
        if (x - a > skipSymbolMargin)  // don't draw spine if error is so small
16151
                                       // it's within skipSymbolmargin
16152
          painter->drawLine(QLineF(a, y, x - skipSymbolMargin, y));
16153
        if (b - x > skipSymbolMargin)
16154
          painter->drawLine(QLineF(x + skipSymbolMargin, y, b, y));
16155
      } else
16156
        painter->drawLine(QLineF(a, y, b, y));
16157
      // draw handles:
16158
      painter->drawLine(QLineF(a, y - barWidthHalf, a, y + barWidthHalf));
16159
      painter->drawLine(QLineF(b, y - barWidthHalf, b, y + barWidthHalf));
16160
    }
16161
  } else  // mKeyAxis->orientation() is Qt::Horizontal
16162
  {
16163
    // draw value error vertically and key error horizontally
16164
    if (mErrorType == etKey || mErrorType == etBoth) {
16165
      a = keyAxis->coordToPixel(data.key - data.keyErrorMinus);
16166
      b = keyAxis->coordToPixel(data.key + data.keyErrorPlus);
16167
      if (keyAxis->rangeReversed()) qSwap(a, b);
16168
      // draw spine:
16169
      if (mErrorBarSkipSymbol) {
16170
        if (x - a > skipSymbolMargin)  // don't draw spine if error is so small
16171
                                       // it's within skipSymbolmargin
16172
          painter->drawLine(QLineF(a, y, x - skipSymbolMargin, y));
16173
        if (b - x > skipSymbolMargin)
16174
          painter->drawLine(QLineF(x + skipSymbolMargin, y, b, y));
16175
      } else
16176
        painter->drawLine(QLineF(a, y, b, y));
16177
      // draw handles:
16178
      painter->drawLine(QLineF(a, y - barWidthHalf, a, y + barWidthHalf));
16179
      painter->drawLine(QLineF(b, y - barWidthHalf, b, y + barWidthHalf));
16180
    }
16181
    if (mErrorType == etValue || mErrorType == etBoth) {
16182
      a = valueAxis->coordToPixel(data.value - data.valueErrorMinus);
16183
      b = valueAxis->coordToPixel(data.value + data.valueErrorPlus);
16184
      if (valueAxis->rangeReversed()) qSwap(a, b);
16185
      // draw spine:
16186
      if (mErrorBarSkipSymbol) {
16187
        if (a - y > skipSymbolMargin)  // don't draw spine if error is so small
16188
                                       // it's within skipSymbolmargin
16189
          painter->drawLine(QLineF(x, a, x, y + skipSymbolMargin));
16190
        if (y - b > skipSymbolMargin)
16191
          painter->drawLine(QLineF(x, y - skipSymbolMargin, x, b));
16192
      } else
16193
        painter->drawLine(QLineF(x, a, x, b));
16194
      // draw handles:
16195
      painter->drawLine(QLineF(x - barWidthHalf, a, x + barWidthHalf, a));
16196
      painter->drawLine(QLineF(x - barWidthHalf, b, x + barWidthHalf, b));
16197
    }
16198
  }
16199
}
16200
16201
/*!  \internal
16202
16203
  called by \ref getPreparedData to determine which data (key) range is visible
16204
  at the current key axis range setting, so only that needs to be processed.
16205
16206
  \a lower returns an iterator to the lowest data point that needs to be taken
16207
  into account when plotting. Note that in order to get a clean plot all the way
16208
  to the edge of the axis rect, \a lower may still be just outside the visible
16209
  range.
16210
16211
  \a upper returns an iterator to the highest data point. Same as before, \a
16212
  upper may also lie just outside of the visible range.
16213
16214
  if the graph contains no data, both \a lower and \a upper point to constEnd.
16215
*/
16216
void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower,
16217
                                    QCPDataMap::const_iterator &upper) const {
16218
  if (!mKeyAxis) {
16219
    qDebug() << Q_FUNC_INFO << "invalid key axis";
16220
    return;
16221
  }
16222
  if (mData->isEmpty()) {
16223
    lower = mData->constEnd();
16224
    upper = mData->constEnd();
16225
    return;
16226
  }
16227
16228
  // get visible data range as QMap iterators
16229
  QCPDataMap::const_iterator lbound =
16230
      mData->lowerBound(mKeyAxis.data()->range().lower);
16231
  QCPDataMap::const_iterator ubound =
16232
      mData->upperBound(mKeyAxis.data()->range().upper);
16233
  bool lowoutlier =
16234
      lbound != mData->constBegin();  // indicates whether there exist points
16235
                                      // below axis range
16236
  bool highoutlier =
16237
      ubound != mData->constEnd();  // indicates whether there exist points
16238
                                    // above axis range
16239
16240
  lower =
16241
      (lowoutlier ? lbound - 1
16242
                  : lbound);  // data point range that will be actually drawn
16243
  upper = (highoutlier
16244
               ? ubound
16245
               : ubound - 1);  // data point range that will be actually drawn
16246
}
16247
16248
/*!  \internal
16249
16250
  Counts the number of data points between \a lower and \a upper (including
16251
  them), up to a maximum of \a maxCount.
16252
16253
  This function is used by \ref getPreparedData to determine whether adaptive
16254
  sampling shall be used (if enabled via \ref setAdaptiveSampling) or not. This
16255
  is also why counting of data points only needs to be done until \a maxCount is
16256
  reached, which should be set to the number of data points at which adaptive
16257
  sampling sets in.
16258
*/
16259
int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower,
16260
                                const QCPDataMap::const_iterator &upper,
16261
                                int maxCount) const {
16262
  if (upper == mData->constEnd() && lower == mData->constEnd()) return 0;
16263
  QCPDataMap::const_iterator it = lower;
16264
  int count = 1;
16265
  while (it != upper && count < maxCount) {
16266
    ++it;
16267
    ++count;
16268
  }
16269
  return count;
16270
}
16271
16272
/*! \internal
16273
16274
  The line data vector generated by e.g. getLinePlotData contains only the line
16275
  that connects the data points. If the graph needs to be filled, two additional
16276
  points need to be added at the value-zero-line in the lower and upper key
16277
  positions of the graph. This function calculates these points and adds them to
16278
  the end of \a lineData. Since the fill is typically drawn before the line
16279
  stroke, these added points need to be removed again after the fill is done,
16280
  with the removeFillBasePoints function.
16281
16282
  The expanding of \a lineData by two points will not cause unnecessary memory
16283
  reallocations, because the data vector generation functions (getLinePlotData
16284
  etc.) reserve two extra points when they allocate memory for \a lineData.
16285
16286
  \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16287
*/
16288
void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const {
16289
  if (!mKeyAxis) {
16290
    qDebug() << Q_FUNC_INFO << "invalid key axis";
16291
    return;
16292
  }
16293
  if (!lineData) {
16294
    qDebug() << Q_FUNC_INFO << "passed null as lineData";
16295
    return;
16296
  }
16297
  if (lineData->isEmpty()) return;
16298
16299
  // append points that close the polygon fill at the key axis:
16300
  if (mKeyAxis.data()->orientation() == Qt::Vertical) {
16301
    *lineData << upperFillBasePoint(lineData->last().y());
16302
    *lineData << lowerFillBasePoint(lineData->first().y());
16303
  } else {
16304
    *lineData << upperFillBasePoint(lineData->last().x());
16305
    *lineData << lowerFillBasePoint(lineData->first().x());
16306
  }
16307
}
16308
16309
/*! \internal
16310
16311
  removes the two points from \a lineData that were added by \ref
16312
  addFillBasePoints.
16313
16314
  \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16315
*/
16316
void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const {
16317
  if (!lineData) {
16318
    qDebug() << Q_FUNC_INFO << "passed null as lineData";
16319
    return;
16320
  }
16321
  if (lineData->isEmpty()) return;
16322
16323
  lineData->remove(lineData->size() - 2, 2);
16324
}
16325
16326
/*! \internal
16327
16328
  called by \ref addFillBasePoints to conveniently assign the point which closes
16329
  the fill polygon on the lower side of the zero-value-line parallel to the key
16330
  axis. The logarithmic axis scale case is a bit special, since the
16331
  zero-value-line in pixel coordinates is in positive or negative infinity. So
16332
  this case is handled separately by just closing the fill polygon on the axis
16333
  which lies in the direction towards the zero value.
16334
16335
  \a lowerKey will be the the key (in pixels) of the returned point. Depending
16336
  on whether the key axis is horizontal or vertical, \a lowerKey will end up as
16337
  the x or y value of the returned point, respectively.
16338
16339
  \see upperFillBasePoint, addFillBasePoints
16340
*/
16341
QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const {
16342
  QCPAxis *keyAxis = mKeyAxis.data();
16343
  QCPAxis *valueAxis = mValueAxis.data();
16344
  if (!keyAxis || !valueAxis) {
16345
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16346
    return QPointF();
16347
  }
16348
16349
  QPointF point;
16350
  if (valueAxis->scaleType() == QCPAxis::stLinear) {
16351
    if (keyAxis->axisType() == QCPAxis::atLeft) {
16352
      point.setX(valueAxis->coordToPixel(0));
16353
      point.setY(lowerKey);
16354
    } else if (keyAxis->axisType() == QCPAxis::atRight) {
16355
      point.setX(valueAxis->coordToPixel(0));
16356
      point.setY(lowerKey);
16357
    } else if (keyAxis->axisType() == QCPAxis::atTop) {
16358
      point.setX(lowerKey);
16359
      point.setY(valueAxis->coordToPixel(0));
16360
    } else if (keyAxis->axisType() == QCPAxis::atBottom) {
16361
      point.setX(lowerKey);
16362
      point.setY(valueAxis->coordToPixel(0));
16363
    }
16364
  } else  // valueAxis->mScaleType == QCPAxis::stLogarithmic
16365
  {
16366
    // In logarithmic scaling we can't just draw to value zero so we just fill
16367
    // all the way to the axis which is in the direction towards zero
16368
    if (keyAxis->orientation() == Qt::Vertical) {
16369
      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16370
          (valueAxis->range().upper > 0 &&
16371
           valueAxis->rangeReversed()))  // if range is negative, zero is on
16372
                                         // opposite side of key axis
16373
        point.setX(keyAxis->axisRect()->right());
16374
      else
16375
        point.setX(keyAxis->axisRect()->left());
16376
      point.setY(lowerKey);
16377
    } else if (keyAxis->axisType() == QCPAxis::atTop ||
16378
               keyAxis->axisType() == QCPAxis::atBottom) {
16379
      point.setX(lowerKey);
16380
      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16381
          (valueAxis->range().upper > 0 &&
16382
           valueAxis->rangeReversed()))  // if range is negative, zero is on
16383
                                         // opposite side of key axis
16384
        point.setY(keyAxis->axisRect()->top());
16385
      else
16386
        point.setY(keyAxis->axisRect()->bottom());
16387
    }
16388
  }
16389
  return point;
16390
}
16391
16392
/*! \internal
16393
16394
  called by \ref addFillBasePoints to conveniently assign the point which closes
16395
  the fill polygon on the upper side of the zero-value-line parallel to the key
16396
  axis. The logarithmic axis scale case is a bit special, since the
16397
  zero-value-line in pixel coordinates is in positive or negative infinity. So
16398
  this case is handled separately by just closing the fill polygon on the axis
16399
  which lies in the direction towards the zero value.
16400
16401
  \a upperKey will be the the key (in pixels) of the returned point. Depending
16402
  on whether the key axis is horizontal or vertical, \a upperKey will end up as
16403
  the x or y value of the returned point, respectively.
16404
16405
  \see lowerFillBasePoint, addFillBasePoints
16406
*/
16407
QPointF QCPGraph::upperFillBasePoint(double upperKey) const {
16408
  QCPAxis *keyAxis = mKeyAxis.data();
16409
  QCPAxis *valueAxis = mValueAxis.data();
16410
  if (!keyAxis || !valueAxis) {
16411
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16412
    return QPointF();
16413
  }
16414
16415
  QPointF point;
16416
  if (valueAxis->scaleType() == QCPAxis::stLinear) {
16417
    if (keyAxis->axisType() == QCPAxis::atLeft) {
16418
      point.setX(valueAxis->coordToPixel(0));
16419
      point.setY(upperKey);
16420
    } else if (keyAxis->axisType() == QCPAxis::atRight) {
16421
      point.setX(valueAxis->coordToPixel(0));
16422
      point.setY(upperKey);
16423
    } else if (keyAxis->axisType() == QCPAxis::atTop) {
16424
      point.setX(upperKey);
16425
      point.setY(valueAxis->coordToPixel(0));
16426
    } else if (keyAxis->axisType() == QCPAxis::atBottom) {
16427
      point.setX(upperKey);
16428
      point.setY(valueAxis->coordToPixel(0));
16429
    }
16430
  } else  // valueAxis->mScaleType == QCPAxis::stLogarithmic
16431
  {
16432
    // In logarithmic scaling we can't just draw to value 0 so we just fill all
16433
    // the way to the axis which is in the direction towards 0
16434
    if (keyAxis->orientation() == Qt::Vertical) {
16435
      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16436
          (valueAxis->range().upper > 0 &&
16437
           valueAxis->rangeReversed()))  // if range is negative, zero is on
16438
                                         // opposite side of key axis
16439
        point.setX(keyAxis->axisRect()->right());
16440
      else
16441
        point.setX(keyAxis->axisRect()->left());
16442
      point.setY(upperKey);
16443
    } else if (keyAxis->axisType() == QCPAxis::atTop ||
16444
               keyAxis->axisType() == QCPAxis::atBottom) {
16445
      point.setX(upperKey);
16446
      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16447
          (valueAxis->range().upper > 0 &&
16448
           valueAxis->rangeReversed()))  // if range is negative, zero is on
16449
                                         // opposite side of key axis
16450
        point.setY(keyAxis->axisRect()->top());
16451
      else
16452
        point.setY(keyAxis->axisRect()->bottom());
16453
    }
16454
  }
16455
  return point;
16456
}
16457
16458
/*! \internal
16459
16460
  Generates the polygon needed for drawing channel fills between this graph
16461
  (data passed via \a lineData) and the graph specified by mChannelFillGraph
16462
  (data generated by calling its \ref getPlotData function). May return an empty
16463
  polygon if the key ranges have no overlap or fill target graph and this graph
16464
  don't have same orientation (i.e. both key axes horizontal or both key axes
16465
  vertical). For increased performance (due to implicit sharing), keep the
16466
  returned QPolygonF const.
16467
*/
16468
const QPolygonF QCPGraph::getChannelFillPolygon(
16469
    const QVector<QPointF> *lineData) const {
16470
  if (!mChannelFillGraph) return QPolygonF();
16471
16472
  QCPAxis *keyAxis = mKeyAxis.data();
16473
  QCPAxis *valueAxis = mValueAxis.data();
16474
  if (!keyAxis || !valueAxis) {
16475
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16476
    return QPolygonF();
16477
  }
16478
  if (!mChannelFillGraph.data()->mKeyAxis) {
16479
    qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid";
16480
    return QPolygonF();
16481
  }
16482
16483
  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() !=
16484
      keyAxis->orientation())
16485
    return QPolygonF();  // don't have same axis orientation, can't fill that
16486
                         // (Note: if keyAxis fits, valueAxis will fit too,
16487
                         // because it's always orthogonal to keyAxis)
16488
16489
  if (lineData->isEmpty()) return QPolygonF();
16490
  QVector<QPointF> otherData;
16491
  mChannelFillGraph.data()->getPlotData(&otherData, 0);
16492
  if (otherData.isEmpty()) return QPolygonF();
16493
  QVector<QPointF> thisData;
16494
  thisData.reserve(lineData->size() +
16495
                   otherData.size());  // because we will join both vectors at
16496
                                       // end of this function
16497
  for (int i = 0; i < lineData->size();
16498
       ++i)  // don't use the vector<<(vector),  it squeezes internally, which
16499
             // ruins the performance tuning with reserve()
16500
    thisData << lineData->at(i);
16501
16502
  // pointers to be able to swap them, depending which data range needs
16503
  // cropping:
16504
  QVector<QPointF> *staticData = &thisData;
16505
  QVector<QPointF> *croppedData = &otherData;
16506
16507
  // crop both vectors to ranges in which the keys overlap (which coord is key,
16508
  // depends on axisType):
16509
  if (keyAxis->orientation() == Qt::Horizontal) {
16510
    // x is key
16511
    // if an axis range is reversed, the data point keys will be descending.
16512
    // Reverse them, since following algorithm assumes ascending keys:
16513
    if (staticData->first().x() > staticData->last().x()) {
16514
      int size = staticData->size();
16515
      for (int i = 0; i < size / 2; ++i)
16516
        qSwap((*staticData)[i], (*staticData)[size - 1 - i]);
16517
    }
16518
    if (croppedData->first().x() > croppedData->last().x()) {
16519
      int size = croppedData->size();
16520
      for (int i = 0; i < size / 2; ++i)
16521
        qSwap((*croppedData)[i], (*croppedData)[size - 1 - i]);
16522
    }
16523
    // crop lower bound:
16524
    if (staticData->first().x() <
16525
        croppedData->first().x())  // other one must be cropped
16526
      qSwap(staticData, croppedData);
16527
    int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16528
    if (lowBound == -1) return QPolygonF();  // key ranges have no overlap
16529
    croppedData->remove(0, lowBound);
16530
    // set lowest point of cropped data to fit exactly key position of first
16531
    // static data point via linear interpolation:
16532
    if (croppedData->size() < 2)
16533
      return QPolygonF();  // need at least two points for interpolation
16534
    double slope;
16535
    if (croppedData->at(1).x() - croppedData->at(0).x() != 0)
16536
      slope = (croppedData->at(1).y() - croppedData->at(0).y()) /
16537
              (croppedData->at(1).x() - croppedData->at(0).x());
16538
    else
16539
      slope = 0;
16540
    (*croppedData)[0].setY(
16541
        croppedData->at(0).y() +
16542
        slope * (staticData->first().x() - croppedData->at(0).x()));
16543
    (*croppedData)[0].setX(staticData->first().x());
16544
16545
    // crop upper bound:
16546
    if (staticData->last().x() >
16547
        croppedData->last().x())  // other one must be cropped
16548
      qSwap(staticData, croppedData);
16549
    int highBound = findIndexAboveX(croppedData, staticData->last().x());
16550
    if (highBound == -1) return QPolygonF();  // key ranges have no overlap
16551
    croppedData->remove(highBound + 1, croppedData->size() - (highBound + 1));
16552
    // set highest point of cropped data to fit exactly key position of last
16553
    // static data point via linear interpolation:
16554
    if (croppedData->size() < 2)
16555
      return QPolygonF();  // need at least two points for interpolation
16556
    int li = croppedData->size() - 1;  // last index
16557
    if (croppedData->at(li).x() - croppedData->at(li - 1).x() != 0)
16558
      slope = (croppedData->at(li).y() - croppedData->at(li - 1).y()) /
16559
              (croppedData->at(li).x() - croppedData->at(li - 1).x());
16560
    else
16561
      slope = 0;
16562
    (*croppedData)[li].setY(
16563
        croppedData->at(li - 1).y() +
16564
        slope * (staticData->last().x() - croppedData->at(li - 1).x()));
16565
    (*croppedData)[li].setX(staticData->last().x());
16566
  } else  // mKeyAxis->orientation() == Qt::Vertical
16567
  {
16568
    // y is key
16569
    // similar to "x is key" but switched x,y. Further, lower/upper meaning is
16570
    // inverted compared to x, because in pixel coordinates, y increases from
16571
    // top to bottom, not bottom to top like data coordinate. if an axis range
16572
    // is reversed, the data point keys will be descending. Reverse them, since
16573
    // following algorithm assumes ascending keys:
16574
    if (staticData->first().y() < staticData->last().y()) {
16575
      int size = staticData->size();
16576
      for (int i = 0; i < size / 2; ++i)
16577
        qSwap((*staticData)[i], (*staticData)[size - 1 - i]);
16578
    }
16579
    if (croppedData->first().y() < croppedData->last().y()) {
16580
      int size = croppedData->size();
16581
      for (int i = 0; i < size / 2; ++i)
16582
        qSwap((*croppedData)[i], (*croppedData)[size - 1 - i]);
16583
    }
16584
    // crop lower bound:
16585
    if (staticData->first().y() >
16586
        croppedData->first().y())  // other one must be cropped
16587
      qSwap(staticData, croppedData);
16588
    int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16589
    if (lowBound == -1) return QPolygonF();  // key ranges have no overlap
16590
    croppedData->remove(0, lowBound);
16591
    // set lowest point of cropped data to fit exactly key position of first
16592
    // static data point via linear interpolation:
16593
    if (croppedData->size() < 2)
16594
      return QPolygonF();  // need at least two points for interpolation
16595
    double slope;
16596
    if (croppedData->at(1).y() - croppedData->at(0).y() !=
16597
        0)  // avoid division by zero in step plots
16598
      slope = (croppedData->at(1).x() - croppedData->at(0).x()) /
16599
              (croppedData->at(1).y() - croppedData->at(0).y());
16600
    else
16601
      slope = 0;
16602
    (*croppedData)[0].setX(
16603
        croppedData->at(0).x() +
16604
        slope * (staticData->first().y() - croppedData->at(0).y()));
16605
    (*croppedData)[0].setY(staticData->first().y());
16606
16607
    // crop upper bound:
16608
    if (staticData->last().y() <
16609
        croppedData->last().y())  // other one must be cropped
16610
      qSwap(staticData, croppedData);
16611
    int highBound = findIndexBelowY(croppedData, staticData->last().y());
16612
    if (highBound == -1) return QPolygonF();  // key ranges have no overlap
16613
    croppedData->remove(highBound + 1, croppedData->size() - (highBound + 1));
16614
    // set highest point of cropped data to fit exactly key position of last
16615
    // static data point via linear interpolation:
16616
    if (croppedData->size() < 2)
16617
      return QPolygonF();  // need at least two points for interpolation
16618
    int li = croppedData->size() - 1;  // last index
16619
    if (croppedData->at(li).y() - croppedData->at(li - 1).y() !=
16620
        0)  // avoid division by zero in step plots
16621
      slope = (croppedData->at(li).x() - croppedData->at(li - 1).x()) /
16622
              (croppedData->at(li).y() - croppedData->at(li - 1).y());
16623
    else
16624
      slope = 0;
16625
    (*croppedData)[li].setX(
16626
        croppedData->at(li - 1).x() +
16627
        slope * (staticData->last().y() - croppedData->at(li - 1).y()));
16628
    (*croppedData)[li].setY(staticData->last().y());
16629
  }
16630
16631
  // return joined:
16632
  for (int i = otherData.size() - 1; i >= 0;
16633
       --i)  // insert reversed, otherwise the polygon will be twisted
16634
    thisData << otherData.at(i);
16635
  return QPolygonF(thisData);
16636
}
16637
16638
/*! \internal
16639
16640
  Finds the smallest index of \a data, whose points x value is just above \a x.
16641
  Assumes x values in \a data points are ordered ascending, as is the case when
16642
  plotting with horizontal key axis.
16643
16644
  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16645
*/
16646
int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const {
16647
  for (int i = data->size() - 1; i >= 0; --i) {
16648
    if (data->at(i).x() < x) {
16649
      if (i < data->size() - 1)
16650
        return i + 1;
16651
      else
16652
        return data->size() - 1;
16653
    }
16654
  }
16655
  return -1;
16656
}
16657
16658
/*! \internal
16659
16660
  Finds the highest index of \a data, whose points x value is just below \a x.
16661
  Assumes x values in \a data points are ordered ascending, as is the case when
16662
  plotting with horizontal key axis.
16663
16664
  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16665
*/
16666
int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const {
16667
  for (int i = 0; i < data->size(); ++i) {
16668
    if (data->at(i).x() > x) {
16669
      if (i > 0)
16670
        return i - 1;
16671
      else
16672
        return 0;
16673
    }
16674
  }
16675
  return -1;
16676
}
16677
16678
/*! \internal
16679
16680
  Finds the smallest index of \a data, whose points y value is just above \a y.
16681
  Assumes y values in \a data points are ordered descending, as is the case when
16682
  plotting with vertical key axis.
16683
16684
  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16685
*/
16686
int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const {
16687
  for (int i = 0; i < data->size(); ++i) {
16688
    if (data->at(i).y() < y) {
16689
      if (i > 0)
16690
        return i - 1;
16691
      else
16692
        return 0;
16693
    }
16694
  }
16695
  return -1;
16696
}
16697
16698
/*! \internal
16699
16700
  Calculates the (minimum) distance (in pixels) the graph's representation has
16701
  from the given \a pixelPoint in pixels. This is used to determine whether the
16702
  graph was clicked or not, e.g. in \ref selectTest.
16703
16704
  If either the graph has no data or if the line style is \ref lsNone and the
16705
  scatter style's shape is \ref QCPScatterStyle::ssNone (i.e. there is no visual
16706
  representation of the graph), returns -1.0.
16707
*/
16708
double QCPGraph::pointDistance(const QPointF &pixelPoint) const {
16709
  if (mData->isEmpty()) return -1.0;
16710
  if (mLineStyle == lsNone && mScatterStyle.isNone()) return -1.0;
16711
16712
  // calculate minimum distances to graph representation:
16713
  if (mLineStyle == lsNone) {
16714
    // no line displayed, only calculate distance to scatter points:
16715
    QVector<QCPData> scatterData;
16716
    getScatterPlotData(&scatterData);
16717
    if (scatterData.size() > 0) {
16718
      double minDistSqr = std::numeric_limits<double>::max();
16719
      for (int i = 0; i < scatterData.size(); ++i) {
16720
        double currentDistSqr =
16721
            QVector2D(
16722
                coordsToPixels(scatterData.at(i).key, scatterData.at(i).value) -
16723
                pixelPoint)
16724
                .lengthSquared();
16725
        if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
16726
      }
16727
      return qSqrt(minDistSqr);
16728
    } else  // no data available in view to calculate distance to
16729
      return -1.0;
16730
  } else {
16731
    // line displayed, calculate distance to line segments:
16732
    QVector<QPointF> lineData;
16733
    getPlotData(
16734
        &lineData,
16735
        0);  // unlike with getScatterPlotData we get pixel coordinates here
16736
    if (lineData.size() >
16737
        1)  // at least one line segment, compare distance to line segments
16738
    {
16739
      double minDistSqr = std::numeric_limits<double>::max();
16740
      if (mLineStyle == lsImpulse) {
16741
        // impulse plot differs from other line styles in that the lineData
16742
        // points are only pairwise connected:
16743
        for (int i = 0; i < lineData.size() - 1; i += 2)  // iterate pairs
16744
        {
16745
          double currentDistSqr =
16746
              distSqrToLine(lineData.at(i), lineData.at(i + 1), pixelPoint);
16747
          if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
16748
        }
16749
      } else {
16750
        // all other line plots (line and step) connect points directly:
16751
        for (int i = 0; i < lineData.size() - 1; ++i) {
16752
          double currentDistSqr =
16753
              distSqrToLine(lineData.at(i), lineData.at(i + 1), pixelPoint);
16754
          if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
16755
        }
16756
      }
16757
      return qSqrt(minDistSqr);
16758
    } else if (lineData.size() >
16759
               0)  // only single data point, calculate distance to that point
16760
    {
16761
      return QVector2D(lineData.at(0) - pixelPoint).length();
16762
    } else  // no data available in view to calculate distance to
16763
      return -1.0;
16764
  }
16765
}
16766
16767
/*! \internal
16768
16769
  Finds the highest index of \a data, whose points y value is just below \a y.
16770
  Assumes y values in \a data points are ordered descending, as is the case when
16771
  plotting with vertical key axis (since keys are ordered ascending).
16772
16773
  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16774
*/
16775
int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const {
16776
  for (int i = data->size() - 1; i >= 0; --i) {
16777
    if (data->at(i).y() > y) {
16778
      if (i < data->size() - 1)
16779
        return i + 1;
16780
      else
16781
        return data->size() - 1;
16782
    }
16783
  }
16784
  return -1;
16785
}
16786
16787
/* inherits documentation from base class */
16788
QCPRange QCPGraph::getKeyRange(bool &foundRange,
16789
                               SignDomain inSignDomain) const {
16790
  // just call the specialized version which takes an additional argument
16791
  // whether error bars should also be taken into consideration for range
16792
  // calculation. We set this to true here.
16793
  return getKeyRange(foundRange, inSignDomain, true);
16794
}
16795
16796
/* inherits documentation from base class */
16797
QCPRange QCPGraph::getValueRange(bool &foundRange,
16798
                                 SignDomain inSignDomain) const {
16799
  // just call the specialized version which takes an additional argument
16800
  // whether error bars should also be taken into consideration for range
16801
  // calculation. We set this to true here.
16802
  return getValueRange(foundRange, inSignDomain, true);
16803
}
16804
16805
/*! \overload
16806
16807
  Allows to specify whether the error bars should be included in the range
16808
  calculation.
16809
16810
  \see getKeyRange(bool &foundRange, SignDomain inSignDomain)
16811
*/
16812
QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain,
16813
                               bool includeErrors) const {
16814
  QCPRange range;
16815
  bool haveLower = false;
16816
  bool haveUpper = false;
16817
16818
  double current, currentErrorMinus, currentErrorPlus;
16819
16820
  if (inSignDomain == sdBoth)  // range may be anywhere
16821
  {
16822
    QCPDataMap::const_iterator it = mData->constBegin();
16823
    while (it != mData->constEnd()) {
16824
      if (!qIsNaN(it.value().value)) {
16825
        current = it.value().key;
16826
        currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16827
        currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16828
        if (current - currentErrorMinus < range.lower || !haveLower) {
16829
          range.lower = current - currentErrorMinus;
16830
          haveLower = true;
16831
        }
16832
        if (current + currentErrorPlus > range.upper || !haveUpper) {
16833
          range.upper = current + currentErrorPlus;
16834
          haveUpper = true;
16835
        }
16836
      }
16837
      ++it;
16838
    }
16839
  } else if (inSignDomain ==
16840
             sdNegative)  // range may only be in the negative sign domain
16841
  {
16842
    QCPDataMap::const_iterator it = mData->constBegin();
16843
    while (it != mData->constEnd()) {
16844
      if (!qIsNaN(it.value().value)) {
16845
        current = it.value().key;
16846
        currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16847
        currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16848
        if ((current - currentErrorMinus < range.lower || !haveLower) &&
16849
            current - currentErrorMinus < 0) {
16850
          range.lower = current - currentErrorMinus;
16851
          haveLower = true;
16852
        }
16853
        if ((current + currentErrorPlus > range.upper || !haveUpper) &&
16854
            current + currentErrorPlus < 0) {
16855
          range.upper = current + currentErrorPlus;
16856
          haveUpper = true;
16857
        }
16858
        if (includeErrors)  // in case point is in valid sign domain but
16859
                            // errobars stretch beyond it, we still want to geht
16860
                            // that point.
16861
        {
16862
          if ((current < range.lower || !haveLower) && current < 0) {
16863
            range.lower = current;
16864
            haveLower = true;
16865
          }
16866
          if ((current > range.upper || !haveUpper) && current < 0) {
16867
            range.upper = current;
16868
            haveUpper = true;
16869
          }
16870
        }
16871
      }
16872
      ++it;
16873
    }
16874
  } else if (inSignDomain ==
16875
             sdPositive)  // range may only be in the positive sign domain
16876
  {
16877
    QCPDataMap::const_iterator it = mData->constBegin();
16878
    while (it != mData->constEnd()) {
16879
      if (!qIsNaN(it.value().value)) {
16880
        current = it.value().key;
16881
        currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16882
        currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16883
        if ((current - currentErrorMinus < range.lower || !haveLower) &&
16884
            current - currentErrorMinus > 0) {
16885
          range.lower = current - currentErrorMinus;
16886
          haveLower = true;
16887
        }
16888
        if ((current + currentErrorPlus > range.upper || !haveUpper) &&
16889
            current + currentErrorPlus > 0) {
16890
          range.upper = current + currentErrorPlus;
16891
          haveUpper = true;
16892
        }
16893
        if (includeErrors)  // in case point is in valid sign domain but
16894
                            // errobars stretch beyond it, we still want to get
16895
                            // that point.
16896
        {
16897
          if ((current < range.lower || !haveLower) && current > 0) {
16898
            range.lower = current;
16899
            haveLower = true;
16900
          }
16901
          if ((current > range.upper || !haveUpper) && current > 0) {
16902
            range.upper = current;
16903
            haveUpper = true;
16904
          }
16905
        }
16906
      }
16907
      ++it;
16908
    }
16909
  }
16910
16911
  foundRange = haveLower && haveUpper;
16912
  return range;
16913
}
16914
16915
/*! \overload
16916
16917
  Allows to specify whether the error bars should be included in the range
16918
  calculation.
16919
16920
  \see getValueRange(bool &foundRange, SignDomain inSignDomain)
16921
*/
16922
QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain,
16923
                                 bool includeErrors) const {
16924
  QCPRange range;
16925
  bool haveLower = false;
16926
  bool haveUpper = false;
16927
16928
  double current, currentErrorMinus, currentErrorPlus;
16929
16930
  if (inSignDomain == sdBoth)  // range may be anywhere
16931
  {
16932
    QCPDataMap::const_iterator it = mData->constBegin();
16933
    while (it != mData->constEnd()) {
16934
      current = it.value().value;
16935
      if (!qIsNaN(current)) {
16936
        currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16937
        currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16938
        if (current - currentErrorMinus < range.lower || !haveLower) {
16939
          range.lower = current - currentErrorMinus;
16940
          haveLower = true;
16941
        }
16942
        if (current + currentErrorPlus > range.upper || !haveUpper) {
16943
          range.upper = current + currentErrorPlus;
16944
          haveUpper = true;
16945
        }
16946
      }
16947
      ++it;
16948
    }
16949
  } else if (inSignDomain ==
16950
             sdNegative)  // range may only be in the negative sign domain
16951
  {
16952
    QCPDataMap::const_iterator it = mData->constBegin();
16953
    while (it != mData->constEnd()) {
16954
      current = it.value().value;
16955
      if (!qIsNaN(current)) {
16956
        currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16957
        currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16958
        if ((current - currentErrorMinus < range.lower || !haveLower) &&
16959
            current - currentErrorMinus < 0) {
16960
          range.lower = current - currentErrorMinus;
16961
          haveLower = true;
16962
        }
16963
        if ((current + currentErrorPlus > range.upper || !haveUpper) &&
16964
            current + currentErrorPlus < 0) {
16965
          range.upper = current + currentErrorPlus;
16966
          haveUpper = true;
16967
        }
16968
        if (includeErrors)  // in case point is in valid sign domain but
16969
                            // errobars stretch beyond it, we still want to get
16970
                            // that point.
16971
        {
16972
          if ((current < range.lower || !haveLower) && current < 0) {
16973
            range.lower = current;
16974
            haveLower = true;
16975
          }
16976
          if ((current > range.upper || !haveUpper) && current < 0) {
16977
            range.upper = current;
16978
            haveUpper = true;
16979
          }
16980
        }
16981
      }
16982
      ++it;
16983
    }
16984
  } else if (inSignDomain ==
16985
             sdPositive)  // range may only be in the positive sign domain
16986
  {
16987
    QCPDataMap::const_iterator it = mData->constBegin();
16988
    while (it != mData->constEnd()) {
16989
      current = it.value().value;
16990
      if (!qIsNaN(current)) {
16991
        currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16992
        currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16993
        if ((current - currentErrorMinus < range.lower || !haveLower) &&
16994
            current - currentErrorMinus > 0) {
16995
          range.lower = current - currentErrorMinus;
16996
          haveLower = true;
16997
        }
16998
        if ((current + currentErrorPlus > range.upper || !haveUpper) &&
16999
            current + currentErrorPlus > 0) {
17000
          range.upper = current + currentErrorPlus;
17001
          haveUpper = true;
17002
        }
17003
        if (includeErrors)  // in case point is in valid sign domain but
17004
                            // errobars stretch beyond it, we still want to geht
17005
                            // that point.
17006
        {
17007
          if ((current < range.lower || !haveLower) && current > 0) {
17008
            range.lower = current;
17009
            haveLower = true;
17010
          }
17011
          if ((current > range.upper || !haveUpper) && current > 0) {
17012
            range.upper = current;
17013
            haveUpper = true;
17014
          }
17015
        }
17016
      }
17017
      ++it;
17018
    }
17019
  }
17020
17021
  foundRange = haveLower && haveUpper;
17022
  return range;
17023
}
17024
17025
////////////////////////////////////////////////////////////////////////////////////////////////////
17026
//////////////////// QCPCurveData
17027
////////////////////////////////////////////////////////////////////////////////////////////////////
17028
17029
/*! \class QCPCurveData
17030
  \brief Holds the data of one single data point for QCPCurve.
17031
17032
  The container for storing multiple data points is \ref QCPCurveDataMap.
17033
17034
  The stored data is:
17035
  \li \a t: the free parameter of the curve at this curve point (cp. the
17036
  mathematical vector <em>(x(t), y(t))</em>) \li \a key: coordinate on the key
17037
  axis of this curve point \li \a value: coordinate on the value axis of this
17038
  curve point
17039
17040
  \see QCPCurveDataMap
17041
*/
17042
17043
/*!
17044
  Constructs a curve data point with t, key and value set to zero.
17045
*/
17046
QCPCurveData::QCPCurveData() : t(0), key(0), value(0) {}
17047
17048
/*!
17049
  Constructs a curve data point with the specified \a t, \a key and \a value.
17050
*/
17051
QCPCurveData::QCPCurveData(double t, double key, double value)
17052
    : t(t), key(key), value(value) {}
17053
17054
////////////////////////////////////////////////////////////////////////////////////////////////////
17055
//////////////////// QCPCurve
17056
////////////////////////////////////////////////////////////////////////////////////////////////////
17057
17058
/*! \class QCPCurve
17059
  \brief A plottable representing a parametric curve in a plot.
17060
17061
  \image html QCPCurve.png
17062
17063
  Unlike QCPGraph, plottables of this type may have multiple points with the
17064
  same key coordinate, so their visual representation can have \a loops. This is
17065
  realized by introducing a third coordinate \a t, which defines the order of
17066
  the points described by the other two coordinates \a x and \a y.
17067
17068
  To plot data, assign it with the \ref setData or \ref addData functions.
17069
17070
  Gaps in the curve can be created by adding data points with NaN as key and
17071
  value
17072
  (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in
17073
  between the two data points that shall be separated.
17074
17075
  \section appearance Changing the appearance
17076
17077
  The appearance of the curve is determined by the pen and the brush (\ref
17078
  setPen, \ref setBrush). \section usage Usage
17079
17080
  Like all data representing objects in QCustomPlot, the QCPCurve is a plottable
17081
  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
17082
  (QCustomPlot::plottable, QCustomPlot::addPlottable,
17083
  QCustomPlot::removePlottable, etc.)
17084
17085
  Usually, you first create an instance and add it to the customPlot:
17086
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1
17087
  and then modify the properties of the newly created plottable, e.g.:
17088
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2
17089
*/
17090
17091
/*!
17092
  Constructs a curve which uses \a keyAxis as its key axis ("x") and \a
17093
  valueAxis as its value axis ("y"). \a keyAxis and \a valueAxis must reside in
17094
  the same QCustomPlot instance and not have the same orientation. If either of
17095
  these restrictions is violated, a corresponding message is printed to the
17096
  debug output (qDebug), the construction is not aborted, though.
17097
17098
  The constructed QCPCurve can be added to the plot with
17099
  QCustomPlot::addPlottable, QCustomPlot then takes ownership of the graph.
17100
*/
17101
QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
17102
    : QCPAbstractPlottable(keyAxis, valueAxis) {
17103
  mData = new QCPCurveDataMap;
17104
  mPen.setColor(Qt::blue);
17105
  mPen.setStyle(Qt::SolidLine);
17106
  mBrush.setColor(Qt::blue);
17107
  mBrush.setStyle(Qt::NoBrush);
17108
  mSelectedPen = mPen;
17109
  mSelectedPen.setWidthF(2.5);
17110
  mSelectedPen.setColor(QColor(80, 80, 255));  // lighter than Qt::blue of mPen
17111
  mSelectedBrush = mBrush;
17112
17113
  setScatterStyle(QCPScatterStyle());
17114
  setLineStyle(lsLine);
17115
}
17116
17117
QCPCurve::~QCPCurve() { delete mData; }
17118
17119
/*!
17120
  Replaces the current data with the provided \a data.
17121
17122
  If \a copy is set to true, data points in \a data will only be copied. if
17123
  false, the plottable takes ownership of the passed data and replaces the
17124
  internal data pointer with it. This is significantly faster than copying for
17125
  large datasets.
17126
*/
17127
void QCPCurve::setData(QCPCurveDataMap *data, bool copy) {
17128
  if (mData == data) {
17129
    qDebug() << Q_FUNC_INFO
17130
             << "The data pointer is already in (and owned by) this plottable"
17131
             << reinterpret_cast<quintptr>(data);
17132
    return;
17133
  }
17134
  if (copy) {
17135
    *mData = *data;
17136
  } else {
17137
    delete mData;
17138
    mData = data;
17139
  }
17140
}
17141
17142
/*! \overload
17143
17144
  Replaces the current data with the provided points in \a t, \a key and \a
17145
  value tuples. The provided vectors should have equal length. Else, the number
17146
  of added points will be the size of the smallest vector.
17147
*/
17148
void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key,
17149
                       const QVector<double> &value) {
17150
  mData->clear();
17151
  int n = t.size();
17152
  n = qMin(n, key.size());
17153
  n = qMin(n, value.size());
17154
  QCPCurveData newData;
17155
  for (int i = 0; i < n; ++i) {
17156
    newData.t = t[i];
17157
    newData.key = key[i];
17158
    newData.value = value[i];
17159
    mData->insertMulti(newData.t, newData);
17160
  }
17161
}
17162
17163
/*! \overload
17164
17165
  Replaces the current data with the provided \a key and \a value pairs. The t
17166
  parameter of each data point will be set to the integer index of the
17167
  respective key/value pair.
17168
*/
17169
void QCPCurve::setData(const QVector<double> &key,
17170
                       const QVector<double> &value) {
17171
  mData->clear();
17172
  int n = key.size();
17173
  n = qMin(n, value.size());
17174
  QCPCurveData newData;
17175
  for (int i = 0; i < n; ++i) {
17176
    newData.t =
17177
        i;  // no t vector given, so we assign t the index of the key/value pair
17178
    newData.key = key[i];
17179
    newData.value = value[i];
17180
    mData->insertMulti(newData.t, newData);
17181
  }
17182
}
17183
17184
/*!
17185
  Sets the visual appearance of single data points in the plot. If set to \ref
17186
  QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots
17187
  with appropriate line style).
17188
17189
  \see QCPScatterStyle, setLineStyle
17190
*/
17191
void QCPCurve::setScatterStyle(const QCPScatterStyle &style) {
17192
  mScatterStyle = style;
17193
}
17194
17195
/*!
17196
  Sets how the single data points are connected in the plot or how they are
17197
  represented visually apart from the scatter symbol. For scatter-only plots,
17198
  set \a style to \ref lsNone and \ref setScatterStyle to the desired scatter
17199
  style.
17200
17201
  \see setScatterStyle
17202
*/
17203
void QCPCurve::setLineStyle(QCPCurve::LineStyle style) { mLineStyle = style; }
17204
17205
/*!
17206
  Adds the provided data points in \a dataMap to the current data.
17207
  \see removeData
17208
*/
17209
void QCPCurve::addData(const QCPCurveDataMap &dataMap) {
17210
  mData->unite(dataMap);
17211
}
17212
17213
/*! \overload
17214
  Adds the provided single data point in \a data to the current data.
17215
  \see removeData
17216
*/
17217
void QCPCurve::addData(const QCPCurveData &data) {
17218
  mData->insertMulti(data.t, data);
17219
}
17220
17221
/*! \overload
17222
  Adds the provided single data point as \a t, \a key and \a value tuple to the
17223
  current data \see removeData
17224
*/
17225
void QCPCurve::addData(double t, double key, double value) {
17226
  QCPCurveData newData;
17227
  newData.t = t;
17228
  newData.key = key;
17229
  newData.value = value;
17230
  mData->insertMulti(newData.t, newData);
17231
}
17232
17233
/*! \overload
17234
17235
  Adds the provided single data point as \a key and \a value pair to the current
17236
  data The t parameter of the data point is set to the t of the last data point
17237
  plus 1. If there is no last data point, t will be set to 0.
17238
17239
  \see removeData
17240
*/
17241
void QCPCurve::addData(double key, double value) {
17242
  QCPCurveData newData;
17243
  if (!mData->isEmpty())
17244
    newData.t = (mData->constEnd() - 1).key() + 1;
17245
  else
17246
    newData.t = 0;
17247
  newData.key = key;
17248
  newData.value = value;
17249
  mData->insertMulti(newData.t, newData);
17250
}
17251
17252
/*! \overload
17253
  Adds the provided data points as \a t, \a key and \a value tuples to the
17254
  current data. \see removeData
17255
*/
17256
void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys,
17257
                       const QVector<double> &values) {
17258
  int n = ts.size();
17259
  n = qMin(n, keys.size());
17260
  n = qMin(n, values.size());
17261
  QCPCurveData newData;
17262
  for (int i = 0; i < n; ++i) {
17263
    newData.t = ts[i];
17264
    newData.key = keys[i];
17265
    newData.value = values[i];
17266
    mData->insertMulti(newData.t, newData);
17267
  }
17268
}
17269
17270
/*!
17271
  Removes all data points with curve parameter t smaller than \a t.
17272
  \see addData, clearData
17273
*/
17274
void QCPCurve::removeDataBefore(double t) {
17275
  QCPCurveDataMap::iterator it = mData->begin();
17276
  while (it != mData->end() && it.key() < t) it = mData->erase(it);
17277
}
17278
17279
/*!
17280
  Removes all data points with curve parameter t greater than \a t.
17281
  \see addData, clearData
17282
*/
17283
void QCPCurve::removeDataAfter(double t) {
17284
  if (mData->isEmpty()) return;
17285
  QCPCurveDataMap::iterator it = mData->upperBound(t);
17286
  while (it != mData->end()) it = mData->erase(it);
17287
}
17288
17289
/*!
17290
  Removes all data points with curve parameter t between \a fromt and \a tot. if
17291
  \a fromt is greater or equal to \a tot, the function does nothing. To remove a
17292
  single data point with known t, use \ref removeData(double t).
17293
17294
  \see addData, clearData
17295
*/
17296
void QCPCurve::removeData(double fromt, double tot) {
17297
  if (fromt >= tot || mData->isEmpty()) return;
17298
  QCPCurveDataMap::iterator it = mData->upperBound(fromt);
17299
  QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
17300
  while (it != itEnd) it = mData->erase(it);
17301
}
17302
17303
/*! \overload
17304
17305
  Removes a single data point at curve parameter \a t. If the position is not
17306
  known with absolute precision, consider using \ref removeData(double fromt,
17307
  double tot) with a small fuzziness interval around the suspected position,
17308
  depeding on the precision with which the curve parameter is known.
17309
17310
  \see addData, clearData
17311
*/
17312
void QCPCurve::removeData(double t) { mData->remove(t); }
17313
17314
/*!
17315
  Removes all data points.
17316
  \see removeData, removeDataAfter, removeDataBefore
17317
*/
17318
void QCPCurve::clearData() { mData->clear(); }
17319
17320
/* inherits documentation from base class */
17321
double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable,
17322
                            QVariant *details) const {
17323
  Q_UNUSED(details)
17324
  if ((onlySelectable && !mSelectable) || mData->isEmpty()) return -1;
17325
  if (!mKeyAxis || !mValueAxis) {
17326
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
17327
    return -1;
17328
  }
17329
17330
  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17331
    return pointDistance(pos);
17332
  else
17333
    return -1;
17334
}
17335
17336
/* inherits documentation from base class */
17337
void QCPCurve::draw(QCPPainter *painter) {
17338
  if (mData->isEmpty()) return;
17339
17340
  // allocate line vector:
17341
  QVector<QPointF> *lineData = new QVector<QPointF>;
17342
17343
  // fill with curve data:
17344
  getCurveData(lineData);
17345
17346
  // check data validity if flag set:
17347
#ifdef QCUSTOMPLOT_CHECK_DATA
17348
  QCPCurveDataMap::const_iterator it;
17349
  for (it = mData->constBegin(); it != mData->constEnd(); ++it) {
17350
    if (QCP::isInvalidData(it.value().t) ||
17351
        QCP::isInvalidData(it.value().key, it.value().value))
17352
      qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid."
17353
               << "Plottable name:" << name();
17354
  }
17355
#endif
17356
17357
  // draw curve fill:
17358
  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) {
17359
    applyFillAntialiasingHint(painter);
17360
    painter->setPen(Qt::NoPen);
17361
    painter->setBrush(mainBrush());
17362
    painter->drawPolygon(QPolygonF(*lineData));
17363
  }
17364
17365
  // draw curve line:
17366
  if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen &&
17367
      mainPen().color().alpha() != 0) {
17368
    applyDefaultAntialiasingHint(painter);
17369
    painter->setPen(mainPen());
17370
    painter->setBrush(Qt::NoBrush);
17371
    // if drawing solid line and not in PDF, use much faster line drawing
17372
    // instead of polyline:
17373
    if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17374
        painter->pen().style() == Qt::SolidLine &&
17375
        !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17376
        !painter->modes().testFlag(QCPPainter::pmNoCaching)) {
17377
      int i = 0;
17378
      bool lastIsNan = false;
17379
      const int lineDataSize = lineData->size();
17380
      while (i < lineDataSize &&
17381
             (qIsNaN(lineData->at(i).y()) ||
17382
              qIsNaN(lineData->at(i).x())))  // make sure first point is not NaN
17383
        ++i;
17384
      ++i;  // because drawing works in 1 point retrospect
17385
      while (i < lineDataSize) {
17386
        if (!qIsNaN(lineData->at(i).y()) &&
17387
            !qIsNaN(lineData->at(i).x()))  // NaNs create a gap in the line
17388
        {
17389
          if (!lastIsNan)
17390
            painter->drawLine(lineData->at(i - 1), lineData->at(i));
17391
          else
17392
            lastIsNan = false;
17393
        } else
17394
          lastIsNan = true;
17395
        ++i;
17396
      }
17397
    } else {
17398
      int segmentStart = 0;
17399
      int i = 0;
17400
      const int lineDataSize = lineData->size();
17401
      while (i < lineDataSize) {
17402
        if (qIsNaN(lineData->at(i).y()) ||
17403
            qIsNaN(lineData->at(i).x()))  // NaNs create a gap in the line
17404
        {
17405
          painter->drawPolyline(
17406
              lineData->constData() + segmentStart,
17407
              i - segmentStart);  // i, because we don't want to include the
17408
                                  // current NaN point
17409
          segmentStart = i + 1;
17410
        }
17411
        ++i;
17412
      }
17413
      // draw last segment:
17414
      painter->drawPolyline(lineData->constData() + segmentStart,
17415
                            lineDataSize - segmentStart);
17416
    }
17417
  }
17418
17419
  // draw scatters:
17420
  if (!mScatterStyle.isNone()) drawScatterPlot(painter, lineData);
17421
17422
  // free allocated line data:
17423
  delete lineData;
17424
}
17425
17426
/* inherits documentation from base class */
17427
void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
17428
  // draw fill:
17429
  if (mBrush.style() != Qt::NoBrush) {
17430
    applyFillAntialiasingHint(painter);
17431
    painter->fillRect(QRectF(rect.left(), rect.top() + rect.height() / 2.0,
17432
                             rect.width(), rect.height() / 3.0),
17433
                      mBrush);
17434
  }
17435
  // draw line vertically centered:
17436
  if (mLineStyle != lsNone) {
17437
    applyDefaultAntialiasingHint(painter);
17438
    painter->setPen(mPen);
17439
    painter->drawLine(QLineF(
17440
        rect.left(), rect.top() + rect.height() / 2.0, rect.right() + 5,
17441
        rect.top() + rect.height() / 2.0));  // +5 on x2 else last segment is
17442
                                             // missing from dashed/dotted pens
17443
  }
17444
  // draw scatter symbol:
17445
  if (!mScatterStyle.isNone()) {
17446
    applyScattersAntialiasingHint(painter);
17447
    // scale scatter pixmap if it's too large to fit in legend icon rect:
17448
    if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap &&
17449
        (mScatterStyle.pixmap().size().width() > rect.width() ||
17450
         mScatterStyle.pixmap().size().height() > rect.height())) {
17451
      QCPScatterStyle scaledStyle(mScatterStyle);
17452
      scaledStyle.setPixmap(scaledStyle.pixmap().scaled(
17453
          rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17454
      scaledStyle.applyTo(painter, mPen);
17455
      scaledStyle.drawShape(painter, QRectF(rect).center());
17456
    } else {
17457
      mScatterStyle.applyTo(painter, mPen);
17458
      mScatterStyle.drawShape(painter, QRectF(rect).center());
17459
    }
17460
  }
17461
}
17462
17463
/*! \internal
17464
17465
  Draws scatter symbols at every data point passed in \a pointData. scatter
17466
  symbols are independent of the line style and are always drawn if scatter
17467
  shape is not \ref QCPScatterStyle::ssNone.
17468
*/
17469
void QCPCurve::drawScatterPlot(QCPPainter *painter,
17470
                               const QVector<QPointF> *pointData) const {
17471
  // draw scatter point symbols:
17472
  applyScattersAntialiasingHint(painter);
17473
  mScatterStyle.applyTo(painter, mPen);
17474
  for (int i = 0; i < pointData->size(); ++i)
17475
    if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17476
      mScatterStyle.drawShape(painter, pointData->at(i));
17477
}
17478
17479
/*! \internal
17480
17481
  called by QCPCurve::draw to generate a point vector (in pixel coordinates)
17482
  which represents the line of the curve.
17483
17484
  Line segments that aren't visible in the current axis rect are handled in an
17485
  optimized way. They are projected onto a rectangle slightly larger than the
17486
  visible axis rect and simplified regarding point count. The algorithm makes
17487
  sure to preserve appearance of lines and fills inside the visible axis rect by
17488
  generating new temporary points on the outer rect if necessary.
17489
17490
  Methods that are also involved in the algorithm are: \ref getRegion, \ref
17491
  getOptimizedPoint, \ref getOptimizedCornerPoints \ref mayTraverse, \ref
17492
  getTraverse, \ref getTraverseCornerPoints.
17493
*/
17494
void QCPCurve::getCurveData(QVector<QPointF> *lineData) const {
17495
  QCPAxis *keyAxis = mKeyAxis.data();
17496
  QCPAxis *valueAxis = mValueAxis.data();
17497
  if (!keyAxis || !valueAxis) {
17498
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
17499
    return;
17500
  }
17501
17502
  // add margins to rect to compensate for stroke width
17503
  double strokeMargin =
17504
      qMax(qreal(1.0),
17505
           qreal(mainPen().widthF() * 0.75));  // stroke radius + 50% safety
17506
  if (!mScatterStyle.isNone())
17507
    strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17508
  double rectLeft = keyAxis->pixelToCoord(
17509
      keyAxis->coordToPixel(keyAxis->range().lower) -
17510
      strokeMargin *
17511
          ((keyAxis->orientation() == Qt::Vertical) != keyAxis->rangeReversed()
17512
               ? -1
17513
               : 1));
17514
  double rectRight = keyAxis->pixelToCoord(
17515
      keyAxis->coordToPixel(keyAxis->range().upper) +
17516
      strokeMargin *
17517
          ((keyAxis->orientation() == Qt::Vertical) != keyAxis->rangeReversed()
17518
               ? -1
17519
               : 1));
17520
  double rectBottom = valueAxis->pixelToCoord(
17521
      valueAxis->coordToPixel(valueAxis->range().lower) +
17522
      strokeMargin * ((valueAxis->orientation() == Qt::Horizontal) !=
17523
                              valueAxis->rangeReversed()
17524
                          ? -1
17525
                          : 1));
17526
  double rectTop = valueAxis->pixelToCoord(
17527
      valueAxis->coordToPixel(valueAxis->range().upper) -
17528
      strokeMargin * ((valueAxis->orientation() == Qt::Horizontal) !=
17529
                              valueAxis->rangeReversed()
17530
                          ? -1
17531
                          : 1));
17532
  int currentRegion;
17533
  QCPCurveDataMap::const_iterator it = mData->constBegin();
17534
  QCPCurveDataMap::const_iterator prevIt = mData->constEnd() - 1;
17535
  int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft,
17536
                             rectTop, rectRight, rectBottom);
17537
  QVector<QPointF>
17538
      trailingPoints;  // points that must be applied after all other points
17539
                       // (are generated only when handling first point to get
17540
                       // virtual segment between last and first point right)
17541
  while (it != mData->constEnd()) {
17542
    currentRegion = getRegion(it.value().key, it.value().value, rectLeft,
17543
                              rectTop, rectRight, rectBottom);
17544
    if (currentRegion !=
17545
        prevRegion)  // changed region, possibly need to add some optimized edge
17546
                     // points or original points if entering R
17547
    {
17548
      if (currentRegion !=
17549
          5)  // segment doesn't end in R, so it's a candidate for removal
17550
      {
17551
        QPointF crossA, crossB;
17552
        if (prevRegion ==
17553
            5)  // we're coming from R, so add this point optimized
17554
        {
17555
          lineData->append(
17556
              getOptimizedPoint(currentRegion, it.value().key, it.value().value,
17557
                                prevIt.value().key, prevIt.value().value,
17558
                                rectLeft, rectTop, rectRight, rectBottom));
17559
          // in the situations 5->1/7/9/3 the segment may leave R and directly
17560
          // cross through two outer regions. In these cases we need to add an
17561
          // additional corner point
17562
          *lineData << getOptimizedCornerPoints(
17563
              prevRegion, currentRegion, prevIt.value().key,
17564
              prevIt.value().value, it.value().key, it.value().value, rectLeft,
17565
              rectTop, rectRight, rectBottom);
17566
        } else if (mayTraverse(prevRegion, currentRegion) &&
17567
                   getTraverse(prevIt.value().key, prevIt.value().value,
17568
                               it.value().key, it.value().value, rectLeft,
17569
                               rectTop, rectRight, rectBottom, crossA,
17570
                               crossB)) {
17571
          // add the two cross points optimized if segment crosses R and if
17572
          // segment isn't virtual zeroth segment between last and first curve
17573
          // point:
17574
          QVector<QPointF> beforeTraverseCornerPoints,
17575
              afterTraverseCornerPoints;
17576
          getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop,
17577
                                  rectRight, rectBottom,
17578
                                  beforeTraverseCornerPoints,
17579
                                  afterTraverseCornerPoints);
17580
          if (it != mData->constBegin()) {
17581
            *lineData << beforeTraverseCornerPoints;
17582
            lineData->append(crossA);
17583
            lineData->append(crossB);
17584
            *lineData << afterTraverseCornerPoints;
17585
          } else {
17586
            lineData->append(crossB);
17587
            *lineData << afterTraverseCornerPoints;
17588
            trailingPoints << beforeTraverseCornerPoints << crossA;
17589
          }
17590
        } else  // doesn't cross R, line is just moving around in outside
17591
                // regions, so only need to add optimized point(s) at the
17592
                // boundary corner(s)
17593
        {
17594
          *lineData << getOptimizedCornerPoints(
17595
              prevRegion, currentRegion, prevIt.value().key,
17596
              prevIt.value().value, it.value().key, it.value().value, rectLeft,
17597
              rectTop, rectRight, rectBottom);
17598
        }
17599
      } else  // segment does end in R, so we add previous point optimized and
17600
              // this point at original position
17601
      {
17602
        if (it ==
17603
            mData->constBegin())  // it is first point in curve and prevIt is
17604
                                  // last one. So save optimized point for
17605
                                  // adding it to the lineData in the end
17606
          trailingPoints << getOptimizedPoint(
17607
              prevRegion, prevIt.value().key, prevIt.value().value,
17608
              it.value().key, it.value().value, rectLeft, rectTop, rectRight,
17609
              rectBottom);
17610
        else
17611
          lineData->append(getOptimizedPoint(
17612
              prevRegion, prevIt.value().key, prevIt.value().value,
17613
              it.value().key, it.value().value, rectLeft, rectTop, rectRight,
17614
              rectBottom));
17615
        lineData->append(coordsToPixels(it.value().key, it.value().value));
17616
      }
17617
    } else  // region didn't change
17618
    {
17619
      if (currentRegion == 5)  // still in R, keep adding original points
17620
      {
17621
        lineData->append(coordsToPixels(it.value().key, it.value().value));
17622
      } else  // still outside R, no need to add anything
17623
      {
17624
        // see how this is not doing anything? That's the main optimization...
17625
      }
17626
    }
17627
    prevIt = it;
17628
    prevRegion = currentRegion;
17629
    ++it;
17630
  }
17631
  *lineData << trailingPoints;
17632
}
17633
17634
/*! \internal
17635
17636
  This function is part of the curve optimization algorithm of \ref
17637
  getCurveData.
17638
17639
  It returns the region of the given point (\a x, \a y) with respect to a
17640
  rectangle defined by \a rectLeft, \a rectTop, \a rectRight, and \a rectBottom.
17641
17642
  The regions are enumerated from top to bottom and left to right:
17643
17644
  <table style="width:10em; text-align:center">
17645
    <tr><td>1</td><td>4</td><td>7</td></tr>
17646
    <tr><td>2</td><td style="border:1px solid black">5</td><td>8</td></tr>
17647
    <tr><td>3</td><td>6</td><td>9</td></tr>
17648
  </table>
17649
17650
  With the rectangle being region 5, and the outer regions extending infinitely
17651
  outwards. In the curve optimization algorithm, region 5 is considered to be
17652
  the visible portion of the plot.
17653
*/
17654
int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop,
17655
                        double rectRight, double rectBottom) const {
17656
  if (x < rectLeft)  // region 123
17657
  {
17658
    if (y > rectTop)
17659
      return 1;
17660
    else if (y < rectBottom)
17661
      return 3;
17662
    else
17663
      return 2;
17664
  } else if (x > rectRight)  // region 789
17665
  {
17666
    if (y > rectTop)
17667
      return 7;
17668
    else if (y < rectBottom)
17669
      return 9;
17670
    else
17671
      return 8;
17672
  } else  // region 456
17673
  {
17674
    if (y > rectTop)
17675
      return 4;
17676
    else if (y < rectBottom)
17677
      return 6;
17678
    else
17679
      return 5;
17680
  }
17681
}
17682
17683
/*! \internal
17684
17685
  This function is part of the curve optimization algorithm of \ref
17686
  getCurveData.
17687
17688
  This method is used in case the current segment passes from inside the visible
17689
  rect (region 5, see \ref getRegion) to any of the outer regions (\a
17690
  otherRegion). The current segment is given by the line connecting (\a key, \a
17691
  value) with (\a otherKey, \a otherValue).
17692
17693
  It returns the intersection point of the segment with the border of region 5.
17694
17695
  For this function it doesn't matter whether (\a key, \a value) is the point
17696
  inside region 5 or whether it's (\a otherKey, \a otherValue), i.e. whether the
17697
  segment is coming from region 5 or leaving it. It is important though that \a
17698
  otherRegion correctly identifies the other region not equal to 5.
17699
*/
17700
QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey,
17701
                                    double otherValue, double key, double value,
17702
                                    double rectLeft, double rectTop,
17703
                                    double rectRight, double rectBottom) const {
17704
  double intersectKey = rectLeft;   // initial value is just fail-safe
17705
  double intersectValue = rectTop;  // initial value is just fail-safe
17706
  switch (otherRegion) {
17707
    case 1:  // top and left edge
17708
    {
17709
      intersectValue = rectTop;
17710
      intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
17711
                                    (intersectValue - otherValue);
17712
      if (intersectKey < rectLeft ||
17713
          intersectKey >
17714
              rectRight)  // doesn't intersect, so must intersect other:
17715
      {
17716
        intersectKey = rectLeft;
17717
        intersectValue = otherValue + (value - otherValue) / (key - otherKey) *
17718
                                          (intersectKey - otherKey);
17719
      }
17720
      break;
17721
    }
17722
    case 2:  // left edge
17723
    {
17724
      intersectKey = rectLeft;
17725
      intersectValue = otherValue + (value - otherValue) / (key - otherKey) *
17726
                                        (intersectKey - otherKey);
17727
      break;
17728
    }
17729
    case 3:  // bottom and left edge
17730
    {
17731
      intersectValue = rectBottom;
17732
      intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
17733
                                    (intersectValue - otherValue);
17734
      if (intersectKey < rectLeft ||
17735
          intersectKey >
17736
              rectRight)  // doesn't intersect, so must intersect other:
17737
      {
17738
        intersectKey = rectLeft;
17739
        intersectValue = otherValue + (value - otherValue) / (key - otherKey) *
17740
                                          (intersectKey - otherKey);
17741
      }
17742
      break;
17743
    }
17744
    case 4:  // top edge
17745
    {
17746
      intersectValue = rectTop;
17747
      intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
17748
                                    (intersectValue - otherValue);
17749
      break;
17750
    }
17751
    case 5: {
17752
      break;  // case 5 shouldn't happen for this function but we add it anyway
17753
              // to prevent potential discontinuity in branch table
17754
    }
17755
    case 6:  // bottom edge
17756
    {
17757
      intersectValue = rectBottom;
17758
      intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
17759
                                    (intersectValue - otherValue);
17760
      break;
17761
    }
17762
    case 7:  // top and right edge
17763
    {
17764
      intersectValue = rectTop;
17765
      intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
17766
                                    (intersectValue - otherValue);
17767
      if (intersectKey < rectLeft ||
17768
          intersectKey >
17769
              rectRight)  // doesn't intersect, so must intersect other:
17770
      {
17771
        intersectKey = rectRight;
17772
        intersectValue = otherValue + (value - otherValue) / (key - otherKey) *
17773
                                          (intersectKey - otherKey);
17774
      }
17775
      break;
17776
    }
17777
    case 8:  // right edge
17778
    {
17779
      intersectKey = rectRight;
17780
      intersectValue = otherValue + (value - otherValue) / (key - otherKey) *
17781
                                        (intersectKey - otherKey);
17782
      break;
17783
    }
17784
    case 9:  // bottom and right edge
17785
    {
17786
      intersectValue = rectBottom;
17787
      intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
17788
                                    (intersectValue - otherValue);
17789
      if (intersectKey < rectLeft ||
17790
          intersectKey >
17791
              rectRight)  // doesn't intersect, so must intersect other:
17792
      {
17793
        intersectKey = rectRight;
17794
        intersectValue = otherValue + (value - otherValue) / (key - otherKey) *
17795
                                          (intersectKey - otherKey);
17796
      }
17797
      break;
17798
    }
17799
  }
17800
  return coordsToPixels(intersectKey, intersectValue);
17801
}
17802
17803
/*! \internal
17804
17805
  This function is part of the curve optimization algorithm of \ref
17806
  getCurveData.
17807
17808
  In situations where a single segment skips over multiple regions it might
17809
  become necessary to add extra points at the corners of region 5 (see \ref
17810
  getRegion) such that the optimized segment doesn't unintentionally cut through
17811
  the visible area of the axis rect and create plot artifacts. This method
17812
  provides these points that must be added, assuming the original segment
17813
  doesn't start, end, or traverse region 5. (Corner points where region 5 is
17814
  traversed are calculated by \ref getTraverseCornerPoints.)
17815
17816
  For example, consider a segment which directly goes from region 4 to 2 but
17817
  originally is far out to the top left such that it doesn't cross region 5.
17818
  Naively optimizing these points by projecting them on the top and left borders
17819
  of region 5 will create a segment that surely crosses 5, creating a visual
17820
  artifact in the plot. This method prevents this by providing extra points at
17821
  the top left corner, making the optimized curve correctly pass from region 4
17822
  to 1 to 2 without traversing 5.
17823
*/
17824
QVector<QPointF> QCPCurve::getOptimizedCornerPoints(
17825
    int prevRegion, int currentRegion, double prevKey, double prevValue,
17826
    double key, double value, double rectLeft, double rectTop, double rectRight,
17827
    double rectBottom) const {
17828
  QVector<QPointF> result;
17829
  switch (prevRegion) {
17830
    case 1: {
17831
      switch (currentRegion) {
17832
        case 2: {
17833
          result << coordsToPixels(rectLeft, rectTop);
17834
          break;
17835
        }
17836
        case 4: {
17837
          result << coordsToPixels(rectLeft, rectTop);
17838
          break;
17839
        }
17840
        case 3: {
17841
          result << coordsToPixels(rectLeft, rectTop)
17842
                 << coordsToPixels(rectLeft, rectBottom);
17843
          break;
17844
        }
17845
        case 7: {
17846
          result << coordsToPixels(rectLeft, rectTop)
17847
                 << coordsToPixels(rectRight, rectTop);
17848
          break;
17849
        }
17850
        case 6: {
17851
          result << coordsToPixels(rectLeft, rectTop)
17852
                 << coordsToPixels(rectLeft, rectBottom);
17853
          result.append(result.last());
17854
          break;
17855
        }
17856
        case 8: {
17857
          result << coordsToPixels(rectLeft, rectTop)
17858
                 << coordsToPixels(rectRight, rectTop);
17859
          result.append(result.last());
17860
          break;
17861
        }
17862
        case 9: {  // in this case we need another distinction of cases: segment
17863
                   // may pass below or above rect, requiring either bottom
17864
                   // right or top left corner points
17865
          if ((value - prevValue) / (key - prevKey) * (rectLeft - key) + value <
17866
              rectBottom)  // segment passes below R
17867
          {
17868
            result << coordsToPixels(rectLeft, rectTop)
17869
                   << coordsToPixels(rectLeft, rectBottom);
17870
            result.append(result.last());
17871
            result << coordsToPixels(rectRight, rectBottom);
17872
          } else {
17873
            result << coordsToPixels(rectLeft, rectTop)
17874
                   << coordsToPixels(rectRight, rectTop);
17875
            result.append(result.last());
17876
            result << coordsToPixels(rectRight, rectBottom);
17877
          }
17878
          break;
17879
        }
17880
      }
17881
      break;
17882
    }
17883
    case 2: {
17884
      switch (currentRegion) {
17885
        case 1: {
17886
          result << coordsToPixels(rectLeft, rectTop);
17887
          break;
17888
        }
17889
        case 3: {
17890
          result << coordsToPixels(rectLeft, rectBottom);
17891
          break;
17892
        }
17893
        case 4: {
17894
          result << coordsToPixels(rectLeft, rectTop);
17895
          result.append(result.last());
17896
          break;
17897
        }
17898
        case 6: {
17899
          result << coordsToPixels(rectLeft, rectBottom);
17900
          result.append(result.last());
17901
          break;
17902
        }
17903
        case 7: {
17904
          result << coordsToPixels(rectLeft, rectTop);
17905
          result.append(result.last());
17906
          result << coordsToPixels(rectRight, rectTop);
17907
          break;
17908
        }
17909
        case 9: {
17910
          result << coordsToPixels(rectLeft, rectBottom);
17911
          result.append(result.last());
17912
          result << coordsToPixels(rectRight, rectBottom);
17913
          break;
17914
        }
17915
      }
17916
      break;
17917
    }
17918
    case 3: {
17919
      switch (currentRegion) {
17920
        case 2: {
17921
          result << coordsToPixels(rectLeft, rectBottom);
17922
          break;
17923
        }
17924
        case 6: {
17925
          result << coordsToPixels(rectLeft, rectBottom);
17926
          break;
17927
        }
17928
        case 1: {
17929
          result << coordsToPixels(rectLeft, rectBottom)
17930
                 << coordsToPixels(rectLeft, rectTop);
17931
          break;
17932
        }
17933
        case 9: {
17934
          result << coordsToPixels(rectLeft, rectBottom)
17935
                 << coordsToPixels(rectRight, rectBottom);
17936
          break;
17937
        }
17938
        case 4: {
17939
          result << coordsToPixels(rectLeft, rectBottom)
17940
                 << coordsToPixels(rectLeft, rectTop);
17941
          result.append(result.last());
17942
          break;
17943
        }
17944
        case 8: {
17945
          result << coordsToPixels(rectLeft, rectBottom)
17946
                 << coordsToPixels(rectRight, rectBottom);
17947
          result.append(result.last());
17948
          break;
17949
        }
17950
        case 7: {  // in this case we need another distinction of cases: segment
17951
                   // may pass below or above rect, requiring either bottom
17952
                   // right or top left corner points
17953
          if ((value - prevValue) / (key - prevKey) * (rectRight - key) +
17954
                  value <
17955
              rectBottom)  // segment passes below R
17956
          {
17957
            result << coordsToPixels(rectLeft, rectBottom)
17958
                   << coordsToPixels(rectRight, rectBottom);
17959
            result.append(result.last());
17960
            result << coordsToPixels(rectRight, rectTop);
17961
          } else {
17962
            result << coordsToPixels(rectLeft, rectBottom)
17963
                   << coordsToPixels(rectLeft, rectTop);
17964
            result.append(result.last());
17965
            result << coordsToPixels(rectRight, rectTop);
17966
          }
17967
          break;
17968
        }
17969
      }
17970
      break;
17971
    }
17972
    case 4: {
17973
      switch (currentRegion) {
17974
        case 1: {
17975
          result << coordsToPixels(rectLeft, rectTop);
17976
          break;
17977
        }
17978
        case 7: {
17979
          result << coordsToPixels(rectRight, rectTop);
17980
          break;
17981
        }
17982
        case 2: {
17983
          result << coordsToPixels(rectLeft, rectTop);
17984
          result.append(result.last());
17985
          break;
17986
        }
17987
        case 8: {
17988
          result << coordsToPixels(rectRight, rectTop);
17989
          result.append(result.last());
17990
          break;
17991
        }
17992
        case 3: {
17993
          result << coordsToPixels(rectLeft, rectTop);
17994
          result.append(result.last());
17995
          result << coordsToPixels(rectLeft, rectBottom);
17996
          break;
17997
        }
17998
        case 9: {
17999
          result << coordsToPixels(rectRight, rectTop);
18000
          result.append(result.last());
18001
          result << coordsToPixels(rectRight, rectBottom);
18002
          break;
18003
        }
18004
      }
18005
      break;
18006
    }
18007
    case 5: {
18008
      switch (currentRegion) {
18009
        case 1: {
18010
          result << coordsToPixels(rectLeft, rectTop);
18011
          break;
18012
        }
18013
        case 7: {
18014
          result << coordsToPixels(rectRight, rectTop);
18015
          break;
18016
        }
18017
        case 9: {
18018
          result << coordsToPixels(rectRight, rectBottom);
18019
          break;
18020
        }
18021
        case 3: {
18022
          result << coordsToPixels(rectLeft, rectBottom);
18023
          break;
18024
        }
18025
      }
18026
      break;
18027
    }
18028
    case 6: {
18029
      switch (currentRegion) {
18030
        case 3: {
18031
          result << coordsToPixels(rectLeft, rectBottom);
18032
          break;
18033
        }
18034
        case 9: {
18035
          result << coordsToPixels(rectRight, rectBottom);
18036
          break;
18037
        }
18038
        case 2: {
18039
          result << coordsToPixels(rectLeft, rectBottom);
18040
          result.append(result.last());
18041
          break;
18042
        }
18043
        case 8: {
18044
          result << coordsToPixels(rectRight, rectBottom);
18045
          result.append(result.last());
18046
          break;
18047
        }
18048
        case 1: {
18049
          result << coordsToPixels(rectLeft, rectBottom);
18050
          result.append(result.last());
18051
          result << coordsToPixels(rectLeft, rectTop);
18052
          break;
18053
        }
18054
        case 7: {
18055
          result << coordsToPixels(rectRight, rectBottom);
18056
          result.append(result.last());
18057
          result << coordsToPixels(rectRight, rectTop);
18058
          break;
18059
        }
18060
      }
18061
      break;
18062
    }
18063
    case 7: {
18064
      switch (currentRegion) {
18065
        case 4: {
18066
          result << coordsToPixels(rectRight, rectTop);
18067
          break;
18068
        }
18069
        case 8: {
18070
          result << coordsToPixels(rectRight, rectTop);
18071
          break;
18072
        }
18073
        case 1: {
18074
          result << coordsToPixels(rectRight, rectTop)
18075
                 << coordsToPixels(rectLeft, rectTop);
18076
          break;
18077
        }
18078
        case 9: {
18079
          result << coordsToPixels(rectRight, rectTop)
18080
                 << coordsToPixels(rectRight, rectBottom);
18081
          break;
18082
        }
18083
        case 2: {
18084
          result << coordsToPixels(rectRight, rectTop)
18085
                 << coordsToPixels(rectLeft, rectTop);
18086
          result.append(result.last());
18087
          break;
18088
        }
18089
        case 6: {
18090
          result << coordsToPixels(rectRight, rectTop)
18091
                 << coordsToPixels(rectRight, rectBottom);
18092
          result.append(result.last());
18093
          break;
18094
        }
18095
        case 3: {  // in this case we need another distinction of cases: segment
18096
                   // may pass below or above rect, requiring either bottom
18097
                   // right or top left corner points
18098
          if ((value - prevValue) / (key - prevKey) * (rectRight - key) +
18099
                  value <
18100
              rectBottom)  // segment passes below R
18101
          {
18102
            result << coordsToPixels(rectRight, rectTop)
18103
                   << coordsToPixels(rectRight, rectBottom);
18104
            result.append(result.last());
18105
            result << coordsToPixels(rectLeft, rectBottom);
18106
          } else {
18107
            result << coordsToPixels(rectRight, rectTop)
18108
                   << coordsToPixels(rectLeft, rectTop);
18109
            result.append(result.last());
18110
            result << coordsToPixels(rectLeft, rectBottom);
18111
          }
18112
          break;
18113
        }
18114
      }
18115
      break;
18116
    }
18117
    case 8: {
18118
      switch (currentRegion) {
18119
        case 7: {
18120
          result << coordsToPixels(rectRight, rectTop);
18121
          break;
18122
        }
18123
        case 9: {
18124
          result << coordsToPixels(rectRight, rectBottom);
18125
          break;
18126
        }
18127
        case 4: {
18128
          result << coordsToPixels(rectRight, rectTop);
18129
          result.append(result.last());
18130
          break;
18131
        }
18132
        case 6: {
18133
          result << coordsToPixels(rectRight, rectBottom);
18134
          result.append(result.last());
18135
          break;
18136
        }
18137
        case 1: {
18138
          result << coordsToPixels(rectRight, rectTop);
18139
          result.append(result.last());
18140
          result << coordsToPixels(rectLeft, rectTop);
18141
          break;
18142
        }
18143
        case 3: {
18144
          result << coordsToPixels(rectRight, rectBottom);
18145
          result.append(result.last());
18146
          result << coordsToPixels(rectLeft, rectBottom);
18147
          break;
18148
        }
18149
      }
18150
      break;
18151
    }
18152
    case 9: {
18153
      switch (currentRegion) {
18154
        case 6: {
18155
          result << coordsToPixels(rectRight, rectBottom);
18156
          break;
18157
        }
18158
        case 8: {
18159
          result << coordsToPixels(rectRight, rectBottom);
18160
          break;
18161
        }
18162
        case 3: {
18163
          result << coordsToPixels(rectRight, rectBottom)
18164
                 << coordsToPixels(rectLeft, rectBottom);
18165
          break;
18166
        }
18167
        case 7: {
18168
          result << coordsToPixels(rectRight, rectBottom)
18169
                 << coordsToPixels(rectRight, rectTop);
18170
          break;
18171
        }
18172
        case 2: {
18173
          result << coordsToPixels(rectRight, rectBottom)
18174
                 << coordsToPixels(rectLeft, rectBottom);
18175
          result.append(result.last());
18176
          break;
18177
        }
18178
        case 4: {
18179
          result << coordsToPixels(rectRight, rectBottom)
18180
                 << coordsToPixels(rectRight, rectTop);
18181
          result.append(result.last());
18182
          break;
18183
        }
18184
        case 1: {  // in this case we need another distinction of cases: segment
18185
                   // may pass below or above rect, requiring either bottom
18186
                   // right or top left corner points
18187
          if ((value - prevValue) / (key - prevKey) * (rectLeft - key) + value <
18188
              rectBottom)  // segment passes below R
18189
          {
18190
            result << coordsToPixels(rectRight, rectBottom)
18191
                   << coordsToPixels(rectLeft, rectBottom);
18192
            result.append(result.last());
18193
            result << coordsToPixels(rectLeft, rectTop);
18194
          } else {
18195
            result << coordsToPixels(rectRight, rectBottom)
18196
                   << coordsToPixels(rectRight, rectTop);
18197
            result.append(result.last());
18198
            result << coordsToPixels(rectLeft, rectTop);
18199
          }
18200
          break;
18201
        }
18202
      }
18203
      break;
18204
    }
18205
  }
18206
  return result;
18207
}
18208
18209
/*! \internal
18210
18211
  This function is part of the curve optimization algorithm of \ref
18212
  getCurveData.
18213
18214
  This method returns whether a segment going from \a prevRegion to \a
18215
  currentRegion (see \ref getRegion) may traverse the visible region 5. This
18216
  function assumes that neither \a prevRegion nor \a currentRegion is 5 itself.
18217
18218
  If this method returns false, the segment for sure doesn't pass region 5. If
18219
  it returns true, the segment may or may not pass region 5 and a more
18220
  fine-grained calculation must be used (\ref getTraverse).
18221
*/
18222
bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const {
18223
  switch (prevRegion) {
18224
    case 1: {
18225
      switch (currentRegion) {
18226
        case 4:
18227
        case 7:
18228
        case 2:
18229
        case 3:
18230
          return false;
18231
        default:
18232
          return true;
18233
      }
18234
    }
18235
    case 2: {
18236
      switch (currentRegion) {
18237
        case 1:
18238
        case 3:
18239
          return false;
18240
        default:
18241
          return true;
18242
      }
18243
    }
18244
    case 3: {
18245
      switch (currentRegion) {
18246
        case 1:
18247
        case 2:
18248
        case 6:
18249
        case 9:
18250
          return false;
18251
        default:
18252
          return true;
18253
      }
18254
    }
18255
    case 4: {
18256
      switch (currentRegion) {
18257
        case 1:
18258
        case 7:
18259
          return false;
18260
        default:
18261
          return true;
18262
      }
18263
    }
18264
    case 5:
18265
      return false;  // should never occur
18266
    case 6: {
18267
      switch (currentRegion) {
18268
        case 3:
18269
        case 9:
18270
          return false;
18271
        default:
18272
          return true;
18273
      }
18274
    }
18275
    case 7: {
18276
      switch (currentRegion) {
18277
        case 1:
18278
        case 4:
18279
        case 8:
18280
        case 9:
18281
          return false;
18282
        default:
18283
          return true;
18284
      }
18285
    }
18286
    case 8: {
18287
      switch (currentRegion) {
18288
        case 7:
18289
        case 9:
18290
          return false;
18291
        default:
18292
          return true;
18293
      }
18294
    }
18295
    case 9: {
18296
      switch (currentRegion) {
18297
        case 3:
18298
        case 6:
18299
        case 8:
18300
        case 7:
18301
          return false;
18302
        default:
18303
          return true;
18304
      }
18305
    }
18306
    default:
18307
      return true;
18308
  }
18309
}
18310
18311
/*! \internal
18312
18313
  This function is part of the curve optimization algorithm of \ref
18314
  getCurveData.
18315
18316
  This method assumes that the \ref mayTraverse test has returned true, so there
18317
  is a chance the segment defined by (\a prevKey, \a prevValue) and (\a key, \a
18318
  value) goes through the visible region 5.
18319
18320
  The return value of this method indicates whether the segment actually
18321
  traverses region 5 or not.
18322
18323
  If the segment traverses 5, the output parameters \a crossA and \a crossB
18324
  indicate the entry and exit points of region 5. They will become the optimized
18325
  points for that segment.
18326
*/
18327
bool QCPCurve::getTraverse(double prevKey, double prevValue, double key,
18328
                           double value, double rectLeft, double rectTop,
18329
                           double rectRight, double rectBottom, QPointF &crossA,
18330
                           QPointF &crossB) const {
18331
  QList<QPointF>
18332
      intersections;  // x of QPointF corresponds to key and y to value
18333
  if (qFuzzyIsNull(key - prevKey))  // line is parallel to value axis
18334
  {
18335
    // due to region filter in mayTraverseR(), if line is parallel to value or
18336
    // key axis, R is traversed here
18337
    intersections.append(QPointF(
18338
        key, rectBottom));  // direction will be taken care of at end of method
18339
    intersections.append(QPointF(key, rectTop));
18340
  } else if (qFuzzyIsNull(value - prevValue))  // line is parallel to key axis
18341
  {
18342
    // due to region filter in mayTraverseR(), if line is parallel to value or
18343
    // key axis, R is traversed here
18344
    intersections.append(QPointF(
18345
        rectLeft, value));  // direction will be taken care of at end of method
18346
    intersections.append(QPointF(rectRight, value));
18347
  } else  // line is skewed
18348
  {
18349
    double gamma;
18350
    double keyPerValue = (key - prevKey) / (value - prevValue);
18351
    // check top of rect:
18352
    gamma = prevKey + (rectTop - prevValue) * keyPerValue;
18353
    if (gamma >= rectLeft && gamma <= rectRight)
18354
      intersections.append(QPointF(gamma, rectTop));
18355
    // check bottom of rect:
18356
    gamma = prevKey + (rectBottom - prevValue) * keyPerValue;
18357
    if (gamma >= rectLeft && gamma <= rectRight)
18358
      intersections.append(QPointF(gamma, rectBottom));
18359
    double valuePerKey = 1.0 / keyPerValue;
18360
    // check left of rect:
18361
    gamma = prevValue + (rectLeft - prevKey) * valuePerKey;
18362
    if (gamma >= rectBottom && gamma <= rectTop)
18363
      intersections.append(QPointF(rectLeft, gamma));
18364
    // check right of rect:
18365
    gamma = prevValue + (rectRight - prevKey) * valuePerKey;
18366
    if (gamma >= rectBottom && gamma <= rectTop)
18367
      intersections.append(QPointF(rectRight, gamma));
18368
  }
18369
18370
  // handle cases where found points isn't exactly 2:
18371
  if (intersections.size() > 2) {
18372
    // line probably goes through corner of rect, and we got duplicate points
18373
    // there. single out the point pair with greatest distance in between:
18374
    double distSqrMax = 0;
18375
    QPointF pv1, pv2;
18376
    for (int i = 0; i < intersections.size() - 1; ++i) {
18377
      for (int k = i + 1; k < intersections.size(); ++k) {
18378
        QPointF distPoint = intersections.at(i) - intersections.at(k);
18379
        double distSqr =
18380
            distPoint.x() * distPoint.x() + distPoint.y() + distPoint.y();
18381
        if (distSqr > distSqrMax) {
18382
          pv1 = intersections.at(i);
18383
          pv2 = intersections.at(k);
18384
          distSqrMax = distSqr;
18385
        }
18386
      }
18387
    }
18388
    intersections = QList<QPointF>() << pv1 << pv2;
18389
  } else if (intersections.size() != 2) {
18390
    // one or even zero points found (shouldn't happen unless line perfectly
18391
    // tangent to corner), no need to draw segment
18392
    return false;
18393
  }
18394
18395
  // possibly re-sort points so optimized point segment has same direction as
18396
  // original segment:
18397
  if ((key - prevKey) * (intersections.at(1).x() - intersections.at(0).x()) +
18398
          (value - prevValue) *
18399
              (intersections.at(1).y() - intersections.at(0).y()) <
18400
      0)  // scalar product of both segments < 0 -> opposite direction
18401
    intersections.move(0, 1);
18402
  crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
18403
  crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
18404
  return true;
18405
}
18406
18407
/*! \internal
18408
18409
  This function is part of the curve optimization algorithm of \ref
18410
  getCurveData.
18411
18412
  This method assumes that the \ref getTraverse test has returned true, so the
18413
  segment definitely traverses the visible region 5 when going from \a
18414
  prevRegion to \a currentRegion.
18415
18416
  In certain situations it is not sufficient to merely generate the entry and
18417
  exit points of the segment into/out of region 5, as \ref getTraverse provides.
18418
  It may happen that a single segment, in addition to traversing region 5, skips
18419
  another region outside of region 5, which makes it necessary to add an
18420
  optimized corner point there (very similar to the job \ref
18421
  getOptimizedCornerPoints does for segments that are completely in outside
18422
  regions and don't traverse 5).
18423
18424
  As an example, consider a segment going from region 1 to region 6, traversing
18425
  the lower left corner of region 5. In this configuration, the segment
18426
  additionally crosses the border between region 1 and 2 before entering
18427
  region 5. This makes it necessary to add an additional point in the top left
18428
  corner, before adding the optimized traverse points. So in this case, the
18429
  output parameter \a beforeTraverse will contain the top left corner point, and
18430
  \a afterTraverse will be empty.
18431
18432
  In some cases, such as when going from region 1 to 9, it may even be necessary
18433
  to add additional corner points before and after the traverse. Then both \a
18434
  beforeTraverse and \a afterTraverse return the respective corner points.
18435
*/
18436
void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion,
18437
                                       double rectLeft, double rectTop,
18438
                                       double rectRight, double rectBottom,
18439
                                       QVector<QPointF> &beforeTraverse,
18440
                                       QVector<QPointF> &afterTraverse) const {
18441
  switch (prevRegion) {
18442
    case 1: {
18443
      switch (currentRegion) {
18444
        case 6: {
18445
          beforeTraverse << coordsToPixels(rectLeft, rectTop);
18446
          break;
18447
        }
18448
        case 9: {
18449
          beforeTraverse << coordsToPixels(rectLeft, rectTop);
18450
          afterTraverse << coordsToPixels(rectRight, rectBottom);
18451
          break;
18452
        }
18453
        case 8: {
18454
          beforeTraverse << coordsToPixels(rectLeft, rectTop);
18455
          break;
18456
        }
18457
      }
18458
      break;
18459
    }
18460
    case 2: {
18461
      switch (currentRegion) {
18462
        case 7: {
18463
          afterTraverse << coordsToPixels(rectRight, rectTop);
18464
          break;
18465
        }
18466
        case 9: {
18467
          afterTraverse << coordsToPixels(rectRight, rectBottom);
18468
          break;
18469
        }
18470
      }
18471
      break;
18472
    }
18473
    case 3: {
18474
      switch (currentRegion) {
18475
        case 4: {
18476
          beforeTraverse << coordsToPixels(rectLeft, rectBottom);
18477
          break;
18478
        }
18479
        case 7: {
18480
          beforeTraverse << coordsToPixels(rectLeft, rectBottom);
18481
          afterTraverse << coordsToPixels(rectRight, rectTop);
18482
          break;
18483
        }
18484
        case 8: {
18485
          beforeTraverse << coordsToPixels(rectLeft, rectBottom);
18486
          break;
18487
        }
18488
      }
18489
      break;
18490
    }
18491
    case 4: {
18492
      switch (currentRegion) {
18493
        case 3: {
18494
          afterTraverse << coordsToPixels(rectLeft, rectBottom);
18495
          break;
18496
        }
18497
        case 9: {
18498
          afterTraverse << coordsToPixels(rectRight, rectBottom);
18499
          break;
18500
        }
18501
      }
18502
      break;
18503
    }
18504
    case 5: {
18505
      break;
18506
    }  // shouldn't happen because this method only handles full traverses
18507
    case 6: {
18508
      switch (currentRegion) {
18509
        case 1: {
18510
          afterTraverse << coordsToPixels(rectLeft, rectTop);
18511
          break;
18512
        }
18513
        case 7: {
18514
          afterTraverse << coordsToPixels(rectRight, rectTop);
18515
          break;
18516
        }
18517
      }
18518
      break;
18519
    }
18520
    case 7: {
18521
      switch (currentRegion) {
18522
        case 2: {
18523
          beforeTraverse << coordsToPixels(rectRight, rectTop);
18524
          break;
18525
        }
18526
        case 3: {
18527
          beforeTraverse << coordsToPixels(rectRight, rectTop);
18528
          afterTraverse << coordsToPixels(rectLeft, rectBottom);
18529
          break;
18530
        }
18531
        case 6: {
18532
          beforeTraverse << coordsToPixels(rectRight, rectTop);
18533
          break;
18534
        }
18535
      }
18536
      break;
18537
    }
18538
    case 8: {
18539
      switch (currentRegion) {
18540
        case 1: {
18541
          afterTraverse << coordsToPixels(rectLeft, rectTop);
18542
          break;
18543
        }
18544
        case 3: {
18545
          afterTraverse << coordsToPixels(rectLeft, rectBottom);
18546
          break;
18547
        }
18548
      }
18549
      break;
18550
    }
18551
    case 9: {
18552
      switch (currentRegion) {
18553
        case 2: {
18554
          beforeTraverse << coordsToPixels(rectRight, rectBottom);
18555
          break;
18556
        }
18557
        case 1: {
18558
          beforeTraverse << coordsToPixels(rectRight, rectBottom);
18559
          afterTraverse << coordsToPixels(rectLeft, rectTop);
18560
          break;
18561
        }
18562
        case 4: {
18563
          beforeTraverse << coordsToPixels(rectRight, rectBottom);
18564
          break;
18565
        }
18566
      }
18567
      break;
18568
    }
18569
  }
18570
}
18571
18572
/*! \internal
18573
18574
  Calculates the (minimum) distance (in pixels) the curve's representation has
18575
  from the given \a pixelPoint in pixels. This is used to determine whether the
18576
  curve was clicked or not, e.g. in \ref selectTest.
18577
*/
18578
double QCPCurve::pointDistance(const QPointF &pixelPoint) const {
18579
  if (mData->isEmpty()) {
18580
    qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName
18581
             << "without data";
18582
    return 500;
18583
  }
18584
  if (mData->size() == 1) {
18585
    QPointF dataPoint = coordsToPixels(mData->constBegin().key(),
18586
                                       mData->constBegin().value().value);
18587
    return QVector2D(dataPoint - pixelPoint).length();
18588
  }
18589
18590
  // calculate minimum distance to line segments:
18591
  QVector<QPointF> *lineData = new QVector<QPointF>;
18592
  getCurveData(lineData);
18593
  double minDistSqr = std::numeric_limits<double>::max();
18594
  for (int i = 0; i < lineData->size() - 1; ++i) {
18595
    double currentDistSqr =
18596
        distSqrToLine(lineData->at(i), lineData->at(i + 1), pixelPoint);
18597
    if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
18598
  }
18599
  delete lineData;
18600
  return qSqrt(minDistSqr);
18601
}
18602
18603
/* inherits documentation from base class */
18604
QCPRange QCPCurve::getKeyRange(bool &foundRange,
18605
                               SignDomain inSignDomain) const {
18606
  QCPRange range;
18607
  bool haveLower = false;
18608
  bool haveUpper = false;
18609
18610
  double current;
18611
18612
  QCPCurveDataMap::const_iterator it = mData->constBegin();
18613
  while (it != mData->constEnd()) {
18614
    current = it.value().key;
18615
    if (!qIsNaN(current) && !qIsNaN(it.value().value)) {
18616
      if (inSignDomain == sdBoth ||
18617
          (inSignDomain == sdNegative && current < 0) ||
18618
          (inSignDomain == sdPositive && current > 0)) {
18619
        if (current < range.lower || !haveLower) {
18620
          range.lower = current;
18621
          haveLower = true;
18622
        }
18623
        if (current > range.upper || !haveUpper) {
18624
          range.upper = current;
18625
          haveUpper = true;
18626
        }
18627
      }
18628
    }
18629
    ++it;
18630
  }
18631
18632
  foundRange = haveLower && haveUpper;
18633
  return range;
18634
}
18635
18636
/* inherits documentation from base class */
18637
QCPRange QCPCurve::getValueRange(bool &foundRange,
18638
                                 SignDomain inSignDomain) const {
18639
  QCPRange range;
18640
  bool haveLower = false;
18641
  bool haveUpper = false;
18642
18643
  double current;
18644
18645
  QCPCurveDataMap::const_iterator it = mData->constBegin();
18646
  while (it != mData->constEnd()) {
18647
    current = it.value().value;
18648
    if (!qIsNaN(current) && !qIsNaN(it.value().key)) {
18649
      if (inSignDomain == sdBoth ||
18650
          (inSignDomain == sdNegative && current < 0) ||
18651
          (inSignDomain == sdPositive && current > 0)) {
18652
        if (current < range.lower || !haveLower) {
18653
          range.lower = current;
18654
          haveLower = true;
18655
        }
18656
        if (current > range.upper || !haveUpper) {
18657
          range.upper = current;
18658
          haveUpper = true;
18659
        }
18660
      }
18661
    }
18662
    ++it;
18663
  }
18664
18665
  foundRange = haveLower && haveUpper;
18666
  return range;
18667
}
18668
18669
////////////////////////////////////////////////////////////////////////////////////////////////////
18670
//////////////////// QCPBarsGroup
18671
////////////////////////////////////////////////////////////////////////////////////////////////////
18672
18673
/*! \class QCPBarsGroup
18674
  \brief Groups multiple QCPBars together so they appear side by side
18675
18676
  \image html QCPBarsGroup.png
18677
18678
  When showing multiple QCPBars in one plot which have bars at identical keys,
18679
  it may be desirable to have them appearing next to each other at each key.
18680
  This is what adding the respective QCPBars plottables to a QCPBarsGroup
18681
  achieves. (An alternative approach is to stack them on top of each other, see
18682
  \ref QCPBars::moveAbove.)
18683
18684
  \section qcpbarsgroup-usage Usage
18685
18686
  To add a QCPBars plottable to the group, create a new group and then add the
18687
  respective bars intances: \snippet
18688
  documentation/doc-code-snippets/mainwindow.cpp qcpbarsgroup-creation
18689
  Alternatively to appending to the group like shown above, you can also set the
18690
  group on the QCPBars plottable via \ref QCPBars::setBarsGroup.
18691
18692
  The spacing between the bars can be configured via \ref setSpacingType and
18693
  \ref setSpacing. The bars in this group appear in the plot in the order they
18694
  were appended. To insert a bars plottable at a certain index position, or to
18695
  reposition a bars plottable which is already in the group, use \ref insert.
18696
18697
  To remove specific bars from the group, use either \ref remove or call \ref
18698
  QCPBars::setBarsGroup "QCPBars::setBarsGroup(0)" on the respective bars
18699
  plottable.
18700
18701
  To clear the entire group, call \ref clear, or simply delete the group.
18702
18703
  \section qcpbarsgroup-example Example
18704
18705
  The image above is generated with the following code:
18706
  \snippet documentation/doc-image-generator/mainwindow.cpp qcpbarsgroup-example
18707
*/
18708
18709
/* start of documentation of inline functions */
18710
18711
/*! \fn QList<QCPBars*> QCPBarsGroup::bars() const
18712
18713
  Returns all bars currently in this group.
18714
18715
  \see bars(int index)
18716
*/
18717
18718
/*! \fn int QCPBarsGroup::size() const
18719
18720
  Returns the number of QCPBars plottables that are part of this group.
18721
18722
*/
18723
18724
/*! \fn bool QCPBarsGroup::isEmpty() const
18725
18726
  Returns whether this bars group is empty.
18727
18728
  \see size
18729
*/
18730
18731
/*! \fn bool QCPBarsGroup::contains(QCPBars *bars)
18732
18733
  Returns whether the specified \a bars plottable is part of this group.
18734
18735
*/
18736
18737
/* end of documentation of inline functions */
18738
18739
/*!
18740
  Constructs a new bars group for the specified QCustomPlot instance.
18741
*/
18742
QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot)
18743
    : QObject(parentPlot),
18744
      mParentPlot(parentPlot),
18745
      mSpacingType(stAbsolute),
18746
      mSpacing(4) {}
18747
18748
QCPBarsGroup::~QCPBarsGroup() { clear(); }
18749
18750
/*!
18751
  Sets how the spacing between adjacent bars is interpreted. See \ref
18752
  SpacingType.
18753
18754
  The actual spacing can then be specified with \ref setSpacing.
18755
18756
  \see setSpacing
18757
*/
18758
void QCPBarsGroup::setSpacingType(SpacingType spacingType) {
18759
  mSpacingType = spacingType;
18760
}
18761
18762
/*!
18763
  Sets the spacing between adjacent bars. What the number passed as \a spacing
18764
  actually means, is defined by the current \ref SpacingType, which can be set
18765
  with \ref setSpacingType.
18766
18767
  \see setSpacingType
18768
*/
18769
void QCPBarsGroup::setSpacing(double spacing) { mSpacing = spacing; }
18770
18771
/*!
18772
  Returns the QCPBars instance with the specified \a index in this group. If no
18773
  such QCPBars exists, returns 0.
18774
18775
  \see bars(), size
18776
*/
18777
QCPBars *QCPBarsGroup::bars(int index) const {
18778
  if (index >= 0 && index < mBars.size()) {
18779
    return mBars.at(index);
18780
  } else {
18781
    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
18782
    return 0;
18783
  }
18784
}
18785
18786
/*!
18787
  Removes all QCPBars plottables from this group.
18788
18789
  \see isEmpty
18790
*/
18791
void QCPBarsGroup::clear() {
18792
  foreach (
18793
      QCPBars *bars,
18794
      mBars)  // since foreach takes a copy, removing bars in the loop is okay
18795
    bars->setBarsGroup(0);  // removes itself via removeBars
18796
}
18797
18798
/*!
18799
  Adds the specified \a bars plottable to this group. Alternatively, you can
18800
  also use \ref QCPBars::setBarsGroup on the \a bars instance.
18801
18802
  \see insert, remove
18803
*/
18804
void QCPBarsGroup::append(QCPBars *bars) {
18805
  if (!bars) {
18806
    qDebug() << Q_FUNC_INFO << "bars is 0";
18807
    return;
18808
  }
18809
18810
  if (!mBars.contains(bars))
18811
    bars->setBarsGroup(this);
18812
  else
18813
    qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:"
18814
             << reinterpret_cast<quintptr>(bars);
18815
}
18816
18817
/*!
18818
  Inserts the specified \a bars plottable into this group at the specified index
18819
  position \a i. This gives you full control over the ordering of the bars.
18820
18821
  \a bars may already be part of this group. In that case, \a bars is just moved
18822
  to the new index position.
18823
18824
  \see append, remove
18825
*/
18826
void QCPBarsGroup::insert(int i, QCPBars *bars) {
18827
  if (!bars) {
18828
    qDebug() << Q_FUNC_INFO << "bars is 0";
18829
    return;
18830
  }
18831
18832
  // first append to bars list normally:
18833
  if (!mBars.contains(bars)) bars->setBarsGroup(this);
18834
  // then move to according position:
18835
  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size() - 1));
18836
}
18837
18838
/*!
18839
  Removes the specified \a bars plottable from this group.
18840
18841
  \see contains, clear
18842
*/
18843
void QCPBarsGroup::remove(QCPBars *bars) {
18844
  if (!bars) {
18845
    qDebug() << Q_FUNC_INFO << "bars is 0";
18846
    return;
18847
  }
18848
18849
  if (mBars.contains(bars))
18850
    bars->setBarsGroup(0);
18851
  else
18852
    qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:"
18853
             << reinterpret_cast<quintptr>(bars);
18854
}
18855
18856
/*! \internal
18857
18858
  Adds the specified \a bars to the internal mBars list of bars. This method
18859
  does not change the barsGroup property on \a bars.
18860
18861
  \see unregisterBars
18862
*/
18863
void QCPBarsGroup::registerBars(QCPBars *bars) {
18864
  if (!mBars.contains(bars)) mBars.append(bars);
18865
}
18866
18867
/*! \internal
18868
18869
  Removes the specified \a bars from the internal mBars list of bars. This
18870
  method does not change the barsGroup property on \a bars.
18871
18872
  \see registerBars
18873
*/
18874
void QCPBarsGroup::unregisterBars(QCPBars *bars) { mBars.removeOne(bars); }
18875
18876
/*! \internal
18877
18878
  Returns the pixel offset in the key dimension the specified \a bars plottable
18879
  should have at the given key coordinate \a keyCoord. The offset is relative to
18880
  the pixel position of the key coordinate \a keyCoord.
18881
*/
18882
double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord) {
18883
  // find list of all base bars in case some mBars are stacked:
18884
  QList<const QCPBars *> baseBars;
18885
  foreach (const QCPBars *b, mBars) {
18886
    while (b->barBelow()) b = b->barBelow();
18887
    if (!baseBars.contains(b)) baseBars.append(b);
18888
  }
18889
  // find base bar this "bars" is stacked on:
18890
  const QCPBars *thisBase = bars;
18891
  while (thisBase->barBelow()) thisBase = thisBase->barBelow();
18892
18893
  // determine key pixel offset of this base bars considering all other base
18894
  // bars in this barsgroup:
18895
  double result = 0;
18896
  int index = baseBars.indexOf(thisBase);
18897
  if (index >= 0) {
18898
    int startIndex;
18899
    double lowerPixelWidth, upperPixelWidth;
18900
    if (baseBars.size() % 2 == 1 &&
18901
        index == (baseBars.size() - 1) /
18902
                     2)  // is center bar (int division on purpose)
18903
    {
18904
      return result;
18905
    } else if (index <
18906
               (baseBars.size() - 1) / 2.0)  // bar is to the left of center
18907
    {
18908
      if (baseBars.size() % 2 == 0)  // even number of bars
18909
      {
18910
        startIndex = baseBars.size() / 2 - 1;
18911
        result -= getPixelSpacing(baseBars.at(startIndex), keyCoord) *
18912
                  0.5;  // half of middle spacing
18913
      } else            // uneven number of bars
18914
      {
18915
        startIndex = (baseBars.size() - 1) / 2 - 1;
18916
        baseBars.at((baseBars.size() - 1) / 2)
18917
            ->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18918
        result -= qAbs(upperPixelWidth - lowerPixelWidth) *
18919
                  0.5;  // half of center bar
18920
        result -= getPixelSpacing(baseBars.at((baseBars.size() - 1) / 2),
18921
                                  keyCoord);  // center bar spacing
18922
      }
18923
      for (int i = startIndex; i > index;
18924
           --i)  // add widths and spacings of bars in between center and our
18925
                 // bars
18926
      {
18927
        baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth,
18928
                                      upperPixelWidth);
18929
        result -= qAbs(upperPixelWidth - lowerPixelWidth);
18930
        result -= getPixelSpacing(baseBars.at(i), keyCoord);
18931
      }
18932
      // finally half of our bars width:
18933
      baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth,
18934
                                        upperPixelWidth);
18935
      result -= qAbs(upperPixelWidth - lowerPixelWidth) * 0.5;
18936
    } else  // bar is to the right of center
18937
    {
18938
      if (baseBars.size() % 2 == 0)  // even number of bars
18939
      {
18940
        startIndex = baseBars.size() / 2;
18941
        result += getPixelSpacing(baseBars.at(startIndex), keyCoord) *
18942
                  0.5;  // half of middle spacing
18943
      } else            // uneven number of bars
18944
      {
18945
        startIndex = (baseBars.size() - 1) / 2 + 1;
18946
        baseBars.at((baseBars.size() - 1) / 2)
18947
            ->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18948
        result += qAbs(upperPixelWidth - lowerPixelWidth) *
18949
                  0.5;  // half of center bar
18950
        result += getPixelSpacing(baseBars.at((baseBars.size() - 1) / 2),
18951
                                  keyCoord);  // center bar spacing
18952
      }
18953
      for (int i = startIndex; i < index;
18954
           ++i)  // add widths and spacings of bars in between center and our
18955
                 // bars
18956
      {
18957
        baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth,
18958
                                      upperPixelWidth);
18959
        result += qAbs(upperPixelWidth - lowerPixelWidth);
18960
        result += getPixelSpacing(baseBars.at(i), keyCoord);
18961
      }
18962
      // finally half of our bars width:
18963
      baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth,
18964
                                        upperPixelWidth);
18965
      result += qAbs(upperPixelWidth - lowerPixelWidth) * 0.5;
18966
    }
18967
  }
18968
  return result;
18969
}
18970
18971
/*! \internal
18972
18973
  Returns the spacing in pixels which is between this \a bars and the following
18974
  one, both at the key coordinate \a keyCoord.
18975
18976
  \note Typically the returned value doesn't depend on \a bars or \a keyCoord.
18977
  \a bars is only needed to get acces to the key axis transformation and axis
18978
  rect for the modes \ref stAxisRectRatio and \ref stPlotCoords. The \a keyCoord
18979
  is only relevant for spacings given in \ref stPlotCoords on a logarithmic
18980
  axis.
18981
*/
18982
double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord) {
18983
  switch (mSpacingType) {
18984
    case stAbsolute: {
18985
      return mSpacing;
18986
    }
18987
    case stAxisRectRatio: {
18988
      if (bars->keyAxis()->orientation() == Qt::Horizontal)
18989
        return bars->keyAxis()->axisRect()->width() * mSpacing;
18990
      else
18991
        return bars->keyAxis()->axisRect()->height() * mSpacing;
18992
    }
18993
    case stPlotCoords: {
18994
      double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
18995
      return bars->keyAxis()->coordToPixel(keyCoord + mSpacing) - keyPixel;
18996
    }
18997
  }
18998
  return 0;
18999
}
19000
19001
////////////////////////////////////////////////////////////////////////////////////////////////////
19002
//////////////////// QCPBarData
19003
////////////////////////////////////////////////////////////////////////////////////////////////////
19004
19005
/*! \class QCPBarData
19006
  \brief Holds the data of one single data point (one bar) for QCPBars.
19007
19008
  The container for storing multiple data points is \ref QCPBarDataMap.
19009
19010
  The stored data is:
19011
  \li \a key: coordinate on the key axis of this bar
19012
  \li \a value: height coordinate on the value axis of this bar
19013
19014
  \see QCPBarDataaMap
19015
*/
19016
19017
/*!
19018
  Constructs a bar data point with key and value set to zero.
19019
*/
19020
QCPBarData::QCPBarData() : key(0), value(0) {}
19021
19022
/*!
19023
  Constructs a bar data point with the specified \a key and \a value.
19024
*/
19025
QCPBarData::QCPBarData(double key, double value) : key(key), value(value) {}
19026
19027
////////////////////////////////////////////////////////////////////////////////////////////////////
19028
//////////////////// QCPBars
19029
////////////////////////////////////////////////////////////////////////////////////////////////////
19030
19031
/*! \class QCPBars
19032
  \brief A plottable representing a bar chart in a plot.
19033
19034
  \image html QCPBars.png
19035
19036
  To plot data, assign it with the \ref setData or \ref addData functions.
19037
19038
  \section appearance Changing the appearance
19039
19040
  The appearance of the bars is determined by the pen and the brush (\ref
19041
  setPen, \ref setBrush). The width of the individual bars can be controlled
19042
  with \ref setWidthType and \ref setWidth.
19043
19044
  Bar charts are stackable. This means, two QCPBars plottables can be placed on
19045
  top of each other (see \ref QCPBars::moveAbove). So when two bars are at the
19046
  same key position, they will appear stacked.
19047
19048
  If you would like to group multiple QCPBars plottables together so they appear
19049
  side by side as shown below, use QCPBarsGroup.
19050
19051
  \image html QCPBarsGroup.png
19052
19053
  \section usage Usage
19054
19055
  Like all data representing objects in QCustomPlot, the QCPBars is a plottable
19056
  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
19057
  (QCustomPlot::plottable, QCustomPlot::addPlottable,
19058
  QCustomPlot::removePlottable, etc.)
19059
19060
  Usually, you first create an instance:
19061
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1
19062
  add it to the customPlot with QCustomPlot::addPlottable:
19063
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2
19064
  and then modify the properties of the newly created plottable, e.g.:
19065
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-3
19066
*/
19067
19068
/* start of documentation of inline functions */
19069
19070
/*! \fn QCPBars *QCPBars::barBelow() const
19071
  Returns the bars plottable that is directly below this bars plottable.
19072
  If there is no such plottable, returns 0.
19073
19074
  \see barAbove, moveBelow, moveAbove
19075
*/
19076
19077
/*! \fn QCPBars *QCPBars::barAbove() const
19078
  Returns the bars plottable that is directly above this bars plottable.
19079
  If there is no such plottable, returns 0.
19080
19081
  \see barBelow, moveBelow, moveAbove
19082
*/
19083
19084
/* end of documentation of inline functions */
19085
19086
/*!
19087
  Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a
19088
  valueAxis as its value axis ("y"). \a keyAxis and \a valueAxis must reside in
19089
  the same QCustomPlot instance and not have the same orientation. If either of
19090
  these restrictions is violated, a corresponding message is printed to the
19091
  debug output (qDebug), the construction is not aborted, though.
19092
19093
  The constructed QCPBars can be added to the plot with
19094
  QCustomPlot::addPlottable, QCustomPlot then takes ownership of the bar chart.
19095
*/
19096
QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
19097
    : QCPAbstractPlottable(keyAxis, valueAxis),
19098
      mData(new QCPBarDataMap),
19099
      mWidth(0.75),
19100
      mWidthType(wtPlotCoords),
19101
      mBarsGroup(0),
19102
      mBaseValue(0) {
19103
  // modify inherited properties from abstract plottable:
19104
  mPen.setColor(Qt::blue);
19105
  mPen.setStyle(Qt::SolidLine);
19106
  mBrush.setColor(QColor(40, 50, 255, 30));
19107
  mBrush.setStyle(Qt::SolidPattern);
19108
  mSelectedPen = mPen;
19109
  mSelectedPen.setWidthF(2.5);
19110
  mSelectedPen.setColor(QColor(80, 80, 255));  // lighter than Qt::blue of mPen
19111
  mSelectedBrush = mBrush;
19112
}
19113
19114
QCPBars::~QCPBars() {
19115
  setBarsGroup(0);
19116
  if (mBarBelow || mBarAbove)
19117
    connectBars(mBarBelow.data(),
19118
                mBarAbove.data());  // take this bar out of any stacking
19119
  delete mData;
19120
}
19121
19122
/*!
19123
  Sets the width of the bars.
19124
19125
  How the number passed as \a width is interpreted (e.g. screen pixels, plot
19126
  coordinates,...), depends on the currently set width type, see \ref
19127
  setWidthType and \ref WidthType.
19128
*/
19129
void QCPBars::setWidth(double width) { mWidth = width; }
19130
19131
/*!
19132
  Sets how the width of the bars is defined. See the documentation of \ref
19133
  WidthType for an explanation of the possible values for \a widthType.
19134
19135
  The default value is \ref wtPlotCoords.
19136
19137
  \see setWidth
19138
*/
19139
void QCPBars::setWidthType(QCPBars::WidthType widthType) {
19140
  mWidthType = widthType;
19141
}
19142
19143
/*!
19144
  Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively,
19145
  you can also use \ref QCPBarsGroup::append.
19146
19147
  To remove this QCPBars from any group, set \a barsGroup to 0.
19148
*/
19149
void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup) {
19150
  // deregister at old group:
19151
  if (mBarsGroup) mBarsGroup->unregisterBars(this);
19152
  mBarsGroup = barsGroup;
19153
  // register at new group:
19154
  if (mBarsGroup) mBarsGroup->registerBars(this);
19155
}
19156
19157
/*!
19158
  Sets the base value of this bars plottable.
19159
19160
  The base value defines where on the value coordinate the bars start. How far
19161
  the bars extend from the base value is given by their individual value data.
19162
  For example, if the base value is set to 1, a bar with data value 2 will have
19163
  its lowest point at value coordinate 1 and highest point at 3.
19164
19165
  For stacked bars, only the base value of the bottom-most QCPBars has meaning.
19166
19167
  The default base value is 0.
19168
*/
19169
void QCPBars::setBaseValue(double baseValue) { mBaseValue = baseValue; }
19170
19171
/*!
19172
  Replaces the current data with the provided \a data.
19173
19174
  If \a copy is set to true, data points in \a data will only be copied. if
19175
  false, the plottable takes ownership of the passed data and replaces the
19176
  internal data pointer with it. This is significantly faster than copying for
19177
  large datasets.
19178
*/
19179
void QCPBars::setData(QCPBarDataMap *data, bool copy) {
19180
  if (mData == data) {
19181
    qDebug() << Q_FUNC_INFO
19182
             << "The data pointer is already in (and owned by) this plottable"
19183
             << reinterpret_cast<quintptr>(data);
19184
    return;
19185
  }
19186
  if (copy) {
19187
    *mData = *data;
19188
  } else {
19189
    delete mData;
19190
    mData = data;
19191
  }
19192
}
19193
19194
/*! \overload
19195
19196
  Replaces the current data with the provided points in \a key and \a value
19197
  tuples. The provided vectors should have equal length. Else, the number of
19198
  added points will be the size of the smallest vector.
19199
*/
19200
void QCPBars::setData(const QVector<double> &key,
19201
                      const QVector<double> &value) {
19202
  mData->clear();
19203
  int n = key.size();
19204
  n = qMin(n, value.size());
19205
  QCPBarData newData;
19206
  for (int i = 0; i < n; ++i) {
19207
    newData.key = key[i];
19208
    newData.value = value[i];
19209
    mData->insertMulti(newData.key, newData);
19210
  }
19211
}
19212
19213
/*!
19214
  Moves this bars plottable below \a bars. In other words, the bars of this
19215
  plottable will appear below the bars of \a bars. The move target \a bars must
19216
  use the same key and value axis as this plottable.
19217
19218
  Inserting into and removing from existing bar stacking is handled gracefully.
19219
  If \a bars already has a bars object below itself, this bars object is
19220
  inserted between the two. If this bars object is already between two other
19221
  bars, the two other bars will be stacked on top of each other after the
19222
  operation.
19223
19224
  To remove this bars plottable from any stacking, set \a bars to 0.
19225
19226
  \see moveBelow, barAbove, barBelow
19227
*/
19228
void QCPBars::moveBelow(QCPBars *bars) {
19229
  if (bars == this) return;
19230
  if (bars && (bars->keyAxis() != mKeyAxis.data() ||
19231
               bars->valueAxis() != mValueAxis.data())) {
19232
    qDebug() << Q_FUNC_INFO
19233
             << "passed QCPBars* doesn't have same key and value axis as this "
19234
                "QCPBars";
19235
    return;
19236
  }
19237
  // remove from stacking:
19238
  connectBars(
19239
      mBarBelow.data(),
19240
      mBarAbove.data());  // Note: also works if one (or both) of them is 0
19241
  // if new bar given, insert this bar below it:
19242
  if (bars) {
19243
    if (bars->mBarBelow) connectBars(bars->mBarBelow.data(), this);
19244
    connectBars(this, bars);
19245
  }
19246
}
19247
19248
/*!
19249
  Moves this bars plottable above \a bars. In other words, the bars of this
19250
  plottable will appear above the bars of \a bars. The move target \a bars must
19251
  use the same key and value axis as this plottable.
19252
19253
  Inserting into and removing from existing bar stacking is handled gracefully.
19254
  If \a bars already has a bars object above itself, this bars object is
19255
  inserted between the two. If this bars object is already between two other
19256
  bars, the two other bars will be stacked on top of each other after the
19257
  operation.
19258
19259
  To remove this bars plottable from any stacking, set \a bars to 0.
19260
19261
  \see moveBelow, barBelow, barAbove
19262
*/
19263
void QCPBars::moveAbove(QCPBars *bars) {
19264
  if (bars == this) return;
19265
  if (bars && (bars->keyAxis() != mKeyAxis.data() ||
19266
               bars->valueAxis() != mValueAxis.data())) {
19267
    qDebug() << Q_FUNC_INFO
19268
             << "passed QCPBars* doesn't have same key and value axis as this "
19269
                "QCPBars";
19270
    return;
19271
  }
19272
  // remove from stacking:
19273
  connectBars(
19274
      mBarBelow.data(),
19275
      mBarAbove.data());  // Note: also works if one (or both) of them is 0
19276
  // if new bar given, insert this bar above it:
19277
  if (bars) {
19278
    if (bars->mBarAbove) connectBars(this, bars->mBarAbove.data());
19279
    connectBars(bars, this);
19280
  }
19281
}
19282
19283
/*!
19284
  Adds the provided data points in \a dataMap to the current data.
19285
  \see removeData
19286
*/
19287
void QCPBars::addData(const QCPBarDataMap &dataMap) { mData->unite(dataMap); }
19288
19289
/*! \overload
19290
  Adds the provided single data point in \a data to the current data.
19291
  \see removeData
19292
*/
19293
void QCPBars::addData(const QCPBarData &data) {
19294
  mData->insertMulti(data.key, data);
19295
}
19296
19297
/*! \overload
19298
  Adds the provided single data point as \a key and \a value tuple to the
19299
  current data \see removeData
19300
*/
19301
void QCPBars::addData(double key, double value) {
19302
  QCPBarData newData;
19303
  newData.key = key;
19304
  newData.value = value;
19305
  mData->insertMulti(newData.key, newData);
19306
}
19307
19308
/*! \overload
19309
  Adds the provided data points as \a key and \a value tuples to the current
19310
  data. \see removeData
19311
*/
19312
void QCPBars::addData(const QVector<double> &keys,
19313
                      const QVector<double> &values) {
19314
  int n = keys.size();
19315
  n = qMin(n, values.size());
19316
  QCPBarData newData;
19317
  for (int i = 0; i < n; ++i) {
19318
    newData.key = keys[i];
19319
    newData.value = values[i];
19320
    mData->insertMulti(newData.key, newData);
19321
  }
19322
}
19323
19324
/*!
19325
  Removes all data points with key smaller than \a key.
19326
  \see addData, clearData
19327
*/
19328
void QCPBars::removeDataBefore(double key) {
19329
  QCPBarDataMap::iterator it = mData->begin();
19330
  while (it != mData->end() && it.key() < key) it = mData->erase(it);
19331
}
19332
19333
/*!
19334
  Removes all data points with key greater than \a key.
19335
  \see addData, clearData
19336
*/
19337
void QCPBars::removeDataAfter(double key) {
19338
  if (mData->isEmpty()) return;
19339
  QCPBarDataMap::iterator it = mData->upperBound(key);
19340
  while (it != mData->end()) it = mData->erase(it);
19341
}
19342
19343
/*!
19344
  Removes all data points with key between \a fromKey and \a toKey. if \a
19345
  fromKey is greater or equal to \a toKey, the function does nothing. To remove
19346
  a single data point with known key, use \ref removeData(double key).
19347
19348
  \see addData, clearData
19349
*/
19350
void QCPBars::removeData(double fromKey, double toKey) {
19351
  if (fromKey >= toKey || mData->isEmpty()) return;
19352
  QCPBarDataMap::iterator it = mData->upperBound(fromKey);
19353
  QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
19354
  while (it != itEnd) it = mData->erase(it);
19355
}
19356
19357
/*! \overload
19358
19359
  Removes a single data point at \a key. If the position is not known with
19360
  absolute precision, consider using \ref removeData(double fromKey, double
19361
  toKey) with a small fuzziness interval around the suspected position, depeding
19362
  on the precision with which the key is known.
19363
19364
  \see addData, clearData
19365
*/
19366
void QCPBars::removeData(double key) { mData->remove(key); }
19367
19368
/*!
19369
  Removes all data points.
19370
  \see removeData, removeDataAfter, removeDataBefore
19371
*/
19372
void QCPBars::clearData() { mData->clear(); }
19373
19374
/* inherits documentation from base class */
19375
double QCPBars::selectTest(const QPointF &pos, bool onlySelectable,
19376
                           QVariant *details) const {
19377
  Q_UNUSED(details)
19378
  if (onlySelectable && !mSelectable) return -1;
19379
  if (!mKeyAxis || !mValueAxis) {
19380
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
19381
    return -1;
19382
  }
19383
19384
  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
19385
    QCPBarDataMap::ConstIterator it;
19386
    for (it = mData->constBegin(); it != mData->constEnd(); ++it) {
19387
      if (getBarPolygon(it.value().key, it.value().value)
19388
              .boundingRect()
19389
              .contains(pos))
19390
        return mParentPlot->selectionTolerance() * 0.99;
19391
    }
19392
  }
19393
  return -1;
19394
}
19395
19396
/* inherits documentation from base class */
19397
void QCPBars::draw(QCPPainter *painter) {
19398
  if (!mKeyAxis || !mValueAxis) {
19399
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
19400
    return;
19401
  }
19402
  if (mData->isEmpty()) return;
19403
19404
  QCPBarDataMap::const_iterator it, lower, upperEnd;
19405
  getVisibleDataBounds(lower, upperEnd);
19406
  for (it = lower; it != upperEnd; ++it) {
19407
    // check data validity if flag set:
19408
#ifdef QCUSTOMPLOT_CHECK_DATA
19409
    if (QCP::isInvalidData(it.value().key, it.value().value))
19410
      qDebug() << Q_FUNC_INFO << "Data point at" << it.key()
19411
               << "of drawn range invalid." << "Plottable name:" << name();
19412
#endif
19413
    QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
19414
    // draw bar fill:
19415
    if (mainBrush().style() != Qt::NoBrush &&
19416
        mainBrush().color().alpha() != 0) {
19417
      applyFillAntialiasingHint(painter);
19418
      painter->setPen(Qt::NoPen);
19419
      painter->setBrush(mainBrush());
19420
      painter->drawPolygon(barPolygon);
19421
    }
19422
    // draw bar line:
19423
    if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) {
19424
      applyDefaultAntialiasingHint(painter);
19425
      painter->setPen(mainPen());
19426
      painter->setBrush(Qt::NoBrush);
19427
      painter->drawPolyline(barPolygon);
19428
    }
19429
  }
19430
}
19431
19432
/* inherits documentation from base class */
19433
void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
19434
  // draw filled rect:
19435
  applyDefaultAntialiasingHint(painter);
19436
  painter->setBrush(mBrush);
19437
  painter->setPen(mPen);
19438
  QRectF r = QRectF(0, 0, rect.width() * 0.67, rect.height() * 0.67);
19439
  r.moveCenter(rect.center());
19440
  painter->drawRect(r);
19441
}
19442
19443
/*!  \internal
19444
19445
  called by \ref draw to determine which data (key) range is visible at the
19446
  current key axis range setting, so only that needs to be processed. It also
19447
  takes into account the bar width.
19448
19449
  \a lower returns an iterator to the lowest data point that needs to be taken
19450
  into account when plotting. Note that in order to get a clean plot all the way
19451
  to the edge of the axis rect, \a lower may still be just outside the visible
19452
  range.
19453
19454
  \a upperEnd returns an iterator one higher than the highest visible data
19455
  point. Same as before, \a upperEnd may also lie just outside of the visible
19456
  range.
19457
19458
  if the bars plottable contains no data, both \a lower and \a upperEnd point to
19459
  constEnd.
19460
*/
19461
void QCPBars::getVisibleDataBounds(
19462
    QCPBarDataMap::const_iterator &lower,
19463
    QCPBarDataMap::const_iterator &upperEnd) const {
19464
  if (!mKeyAxis) {
19465
    qDebug() << Q_FUNC_INFO << "invalid key axis";
19466
    return;
19467
  }
19468
  if (mData->isEmpty()) {
19469
    lower = mData->constEnd();
19470
    upperEnd = mData->constEnd();
19471
    return;
19472
  }
19473
19474
  // get visible data range as QMap iterators
19475
  lower = mData->lowerBound(mKeyAxis.data()->range().lower);
19476
  upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
19477
  double lowerPixelBound =
19478
      mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
19479
  double upperPixelBound =
19480
      mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
19481
  bool isVisible = false;
19482
  // walk left from lbound to find lower bar that actually is completely outside
19483
  // visible pixel range:
19484
  QCPBarDataMap::const_iterator it = lower;
19485
  while (it != mData->constBegin()) {
19486
    --it;
19487
    QRectF barBounds =
19488
        getBarPolygon(it.value().key, it.value().value).boundingRect();
19489
    if (mKeyAxis.data()->orientation() == Qt::Horizontal)
19490
      isVisible = ((!mKeyAxis.data()->rangeReversed() &&
19491
                    barBounds.right() >= lowerPixelBound) ||
19492
                   (mKeyAxis.data()->rangeReversed() &&
19493
                    barBounds.left() <= lowerPixelBound));
19494
    else  // keyaxis is vertical
19495
      isVisible = ((!mKeyAxis.data()->rangeReversed() &&
19496
                    barBounds.top() <= lowerPixelBound) ||
19497
                   (mKeyAxis.data()->rangeReversed() &&
19498
                    barBounds.bottom() >= lowerPixelBound));
19499
    if (isVisible)
19500
      lower = it;
19501
    else
19502
      break;
19503
  }
19504
  // walk right from ubound to find upper bar that actually is completely
19505
  // outside visible pixel range:
19506
  it = upperEnd;
19507
  while (it != mData->constEnd()) {
19508
    QRectF barBounds =
19509
        getBarPolygon(upperEnd.value().key, upperEnd.value().value)
19510
            .boundingRect();
19511
    if (mKeyAxis.data()->orientation() == Qt::Horizontal)
19512
      isVisible = ((!mKeyAxis.data()->rangeReversed() &&
19513
                    barBounds.left() <= upperPixelBound) ||
19514
                   (mKeyAxis.data()->rangeReversed() &&
19515
                    barBounds.right() >= upperPixelBound));
19516
    else  // keyaxis is vertical
19517
      isVisible = ((!mKeyAxis.data()->rangeReversed() &&
19518
                    barBounds.bottom() >= upperPixelBound) ||
19519
                   (mKeyAxis.data()->rangeReversed() &&
19520
                    barBounds.top() <= upperPixelBound));
19521
    if (isVisible)
19522
      upperEnd = it + 1;
19523
    else
19524
      break;
19525
    ++it;
19526
  }
19527
}
19528
19529
/*! \internal
19530
19531
  Returns the polygon of a single bar with \a key and \a value. The Polygon is
19532
  open at the bottom and shifted according to the bar stacking (see \ref
19533
  moveAbove) and base value (see \ref setBaseValue).
19534
*/
19535
QPolygonF QCPBars::getBarPolygon(double key, double value) const {
19536
  QCPAxis *keyAxis = mKeyAxis.data();
19537
  QCPAxis *valueAxis = mValueAxis.data();
19538
  if (!keyAxis || !valueAxis) {
19539
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
19540
    return QPolygonF();
19541
  }
19542
19543
  QPolygonF result;
19544
  double lowerPixelWidth, upperPixelWidth;
19545
  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
19546
  double base = getStackedBaseValue(key, value >= 0);
19547
  double basePixel = valueAxis->coordToPixel(base);
19548
  double valuePixel = valueAxis->coordToPixel(base + value);
19549
  double keyPixel = keyAxis->coordToPixel(key);
19550
  if (mBarsGroup) keyPixel += mBarsGroup->keyPixelOffset(this, key);
19551
  if (keyAxis->orientation() == Qt::Horizontal) {
19552
    result << QPointF(keyPixel + lowerPixelWidth, basePixel);
19553
    result << QPointF(keyPixel + lowerPixelWidth, valuePixel);
19554
    result << QPointF(keyPixel + upperPixelWidth, valuePixel);
19555
    result << QPointF(keyPixel + upperPixelWidth, basePixel);
19556
  } else {
19557
    result << QPointF(basePixel, keyPixel + lowerPixelWidth);
19558
    result << QPointF(valuePixel, keyPixel + lowerPixelWidth);
19559
    result << QPointF(valuePixel, keyPixel + upperPixelWidth);
19560
    result << QPointF(basePixel, keyPixel + upperPixelWidth);
19561
  }
19562
  return result;
19563
}
19564
19565
/*! \internal
19566
19567
  This function is used to determine the width of the bar at coordinate \a key,
19568
  according to the specified width (\ref setWidth) and width type (\ref
19569
  setWidthType).
19570
19571
  The output parameters \a lower and \a upper return the number of pixels the
19572
  bar extends to lower and higher keys, relative to the \a key coordinate (so
19573
  with a non-reversed horizontal axis, \a lower is negative and \a upper
19574
  positive).
19575
*/
19576
void QCPBars::getPixelWidth(double key, double &lower, double &upper) const {
19577
  switch (mWidthType) {
19578
    case wtAbsolute: {
19579
      upper = mWidth * 0.5;
19580
      lower = -upper;
19581
      if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^
19582
                       (mKeyAxis.data()->orientation() == Qt::Vertical)))
19583
        qSwap(lower, upper);
19584
      break;
19585
    }
19586
    case wtAxisRectRatio: {
19587
      if (mKeyAxis && mKeyAxis.data()->axisRect()) {
19588
        if (mKeyAxis.data()->orientation() == Qt::Horizontal)
19589
          upper = mKeyAxis.data()->axisRect()->width() * mWidth * 0.5;
19590
        else
19591
          upper = mKeyAxis.data()->axisRect()->height() * mWidth * 0.5;
19592
        lower = -upper;
19593
        if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^
19594
                         (mKeyAxis.data()->orientation() == Qt::Vertical)))
19595
          qSwap(lower, upper);
19596
      } else
19597
        qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
19598
      break;
19599
    }
19600
    case wtPlotCoords: {
19601
      if (mKeyAxis) {
19602
        double keyPixel = mKeyAxis.data()->coordToPixel(key);
19603
        upper = mKeyAxis.data()->coordToPixel(key + mWidth * 0.5) - keyPixel;
19604
        lower = mKeyAxis.data()->coordToPixel(key - mWidth * 0.5) - keyPixel;
19605
        // no need to qSwap(lower, higher) when range reversed, because
19606
        // higher/lower are gained by coordinate transform which includes range
19607
        // direction
19608
      } else
19609
        qDebug() << Q_FUNC_INFO << "No key axis defined";
19610
      break;
19611
    }
19612
  }
19613
}
19614
19615
/*! \internal
19616
19617
  This function is called to find at which value to start drawing the base of a
19618
  bar at \a key, when it is stacked on top of another QCPBars (e.g. with \ref
19619
  moveAbove).
19620
19621
  positive and negative bars are separated per stack (positive are stacked above
19622
  baseValue upwards, negative are stacked below baseValue downwards). This can
19623
  be indicated with \a positive. So if the bar for which we need the base value
19624
  is negative, set \a positive to false.
19625
*/
19626
double QCPBars::getStackedBaseValue(double key, bool positive) const {
19627
  if (mBarBelow) {
19628
    double max = 0;  // don't use mBaseValue here because only base value of
19629
                     // bottom-most bar has meaning in a bar stack
19630
    // find bars of mBarBelow that are approximately at key and find largest
19631
    // one:
19632
    double epsilon =
19633
        qAbs(key) *
19634
        1e-6;  // should be safe even when changed to use float at some point
19635
    if (key == 0) epsilon = 1e-6;
19636
    QCPBarDataMap::const_iterator it =
19637
        mBarBelow.data()->mData->lowerBound(key - epsilon);
19638
    QCPBarDataMap::const_iterator itEnd =
19639
        mBarBelow.data()->mData->upperBound(key + epsilon);
19640
    while (it != itEnd) {
19641
      if ((positive && it.value().value > max) ||
19642
          (!positive && it.value().value < max))
19643
        max = it.value().value;
19644
      ++it;
19645
    }
19646
    // recurse down the bar-stack to find the total height:
19647
    return max + mBarBelow.data()->getStackedBaseValue(key, positive);
19648
  } else
19649
    return mBaseValue;
19650
}
19651
19652
/*! \internal
19653
19654
  Connects \a below and \a above to each other via their mBarAbove/mBarBelow
19655
  properties. The bar(s) currently above lower and below upper will become
19656
  disconnected to lower/upper.
19657
19658
  If lower is zero, upper will be disconnected at the bottom.
19659
  If upper is zero, lower will be disconnected at the top.
19660
*/
19661
void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) {
19662
  if (!lower && !upper) return;
19663
19664
  if (!lower)  // disconnect upper at bottom
19665
  {
19666
    // disconnect old bar below upper:
19667
    if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19668
      upper->mBarBelow.data()->mBarAbove = 0;
19669
    upper->mBarBelow = 0;
19670
  } else if (!upper)  // disconnect lower at top
19671
  {
19672
    // disconnect old bar above lower:
19673
    if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19674
      lower->mBarAbove.data()->mBarBelow = 0;
19675
    lower->mBarAbove = 0;
19676
  } else  // connect lower and upper
19677
  {
19678
    // disconnect old bar above lower:
19679
    if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19680
      lower->mBarAbove.data()->mBarBelow = 0;
19681
    // disconnect old bar below upper:
19682
    if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19683
      upper->mBarBelow.data()->mBarAbove = 0;
19684
    lower->mBarAbove = upper;
19685
    upper->mBarBelow = lower;
19686
  }
19687
}
19688
19689
/* inherits documentation from base class */
19690
QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const {
19691
  QCPRange range;
19692
  bool haveLower = false;
19693
  bool haveUpper = false;
19694
19695
  double current;
19696
  QCPBarDataMap::const_iterator it = mData->constBegin();
19697
  while (it != mData->constEnd()) {
19698
    current = it.value().key;
19699
    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) ||
19700
        (inSignDomain == sdPositive && current > 0)) {
19701
      if (current < range.lower || !haveLower) {
19702
        range.lower = current;
19703
        haveLower = true;
19704
      }
19705
      if (current > range.upper || !haveUpper) {
19706
        range.upper = current;
19707
        haveUpper = true;
19708
      }
19709
    }
19710
    ++it;
19711
  }
19712
  // determine exact range of bars by including bar width and barsgroup offset:
19713
  if (haveLower && mKeyAxis) {
19714
    double lowerPixelWidth, upperPixelWidth, keyPixel;
19715
    getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
19716
    keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
19717
    if (mBarsGroup) keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
19718
    range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
19719
  }
19720
  if (haveUpper && mKeyAxis) {
19721
    double lowerPixelWidth, upperPixelWidth, keyPixel;
19722
    getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
19723
    keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
19724
    if (mBarsGroup) keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
19725
    range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
19726
  }
19727
  foundRange = haveLower && haveUpper;
19728
  return range;
19729
}
19730
19731
/* inherits documentation from base class */
19732
QCPRange QCPBars::getValueRange(bool &foundRange,
19733
                                SignDomain inSignDomain) const {
19734
  QCPRange range;
19735
  range.lower = mBaseValue;
19736
  range.upper = mBaseValue;
19737
  bool haveLower = true;  // set to true, because baseValue should always be
19738
                          // visible in bar charts
19739
  bool haveUpper = true;  // set to true, because baseValue should always be
19740
                          // visible in bar charts
19741
  double current;
19742
19743
  QCPBarDataMap::const_iterator it = mData->constBegin();
19744
  while (it != mData->constEnd()) {
19745
    current = it.value().value +
19746
              getStackedBaseValue(it.value().key, it.value().value >= 0);
19747
    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) ||
19748
        (inSignDomain == sdPositive && current > 0)) {
19749
      if (current < range.lower || !haveLower) {
19750
        range.lower = current;
19751
        haveLower = true;
19752
      }
19753
      if (current > range.upper || !haveUpper) {
19754
        range.upper = current;
19755
        haveUpper = true;
19756
      }
19757
    }
19758
    ++it;
19759
  }
19760
19761
  foundRange =
19762
      true;  // return true because bar charts always have the 0-line visible
19763
  return range;
19764
}
19765
19766
////////////////////////////////////////////////////////////////////////////////////////////////////
19767
//////////////////// QCPStatisticalBox
19768
////////////////////////////////////////////////////////////////////////////////////////////////////
19769
19770
/*! \class QCPStatisticalBox
19771
  \brief A plottable representing a single statistical box in a plot.
19772
19773
  \image html QCPStatisticalBox.png
19774
19775
  To plot data, assign it with the individual parameter functions or use \ref
19776
  setData to set all parameters at once. The individual functions are: \li \ref
19777
  setMinimum \li \ref setLowerQuartile \li \ref setMedian \li \ref
19778
  setUpperQuartile \li \ref setMaximum
19779
19780
  Additionally you can define a list of outliers, drawn as scatter datapoints:
19781
  \li \ref setOutliers
19782
19783
  \section appearance Changing the appearance
19784
19785
  The appearance of the box itself is controlled via \ref setPen and \ref
19786
  setBrush. You may change the width of the box with \ref setWidth in plot
19787
  coordinates (not pixels).
19788
19789
  Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen,
19790
  \ref setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of
19791
  the bar at the top (maximum) and bottom (minimum).
19792
19793
  The median indicator line has its own pen, \ref setMedianPen.
19794
19795
  If the whisker backbone pen is changed, make sure to set the capStyle to
19796
  Qt::FlatCap. Else, the backbone line might exceed the whisker bars by a few
19797
  pixels due to the pen cap being not perfectly flat.
19798
19799
  The Outlier data points are drawn as normal scatter points. Their look can be
19800
  controlled with \ref setOutlierStyle
19801
19802
  \section usage Usage
19803
19804
  Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a
19805
  plottable. Usually, you first create an instance and add it to the customPlot:
19806
  \snippet documentation/doc-code-snippets/mainwindow.cpp
19807
  qcpstatisticalbox-creation-1 and then modify the properties of the newly
19808
  created plottable, e.g.: \snippet
19809
  documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2
19810
*/
19811
19812
/*!
19813
  Constructs a statistical box which uses \a keyAxis as its key axis ("x") and
19814
  \a valueAxis as its value axis ("y"). \a keyAxis and \a valueAxis must reside
19815
  in the same QCustomPlot instance and not have the same orientation. If either
19816
  of these restrictions is violated, a corresponding message is printed to the
19817
  debug output (qDebug), the construction is not aborted, though.
19818
19819
  The constructed statistical box can be added to the plot with
19820
  QCustomPlot::addPlottable, QCustomPlot then takes ownership of the statistical
19821
  box.
19822
*/
19823
QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
19824
    : QCPAbstractPlottable(keyAxis, valueAxis),
19825
      mKey(0),
19826
      mMinimum(0),
19827
      mLowerQuartile(0),
19828
      mMedian(0),
19829
      mUpperQuartile(0),
19830
      mMaximum(0) {
19831
  setOutlierStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, 6));
19832
  setWhiskerWidth(0.2);
19833
  setWidth(0.5);
19834
19835
  setPen(QPen(Qt::black));
19836
  setSelectedPen(QPen(Qt::blue, 2.5));
19837
  setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
19838
  setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
19839
  setWhiskerBarPen(QPen(Qt::black));
19840
  setBrush(Qt::NoBrush);
19841
  setSelectedBrush(Qt::NoBrush);
19842
}
19843
19844
/*!
19845
  Sets the key coordinate of the statistical box.
19846
*/
19847
void QCPStatisticalBox::setKey(double key) { mKey = key; }
19848
19849
/*!
19850
  Sets the parameter "minimum" of the statistical box plot. This is the position
19851
  of the lower whisker, typically the minimum measurement of the sample that's
19852
  not considered an outlier.
19853
19854
  \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19855
*/
19856
void QCPStatisticalBox::setMinimum(double value) { mMinimum = value; }
19857
19858
/*!
19859
  Sets the parameter "lower Quartile" of the statistical box plot. This is the
19860
  lower end of the box. The lower and the upper quartiles are the two
19861
  statistical quartiles around the median of the sample, they contain 50% of the
19862
  sample data.
19863
19864
  \see setUpperQuartile, setPen, setBrush, setWidth
19865
*/
19866
void QCPStatisticalBox::setLowerQuartile(double value) {
19867
  mLowerQuartile = value;
19868
}
19869
19870
/*!
19871
  Sets the parameter "median" of the statistical box plot. This is the value of
19872
  the median mark inside the quartile box. The median separates the sample data
19873
  in half (50% of the sample data is below/above the median).
19874
19875
  \see setMedianPen
19876
*/
19877
void QCPStatisticalBox::setMedian(double value) { mMedian = value; }
19878
19879
/*!
19880
  Sets the parameter "upper Quartile" of the statistical box plot. This is the
19881
  upper end of the box. The lower and the upper quartiles are the two
19882
  statistical quartiles around the median of the sample, they contain 50% of the
19883
  sample data.
19884
19885
  \see setLowerQuartile, setPen, setBrush, setWidth
19886
*/
19887
void QCPStatisticalBox::setUpperQuartile(double value) {
19888
  mUpperQuartile = value;
19889
}
19890
19891
/*!
19892
  Sets the parameter "maximum" of the statistical box plot. This is the position
19893
  of the upper whisker, typically the maximum measurement of the sample that's
19894
  not considered an outlier.
19895
19896
  \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19897
*/
19898
void QCPStatisticalBox::setMaximum(double value) { mMaximum = value; }
19899
19900
/*!
19901
  Sets a vector of outlier values that will be drawn as scatters. Any data
19902
  points in the sample that are not within the whiskers (\ref setMinimum, \ref
19903
  setMaximum) should be considered outliers and displayed as such.
19904
19905
  \see setOutlierStyle
19906
*/
19907
void QCPStatisticalBox::setOutliers(const QVector<double> &values) {
19908
  mOutliers = values;
19909
}
19910
19911
/*!
19912
  Sets all parameters of the statistical box plot at once.
19913
19914
  \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile,
19915
  setMaximum
19916
*/
19917
void QCPStatisticalBox::setData(double key, double minimum,
19918
                                double lowerQuartile, double median,
19919
                                double upperQuartile, double maximum) {
19920
  setKey(key);
19921
  setMinimum(minimum);
19922
  setLowerQuartile(lowerQuartile);
19923
  setMedian(median);
19924
  setUpperQuartile(upperQuartile);
19925
  setMaximum(maximum);
19926
}
19927
19928
/*!
19929
  Sets the width of the box in key coordinates.
19930
19931
  \see setWhiskerWidth
19932
*/
19933
void QCPStatisticalBox::setWidth(double width) { mWidth = width; }
19934
19935
/*!
19936
  Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key
19937
  coordinates.
19938
19939
  \see setWidth
19940
*/
19941
void QCPStatisticalBox::setWhiskerWidth(double width) { mWhiskerWidth = width; }
19942
19943
/*!
19944
  Sets the pen used for drawing the whisker backbone (That's the line parallel
19945
  to the value axis).
19946
19947
  Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the whisker
19948
  backbone from reaching a few pixels past the whisker bars, when using a
19949
  non-zero pen width.
19950
19951
  \see setWhiskerBarPen
19952
*/
19953
void QCPStatisticalBox::setWhiskerPen(const QPen &pen) { mWhiskerPen = pen; }
19954
19955
/*!
19956
  Sets the pen used for drawing the whisker bars (Those are the lines parallel
19957
  to the key axis at each end of the whisker backbone).
19958
19959
  \see setWhiskerPen
19960
*/
19961
void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen) {
19962
  mWhiskerBarPen = pen;
19963
}
19964
19965
/*!
19966
  Sets the pen used for drawing the median indicator line inside the statistical
19967
  box.
19968
*/
19969
void QCPStatisticalBox::setMedianPen(const QPen &pen) { mMedianPen = pen; }
19970
19971
/*!
19972
  Sets the appearance of the outlier data points.
19973
19974
  \see setOutliers
19975
*/
19976
void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style) {
19977
  mOutlierStyle = style;
19978
}
19979
19980
/* inherits documentation from base class */
19981
void QCPStatisticalBox::clearData() {
19982
  setOutliers(QVector<double>());
19983
  setKey(0);
19984
  setMinimum(0);
19985
  setLowerQuartile(0);
19986
  setMedian(0);
19987
  setUpperQuartile(0);
19988
  setMaximum(0);
19989
}
19990
19991
/* inherits documentation from base class */
19992
double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable,
19993
                                     QVariant *details) const {
19994
  Q_UNUSED(details)
19995
  if (onlySelectable && !mSelectable) return -1;
19996
  if (!mKeyAxis || !mValueAxis) {
19997
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
19998
    return -1;
19999
  }
20000
20001
  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
20002
    double posKey, posValue;
20003
    pixelsToCoords(pos, posKey, posValue);
20004
    // quartile box:
20005
    QCPRange keyRange(mKey - mWidth * 0.5, mKey + mWidth * 0.5);
20006
    QCPRange valueRange(mLowerQuartile, mUpperQuartile);
20007
    if (keyRange.contains(posKey) && valueRange.contains(posValue))
20008
      return mParentPlot->selectionTolerance() * 0.99;
20009
20010
    // min/max whiskers:
20011
    if (QCPRange(mMinimum, mMaximum).contains(posValue))
20012
      return qAbs(mKeyAxis.data()->coordToPixel(mKey) -
20013
                  mKeyAxis.data()->coordToPixel(posKey));
20014
  }
20015
  return -1;
20016
}
20017
20018
/* inherits documentation from base class */
20019
void QCPStatisticalBox::draw(QCPPainter *painter) {
20020
  if (!mKeyAxis || !mValueAxis) {
20021
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
20022
    return;
20023
  }
20024
20025
  // check data validity if flag set:
20026
#ifdef QCUSTOMPLOT_CHECK_DATA
20027
  if (QCP::isInvalidData(mKey, mMedian) ||
20028
      QCP::isInvalidData(mLowerQuartile, mUpperQuartile) ||
20029
      QCP::isInvalidData(mMinimum, mMaximum))
20030
    qDebug() << Q_FUNC_INFO << "Data point at" << mKey
20031
             << "of drawn range has invalid data."
20032
             << "Plottable name:" << name();
20033
  for (int i = 0; i < mOutliers.size(); ++i)
20034
    if (QCP::isInvalidData(mOutliers.at(i)))
20035
      qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey
20036
               << "of drawn range invalid." << "Plottable name:" << name();
20037
#endif
20038
20039
  QRectF quartileBox;
20040
  drawQuartileBox(painter, &quartileBox);
20041
20042
  painter->save();
20043
  painter->setClipRect(quartileBox, Qt::IntersectClip);
20044
  drawMedian(painter);
20045
  painter->restore();
20046
20047
  drawWhiskers(painter);
20048
  drawOutliers(painter);
20049
}
20050
20051
/* inherits documentation from base class */
20052
void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter,
20053
                                       const QRectF &rect) const {
20054
  // draw filled rect:
20055
  applyDefaultAntialiasingHint(painter);
20056
  painter->setPen(mPen);
20057
  painter->setBrush(mBrush);
20058
  QRectF r = QRectF(0, 0, rect.width() * 0.67, rect.height() * 0.67);
20059
  r.moveCenter(rect.center());
20060
  painter->drawRect(r);
20061
}
20062
20063
/*! \internal
20064
20065
  Draws the quartile box. \a box is an output parameter that returns the
20066
  quartile box (in pixel coordinates) which is used to set the clip rect of the
20067
  painter before calling \ref drawMedian (so the median doesn't draw outside the
20068
  quartile box).
20069
*/
20070
void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter,
20071
                                        QRectF *quartileBox) const {
20072
  QRectF box;
20073
  box.setTopLeft(coordsToPixels(mKey - mWidth * 0.5, mUpperQuartile));
20074
  box.setBottomRight(coordsToPixels(mKey + mWidth * 0.5, mLowerQuartile));
20075
  applyDefaultAntialiasingHint(painter);
20076
  painter->setPen(mainPen());
20077
  painter->setBrush(mainBrush());
20078
  painter->drawRect(box);
20079
  if (quartileBox) *quartileBox = box;
20080
}
20081
20082
/*! \internal
20083
20084
  Draws the median line inside the quartile box.
20085
*/
20086
void QCPStatisticalBox::drawMedian(QCPPainter *painter) const {
20087
  QLineF medianLine;
20088
  medianLine.setP1(coordsToPixels(mKey - mWidth * 0.5, mMedian));
20089
  medianLine.setP2(coordsToPixels(mKey + mWidth * 0.5, mMedian));
20090
  applyDefaultAntialiasingHint(painter);
20091
  painter->setPen(mMedianPen);
20092
  painter->drawLine(medianLine);
20093
}
20094
20095
/*! \internal
20096
20097
  Draws both whisker backbones and bars.
20098
*/
20099
void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const {
20100
  QLineF backboneMin, backboneMax, barMin, barMax;
20101
  backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile),
20102
                        coordsToPixels(mKey, mMaximum));
20103
  backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile),
20104
                        coordsToPixels(mKey, mMinimum));
20105
  barMax.setPoints(coordsToPixels(mKey - mWhiskerWidth * 0.5, mMaximum),
20106
                   coordsToPixels(mKey + mWhiskerWidth * 0.5, mMaximum));
20107
  barMin.setPoints(coordsToPixels(mKey - mWhiskerWidth * 0.5, mMinimum),
20108
                   coordsToPixels(mKey + mWhiskerWidth * 0.5, mMinimum));
20109
  applyErrorBarsAntialiasingHint(painter);
20110
  painter->setPen(mWhiskerPen);
20111
  painter->drawLine(backboneMin);
20112
  painter->drawLine(backboneMax);
20113
  painter->setPen(mWhiskerBarPen);
20114
  painter->drawLine(barMin);
20115
  painter->drawLine(barMax);
20116
}
20117
20118
/*! \internal
20119
20120
  Draws the outlier scatter points.
20121
*/
20122
void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const {
20123
  applyScattersAntialiasingHint(painter);
20124
  mOutlierStyle.applyTo(painter, mPen);
20125
  for (int i = 0; i < mOutliers.size(); ++i)
20126
    mOutlierStyle.drawShape(painter, coordsToPixels(mKey, mOutliers.at(i)));
20127
}
20128
20129
/* inherits documentation from base class */
20130
QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange,
20131
                                        SignDomain inSignDomain) const {
20132
  foundRange = true;
20133
  if (inSignDomain == sdBoth) {
20134
    return QCPRange(mKey - mWidth * 0.5, mKey + mWidth * 0.5);
20135
  } else if (inSignDomain == sdNegative) {
20136
    if (mKey + mWidth * 0.5 < 0)
20137
      return QCPRange(mKey - mWidth * 0.5, mKey + mWidth * 0.5);
20138
    else if (mKey < 0)
20139
      return QCPRange(mKey - mWidth * 0.5, mKey);
20140
    else {
20141
      foundRange = false;
20142
      return QCPRange();
20143
    }
20144
  } else if (inSignDomain == sdPositive) {
20145
    if (mKey - mWidth * 0.5 > 0)
20146
      return QCPRange(mKey - mWidth * 0.5, mKey + mWidth * 0.5);
20147
    else if (mKey > 0)
20148
      return QCPRange(mKey, mKey + mWidth * 0.5);
20149
    else {
20150
      foundRange = false;
20151
      return QCPRange();
20152
    }
20153
  }
20154
  foundRange = false;
20155
  return QCPRange();
20156
}
20157
20158
/* inherits documentation from base class */
20159
QCPRange QCPStatisticalBox::getValueRange(bool &foundRange,
20160
                                          SignDomain inSignDomain) const {
20161
  QVector<double> values;  // values that must be considered (i.e. all outliers
20162
                           // and the five box-parameters)
20163
  values.reserve(mOutliers.size() + 5);
20164
  values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
20165
  values << mOutliers;
20166
  // go through values and find the ones in legal range:
20167
  bool haveUpper = false;
20168
  bool haveLower = false;
20169
  double upper = 0;
20170
  double lower = 0;
20171
  for (int i = 0; i < values.size(); ++i) {
20172
    if ((inSignDomain == sdNegative && values.at(i) < 0) ||
20173
        (inSignDomain == sdPositive && values.at(i) > 0) ||
20174
        (inSignDomain == sdBoth)) {
20175
      if (values.at(i) > upper || !haveUpper) {
20176
        upper = values.at(i);
20177
        haveUpper = true;
20178
      }
20179
      if (values.at(i) < lower || !haveLower) {
20180
        lower = values.at(i);
20181
        haveLower = true;
20182
      }
20183
    }
20184
  }
20185
  // return the bounds if we found some sensible values:
20186
  if (haveLower && haveUpper) {
20187
    foundRange = true;
20188
    return QCPRange(lower, upper);
20189
  } else  // might happen if all values are in other sign domain
20190
  {
20191
    foundRange = false;
20192
    return QCPRange();
20193
  }
20194
}
20195
20196
////////////////////////////////////////////////////////////////////////////////////////////////////
20197
//////////////////// QCPColorMapData
20198
////////////////////////////////////////////////////////////////////////////////////////////////////
20199
20200
/*! \class QCPColorMapData
20201
  \brief Holds the two-dimensional data of a QCPColorMap plottable.
20202
20203
  This class is a data storage for \ref QCPColorMap. It holds a two-dimensional
20204
  array, which \ref QCPColorMap then displays as a 2D image in the plot, where
20205
  the array values are represented by a color, depending on the value.
20206
20207
  The size of the array can be controlled via \ref setSize (or \ref setKeySize,
20208
  \ref setValueSize). Which plot coordinates these cells correspond to can be
20209
  configured with \ref setRange (or \ref setKeyRange, \ref setValueRange).
20210
20211
  The data cells can be accessed in two ways: They can be directly addressed by
20212
  an integer index with \ref setCell. This is the fastest method. Alternatively,
20213
  they can be addressed by their plot coordinate with \ref setData. plot
20214
  coordinate to cell index transformations and vice versa are provided by the
20215
  functions \ref coordToCell and \ref cellToCoord.
20216
20217
  This class also buffers the minimum and maximum values that are in the data
20218
  set, to provide QCPColorMap::rescaleDataRange with the necessary information
20219
  quickly. Setting a cell to a value that is greater than the current maximum
20220
  increases this maximum to the new value. However, setting the cell that
20221
  currently holds the maximum value to a smaller value doesn't decrease the
20222
  maximum again, because finding the true new maximum would require going
20223
  through the entire data array, which might be time consuming. The same holds
20224
  for the data minimum. This functionality is given by \ref
20225
  recalculateDataBounds, such that you can decide when it is sensible to find
20226
  the true current minimum and maximum. The method QCPColorMap::rescaleDataRange
20227
  offers a convenience parameter \a recalculateDataBounds which may be set to
20228
  true to automatically call \ref recalculateDataBounds internally.
20229
*/
20230
20231
/* start of documentation of inline functions */
20232
20233
/*! \fn bool QCPColorMapData::isEmpty() const
20234
20235
  Returns whether this instance carries no data. This is equivalent to having a
20236
  size where at least one of the dimensions is 0 (see \ref setSize).
20237
*/
20238
20239
/* end of documentation of inline functions */
20240
20241
/*!
20242
  Constructs a new QCPColorMapData instance. The instance has \a keySize cells
20243
  in the key direction and \a valueSize cells in the value direction. These
20244
  cells will be displayed by the \ref QCPColorMap at the coordinates \a keyRange
20245
  and \a valueRange.
20246
20247
  \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange
20248
*/
20249
QCPColorMapData::QCPColorMapData(int keySize, int valueSize,
20250
                                 const QCPRange &keyRange,
20251
                                 const QCPRange &valueRange)
20252
    : mKeySize(0),
20253
      mValueSize(0),
20254
      mKeyRange(keyRange),
20255
      mValueRange(valueRange),
20256
      mIsEmpty(true),
20257
      mData(0),
20258
      mDataModified(true) {
20259
  setSize(keySize, valueSize);
20260
  fill(0);
20261
}
20262
20263
QCPColorMapData::~QCPColorMapData() {
20264
  if (mData) delete[] mData;
20265
}
20266
20267
/*!
20268
  Constructs a new QCPColorMapData instance copying the data and range of \a
20269
  other.
20270
*/
20271
QCPColorMapData::QCPColorMapData(const QCPColorMapData &other)
20272
    : mKeySize(0),
20273
      mValueSize(0),
20274
      mIsEmpty(true),
20275
      mData(0),
20276
      mDataModified(true) {
20277
  *this = other;
20278
}
20279
20280
/*!
20281
  Overwrites this color map data instance with the data stored in \a other.
20282
*/
20283
QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other) {
20284
  if (&other != this) {
20285
    const int keySize = other.keySize();
20286
    const int valueSize = other.valueSize();
20287
    setSize(keySize, valueSize);
20288
    setRange(other.keyRange(), other.valueRange());
20289
    if (!mIsEmpty)
20290
      memcpy(mData, other.mData, sizeof(mData[0]) * keySize * valueSize);
20291
    mDataBounds = other.mDataBounds;
20292
    mDataModified = true;
20293
  }
20294
  return *this;
20295
}
20296
20297
/* undocumented getter */
20298
double QCPColorMapData::data(double key, double value) {
20299
  int keyCell = (key - mKeyRange.lower) / (mKeyRange.upper - mKeyRange.lower) *
20300
                    (mKeySize - 1) +
20301
                0.5;
20302
  int valueCell = (value - mValueRange.lower) /
20303
                      (mValueRange.upper - mValueRange.lower) *
20304
                      (mValueSize - 1) +
20305
                  0.5;
20306
  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 &&
20307
      valueCell < mValueSize)
20308
    return mData[valueCell * mKeySize + keyCell];
20309
  else
20310
    return 0;
20311
}
20312
20313
/* undocumented getter */
20314
double QCPColorMapData::cell(int keyIndex, int valueIndex) {
20315
  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 &&
20316
      valueIndex < mValueSize)
20317
    return mData[valueIndex * mKeySize + keyIndex];
20318
  else
20319
    return 0;
20320
}
20321
20322
/*!
20323
  Resizes the data array to have \a keySize cells in the key dimension and \a
20324
  valueSize cells in the value dimension.
20325
20326
  The current data is discarded and the map cells are set to 0, unless the map
20327
  had already the requested size.
20328
20329
  Setting at least one of \a keySize or \a valueSize to zero frees the internal
20330
  data array and \ref isEmpty returns true.
20331
20332
  \see setRange, setKeySize, setValueSize
20333
*/
20334
void QCPColorMapData::setSize(int keySize, int valueSize) {
20335
  if (keySize != mKeySize || valueSize != mValueSize) {
20336
    mKeySize = keySize;
20337
    mValueSize = valueSize;
20338
    if (mData) delete[] mData;
20339
    mIsEmpty = mKeySize == 0 || mValueSize == 0;
20340
    if (!mIsEmpty) {
20341
#ifdef __EXCEPTIONS
20342
      try {  // 2D arrays get memory intensive fast. So if the allocation fails,
20343
             // at least output debug message
20344
#endif
20345
        mData = new double[mKeySize * mValueSize];
20346
#ifdef __EXCEPTIONS
20347
      } catch (...) {
20348
        mData = 0;
20349
      }
20350
#endif
20351
      if (mData)
20352
        fill(0);
20353
      else
20354
        qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "
20355
                 << mKeySize << "*" << mValueSize;
20356
    } else
20357
      mData = 0;
20358
    mDataModified = true;
20359
  }
20360
}
20361
20362
/*!
20363
  Resizes the data array to have \a keySize cells in the key dimension.
20364
20365
  The current data is discarded and the map cells are set to 0, unless the map
20366
  had already the requested size.
20367
20368
  Setting \a keySize to zero frees the internal data array and \ref isEmpty
20369
  returns true.
20370
20371
  \see setKeyRange, setSize, setValueSize
20372
*/
20373
void QCPColorMapData::setKeySize(int keySize) { setSize(keySize, mValueSize); }
20374
20375
/*!
20376
  Resizes the data array to have \a valueSize cells in the value dimension.
20377
20378
  The current data is discarded and the map cells are set to 0, unless the map
20379
  had already the requested size.
20380
20381
  Setting \a valueSize to zero frees the internal data array and \ref isEmpty
20382
  returns true.
20383
20384
  \see setValueRange, setSize, setKeySize
20385
*/
20386
void QCPColorMapData::setValueSize(int valueSize) {
20387
  setSize(mKeySize, valueSize);
20388
}
20389
20390
/*!
20391
  Sets the coordinate ranges the data shall be distributed over. This defines
20392
  the rectangular area covered by the color map in plot coordinates.
20393
20394
  The outer cells will be centered on the range boundaries given to this
20395
  function. For example, if the key size (\ref setKeySize) is 3 and \a keyRange
20396
  is set to <tt>QCPRange(2, 3)</tt> there will be cells centered on the key
20397
  coordinates 2, 2.5 and 3.
20398
20399
  \see setSize
20400
*/
20401
void QCPColorMapData::setRange(const QCPRange &keyRange,
20402
                               const QCPRange &valueRange) {
20403
  setKeyRange(keyRange);
20404
  setValueRange(valueRange);
20405
}
20406
20407
/*!
20408
  Sets the coordinate range the data shall be distributed over in the key
20409
  dimension. Together with the value range, This defines the rectangular area
20410
  covered by the color map in plot coordinates.
20411
20412
  The outer cells will be centered on the range boundaries given to this
20413
  function. For example, if the key size (\ref setKeySize) is 3 and \a keyRange
20414
  is set to <tt>QCPRange(2, 3)</tt> there will be cells centered on the key
20415
  coordinates 2, 2.5 and 3.
20416
20417
  \see setRange, setValueRange, setSize
20418
*/
20419
void QCPColorMapData::setKeyRange(const QCPRange &keyRange) {
20420
  mKeyRange = keyRange;
20421
}
20422
20423
/*!
20424
  Sets the coordinate range the data shall be distributed over in the value
20425
  dimension. Together with the key range, This defines the rectangular area
20426
  covered by the color map in plot coordinates.
20427
20428
  The outer cells will be centered on the range boundaries given to this
20429
  function. For example, if the value size (\ref setValueSize) is 3 and \a
20430
  valueRange is set to <tt>QCPRange(2, 3)</tt> there will be cells centered on
20431
  the value coordinates 2, 2.5 and 3.
20432
20433
  \see setRange, setKeyRange, setSize
20434
*/
20435
void QCPColorMapData::setValueRange(const QCPRange &valueRange) {
20436
  mValueRange = valueRange;
20437
}
20438
20439
/*!
20440
  Sets the data of the cell, which lies at the plot coordinates given by \a key
20441
  and \a value, to \a z.
20442
20443
  \note The QCPColorMap always displays the data at equal key/value intervals,
20444
  even if the key or value axis is set to a logarithmic scaling. If you want to
20445
  use QCPColorMap with logarithmic axes, you shouldn't use the \ref
20446
  QCPColorMapData::setData method as it uses a linear transformation to
20447
  determine the cell index. Rather directly access the cell index with \ref
20448
  QCPColorMapData::setCell.
20449
20450
  \see setCell, setRange
20451
*/
20452
void QCPColorMapData::setData(double key, double value, double z) {
20453
  int keyCell = (key - mKeyRange.lower) / (mKeyRange.upper - mKeyRange.lower) *
20454
                    (mKeySize - 1) +
20455
                0.5;
20456
  int valueCell = (value - mValueRange.lower) /
20457
                      (mValueRange.upper - mValueRange.lower) *
20458
                      (mValueSize - 1) +
20459
                  0.5;
20460
  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 &&
20461
      valueCell < mValueSize) {
20462
    mData[valueCell * mKeySize + keyCell] = z;
20463
    if (z < mDataBounds.lower) mDataBounds.lower = z;
20464
    if (z > mDataBounds.upper) mDataBounds.upper = z;
20465
    mDataModified = true;
20466
  }
20467
}
20468
20469
/*!
20470
  Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z.
20471
  The indices enumerate the cells starting from zero, up to the map's size-1 in
20472
  the respective dimension (see \ref setSize).
20473
20474
  In the standard plot configuration (horizontal key axis and vertical value
20475
  axis, both not range-reversed), the cell with indices (0, 0) is in the bottom
20476
  left corner and the cell with indices (keySize-1, valueSize-1) is in the top
20477
  right corner of the color map.
20478
20479
  \see setData, setSize
20480
*/
20481
void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z) {
20482
  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 &&
20483
      valueIndex < mValueSize) {
20484
    mData[valueIndex * mKeySize + keyIndex] = z;
20485
    if (z < mDataBounds.lower) mDataBounds.lower = z;
20486
    if (z > mDataBounds.upper) mDataBounds.upper = z;
20487
    mDataModified = true;
20488
  }
20489
}
20490
20491
/*!
20492
  Goes through the data and updates the buffered minimum and maximum data
20493
  values.
20494
20495
  Calling this method is only advised if you are about to call \ref
20496
  QCPColorMap::rescaleDataRange and can not guarantee that the cells holding the
20497
  maximum or minimum data haven't been overwritten with a smaller or larger
20498
  value respectively, since the buffered maximum/minimum values have been
20499
  updated the last time. Why this is the case is explained in the class
20500
  description (\ref QCPColorMapData).
20501
20502
  Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter
20503
  \a recalculateDataBounds for convenience. Setting this to true will call this
20504
  method for you, before doing the rescale.
20505
*/
20506
void QCPColorMapData::recalculateDataBounds() {
20507
  if (mKeySize > 0 && mValueSize > 0) {
20508
    double minHeight = mData[0];
20509
    double maxHeight = mData[0];
20510
    const int dataCount = mValueSize * mKeySize;
20511
    for (int i = 0; i < dataCount; ++i) {
20512
      if (mData[i] > maxHeight) maxHeight = mData[i];
20513
      if (mData[i] < minHeight) minHeight = mData[i];
20514
    }
20515
    mDataBounds.lower = minHeight;
20516
    mDataBounds.upper = maxHeight;
20517
  }
20518
}
20519
20520
/*!
20521
  Frees the internal data memory.
20522
20523
  This is equivalent to calling \ref setSize "setSize(0, 0)".
20524
*/
20525
void QCPColorMapData::clear() { setSize(0, 0); }
20526
20527
/*!
20528
  Sets all cells to the value \a z.
20529
*/
20530
void QCPColorMapData::fill(double z) {
20531
  const int dataCount = mValueSize * mKeySize;
20532
  for (int i = 0; i < dataCount; ++i) mData[i] = z;
20533
  mDataBounds = QCPRange(z, z);
20534
  mDataModified = true;
20535
}
20536
20537
/*!
20538
  Transforms plot coordinates given by \a key and \a value to cell indices of
20539
  this QCPColorMapData instance. The resulting cell indices are returned via the
20540
  output parameters \a keyIndex and \a valueIndex.
20541
20542
  The retrieved key/value cell indices can then be used for example with \ref
20543
  setCell.
20544
20545
  If you are only interested in a key or value index, you may pass 0 as \a
20546
  valueIndex or \a keyIndex.
20547
20548
  \note The QCPColorMap always displays the data at equal key/value intervals,
20549
  even if the key or value axis is set to a logarithmic scaling. If you want to
20550
  use QCPColorMap with logarithmic axes, you shouldn't use the \ref
20551
  QCPColorMapData::coordToCell method as it uses a linear transformation to
20552
  determine the cell index.
20553
20554
  \see cellToCoord, QCPAxis::coordToPixel
20555
*/
20556
void QCPColorMapData::coordToCell(double key, double value, int *keyIndex,
20557
                                  int *valueIndex) const {
20558
  if (keyIndex)
20559
    *keyIndex = (key - mKeyRange.lower) / (mKeyRange.upper - mKeyRange.lower) *
20560
                    (mKeySize - 1) +
20561
                0.5;
20562
  if (valueIndex)
20563
    *valueIndex = (value - mValueRange.lower) /
20564
                      (mValueRange.upper - mValueRange.lower) *
20565
                      (mValueSize - 1) +
20566
                  0.5;
20567
}
20568
20569
/*!
20570
  Transforms cell indices given by \a keyIndex and \a valueIndex to cell indices
20571
  of this QCPColorMapData instance. The resulting coordinates are returned via
20572
  the output parameters \a key and \a value.
20573
20574
  If you are only interested in a key or value coordinate, you may pass 0 as \a
20575
  key or \a value.
20576
20577
  \note The QCPColorMap always displays the data at equal key/value intervals,
20578
  even if the key or value axis is set to a logarithmic scaling. If you want to
20579
  use QCPColorMap with logarithmic axes, you shouldn't use the \ref
20580
  QCPColorMapData::cellToCoord method as it uses a linear transformation to
20581
  determine the cell index.
20582
20583
  \see coordToCell, QCPAxis::pixelToCoord
20584
*/
20585
void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key,
20586
                                  double *value) const {
20587
  if (key)
20588
    *key = keyIndex / (double)(mKeySize - 1) *
20589
               (mKeyRange.upper - mKeyRange.lower) +
20590
           mKeyRange.lower;
20591
  if (value)
20592
    *value = valueIndex / (double)(mValueSize - 1) *
20593
                 (mValueRange.upper - mValueRange.lower) +
20594
             mValueRange.lower;
20595
}
20596
20597
////////////////////////////////////////////////////////////////////////////////////////////////////
20598
//////////////////// QCPColorMap
20599
////////////////////////////////////////////////////////////////////////////////////////////////////
20600
20601
/*! \class QCPColorMap
20602
  \brief A plottable representing a two-dimensional color map in a plot.
20603
20604
  \image html QCPColorMap.png
20605
20606
  The data is stored in the class \ref QCPColorMapData, which can be accessed
20607
  via the data() method.
20608
20609
  A color map has three dimensions to represent a data point: The \a key
20610
  dimension, the \a value dimension and the \a data dimension. As with other
20611
  plottables such as graphs, \a key and \a value correspond to two orthogonal
20612
  axes on the QCustomPlot surface that you specify in the QCPColorMap
20613
  constructor. The \a data dimension however is encoded as the color of the
20614
  point at (\a key, \a value).
20615
20616
  Set the number of points (or \a cells) in the key/value dimension via \ref
20617
  QCPColorMapData::setSize. The plot coordinate range over which these points
20618
  will be displayed is specified via \ref QCPColorMapData::setRange. The first
20619
  cell will be centered on the lower range boundary and the last cell will be
20620
  centered on the upper range boundary. The data can be set by either accessing
20621
  the cells directly with QCPColorMapData::setCell or by addressing the cells
20622
  via their plot coordinates with \ref QCPColorMapData::setData. If possible,
20623
  you should prefer setCell, since it doesn't need to do any coordinate
20624
  transformation and thus performs a bit better.
20625
20626
  The cell with index (0, 0) is at the bottom left, if the color map uses normal
20627
  (i.e. not reversed) key and value axes.
20628
20629
  To show the user which colors correspond to which \a data values, a \ref
20630
  QCPColorScale is typically placed to the right of the axis rect. See the
20631
  documentation there for details on how to add and use a color scale.
20632
20633
  \section appearance Changing the appearance
20634
20635
  The central part of the appearance is the color gradient, which can be
20636
  specified via \ref setGradient. See the documentation of \ref QCPColorGradient
20637
  for details on configuring a color gradient.
20638
20639
  The \a data range that is mapped to the colors of the gradient can be
20640
  specified with \ref setDataRange. To make the data range encompass the whole
20641
  data set minimum to maximum, call \ref rescaleDataRange.
20642
20643
  \section usage Usage
20644
20645
  Like all data representing objects in QCustomPlot, the QCPColorMap is a
20646
  plottable (QCPAbstractPlottable). So the plottable-interface of QCustomPlot
20647
  applies (QCustomPlot::plottable, QCustomPlot::addPlottable,
20648
  QCustomPlot::removePlottable, etc.)
20649
20650
  Usually, you first create an instance and add it to the customPlot:
20651
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-1
20652
  and then modify the properties of the newly created color map, e.g.:
20653
  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-2
20654
20655
  \note The QCPColorMap always displays the data at equal key/value intervals,
20656
  even if the key or value axis is set to a logarithmic scaling. If you want to
20657
  use QCPColorMap with logarithmic axes, you shouldn't use the \ref
20658
  QCPColorMapData::setData method as it uses a linear transformation to
20659
  determine the cell index. Rather directly access the cell index with \ref
20660
  QCPColorMapData::setCell.
20661
*/
20662
20663
/* start documentation of inline functions */
20664
20665
/*! \fn QCPColorMapData *QCPColorMap::data() const
20666
20667
  Returns a pointer to the internal data storage of type \ref QCPColorMapData.
20668
  Access this to modify data points (cells) and the color map key/value range.
20669
20670
  \see setData
20671
*/
20672
20673
/* end documentation of inline functions */
20674
20675
/* start documentation of signals */
20676
20677
/*! \fn void QCPColorMap::dataRangeChanged(QCPRange newRange);
20678
20679
  This signal is emitted when the data range changes.
20680
20681
  \see setDataRange
20682
*/
20683
20684
/*! \fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
20685
20686
  This signal is emitted when the data scale type changes.
20687
20688
  \see setDataScaleType
20689
*/
20690
20691
/*! \fn void QCPColorMap::gradientChanged(QCPColorGradient newGradient);
20692
20693
  This signal is emitted when the gradient changes.
20694
20695
  \see setGradient
20696
*/
20697
20698
/* end documentation of signals */
20699
20700
/*!
20701
  Constructs a color map with the specified \a keyAxis and \a valueAxis.
20702
20703
  The constructed QCPColorMap can be added to the plot with
20704
  QCustomPlot::addPlottable, QCustomPlot then takes ownership of the color map.
20705
*/
20706
QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
20707
    : QCPAbstractPlottable(keyAxis, valueAxis),
20708
      mDataScaleType(QCPAxis::stLinear),
20709
      mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
20710
      mInterpolate(true),
20711
      mTightBoundary(false),
20712
      mMapImageInvalidated(true) {}
20713
20714
QCPColorMap::~QCPColorMap() { delete mMapData; }
20715
20716
/*!
20717
  Replaces the current \ref data with the provided \a data.
20718
20719
  If \a copy is set to true, the \a data object will only be copied. if false,
20720
  the color map takes ownership of the passed data and replaces the internal
20721
  data pointer with it. This is significantly faster than copying for large
20722
  datasets.
20723
*/
20724
void QCPColorMap::setData(QCPColorMapData *data, bool copy) {
20725
  if (mMapData == data) {
20726
    qDebug() << Q_FUNC_INFO
20727
             << "The data pointer is already in (and owned by) this plottable"
20728
             << reinterpret_cast<quintptr>(data);
20729
    return;
20730
  }
20731
  if (copy) {
20732
    *mMapData = *data;
20733
  } else {
20734
    delete mMapData;
20735
    mMapData = data;
20736
  }
20737
  mMapImageInvalidated = true;
20738
}
20739
20740
/*!
20741
  Sets the data range of this color map to \a dataRange. The data range defines
20742
  which data values are mapped to the color gradient.
20743
20744
  To make the data range span the full range of the data set, use \ref
20745
  rescaleDataRange.
20746
20747
  \see QCPColorScale::setDataRange
20748
*/
20749
void QCPColorMap::setDataRange(const QCPRange &dataRange) {
20750
  if (!QCPRange::validRange(dataRange)) return;
20751
  if (mDataRange.lower != dataRange.lower ||
20752
      mDataRange.upper != dataRange.upper) {
20753
    if (mDataScaleType == QCPAxis::stLogarithmic)
20754
      mDataRange = dataRange.sanitizedForLogScale();
20755
    else
20756
      mDataRange = dataRange.sanitizedForLinScale();
20757
    mMapImageInvalidated = true;
20758
    emit dataRangeChanged(mDataRange);
20759
  }
20760
}
20761
20762
/*!
20763
  Sets whether the data is correlated with the color gradient linearly or
20764
  logarithmically.
20765
20766
  \see QCPColorScale::setDataScaleType
20767
*/
20768
void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType) {
20769
  if (mDataScaleType != scaleType) {
20770
    mDataScaleType = scaleType;
20771
    mMapImageInvalidated = true;
20772
    emit dataScaleTypeChanged(mDataScaleType);
20773
    if (mDataScaleType == QCPAxis::stLogarithmic)
20774
      setDataRange(mDataRange.sanitizedForLogScale());
20775
  }
20776
}
20777
20778
/*!
20779
  Sets the color gradient that is used to represent the data. For more details
20780
  on how to create an own gradient or use one of the preset gradients, see \ref
20781
  QCPColorGradient.
20782
20783
  The colors defined by the gradient will be used to represent data values in
20784
  the currently set data range, see \ref setDataRange. Data points that are
20785
  outside this data range will either be colored uniformly with the respective
20786
  gradient boundary color, or the gradient will repeat, depending on \ref
20787
  QCPColorGradient::setPeriodic.
20788
20789
  \see QCPColorScale::setGradient
20790
*/
20791
void QCPColorMap::setGradient(const QCPColorGradient &gradient) {
20792
  if (mGradient != gradient) {
20793
    mGradient = gradient;
20794
    mMapImageInvalidated = true;
20795
    emit gradientChanged(mGradient);
20796
  }
20797
}
20798
20799
/*!
20800
  Sets whether the color map image shall use bicubic interpolation when
20801
  displaying the color map shrinked or expanded, and not at a 1:1 pixel-to-data
20802
  scale.
20803
20804
  \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation
20805
  and without interpolation enabled"
20806
*/
20807
void QCPColorMap::setInterpolate(bool enabled) {
20808
  mInterpolate = enabled;
20809
  mMapImageInvalidated =
20810
      true;  // because oversampling factors might need to change
20811
}
20812
20813
/*!
20814
  Sets whether the outer most data rows and columns are clipped to the specified
20815
  key and value range (see \ref QCPColorMapData::setKeyRange, \ref
20816
  QCPColorMapData::setValueRange).
20817
20818
  if \a enabled is set to false, the data points at the border of the color map
20819
  are drawn with the same width and height as all other data points. Since the
20820
  data points are represented by rectangles of one color centered on the data
20821
  coordinate, this means that the shown color map extends by half a data point
20822
  over the specified key/value range in each direction.
20823
20824
  \image html QCPColorMap-tightboundary.png "A color map, with tight boundary
20825
  enabled and disabled"
20826
*/
20827
void QCPColorMap::setTightBoundary(bool enabled) { mTightBoundary = enabled; }
20828
20829
/*!
20830
  Associates the color scale \a colorScale with this color map.
20831
20832
  This means that both the color scale and the color map synchronize their
20833
  gradient, data range and data scale type (\ref setGradient, \ref setDataRange,
20834
  \ref setDataScaleType). Multiple color maps can be associated with one single
20835
  color scale. This causes the color maps to also synchronize those properties,
20836
  via the mutual color scale.
20837
20838
  This function causes the color map to adopt the current color gradient, data
20839
  range and data scale type of \a colorScale. After this call, you may change
20840
  these properties at either the color map or the color scale, and the setting
20841
  will be applied to both.
20842
20843
  Pass 0 as \a colorScale to disconnect the color scale from this color map
20844
  again.
20845
*/
20846
void QCPColorMap::setColorScale(QCPColorScale *colorScale) {
20847
  if (mColorScale)  // unconnect signals from old color scale
20848
  {
20849
    disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(),
20850
               SLOT(setDataRange(QCPRange)));
20851
    disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)),
20852
               mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20853
    disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)),
20854
               mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20855
    disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this,
20856
               SLOT(setDataRange(QCPRange)));
20857
    disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)),
20858
               this, SLOT(setGradient(QCPColorGradient)));
20859
    disconnect(mColorScale.data(),
20860
               SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this,
20861
               SLOT(setDataScaleType(QCPAxis::ScaleType)));
20862
  }
20863
  mColorScale = colorScale;
20864
  if (mColorScale)  // connect signals to new color scale
20865
  {
20866
    setGradient(mColorScale.data()->gradient());
20867
    setDataRange(mColorScale.data()->dataRange());
20868
    setDataScaleType(mColorScale.data()->dataScaleType());
20869
    connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(),
20870
            SLOT(setDataRange(QCPRange)));
20871
    connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)),
20872
            mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20873
    connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(),
20874
            SLOT(setGradient(QCPColorGradient)));
20875
    connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this,
20876
            SLOT(setDataRange(QCPRange)));
20877
    connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this,
20878
            SLOT(setGradient(QCPColorGradient)));
20879
    connect(mColorScale.data(),
20880
            SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this,
20881
            SLOT(setDataScaleType(QCPAxis::ScaleType)));
20882
  }
20883
}
20884
20885
/*!
20886
  Sets the data range (\ref setDataRange) to span the minimum and maximum values
20887
  that occur in the current data set. This corresponds to the \ref
20888
  rescaleKeyAxis or \ref rescaleValueAxis methods, only for the third data
20889
  dimension of the color map.
20890
20891
  The minimum and maximum values of the data set are buffered in the internal
20892
  QCPColorMapData instance (\ref data). As data is updated via its \ref
20893
  QCPColorMapData::setCell or \ref QCPColorMapData::setData, the buffered
20894
  minimum and maximum values are updated, too. For performance reasons, however,
20895
  they are only updated in an expanding fashion. So the buffered maximum can
20896
  only increase and the buffered minimum can only decrease. In consequence,
20897
  changes to the data that actually lower the maximum of the data set (by
20898
  overwriting the cell holding the current maximum with a smaller value), aren't
20899
  recognized and the buffered maximum overestimates the true maximum of the data
20900
  set. The same happens for the buffered minimum. To recalculate the true
20901
  minimum and maximum by explicitly looking at each cell, the method
20902
  QCPColorMapData::recalculateDataBounds can be used. For convenience, setting
20903
  the parameter \a recalculateDataBounds calls this method before setting the
20904
  data range to the buffered minimum and maximum.
20905
20906
  \see setDataRange
20907
*/
20908
void QCPColorMap::rescaleDataRange(bool recalculateDataBounds) {
20909
  if (recalculateDataBounds) mMapData->recalculateDataBounds();
20910
  setDataRange(mMapData->dataBounds());
20911
}
20912
20913
/*!
20914
  Takes the current appearance of the color map and updates the legend icon,
20915
  which is used to represent this color map in the legend (see \ref QCPLegend).
20916
20917
  The \a transformMode specifies whether the rescaling is done by a faster, low
20918
  quality image scaling algorithm (Qt::FastTransformation) or by a slower,
20919
  higher quality algorithm (Qt::SmoothTransformation).
20920
20921
  The current color map appearance is scaled down to \a thumbSize. Ideally, this
20922
  should be equal to the size of the legend icon (see \ref
20923
  QCPLegend::setIconSize). If it isn't exactly the configured legend icon size,
20924
  the thumb will be rescaled during drawing of the legend item.
20925
20926
  \see setDataRange
20927
*/
20928
void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode,
20929
                                   const QSize &thumbSize) {
20930
  if (mMapImage.isNull() && !data()->isEmpty())
20931
    updateMapImage();  // try to update map image if it's null (happens if no
20932
                       // draw has happened yet)
20933
20934
  if (!mMapImage.isNull())  // might still be null, e.g. if data is empty, so
20935
                            // check here again
20936
  {
20937
    bool mirrorX =
20938
        (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())
20939
            ->rangeReversed();
20940
    bool mirrorY =
20941
        (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())
20942
            ->rangeReversed();
20943
    mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY))
20944
                      .scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
20945
  }
20946
}
20947
20948
/*!
20949
  Clears the colormap data by calling \ref QCPColorMapData::clear() on the
20950
  internal data. This also resizes the map to 0x0 cells.
20951
*/
20952
void QCPColorMap::clearData() { mMapData->clear(); }
20953
20954
/* inherits documentation from base class */
20955
double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable,
20956
                               QVariant *details) const {
20957
  Q_UNUSED(details)
20958
  if (onlySelectable && !mSelectable) return -1;
20959
  if (!mKeyAxis || !mValueAxis) {
20960
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
20961
    return -1;
20962
  }
20963
20964
  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
20965
    double posKey, posValue;
20966
    pixelsToCoords(pos, posKey, posValue);
20967
    if (mMapData->keyRange().contains(posKey) &&
20968
        mMapData->valueRange().contains(posValue))
20969
      return mParentPlot->selectionTolerance() * 0.99;
20970
  }
20971
  return -1;
20972
}
20973
20974
/*! \internal
20975
20976
  Updates the internal map image buffer by going through the internal \ref
20977
  QCPColorMapData and turning the data values into color pixels with \ref
20978
  QCPColorGradient::colorize.
20979
20980
  This method is called by \ref QCPColorMap::draw if either the data has been
20981
  modified or the map image has been invalidated for a different reason (e.g. a
20982
  change of the data range with \ref setDataRange).
20983
20984
  If the map cell count is low, the image created will be oversampled in order
20985
  to avoid a QPainter::drawImage bug which makes inner pixel boundaries jitter
20986
  when stretch-drawing images without smooth transform enabled. Accordingly,
20987
  oversampling isn't performed if \ref setInterpolate is true.
20988
*/
20989
void QCPColorMap::updateMapImage() {
20990
  QCPAxis *keyAxis = mKeyAxis.data();
20991
  if (!keyAxis) return;
20992
  if (mMapData->isEmpty()) return;
20993
20994
  const int keySize = mMapData->keySize();
20995
  const int valueSize = mMapData->valueSize();
20996
  int keyOversamplingFactor =
20997
      mInterpolate
20998
          ? 1
20999
          : (int)(1.0 +
21000
                  100.0 /
21001
                      (double)keySize);  // make mMapImage have at least size
21002
                                         // 100, factor becomes 1 if size > 200
21003
                                         // or interpolation is on
21004
  int valueOversamplingFactor =
21005
      mInterpolate
21006
          ? 1
21007
          : (int)(1.0 +
21008
                  100.0 /
21009
                      (double)valueSize);  // make mMapImage have at least size
21010
                                           // 100, factor becomes 1 if size >
21011
                                           // 200 or interpolation is on
21012
21013
  // resize mMapImage to correct dimensions including possible oversampling
21014
  // factors, according to key/value axes orientation:
21015
  if (keyAxis->orientation() == Qt::Horizontal &&
21016
      (mMapImage.width() != keySize * keyOversamplingFactor ||
21017
       mMapImage.height() != valueSize * valueOversamplingFactor))
21018
    mMapImage = QImage(QSize(keySize * keyOversamplingFactor,
21019
                             valueSize * valueOversamplingFactor),
21020
                       QImage::Format_RGB32);
21021
  else if (keyAxis->orientation() == Qt::Vertical &&
21022
           (mMapImage.width() != valueSize * valueOversamplingFactor ||
21023
            mMapImage.height() != keySize * keyOversamplingFactor))
21024
    mMapImage = QImage(QSize(valueSize * valueOversamplingFactor,
21025
                             keySize * keyOversamplingFactor),
21026
                       QImage::Format_RGB32);
21027
21028
  QImage *localMapImage =
21029
      &mMapImage;  // this is the image on which the colorization operates.
21030
                   // Either the final mMapImage, or if we need oversampling,
21031
                   // mUndersampledMapImage
21032
  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) {
21033
    // resize undersampled map image to actual key/value cell sizes:
21034
    if (keyAxis->orientation() == Qt::Horizontal &&
21035
        (mUndersampledMapImage.width() != keySize ||
21036
         mUndersampledMapImage.height() != valueSize))
21037
      mUndersampledMapImage =
21038
          QImage(QSize(keySize, valueSize), QImage::Format_RGB32);
21039
    else if (keyAxis->orientation() == Qt::Vertical &&
21040
             (mUndersampledMapImage.width() != valueSize ||
21041
              mUndersampledMapImage.height() != keySize))
21042
      mUndersampledMapImage =
21043
          QImage(QSize(valueSize, keySize), QImage::Format_RGB32);
21044
    localMapImage = &mUndersampledMapImage;  // make the colorization run on the
21045
                                             // undersampled image
21046
  } else if (!mUndersampledMapImage.isNull())
21047
    mUndersampledMapImage =
21048
        QImage();  // don't need oversampling mechanism anymore (map size has
21049
                   // changed) but mUndersampledMapImage still has nonzero size,
21050
                   // free it
21051
21052
  const double *rawData = mMapData->mData;
21053
  if (keyAxis->orientation() == Qt::Horizontal) {
21054
    const int lineCount = valueSize;
21055
    const int rowCount = keySize;
21056
    for (int line = 0; line < lineCount; ++line) {
21057
      QRgb *pixels = reinterpret_cast<QRgb *>(localMapImage->scanLine(
21058
          lineCount - 1 -
21059
          line));  // invert scanline index because QImage counts scanlines from
21060
                   // top, but our vertical index counts from bottom
21061
                   // (mathematical coordinate system)
21062
      mGradient.colorize(rawData + line * rowCount, mDataRange, pixels,
21063
                         rowCount, 1, mDataScaleType == QCPAxis::stLogarithmic);
21064
    }
21065
  } else  // keyAxis->orientation() == Qt::Vertical
21066
  {
21067
    const int lineCount = keySize;
21068
    const int rowCount = valueSize;
21069
    for (int line = 0; line < lineCount; ++line) {
21070
      QRgb *pixels = reinterpret_cast<QRgb *>(localMapImage->scanLine(
21071
          lineCount - 1 -
21072
          line));  // invert scanline index because QImage counts scanlines from
21073
                   // top, but our vertical index counts from bottom
21074
                   // (mathematical coordinate system)
21075
      mGradient.colorize(rawData + line, mDataRange, pixels, rowCount,
21076
                         lineCount, mDataScaleType == QCPAxis::stLogarithmic);
21077
    }
21078
  }
21079
21080
  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) {
21081
    if (keyAxis->orientation() == Qt::Horizontal)
21082
      mMapImage = mUndersampledMapImage.scaled(
21083
          keySize * keyOversamplingFactor, valueSize * valueOversamplingFactor,
21084
          Qt::IgnoreAspectRatio, Qt::FastTransformation);
21085
    else
21086
      mMapImage = mUndersampledMapImage.scaled(
21087
          valueSize * valueOversamplingFactor, keySize * keyOversamplingFactor,
21088
          Qt::IgnoreAspectRatio, Qt::FastTransformation);
21089
  }
21090
  mMapData->mDataModified = false;
21091
  mMapImageInvalidated = false;
21092
}
21093
21094
/* inherits documentation from base class */
21095
void QCPColorMap::draw(QCPPainter *painter) {
21096
  if (mMapData->isEmpty()) return;
21097
  if (!mKeyAxis || !mValueAxis) return;
21098
  applyDefaultAntialiasingHint(painter);
21099
21100
  if (mMapData->mDataModified || mMapImageInvalidated) updateMapImage();
21101
21102
  // use buffer if painting vectorized (PDF):
21103
  bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
21104
  QCPPainter *localPainter = painter;  // will be redirected to paint on
21105
                                       // mapBuffer if painting vectorized
21106
  QRectF mapBufferTarget;  // the rect in absolute widget coordinates where the
21107
                           // visible map portion/buffer will end up in
21108
  QPixmap mapBuffer;
21109
  double mapBufferPixelRatio =
21110
      3;  // factor by which DPI is increased in embedded bitmaps
21111
  if (useBuffer) {
21112
    mapBufferTarget = painter->clipRegion().boundingRect();
21113
    mapBuffer =
21114
        QPixmap((mapBufferTarget.size() * mapBufferPixelRatio).toSize());
21115
    mapBuffer.fill(Qt::transparent);
21116
    localPainter = new QCPPainter(&mapBuffer);
21117
    localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
21118
    localPainter->translate(-mapBufferTarget.topLeft());
21119
  }
21120
21121
  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower,
21122
                                           mMapData->valueRange().lower),
21123
                            coordsToPixels(mMapData->keyRange().upper,
21124
                                           mMapData->valueRange().upper))
21125
                         .normalized();
21126
  // extend imageRect to contain outer halves/quarters of bordering/cornering
21127
  // pixels (cells are centered on map range boundary):
21128
  double halfCellWidth = 0;   // in pixels
21129
  double halfCellHeight = 0;  // in pixels
21130
  if (keyAxis()->orientation() == Qt::Horizontal) {
21131
    if (mMapData->keySize() > 1)
21132
      halfCellWidth =
21133
          0.5 * imageRect.width() / (double)(mMapData->keySize() - 1);
21134
    if (mMapData->valueSize() > 1)
21135
      halfCellHeight =
21136
          0.5 * imageRect.height() / (double)(mMapData->valueSize() - 1);
21137
  } else  // keyAxis orientation is Qt::Vertical
21138
  {
21139
    if (mMapData->keySize() > 1)
21140
      halfCellHeight =
21141
          0.5 * imageRect.height() / (double)(mMapData->keySize() - 1);
21142
    if (mMapData->valueSize() > 1)
21143
      halfCellWidth =
21144
          0.5 * imageRect.width() / (double)(mMapData->valueSize() - 1);
21145
  }
21146
  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth,
21147
                   halfCellHeight);
21148
  bool mirrorX =
21149
      (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())
21150
          ->rangeReversed();
21151
  bool mirrorY =
21152
      (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())
21153
          ->rangeReversed();
21154
  bool smoothBackup =
21155
      localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
21156
  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
21157
  QRegion clipBackup;
21158
  if (mTightBoundary) {
21159
    clipBackup = localPainter->clipRegion();
21160
    QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower,
21161
                                                 mMapData->valueRange().lower),
21162
                                  coordsToPixels(mMapData->keyRange().upper,
21163
                                                 mMapData->valueRange().upper))
21164
                               .normalized();
21165
    localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
21166
  }
21167
  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
21168
  if (mTightBoundary) localPainter->setClipRegion(clipBackup);
21169
  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
21170
21171
  if (useBuffer)  // localPainter painted to mapBuffer, so now draw buffer with
21172
                  // original painter
21173
  {
21174
    delete localPainter;
21175
    painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
21176
  }
21177
}
21178
21179
/* inherits documentation from base class */
21180
void QCPColorMap::drawLegendIcon(QCPPainter *painter,
21181
                                 const QRectF &rect) const {
21182
  applyDefaultAntialiasingHint(painter);
21183
  // draw map thumbnail:
21184
  if (!mLegendIcon.isNull()) {
21185
    QPixmap scaledIcon = mLegendIcon.scaled(
21186
        rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
21187
    QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
21188
    iconRect.moveCenter(rect.center());
21189
    painter->drawPixmap(iconRect.topLeft(), scaledIcon);
21190
  }
21191
  /*
21192
  // draw frame:
21193
  painter->setBrush(Qt::NoBrush);
21194
  painter->setPen(Qt::black);
21195
  painter->drawRect(rect.adjusted(1, 1, 0, 0));
21196
  */
21197
}
21198
21199
/* inherits documentation from base class */
21200
QCPRange QCPColorMap::getKeyRange(bool &foundRange,
21201
                                  SignDomain inSignDomain) const {
21202
  foundRange = true;
21203
  QCPRange result = mMapData->keyRange();
21204
  result.normalize();
21205
  if (inSignDomain == QCPAbstractPlottable::sdPositive) {
21206
    if (result.lower <= 0 && result.upper > 0)
21207
      result.lower = result.upper * 1e-3;
21208
    else if (result.lower <= 0 && result.upper <= 0)
21209
      foundRange = false;
21210
  } else if (inSignDomain == QCPAbstractPlottable::sdNegative) {
21211
    if (result.upper >= 0 && result.lower < 0)
21212
      result.upper = result.lower * 1e-3;
21213
    else if (result.upper >= 0 && result.lower >= 0)
21214
      foundRange = false;
21215
  }
21216
  return result;
21217
}
21218
21219
/* inherits documentation from base class */
21220
QCPRange QCPColorMap::getValueRange(bool &foundRange,
21221
                                    SignDomain inSignDomain) const {
21222
  foundRange = true;
21223
  QCPRange result = mMapData->valueRange();
21224
  result.normalize();
21225
  if (inSignDomain == QCPAbstractPlottable::sdPositive) {
21226
    if (result.lower <= 0 && result.upper > 0)
21227
      result.lower = result.upper * 1e-3;
21228
    else if (result.lower <= 0 && result.upper <= 0)
21229
      foundRange = false;
21230
  } else if (inSignDomain == QCPAbstractPlottable::sdNegative) {
21231
    if (result.upper >= 0 && result.lower < 0)
21232
      result.upper = result.lower * 1e-3;
21233
    else if (result.upper >= 0 && result.lower >= 0)
21234
      foundRange = false;
21235
  }
21236
  return result;
21237
}
21238
21239
////////////////////////////////////////////////////////////////////////////////////////////////////
21240
//////////////////// QCPFinancialData
21241
////////////////////////////////////////////////////////////////////////////////////////////////////
21242
21243
/*! \class QCPFinancialData
21244
  \brief Holds the data of one single data point for QCPFinancial.
21245
21246
  The container for storing multiple data points is \ref QCPFinancialDataMap.
21247
21248
  The stored data is:
21249
  \li \a key: coordinate on the key axis of this data point
21250
  \li \a open: The opening value at the data point
21251
  \li \a high: The high/maximum value at the data point
21252
  \li \a low: The low/minimum value at the data point
21253
  \li \a close: The closing value at the data point
21254
21255
  \see QCPFinancialDataMap
21256
*/
21257
21258
/*!
21259
  Constructs a data point with key and all values set to zero.
21260
*/
21261
QCPFinancialData::QCPFinancialData()
21262
    : key(0), open(0), high(0), low(0), close(0) {}
21263
21264
/*!
21265
  Constructs a data point with the specified \a key and OHLC values.
21266
*/
21267
QCPFinancialData::QCPFinancialData(double key, double open, double high,
21268
                                   double low, double close)
21269
    : key(key), open(open), high(high), low(low), close(close) {}
21270
21271
////////////////////////////////////////////////////////////////////////////////////////////////////
21272
//////////////////// QCPFinancial
21273
////////////////////////////////////////////////////////////////////////////////////////////////////
21274
21275
/*! \class QCPFinancial
21276
  \brief A plottable representing a financial stock chart
21277
21278
  \image html QCPFinancial.png
21279
21280
  This plottable represents time series data binned to certain intervals, mainly
21281
  used for stock charts. The two common representations OHLC
21282
  (Open-High-Low-Close) bars and Candlesticks can be set via \ref setChartStyle.
21283
21284
  The data is passed via \ref setData as a set of open/high/low/close values at
21285
  certain keys (typically times). This means the data must be already binned
21286
  appropriately. If data is only available as a series of values (e.g. \a price
21287
  against \a time), you can use the static convenience function \ref
21288
  timeSeriesToOhlc to generate binned OHLC-data which can then be passed to \ref
21289
  setData.
21290
21291
  The width of the OHLC bars/candlesticks can be controlled with \ref setWidth
21292
  and is given in plot key coordinates. A typical choice is to set it to (or
21293
  slightly less than) one bin interval width.
21294
21295
  \section appearance Changing the appearance
21296
21297
  Charts can be either single- or two-colored (\ref setTwoColored). If set to be
21298
  single-colored, lines are drawn with the plottable's pen (\ref setPen) and
21299
  fills with the brush (\ref setBrush).
21300
21301
  If set to two-colored, positive changes of the value during an interval (\a
21302
  close >= \a open) are represented with a different pen and brush than negative
21303
  changes (\a close < \a open). These can be configured with \ref
21304
  setPenPositive, \ref setPenNegative, \ref setBrushPositive, and \ref
21305
  setBrushNegative. In two-colored mode, the normal plottable pen/brush is
21306
  ignored. Upon selection however, the normal selected pen/brush (\ref
21307
  setSelectedPen, \ref setSelectedBrush) is used, irrespective of whether the
21308
  chart is single- or two-colored.
21309
21310
*/
21311
21312
/* start of documentation of inline functions */
21313
21314
/*! \fn QCPFinancialDataMap *QCPFinancial::data() const
21315
21316
  Returns a pointer to the internal data storage of type \ref
21317
  QCPFinancialDataMap. You may use it to directly manipulate the data, which may
21318
  be more convenient and faster than using the regular \ref setData or \ref
21319
  addData methods, in certain situations.
21320
*/
21321
21322
/* end of documentation of inline functions */
21323
21324
/*!
21325
  Constructs a financial chart which uses \a keyAxis as its key axis ("x") and
21326
  \a valueAxis as its value axis ("y"). \a keyAxis and \a valueAxis must reside
21327
  in the same QCustomPlot instance and not have the same orientation. If either
21328
  of these restrictions is violated, a corresponding message is printed to the
21329
  debug output (qDebug), the construction is not aborted, though.
21330
21331
  The constructed QCPFinancial can be added to the plot with
21332
  QCustomPlot::addPlottable, QCustomPlot then takes ownership of the financial
21333
  chart.
21334
*/
21335
QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
21336
    : QCPAbstractPlottable(keyAxis, valueAxis),
21337
      mData(0),
21338
      mChartStyle(csOhlc),
21339
      mWidth(0.5),
21340
      mTwoColored(false),
21341
      mBrushPositive(QBrush(QColor(210, 210, 255))),
21342
      mBrushNegative(QBrush(QColor(255, 210, 210))),
21343
      mPenPositive(QPen(QColor(10, 40, 180))),
21344
      mPenNegative(QPen(QColor(180, 40, 10))) {
21345
  mData = new QCPFinancialDataMap;
21346
21347
  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
21348
  setSelectedBrush(QBrush(QColor(80, 80, 255)));
21349
}
21350
21351
QCPFinancial::~QCPFinancial() { delete mData; }
21352
21353
/*!
21354
  Replaces the current data with the provided \a data.
21355
21356
  If \a copy is set to true, data points in \a data will only be copied. if
21357
  false, the plottable takes ownership of the passed data and replaces the
21358
  internal data pointer with it. This is significantly faster than copying for
21359
  large datasets.
21360
21361
  Alternatively, you can also access and modify the plottable's data via the
21362
  \ref data method, which returns a pointer to the internal \ref
21363
  QCPFinancialDataMap.
21364
21365
  \see timeSeriesToOhlc
21366
*/
21367
void QCPFinancial::setData(QCPFinancialDataMap *data, bool copy) {
21368
  if (mData == data) {
21369
    qDebug() << Q_FUNC_INFO
21370
             << "The data pointer is already in (and owned by) this plottable"
21371
             << reinterpret_cast<quintptr>(data);
21372
    return;
21373
  }
21374
  if (copy) {
21375
    *mData = *data;
21376
  } else {
21377
    delete mData;
21378
    mData = data;
21379
  }
21380
}
21381
21382
/*! \overload
21383
21384
  Replaces the current data with the provided open/high/low/close data. The
21385
  provided vectors should have equal length. Else, the number of added points
21386
  will be the size of the smallest vector.
21387
21388
  \see timeSeriesToOhlc
21389
*/
21390
void QCPFinancial::setData(const QVector<double> &key,
21391
                           const QVector<double> &open,
21392
                           const QVector<double> &high,
21393
                           const QVector<double> &low,
21394
                           const QVector<double> &close) {
21395
  mData->clear();
21396
  int n = key.size();
21397
  n = qMin(n, open.size());
21398
  n = qMin(n, high.size());
21399
  n = qMin(n, low.size());
21400
  n = qMin(n, close.size());
21401
  for (int i = 0; i < n; ++i) {
21402
    mData->insertMulti(
21403
        key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
21404
  }
21405
}
21406
21407
/*!
21408
  Sets which representation style shall be used to display the OHLC data.
21409
*/
21410
void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style) {
21411
  mChartStyle = style;
21412
}
21413
21414
/*!
21415
  Sets the width of the individual bars/candlesticks to \a width in plot key
21416
  coordinates.
21417
21418
  A typical choice is to set it to (or slightly less than) one bin interval
21419
  width.
21420
*/
21421
void QCPFinancial::setWidth(double width) { mWidth = width; }
21422
21423
/*!
21424
  Sets whether this chart shall contrast positive from negative trends per data
21425
  point by using two separate colors to draw the respective bars/candlesticks.
21426
21427
  If \a twoColored is false, the normal plottable's pen and brush are used (\ref
21428
  setPen, \ref setBrush).
21429
21430
  \see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative
21431
*/
21432
void QCPFinancial::setTwoColored(bool twoColored) { mTwoColored = twoColored; }
21433
21434
/*!
21435
  If \ref setTwoColored is set to true, this function controls the brush that is
21436
  used to draw fills of data points with a positive trend (i.e.
21437
  bars/candlesticks with close >= open).
21438
21439
  If \a twoColored is false, the normal plottable's pen and brush are used (\ref
21440
  setPen, \ref setBrush).
21441
21442
  \see setBrushNegative, setPenPositive, setPenNegative
21443
*/
21444
void QCPFinancial::setBrushPositive(const QBrush &brush) {
21445
  mBrushPositive = brush;
21446
}
21447
21448
/*!
21449
  If \ref setTwoColored is set to true, this function controls the brush that is
21450
  used to draw fills of data points with a negative trend (i.e.
21451
  bars/candlesticks with close < open).
21452
21453
  If \a twoColored is false, the normal plottable's pen and brush are used (\ref
21454
  setPen, \ref setBrush).
21455
21456
  \see setBrushPositive, setPenNegative, setPenPositive
21457
*/
21458
void QCPFinancial::setBrushNegative(const QBrush &brush) {
21459
  mBrushNegative = brush;
21460
}
21461
21462
/*!
21463
  If \ref setTwoColored is set to true, this function controls the pen that is
21464
  used to draw outlines of data points with a positive trend (i.e.
21465
  bars/candlesticks with close >= open).
21466
21467
  If \a twoColored is false, the normal plottable's pen and brush are used (\ref
21468
  setPen, \ref setBrush).
21469
21470
  \see setPenNegative, setBrushPositive, setBrushNegative
21471
*/
21472
void QCPFinancial::setPenPositive(const QPen &pen) { mPenPositive = pen; }
21473
21474
/*!
21475
  If \ref setTwoColored is set to true, this function controls the pen that is
21476
  used to draw outlines of data points with a negative trend (i.e.
21477
  bars/candlesticks with close < open).
21478
21479
  If \a twoColored is false, the normal plottable's pen and brush are used (\ref
21480
  setPen, \ref setBrush).
21481
21482
  \see setPenPositive, setBrushNegative, setBrushPositive
21483
*/
21484
void QCPFinancial::setPenNegative(const QPen &pen) { mPenNegative = pen; }
21485
21486
/*!
21487
  Adds the provided data points in \a dataMap to the current data.
21488
21489
  Alternatively, you can also access and modify the data via the \ref data
21490
  method, which returns a pointer to the internal \ref QCPFinancialDataMap.
21491
21492
  \see removeData
21493
*/
21494
void QCPFinancial::addData(const QCPFinancialDataMap &dataMap) {
21495
  mData->unite(dataMap);
21496
}
21497
21498
/*! \overload
21499
21500
  Adds the provided single data point in \a data to the current data.
21501
21502
  Alternatively, you can also access and modify the data via the \ref data
21503
  method, which returns a pointer to the internal \ref QCPFinancialData.
21504
21505
  \see removeData
21506
*/
21507
void QCPFinancial::addData(const QCPFinancialData &data) {
21508
  mData->insertMulti(data.key, data);
21509
}
21510
21511
/*! \overload
21512
21513
  Adds the provided single data point given by \a key, \a open, \a high, \a low,
21514
  and \a close to the current data.
21515
21516
  Alternatively, you can also access and modify the data via the \ref data
21517
  method, which returns a pointer to the internal \ref QCPFinancialData.
21518
21519
  \see removeData
21520
*/
21521
void QCPFinancial::addData(double key, double open, double high, double low,
21522
                           double close) {
21523
  mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
21524
}
21525
21526
/*! \overload
21527
21528
  Adds the provided open/high/low/close data to the current data.
21529
21530
  Alternatively, you can also access and modify the data via the \ref data
21531
  method, which returns a pointer to the internal \ref QCPFinancialData.
21532
21533
  \see removeData
21534
*/
21535
void QCPFinancial::addData(const QVector<double> &key,
21536
                           const QVector<double> &open,
21537
                           const QVector<double> &high,
21538
                           const QVector<double> &low,
21539
                           const QVector<double> &close) {
21540
  int n = key.size();
21541
  n = qMin(n, open.size());
21542
  n = qMin(n, high.size());
21543
  n = qMin(n, low.size());
21544
  n = qMin(n, close.size());
21545
  for (int i = 0; i < n; ++i) {
21546
    mData->insertMulti(
21547
        key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
21548
  }
21549
}
21550
21551
/*!
21552
  Removes all data points with keys smaller than \a key.
21553
21554
  \see addData, clearData
21555
*/
21556
void QCPFinancial::removeDataBefore(double key) {
21557
  QCPFinancialDataMap::iterator it = mData->begin();
21558
  while (it != mData->end() && it.key() < key) it = mData->erase(it);
21559
}
21560
21561
/*!
21562
  Removes all data points with keys greater than \a key.
21563
21564
  \see addData, clearData
21565
*/
21566
void QCPFinancial::removeDataAfter(double key) {
21567
  if (mData->isEmpty()) return;
21568
  QCPFinancialDataMap::iterator it = mData->upperBound(key);
21569
  while (it != mData->end()) it = mData->erase(it);
21570
}
21571
21572
/*!
21573
  Removes all data points with keys between \a fromKey and \a toKey. if \a
21574
  fromKey is greater or equal to \a toKey, the function does nothing. To remove
21575
  a single data point with known key, use \ref removeData(double key).
21576
21577
  \see addData, clearData
21578
*/
21579
void QCPFinancial::removeData(double fromKey, double toKey) {
21580
  if (fromKey >= toKey || mData->isEmpty()) return;
21581
  QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
21582
  QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
21583
  while (it != itEnd) it = mData->erase(it);
21584
}
21585
21586
/*! \overload
21587
21588
  Removes a single data point at \a key. If the position is not known with
21589
  absolute precision, consider using \ref removeData(double fromKey, double
21590
  toKey) with a small fuzziness interval around the suspected position, depeding
21591
  on the precision with which the key is known.
21592
21593
  \see addData, clearData
21594
*/
21595
void QCPFinancial::removeData(double key) { mData->remove(key); }
21596
21597
/*!
21598
  Removes all data points.
21599
21600
  \see removeData, removeDataAfter, removeDataBefore
21601
*/
21602
void QCPFinancial::clearData() { mData->clear(); }
21603
21604
/* inherits documentation from base class */
21605
double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable,
21606
                                QVariant *details) const {
21607
  Q_UNUSED(details)
21608
  if (onlySelectable && !mSelectable) return -1;
21609
  if (!mKeyAxis || !mValueAxis) {
21610
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21611
    return -1;
21612
  }
21613
21614
  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
21615
    // get visible data range:
21616
    QCPFinancialDataMap::const_iterator lower,
21617
        upper;  // note that upper is the actual upper point, and not 1 step
21618
                // after the upper point
21619
    getVisibleDataBounds(lower, upper);
21620
    if (lower == mData->constEnd() || upper == mData->constEnd()) return -1;
21621
    // perform select test according to configured style:
21622
    switch (mChartStyle) {
21623
      case QCPFinancial::csOhlc:
21624
        return ohlcSelectTest(pos, lower, upper + 1);
21625
        break;
21626
      case QCPFinancial::csCandlestick:
21627
        return candlestickSelectTest(pos, lower, upper + 1);
21628
        break;
21629
    }
21630
  }
21631
  return -1;
21632
}
21633
21634
/*!
21635
  A convenience function that converts time series data (\a value against \a
21636
  time) to OHLC binned data points. The return value can then be passed on to
21637
  \ref setData.
21638
21639
  The size of the bins can be controlled with \a timeBinSize in the same units
21640
  as \a time is given. For example, if the unit of \a time is seconds and single
21641
  OHLC/Candlesticks should span an hour each, set \a timeBinSize to 3600.
21642
21643
  \a timeBinOffset allows to control precisely at what \a time coordinate a bin
21644
  should start. The value passed as \a timeBinOffset doesn't need to be in the
21645
  range encompassed by the \a time keys. It merely defines the mathematical
21646
  offset/phase of the bins that will be used to process the data.
21647
*/
21648
QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time,
21649
                                                   const QVector<double> &value,
21650
                                                   double timeBinSize,
21651
                                                   double timeBinOffset) {
21652
  QCPFinancialDataMap map;
21653
  int count = qMin(time.size(), value.size());
21654
  if (count == 0) return QCPFinancialDataMap();
21655
21656
  QCPFinancialData currentBinData(0, value.first(), value.first(),
21657
                                  value.first(), value.first());
21658
  int currentBinIndex =
21659
      qFloor((time.first() - timeBinOffset) / timeBinSize + 0.5);
21660
  for (int i = 0; i < count; ++i) {
21661
    int index = qFloor((time.at(i) - timeBinOffset) / timeBinSize + 0.5);
21662
    if (currentBinIndex ==
21663
        index)  // data point still in current bin, extend high/low:
21664
    {
21665
      if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
21666
      if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
21667
      if (i == count - 1)  // last data point is in current bin, finalize bin:
21668
      {
21669
        currentBinData.close = value.at(i);
21670
        currentBinData.key = timeBinOffset + (index)*timeBinSize;
21671
        map.insert(currentBinData.key, currentBinData);
21672
      }
21673
    } else  // data point not anymore in current bin, set close of old and open
21674
            // of new bin, and add old to map:
21675
    {
21676
      // finalize current bin:
21677
      currentBinData.close = value.at(i - 1);
21678
      currentBinData.key = timeBinOffset + (index - 1) * timeBinSize;
21679
      map.insert(currentBinData.key, currentBinData);
21680
      // start next bin:
21681
      currentBinIndex = index;
21682
      currentBinData.open = value.at(i);
21683
      currentBinData.high = value.at(i);
21684
      currentBinData.low = value.at(i);
21685
    }
21686
  }
21687
21688
  return map;
21689
}
21690
21691
/* inherits documentation from base class */
21692
void QCPFinancial::draw(QCPPainter *painter) {
21693
  // get visible data range:
21694
  QCPFinancialDataMap::const_iterator lower,
21695
      upper;  // note that upper is the actual upper point, and not 1 step after
21696
              // the upper point
21697
  getVisibleDataBounds(lower, upper);
21698
  if (lower == mData->constEnd() || upper == mData->constEnd()) return;
21699
21700
  // draw visible data range according to configured style:
21701
  switch (mChartStyle) {
21702
    case QCPFinancial::csOhlc:
21703
      drawOhlcPlot(painter, lower, upper + 1);
21704
      break;
21705
    case QCPFinancial::csCandlestick:
21706
      drawCandlestickPlot(painter, lower, upper + 1);
21707
      break;
21708
  }
21709
}
21710
21711
/* inherits documentation from base class */
21712
void QCPFinancial::drawLegendIcon(QCPPainter *painter,
21713
                                  const QRectF &rect) const {
21714
  painter->setAntialiasing(false);  // legend icon especially of csCandlestick
21715
                                    // looks better without antialiasing
21716
  if (mChartStyle == csOhlc) {
21717
    if (mTwoColored) {
21718
      // draw upper left half icon with positive color:
21719
      painter->setBrush(mBrushPositive);
21720
      painter->setPen(mPenPositive);
21721
      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint()
21722
                                                << rect.topRight().toPoint()
21723
                                                << rect.topLeft().toPoint()));
21724
      painter->drawLine(
21725
          QLineF(0, rect.height() * 0.5, rect.width(), rect.height() * 0.5)
21726
              .translated(rect.topLeft()));
21727
      painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
21728
                               rect.width() * 0.2, rect.height() * 0.5)
21729
                            .translated(rect.topLeft()));
21730
      painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
21731
                               rect.width() * 0.8, rect.height() * 0.7)
21732
                            .translated(rect.topLeft()));
21733
      // draw bottom right hald icon with negative color:
21734
      painter->setBrush(mBrushNegative);
21735
      painter->setPen(mPenNegative);
21736
      painter->setClipRegion(QRegion(
21737
          QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint()
21738
                     << rect.bottomRight().toPoint()));
21739
      painter->drawLine(
21740
          QLineF(0, rect.height() * 0.5, rect.width(), rect.height() * 0.5)
21741
              .translated(rect.topLeft()));
21742
      painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
21743
                               rect.width() * 0.2, rect.height() * 0.5)
21744
                            .translated(rect.topLeft()));
21745
      painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
21746
                               rect.width() * 0.8, rect.height() * 0.7)
21747
                            .translated(rect.topLeft()));
21748
    } else {
21749
      painter->setBrush(mBrush);
21750
      painter->setPen(mPen);
21751
      painter->drawLine(
21752
          QLineF(0, rect.height() * 0.5, rect.width(), rect.height() * 0.5)
21753
              .translated(rect.topLeft()));
21754
      painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
21755
                               rect.width() * 0.2, rect.height() * 0.5)
21756
                            .translated(rect.topLeft()));
21757
      painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
21758
                               rect.width() * 0.8, rect.height() * 0.7)
21759
                            .translated(rect.topLeft()));
21760
    }
21761
  } else if (mChartStyle == csCandlestick) {
21762
    if (mTwoColored) {
21763
      // draw upper left half icon with positive color:
21764
      painter->setBrush(mBrushPositive);
21765
      painter->setPen(mPenPositive);
21766
      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint()
21767
                                                << rect.topRight().toPoint()
21768
                                                << rect.topLeft().toPoint()));
21769
      painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width() * 0.25,
21770
                               rect.height() * 0.5)
21771
                            .translated(rect.topLeft()));
21772
      painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
21773
                               rect.width(), rect.height() * 0.5)
21774
                            .translated(rect.topLeft()));
21775
      painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
21776
                               rect.width() * 0.5, rect.height() * 0.5)
21777
                            .translated(rect.topLeft()));
21778
      // draw bottom right hald icon with negative color:
21779
      painter->setBrush(mBrushNegative);
21780
      painter->setPen(mPenNegative);
21781
      painter->setClipRegion(QRegion(
21782
          QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint()
21783
                     << rect.bottomRight().toPoint()));
21784
      painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width() * 0.25,
21785
                               rect.height() * 0.5)
21786
                            .translated(rect.topLeft()));
21787
      painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
21788
                               rect.width(), rect.height() * 0.5)
21789
                            .translated(rect.topLeft()));
21790
      painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
21791
                               rect.width() * 0.5, rect.height() * 0.5)
21792
                            .translated(rect.topLeft()));
21793
    } else {
21794
      painter->setBrush(mBrush);
21795
      painter->setPen(mPen);
21796
      painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width() * 0.25,
21797
                               rect.height() * 0.5)
21798
                            .translated(rect.topLeft()));
21799
      painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
21800
                               rect.width(), rect.height() * 0.5)
21801
                            .translated(rect.topLeft()));
21802
      painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
21803
                               rect.width() * 0.5, rect.height() * 0.5)
21804
                            .translated(rect.topLeft()));
21805
    }
21806
  }
21807
}
21808
21809
/* inherits documentation from base class */
21810
QCPRange QCPFinancial::getKeyRange(
21811
    bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const {
21812
  QCPRange range;
21813
  bool haveLower = false;
21814
  bool haveUpper = false;
21815
21816
  double current;
21817
  QCPFinancialDataMap::const_iterator it = mData->constBegin();
21818
  while (it != mData->constEnd()) {
21819
    current = it.value().key;
21820
    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) ||
21821
        (inSignDomain == sdPositive && current > 0)) {
21822
      if (current < range.lower || !haveLower) {
21823
        range.lower = current;
21824
        haveLower = true;
21825
      }
21826
      if (current > range.upper || !haveUpper) {
21827
        range.upper = current;
21828
        haveUpper = true;
21829
      }
21830
    }
21831
    ++it;
21832
  }
21833
  // determine exact range by including width of bars/flags:
21834
  if (haveLower && mKeyAxis) range.lower = range.lower - mWidth * 0.5;
21835
  if (haveUpper && mKeyAxis) range.upper = range.upper + mWidth * 0.5;
21836
  foundRange = haveLower && haveUpper;
21837
  return range;
21838
}
21839
21840
/* inherits documentation from base class */
21841
QCPRange QCPFinancial::getValueRange(
21842
    bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const {
21843
  QCPRange range;
21844
  bool haveLower = false;
21845
  bool haveUpper = false;
21846
21847
  QCPFinancialDataMap::const_iterator it = mData->constBegin();
21848
  while (it != mData->constEnd()) {
21849
    // high:
21850
    if (inSignDomain == sdBoth ||
21851
        (inSignDomain == sdNegative && it.value().high < 0) ||
21852
        (inSignDomain == sdPositive && it.value().high > 0)) {
21853
      if (it.value().high < range.lower || !haveLower) {
21854
        range.lower = it.value().high;
21855
        haveLower = true;
21856
      }
21857
      if (it.value().high > range.upper || !haveUpper) {
21858
        range.upper = it.value().high;
21859
        haveUpper = true;
21860
      }
21861
    }
21862
    // low:
21863
    if (inSignDomain == sdBoth ||
21864
        (inSignDomain == sdNegative && it.value().low < 0) ||
21865
        (inSignDomain == sdPositive && it.value().low > 0)) {
21866
      if (it.value().low < range.lower || !haveLower) {
21867
        range.lower = it.value().low;
21868
        haveLower = true;
21869
      }
21870
      if (it.value().low > range.upper || !haveUpper) {
21871
        range.upper = it.value().low;
21872
        haveUpper = true;
21873
      }
21874
    }
21875
    ++it;
21876
  }
21877
21878
  foundRange = haveLower && haveUpper;
21879
  return range;
21880
}
21881
21882
/*! \internal
21883
21884
  Draws the data from \a begin to \a end as OHLC bars with the provided \a
21885
  painter.
21886
21887
  This method is a helper function for \ref draw. It is used when the chart
21888
  style is \ref csOhlc.
21889
*/
21890
void QCPFinancial::drawOhlcPlot(
21891
    QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin,
21892
    const QCPFinancialDataMap::const_iterator &end) {
21893
  QCPAxis *keyAxis = mKeyAxis.data();
21894
  QCPAxis *valueAxis = mValueAxis.data();
21895
  if (!keyAxis || !valueAxis) {
21896
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21897
    return;
21898
  }
21899
21900
  QPen linePen;
21901
21902
  if (keyAxis->orientation() == Qt::Horizontal) {
21903
    for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) {
21904
      if (mSelected)
21905
        linePen = mSelectedPen;
21906
      else if (mTwoColored)
21907
        linePen =
21908
            it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21909
      else
21910
        linePen = mPen;
21911
      painter->setPen(linePen);
21912
      double keyPixel = keyAxis->coordToPixel(it.value().key);
21913
      double openPixel = valueAxis->coordToPixel(it.value().open);
21914
      double closePixel = valueAxis->coordToPixel(it.value().close);
21915
      // draw backbone:
21916
      painter->drawLine(
21917
          QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)),
21918
          QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
21919
      // draw open:
21920
      double keyWidthPixels =
21921
          keyPixel - keyAxis->coordToPixel(
21922
                         it.value().key -
21923
                         mWidth * 0.5);  // sign of this makes sure open/close
21924
                                         // are on correct sides
21925
      painter->drawLine(QPointF(keyPixel - keyWidthPixels, openPixel),
21926
                        QPointF(keyPixel, openPixel));
21927
      // draw close:
21928
      painter->drawLine(QPointF(keyPixel, closePixel),
21929
                        QPointF(keyPixel + keyWidthPixels, closePixel));
21930
    }
21931
  } else {
21932
    for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) {
21933
      if (mSelected)
21934
        linePen = mSelectedPen;
21935
      else if (mTwoColored)
21936
        linePen =
21937
            it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21938
      else
21939
        linePen = mPen;
21940
      painter->setPen(linePen);
21941
      double keyPixel = keyAxis->coordToPixel(it.value().key);
21942
      double openPixel = valueAxis->coordToPixel(it.value().open);
21943
      double closePixel = valueAxis->coordToPixel(it.value().close);
21944
      // draw backbone:
21945
      painter->drawLine(
21946
          QPointF(valueAxis->coordToPixel(it.value().high), keyPixel),
21947
          QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
21948
      // draw open:
21949
      double keyWidthPixels =
21950
          keyPixel - keyAxis->coordToPixel(
21951
                         it.value().key -
21952
                         mWidth * 0.5);  // sign of this makes sure open/close
21953
                                         // are on correct sides
21954
      painter->drawLine(QPointF(openPixel, keyPixel - keyWidthPixels),
21955
                        QPointF(openPixel, keyPixel));
21956
      // draw close:
21957
      painter->drawLine(QPointF(closePixel, keyPixel),
21958
                        QPointF(closePixel, keyPixel + keyWidthPixels));
21959
    }
21960
  }
21961
}
21962
21963
/*! \internal
21964
21965
  Draws the data from \a begin to \a end as Candlesticks with the provided \a
21966
  painter.
21967
21968
  This method is a helper function for \ref draw. It is used when the chart
21969
  style is \ref csCandlestick.
21970
*/
21971
void QCPFinancial::drawCandlestickPlot(
21972
    QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin,
21973
    const QCPFinancialDataMap::const_iterator &end) {
21974
  QCPAxis *keyAxis = mKeyAxis.data();
21975
  QCPAxis *valueAxis = mValueAxis.data();
21976
  if (!keyAxis || !valueAxis) {
21977
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21978
    return;
21979
  }
21980
21981
  QPen linePen;
21982
  QBrush boxBrush;
21983
21984
  if (keyAxis->orientation() == Qt::Horizontal) {
21985
    for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) {
21986
      if (mSelected) {
21987
        linePen = mSelectedPen;
21988
        boxBrush = mSelectedBrush;
21989
      } else if (mTwoColored) {
21990
        if (it.value().close >= it.value().open) {
21991
          linePen = mPenPositive;
21992
          boxBrush = mBrushPositive;
21993
        } else {
21994
          linePen = mPenNegative;
21995
          boxBrush = mBrushNegative;
21996
        }
21997
      } else {
21998
        linePen = mPen;
21999
        boxBrush = mBrush;
22000
      }
22001
      painter->setPen(linePen);
22002
      painter->setBrush(boxBrush);
22003
      double keyPixel = keyAxis->coordToPixel(it.value().key);
22004
      double openPixel = valueAxis->coordToPixel(it.value().open);
22005
      double closePixel = valueAxis->coordToPixel(it.value().close);
22006
      // draw high:
22007
      painter->drawLine(
22008
          QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)),
22009
          QPointF(keyPixel, valueAxis->coordToPixel(
22010
                                qMax(it.value().open, it.value().close))));
22011
      // draw low:
22012
      painter->drawLine(
22013
          QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)),
22014
          QPointF(keyPixel, valueAxis->coordToPixel(
22015
                                qMin(it.value().open, it.value().close))));
22016
      // draw open-close box:
22017
      double keyWidthPixels =
22018
          keyPixel - keyAxis->coordToPixel(it.value().key - mWidth * 0.5);
22019
      painter->drawRect(QRectF(QPointF(keyPixel - keyWidthPixels, closePixel),
22020
                               QPointF(keyPixel + keyWidthPixels, openPixel)));
22021
    }
22022
  } else  // keyAxis->orientation() == Qt::Vertical
22023
  {
22024
    for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) {
22025
      if (mSelected) {
22026
        linePen = mSelectedPen;
22027
        boxBrush = mSelectedBrush;
22028
      } else if (mTwoColored) {
22029
        if (it.value().close >= it.value().open) {
22030
          linePen = mPenPositive;
22031
          boxBrush = mBrushPositive;
22032
        } else {
22033
          linePen = mPenNegative;
22034
          boxBrush = mBrushNegative;
22035
        }
22036
      } else {
22037
        linePen = mPen;
22038
        boxBrush = mBrush;
22039
      }
22040
      painter->setPen(linePen);
22041
      painter->setBrush(boxBrush);
22042
      double keyPixel = keyAxis->coordToPixel(it.value().key);
22043
      double openPixel = valueAxis->coordToPixel(it.value().open);
22044
      double closePixel = valueAxis->coordToPixel(it.value().close);
22045
      // draw high:
22046
      painter->drawLine(
22047
          QPointF(valueAxis->coordToPixel(it.value().high), keyPixel),
22048
          QPointF(
22049
              valueAxis->coordToPixel(qMax(it.value().open, it.value().close)),
22050
              keyPixel));
22051
      // draw low:
22052
      painter->drawLine(
22053
          QPointF(valueAxis->coordToPixel(it.value().low), keyPixel),
22054
          QPointF(
22055
              valueAxis->coordToPixel(qMin(it.value().open, it.value().close)),
22056
              keyPixel));
22057
      // draw open-close box:
22058
      double keyWidthPixels =
22059
          keyPixel - keyAxis->coordToPixel(it.value().key - mWidth * 0.5);
22060
      painter->drawRect(QRectF(QPointF(closePixel, keyPixel - keyWidthPixels),
22061
                               QPointF(openPixel, keyPixel + keyWidthPixels)));
22062
    }
22063
  }
22064
}
22065
22066
/*! \internal
22067
22068
  This method is a helper function for \ref selectTest. It is used to test for
22069
  selection when the chart style is \ref csOhlc. It only tests against the data
22070
  points between \a begin and \a end.
22071
*/
22072
double QCPFinancial::ohlcSelectTest(
22073
    const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin,
22074
    const QCPFinancialDataMap::const_iterator &end) const {
22075
  QCPAxis *keyAxis = mKeyAxis.data();
22076
  QCPAxis *valueAxis = mValueAxis.data();
22077
  if (!keyAxis || !valueAxis) {
22078
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22079
    return -1;
22080
  }
22081
22082
  double minDistSqr = std::numeric_limits<double>::max();
22083
  QCPFinancialDataMap::const_iterator it;
22084
  if (keyAxis->orientation() == Qt::Horizontal) {
22085
    for (it = begin; it != end; ++it) {
22086
      double keyPixel = keyAxis->coordToPixel(it.value().key);
22087
      // calculate distance to backbone:
22088
      double currentDistSqr = distSqrToLine(
22089
          QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)),
22090
          QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
22091
      if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22092
    }
22093
  } else  // keyAxis->orientation() == Qt::Vertical
22094
  {
22095
    for (it = begin; it != end; ++it) {
22096
      double keyPixel = keyAxis->coordToPixel(it.value().key);
22097
      // calculate distance to backbone:
22098
      double currentDistSqr = distSqrToLine(
22099
          QPointF(valueAxis->coordToPixel(it.value().high), keyPixel),
22100
          QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
22101
      if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22102
    }
22103
  }
22104
  return qSqrt(minDistSqr);
22105
}
22106
22107
/*! \internal
22108
22109
  This method is a helper function for \ref selectTest. It is used to test for
22110
  selection when the chart style is \ref csCandlestick. It only tests against
22111
  the data points between \a begin and \a end.
22112
*/
22113
double QCPFinancial::candlestickSelectTest(
22114
    const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin,
22115
    const QCPFinancialDataMap::const_iterator &end) const {
22116
  QCPAxis *keyAxis = mKeyAxis.data();
22117
  QCPAxis *valueAxis = mValueAxis.data();
22118
  if (!keyAxis || !valueAxis) {
22119
    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22120
    return -1;
22121
  }
22122
22123
  double minDistSqr = std::numeric_limits<double>::max();
22124
  QCPFinancialDataMap::const_iterator it;
22125
  if (keyAxis->orientation() == Qt::Horizontal) {
22126
    for (it = begin; it != end; ++it) {
22127
      double currentDistSqr;
22128
      // determine whether pos is in open-close-box:
22129
      QCPRange boxKeyRange(it.value().key - mWidth * 0.5,
22130
                           it.value().key + mWidth * 0.5);
22131
      QCPRange boxValueRange(it.value().close, it.value().open);
22132
      double posKey, posValue;
22133
      pixelsToCoords(pos, posKey, posValue);
22134
      if (boxKeyRange.contains(posKey) &&
22135
          boxValueRange.contains(posValue))  // is in open-close-box
22136
      {
22137
        currentDistSqr = mParentPlot->selectionTolerance() * 0.99 *
22138
                         mParentPlot->selectionTolerance() * 0.99;
22139
      } else {
22140
        // calculate distance to high/low lines:
22141
        double keyPixel = keyAxis->coordToPixel(it.value().key);
22142
        double highLineDistSqr = distSqrToLine(
22143
            QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)),
22144
            QPointF(keyPixel, valueAxis->coordToPixel(
22145
                                  qMax(it.value().open, it.value().close))),
22146
            pos);
22147
        double lowLineDistSqr = distSqrToLine(
22148
            QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)),
22149
            QPointF(keyPixel, valueAxis->coordToPixel(
22150
                                  qMin(it.value().open, it.value().close))),
22151
            pos);
22152
        currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
22153
      }
22154
      if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22155
    }
22156
  } else  // keyAxis->orientation() == Qt::Vertical
22157
  {
22158
    for (it = begin; it != end; ++it) {
22159
      double currentDistSqr;
22160
      // determine whether pos is in open-close-box:
22161
      QCPRange boxKeyRange(it.value().key - mWidth * 0.5,
22162
                           it.value().key + mWidth * 0.5);
22163
      QCPRange boxValueRange(it.value().close, it.value().open);
22164
      double posKey, posValue;
22165
      pixelsToCoords(pos, posKey, posValue);
22166
      if (boxKeyRange.contains(posKey) &&
22167
          boxValueRange.contains(posValue))  // is in open-close-box
22168
      {
22169
        currentDistSqr = mParentPlot->selectionTolerance() * 0.99 *
22170
                         mParentPlot->selectionTolerance() * 0.99;
22171
      } else {
22172
        // calculate distance to high/low lines:
22173
        double keyPixel = keyAxis->coordToPixel(it.value().key);
22174
        double highLineDistSqr = distSqrToLine(
22175
            QPointF(valueAxis->coordToPixel(it.value().high), keyPixel),
22176
            QPointF(valueAxis->coordToPixel(
22177
                        qMax(it.value().open, it.value().close)),
22178
                    keyPixel),
22179
            pos);
22180
        double lowLineDistSqr = distSqrToLine(
22181
            QPointF(valueAxis->coordToPixel(it.value().low), keyPixel),
22182
            QPointF(valueAxis->coordToPixel(
22183
                        qMin(it.value().open, it.value().close)),
22184
                    keyPixel),
22185
            pos);
22186
        currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
22187
      }
22188
      if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22189
    }
22190
  }
22191
  return qSqrt(minDistSqr);
22192
}
22193
22194
/*!  \internal
22195
22196
  called by the drawing methods to determine which data (key) range is visible
22197
  at the current key axis range setting, so only that needs to be processed.
22198
22199
  \a lower returns an iterator to the lowest data point that needs to be taken
22200
  into account when plotting. Note that in order to get a clean plot all the way
22201
  to the edge of the axis rect, \a lower may still be just outside the visible
22202
  range.
22203
22204
  \a upper returns an iterator to the highest data point. Same as before, \a
22205
  upper may also lie just outside of the visible range.
22206
22207
  if the plottable contains no data, both \a lower and \a upper point to
22208
  constEnd.
22209
22210
  \see QCPGraph::getVisibleDataBounds
22211
*/
22212
void QCPFinancial::getVisibleDataBounds(
22213
    QCPFinancialDataMap::const_iterator &lower,
22214
    QCPFinancialDataMap::const_iterator &upper) const {
22215
  if (!mKeyAxis) {
22216
    qDebug() << Q_FUNC_INFO << "invalid key axis";
22217
    return;
22218
  }
22219
  if (mData->isEmpty()) {
22220
    lower = mData->constEnd();
22221
    upper = mData->constEnd();
22222
    return;
22223
  }
22224
22225
  // get visible data range as QMap iterators
22226
  QCPFinancialDataMap::const_iterator lbound =
22227
      mData->lowerBound(mKeyAxis.data()->range().lower);
22228
  QCPFinancialDataMap::const_iterator ubound =
22229
      mData->upperBound(mKeyAxis.data()->range().upper);
22230
  bool lowoutlier =
22231
      lbound != mData->constBegin();  // indicates whether there exist points
22232
                                      // below axis range
22233
  bool highoutlier =
22234
      ubound != mData->constEnd();  // indicates whether there exist points
22235
                                    // above axis range
22236
22237
  lower =
22238
      (lowoutlier ? lbound - 1
22239
                  : lbound);  // data point range that will be actually drawn
22240
  upper = (highoutlier
22241
               ? ubound
22242
               : ubound - 1);  // data point range that will be actually drawn
22243
}
22244
22245
////////////////////////////////////////////////////////////////////////////////////////////////////
22246
//////////////////// QCPItemStraightLine
22247
////////////////////////////////////////////////////////////////////////////////////////////////////
22248
22249
/*! \class QCPItemStraightLine
22250
  \brief A straight line that spans infinitely in both directions
22251
22252
  \image html QCPItemStraightLine.png "Straight line example. Blue dotted
22253
  circles are anchors, solid blue discs are positions."
22254
22255
  It has two positions, \a point1 and \a point2, which define the straight line.
22256
*/
22257
22258
/*!
22259
  Creates a straight line item and sets default values.
22260
22261
  The constructed item can be added to the plot with QCustomPlot::addItem.
22262
*/
22263
QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot)
22264
    : QCPAbstractItem(parentPlot),
22265
      point1(createPosition(QLatin1String("point1"))),
22266
      point2(createPosition(QLatin1String("point2"))) {
22267
  point1->setCoords(0, 0);
22268
  point2->setCoords(1, 1);
22269
22270
  setPen(QPen(Qt::black));
22271
  setSelectedPen(QPen(Qt::blue, 2));
22272
}
22273
22274
QCPItemStraightLine::~QCPItemStraightLine() {}
22275
22276
/*!
22277
  Sets the pen that will be used to draw the line
22278
22279
  \see setSelectedPen
22280
*/
22281
void QCPItemStraightLine::setPen(const QPen &pen) { mPen = pen; }
22282
22283
/*!
22284
  Sets the pen that will be used to draw the line when selected
22285
22286
  \see setPen, setSelected
22287
*/
22288
void QCPItemStraightLine::setSelectedPen(const QPen &pen) {
22289
  mSelectedPen = pen;
22290
}
22291
22292
/* inherits documentation from base class */
22293
double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable,
22294
                                       QVariant *details) const {
22295
  Q_UNUSED(details)
22296
  if (onlySelectable && !mSelectable) return -1;
22297
22298
  return distToStraightLine(
22299
      QVector2D(point1->pixelPoint()),
22300
      QVector2D(point2->pixelPoint() - point1->pixelPoint()), QVector2D(pos));
22301
}
22302
22303
/* inherits documentation from base class */
22304
void QCPItemStraightLine::draw(QCPPainter *painter) {
22305
  QVector2D start(point1->pixelPoint());
22306
  QVector2D end(point2->pixelPoint());
22307
  // get visible segment of straight line inside clipRect:
22308
  double clipPad = mainPen().widthF();
22309
  QLineF line = getRectClippedStraightLine(
22310
      start, end - start,
22311
      clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
22312
  // paint visible segment, if existent:
22313
  if (!line.isNull()) {
22314
    painter->setPen(mainPen());
22315
    painter->drawLine(line);
22316
  }
22317
}
22318
22319
/*! \internal
22320
22321
  finds the shortest distance of \a point to the straight line defined by the
22322
  base point \a base and the direction vector \a vec.
22323
22324
  This is a helper function for \ref selectTest.
22325
*/
22326
double QCPItemStraightLine::distToStraightLine(const QVector2D &base,
22327
                                               const QVector2D &vec,
22328
                                               const QVector2D &point) const {
22329
  return qAbs((base.y() - point.y()) * vec.x() -
22330
              (base.x() - point.x()) * vec.y()) /
22331
         vec.length();
22332
}
22333
22334
/*! \internal
22335
22336
  Returns the section of the straight line defined by \a base and direction
22337
  vector \a vec, that is visible in the specified \a rect.
22338
22339
  This is a helper function for \ref draw.
22340
*/
22341
QLineF QCPItemStraightLine::getRectClippedStraightLine(
22342
    const QVector2D &base, const QVector2D &vec, const QRect &rect) const {
22343
  double bx, by;
22344
  double gamma;
22345
  QLineF result;
22346
  if (vec.x() == 0 && vec.y() == 0) return result;
22347
  if (qFuzzyIsNull(vec.x()))  // line is vertical
22348
  {
22349
    // check top of rect:
22350
    bx = rect.left();
22351
    by = rect.top();
22352
    gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
22353
    if (gamma >= 0 && gamma <= rect.width())
22354
      result.setLine(bx + gamma, rect.top(), bx + gamma,
22355
                     rect.bottom());  // no need to check bottom because we know
22356
                                      // line is vertical
22357
  } else if (qFuzzyIsNull(vec.y()))   // line is horizontal
22358
  {
22359
    // check left of rect:
22360
    bx = rect.left();
22361
    by = rect.top();
22362
    gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
22363
    if (gamma >= 0 && gamma <= rect.height())
22364
      result.setLine(rect.left(), by + gamma, rect.right(),
22365
                     by + gamma);  // no need to check right because we know
22366
                                   // line is horizontal
22367
  } else                           // line is skewed
22368
  {
22369
    QList<QVector2D> pointVectors;
22370
    // check top of rect:
22371
    bx = rect.left();
22372
    by = rect.top();
22373
    gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
22374
    if (gamma >= 0 && gamma <= rect.width())
22375
      pointVectors.append(QVector2D(bx + gamma, by));
22376
    // check bottom of rect:
22377
    bx = rect.left();
22378
    by = rect.bottom();
22379
    gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
22380
    if (gamma >= 0 && gamma <= rect.width())
22381
      pointVectors.append(QVector2D(bx + gamma, by));
22382
    // check left of rect:
22383
    bx = rect.left();
22384
    by = rect.top();
22385
    gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
22386
    if (gamma >= 0 && gamma <= rect.height())
22387
      pointVectors.append(QVector2D(bx, by + gamma));
22388
    // check right of rect:
22389
    bx = rect.right();
22390
    by = rect.top();
22391
    gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
22392
    if (gamma >= 0 && gamma <= rect.height())
22393
      pointVectors.append(QVector2D(bx, by + gamma));
22394
22395
    // evaluate points:
22396
    if (pointVectors.size() == 2) {
22397
      result.setPoints(pointVectors.at(0).toPointF(),
22398
                       pointVectors.at(1).toPointF());
22399
    } else if (pointVectors.size() > 2) {
22400
      // line probably goes through corner of rect, and we got two points there.
22401
      // single out the point pair with greatest distance:
22402
      double distSqrMax = 0;
22403
      QVector2D pv1, pv2;
22404
      for (int i = 0; i < pointVectors.size() - 1; ++i) {
22405
        for (int k = i + 1; k < pointVectors.size(); ++k) {
22406
          double distSqr =
22407
              (pointVectors.at(i) - pointVectors.at(k)).lengthSquared();
22408
          if (distSqr > distSqrMax) {
22409
            pv1 = pointVectors.at(i);
22410
            pv2 = pointVectors.at(k);
22411
            distSqrMax = distSqr;
22412
          }
22413
        }
22414
      }
22415
      result.setPoints(pv1.toPointF(), pv2.toPointF());
22416
    }
22417
  }
22418
  return result;
22419
}
22420
22421
/*! \internal
22422
22423
  Returns the pen that should be used for drawing lines. Returns mPen when the
22424
  item is not selected and mSelectedPen when it is.
22425
*/
22426
QPen QCPItemStraightLine::mainPen() const {
22427
  return mSelected ? mSelectedPen : mPen;
22428
}
22429
22430
////////////////////////////////////////////////////////////////////////////////////////////////////
22431
//////////////////// QCPItemLine
22432
////////////////////////////////////////////////////////////////////////////////////////////////////
22433
22434
/*! \class QCPItemLine
22435
  \brief A line from one point to another
22436
22437
  \image html QCPItemLine.png "Line example. Blue dotted circles are anchors,
22438
  solid blue discs are positions."
22439
22440
  It has two positions, \a start and \a end, which define the end points of the
22441
  line.
22442
22443
  With \ref setHead and \ref setTail you may set different line ending styles,
22444
  e.g. to create an arrow.
22445
*/
22446
22447
/*!
22448
  Creates a line item and sets default values.
22449
22450
  The constructed item can be added to the plot with QCustomPlot::addItem.
22451
*/
22452
QCPItemLine::QCPItemLine(QCustomPlot *parentPlot)
22453
    : QCPAbstractItem(parentPlot),
22454
      start(createPosition(QLatin1String("start"))),
22455
      end(createPosition(QLatin1String("end"))) {
22456
  start->setCoords(0, 0);
22457
  end->setCoords(1, 1);
22458
22459
  setPen(QPen(Qt::black));
22460
  setSelectedPen(QPen(Qt::blue, 2));
22461
}
22462
22463
QCPItemLine::~QCPItemLine() {}
22464
22465
/*!
22466
  Sets the pen that will be used to draw the line
22467
22468
  \see setSelectedPen
22469
*/
22470
void QCPItemLine::setPen(const QPen &pen) { mPen = pen; }
22471
22472
/*!
22473
  Sets the pen that will be used to draw the line when selected
22474
22475
  \see setPen, setSelected
22476
*/
22477
void QCPItemLine::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
22478
22479
/*!
22480
  Sets the line ending style of the head. The head corresponds to the \a end
22481
  position.
22482
22483
  Note that due to the overloaded QCPLineEnding constructor, you may directly
22484
  specify a QCPLineEnding::EndingStyle here, e.g. \code
22485
  setHead(QCPLineEnding::esSpikeArrow) \endcode
22486
22487
  \see setTail
22488
*/
22489
void QCPItemLine::setHead(const QCPLineEnding &head) { mHead = head; }
22490
22491
/*!
22492
  Sets the line ending style of the tail. The tail corresponds to the \a start
22493
  position.
22494
22495
  Note that due to the overloaded QCPLineEnding constructor, you may directly
22496
  specify a QCPLineEnding::EndingStyle here, e.g. \code
22497
  setTail(QCPLineEnding::esSpikeArrow) \endcode
22498
22499
  \see setHead
22500
*/
22501
void QCPItemLine::setTail(const QCPLineEnding &tail) { mTail = tail; }
22502
22503
/* inherits documentation from base class */
22504
double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable,
22505
                               QVariant *details) const {
22506
  Q_UNUSED(details)
22507
  if (onlySelectable && !mSelectable) return -1;
22508
22509
  return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
22510
}
22511
22512
/* inherits documentation from base class */
22513
void QCPItemLine::draw(QCPPainter *painter) {
22514
  QVector2D startVec(start->pixelPoint());
22515
  QVector2D endVec(end->pixelPoint());
22516
  if (startVec.toPoint() == endVec.toPoint()) return;
22517
  // get visible segment of straight line inside clipRect:
22518
  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
22519
  clipPad = qMax(clipPad, (double)mainPen().widthF());
22520
  QLineF line = getRectClippedLine(
22521
      startVec, endVec,
22522
      clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
22523
  // paint visible segment, if existent:
22524
  if (!line.isNull()) {
22525
    painter->setPen(mainPen());
22526
    painter->drawLine(line);
22527
    painter->setBrush(Qt::SolidPattern);
22528
    if (mTail.style() != QCPLineEnding::esNone)
22529
      mTail.draw(painter, startVec, startVec - endVec);
22530
    if (mHead.style() != QCPLineEnding::esNone)
22531
      mHead.draw(painter, endVec, endVec - startVec);
22532
  }
22533
}
22534
22535
/*! \internal
22536
22537
  Returns the section of the line defined by \a start and \a end, that is
22538
  visible in the specified \a rect.
22539
22540
  This is a helper function for \ref draw.
22541
*/
22542
QLineF QCPItemLine::getRectClippedLine(const QVector2D &start,
22543
                                       const QVector2D &end,
22544
                                       const QRect &rect) const {
22545
  bool containsStart = rect.contains(start.x(), start.y());
22546
  bool containsEnd = rect.contains(end.x(), end.y());
22547
  if (containsStart && containsEnd)
22548
    return QLineF(start.toPointF(), end.toPointF());
22549
22550
  QVector2D base = start;
22551
  QVector2D vec = end - start;
22552
  double bx, by;
22553
  double gamma, mu;
22554
  QLineF result;
22555
  QList<QVector2D> pointVectors;
22556
22557
  if (!qFuzzyIsNull(vec.y()))  // line is not horizontal
22558
  {
22559
    // check top of rect:
22560
    bx = rect.left();
22561
    by = rect.top();
22562
    mu = (by - base.y()) / vec.y();
22563
    if (mu >= 0 && mu <= 1) {
22564
      gamma = base.x() - bx + mu * vec.x();
22565
      if (gamma >= 0 && gamma <= rect.width())
22566
        pointVectors.append(QVector2D(bx + gamma, by));
22567
    }
22568
    // check bottom of rect:
22569
    bx = rect.left();
22570
    by = rect.bottom();
22571
    mu = (by - base.y()) / vec.y();
22572
    if (mu >= 0 && mu <= 1) {
22573
      gamma = base.x() - bx + mu * vec.x();
22574
      if (gamma >= 0 && gamma <= rect.width())
22575
        pointVectors.append(QVector2D(bx + gamma, by));
22576
    }
22577
  }
22578
  if (!qFuzzyIsNull(vec.x()))  // line is not vertical
22579
  {
22580
    // check left of rect:
22581
    bx = rect.left();
22582
    by = rect.top();
22583
    mu = (bx - base.x()) / vec.x();
22584
    if (mu >= 0 && mu <= 1) {
22585
      gamma = base.y() - by + mu * vec.y();
22586
      if (gamma >= 0 && gamma <= rect.height())
22587
        pointVectors.append(QVector2D(bx, by + gamma));
22588
    }
22589
    // check right of rect:
22590
    bx = rect.right();
22591
    by = rect.top();
22592
    mu = (bx - base.x()) / vec.x();
22593
    if (mu >= 0 && mu <= 1) {
22594
      gamma = base.y() - by + mu * vec.y();
22595
      if (gamma >= 0 && gamma <= rect.height())
22596
        pointVectors.append(QVector2D(bx, by + gamma));
22597
    }
22598
  }
22599
22600
  if (containsStart) pointVectors.append(start);
22601
  if (containsEnd) pointVectors.append(end);
22602
22603
  // evaluate points:
22604
  if (pointVectors.size() == 2) {
22605
    result.setPoints(pointVectors.at(0).toPointF(),
22606
                     pointVectors.at(1).toPointF());
22607
  } else if (pointVectors.size() > 2) {
22608
    // line probably goes through corner of rect, and we got two points there.
22609
    // single out the point pair with greatest distance:
22610
    double distSqrMax = 0;
22611
    QVector2D pv1, pv2;
22612
    for (int i = 0; i < pointVectors.size() - 1; ++i) {
22613
      for (int k = i + 1; k < pointVectors.size(); ++k) {
22614
        double distSqr =
22615
            (pointVectors.at(i) - pointVectors.at(k)).lengthSquared();
22616
        if (distSqr > distSqrMax) {
22617
          pv1 = pointVectors.at(i);
22618
          pv2 = pointVectors.at(k);
22619
          distSqrMax = distSqr;
22620
        }
22621
      }
22622
    }
22623
    result.setPoints(pv1.toPointF(), pv2.toPointF());
22624
  }
22625
  return result;
22626
}
22627
22628
/*! \internal
22629
22630
  Returns the pen that should be used for drawing lines. Returns mPen when the
22631
  item is not selected and mSelectedPen when it is.
22632
*/
22633
QPen QCPItemLine::mainPen() const { return mSelected ? mSelectedPen : mPen; }
22634
22635
////////////////////////////////////////////////////////////////////////////////////////////////////
22636
//////////////////// QCPItemCurve
22637
////////////////////////////////////////////////////////////////////////////////////////////////////
22638
22639
/*! \class QCPItemCurve
22640
  \brief A curved line from one point to another
22641
22642
  \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors,
22643
  solid blue discs are positions."
22644
22645
  It has four positions, \a start and \a end, which define the end points of the
22646
  line, and two control points which define the direction the line exits from
22647
  the start and the direction from which it approaches the end: \a startDir and
22648
  \a endDir.
22649
22650
  With \ref setHead and \ref setTail you may set different line ending styles,
22651
  e.g. to create an arrow.
22652
22653
  Often it is desirable for the control points to stay at fixed relative
22654
  positions to the start/end point. This can be achieved by setting the parent
22655
  anchor e.g. of \a startDir simply to \a start, and then specify the desired
22656
  pixel offset with QCPItemPosition::setCoords on \a startDir.
22657
*/
22658
22659
/*!
22660
  Creates a curve item and sets default values.
22661
22662
  The constructed item can be added to the plot with QCustomPlot::addItem.
22663
*/
22664
QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot)
22665
    : QCPAbstractItem(parentPlot),
22666
      start(createPosition(QLatin1String("start"))),
22667
      startDir(createPosition(QLatin1String("startDir"))),
22668
      endDir(createPosition(QLatin1String("endDir"))),
22669
      end(createPosition(QLatin1String("end"))) {
22670
  start->setCoords(0, 0);
22671
  startDir->setCoords(0.5, 0);
22672
  endDir->setCoords(0, 0.5);
22673
  end->setCoords(1, 1);
22674
22675
  setPen(QPen(Qt::black));
22676
  setSelectedPen(QPen(Qt::blue, 2));
22677
}
22678
22679
QCPItemCurve::~QCPItemCurve() {}
22680
22681
/*!
22682
  Sets the pen that will be used to draw the line
22683
22684
  \see setSelectedPen
22685
*/
22686
void QCPItemCurve::setPen(const QPen &pen) { mPen = pen; }
22687
22688
/*!
22689
  Sets the pen that will be used to draw the line when selected
22690
22691
  \see setPen, setSelected
22692
*/
22693
void QCPItemCurve::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
22694
22695
/*!
22696
  Sets the line ending style of the head. The head corresponds to the \a end
22697
  position.
22698
22699
  Note that due to the overloaded QCPLineEnding constructor, you may directly
22700
  specify a QCPLineEnding::EndingStyle here, e.g. \code
22701
  setHead(QCPLineEnding::esSpikeArrow) \endcode
22702
22703
  \see setTail
22704
*/
22705
void QCPItemCurve::setHead(const QCPLineEnding &head) { mHead = head; }
22706
22707
/*!
22708
  Sets the line ending style of the tail. The tail corresponds to the \a start
22709
  position.
22710
22711
  Note that due to the overloaded QCPLineEnding constructor, you may directly
22712
  specify a QCPLineEnding::EndingStyle here, e.g. \code
22713
  setTail(QCPLineEnding::esSpikeArrow) \endcode
22714
22715
  \see setHead
22716
*/
22717
void QCPItemCurve::setTail(const QCPLineEnding &tail) { mTail = tail; }
22718
22719
/* inherits documentation from base class */
22720
double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable,
22721
                                QVariant *details) const {
22722
  Q_UNUSED(details)
22723
  if (onlySelectable && !mSelectable) return -1;
22724
22725
  QPointF startVec(start->pixelPoint());
22726
  QPointF startDirVec(startDir->pixelPoint());
22727
  QPointF endDirVec(endDir->pixelPoint());
22728
  QPointF endVec(end->pixelPoint());
22729
22730
  QPainterPath cubicPath(startVec);
22731
  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
22732
22733
  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
22734
  double minDistSqr = std::numeric_limits<double>::max();
22735
  for (int i = 1; i < polygon.size(); ++i) {
22736
    double distSqr = distSqrToLine(polygon.at(i - 1), polygon.at(i), pos);
22737
    if (distSqr < minDistSqr) minDistSqr = distSqr;
22738
  }
22739
  return qSqrt(minDistSqr);
22740
}
22741
22742
/* inherits documentation from base class */
22743
void QCPItemCurve::draw(QCPPainter *painter) {
22744
  QPointF startVec(start->pixelPoint());
22745
  QPointF startDirVec(startDir->pixelPoint());
22746
  QPointF endDirVec(endDir->pixelPoint());
22747
  QPointF endVec(end->pixelPoint());
22748
  if (QVector2D(endVec - startVec).length() >
22749
      1e10f)  // too large curves cause crash
22750
    return;
22751
22752
  QPainterPath cubicPath(startVec);
22753
  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
22754
22755
  // paint visible segment, if existent:
22756
  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
22757
                                   mainPen().widthF(), mainPen().widthF());
22758
  QRect cubicRect = cubicPath.controlPointRect().toRect();
22759
  if (cubicRect.isEmpty())  // may happen when start and end exactly on same x
22760
                            // or y position
22761
    cubicRect.adjust(0, 0, 1, 1);
22762
  if (clip.intersects(cubicRect)) {
22763
    painter->setPen(mainPen());
22764
    painter->drawPath(cubicPath);
22765
    painter->setBrush(Qt::SolidPattern);
22766
    if (mTail.style() != QCPLineEnding::esNone)
22767
      mTail.draw(painter, QVector2D(startVec),
22768
                 M_PI - cubicPath.angleAtPercent(0) / 180.0 * M_PI);
22769
    if (mHead.style() != QCPLineEnding::esNone)
22770
      mHead.draw(painter, QVector2D(endVec),
22771
                 -cubicPath.angleAtPercent(1) / 180.0 * M_PI);
22772
  }
22773
}
22774
22775
/*! \internal
22776
22777
  Returns the pen that should be used for drawing lines. Returns mPen when the
22778
  item is not selected and mSelectedPen when it is.
22779
*/
22780
QPen QCPItemCurve::mainPen() const { return mSelected ? mSelectedPen : mPen; }
22781
22782
////////////////////////////////////////////////////////////////////////////////////////////////////
22783
//////////////////// QCPItemRect
22784
////////////////////////////////////////////////////////////////////////////////////////////////////
22785
22786
/*! \class QCPItemRect
22787
  \brief A rectangle
22788
22789
  \image html QCPItemRect.png "Rectangle example. Blue dotted circles are
22790
  anchors, solid blue discs are positions."
22791
22792
  It has two positions, \a topLeft and \a bottomRight, which define the
22793
  rectangle.
22794
*/
22795
22796
/*!
22797
  Creates a rectangle item and sets default values.
22798
22799
  The constructed item can be added to the plot with QCustomPlot::addItem.
22800
*/
22801
QCPItemRect::QCPItemRect(QCustomPlot *parentPlot)
22802
    : QCPAbstractItem(parentPlot),
22803
      topLeft(createPosition(QLatin1String("topLeft"))),
22804
      bottomRight(createPosition(QLatin1String("bottomRight"))),
22805
      top(createAnchor(QLatin1String("top"), aiTop)),
22806
      topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22807
      right(createAnchor(QLatin1String("right"), aiRight)),
22808
      bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22809
      bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22810
      left(createAnchor(QLatin1String("left"), aiLeft)) {
22811
  topLeft->setCoords(0, 1);
22812
  bottomRight->setCoords(1, 0);
22813
22814
  setPen(QPen(Qt::black));
22815
  setSelectedPen(QPen(Qt::blue, 2));
22816
  setBrush(Qt::NoBrush);
22817
  setSelectedBrush(Qt::NoBrush);
22818
}
22819
22820
QCPItemRect::~QCPItemRect() {}
22821
22822
/*!
22823
  Sets the pen that will be used to draw the line of the rectangle
22824
22825
  \see setSelectedPen, setBrush
22826
*/
22827
void QCPItemRect::setPen(const QPen &pen) { mPen = pen; }
22828
22829
/*!
22830
  Sets the pen that will be used to draw the line of the rectangle when selected
22831
22832
  \see setPen, setSelected
22833
*/
22834
void QCPItemRect::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
22835
22836
/*!
22837
  Sets the brush that will be used to fill the rectangle. To disable filling,
22838
  set \a brush to Qt::NoBrush.
22839
22840
  \see setSelectedBrush, setPen
22841
*/
22842
void QCPItemRect::setBrush(const QBrush &brush) { mBrush = brush; }
22843
22844
/*!
22845
  Sets the brush that will be used to fill the rectangle when selected. To
22846
  disable filling, set \a brush to Qt::NoBrush.
22847
22848
  \see setBrush
22849
*/
22850
void QCPItemRect::setSelectedBrush(const QBrush &brush) {
22851
  mSelectedBrush = brush;
22852
}
22853
22854
/* inherits documentation from base class */
22855
double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable,
22856
                               QVariant *details) const {
22857
  Q_UNUSED(details)
22858
  if (onlySelectable && !mSelectable) return -1;
22859
22860
  QRectF rect =
22861
      QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
22862
  bool filledRect =
22863
      mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
22864
  return rectSelectTest(rect, pos, filledRect);
22865
}
22866
22867
/* inherits documentation from base class */
22868
void QCPItemRect::draw(QCPPainter *painter) {
22869
  QPointF p1 = topLeft->pixelPoint();
22870
  QPointF p2 = bottomRight->pixelPoint();
22871
  if (p1.toPoint() == p2.toPoint()) return;
22872
  QRectF rect = QRectF(p1, p2).normalized();
22873
  double clipPad = mainPen().widthF();
22874
  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22875
  if (boundingRect.intersects(clipRect()))  // only draw if bounding rect of
22876
                                            // rect item is visible in cliprect
22877
  {
22878
    painter->setPen(mainPen());
22879
    painter->setBrush(mainBrush());
22880
    painter->drawRect(rect);
22881
  }
22882
}
22883
22884
/* inherits documentation from base class */
22885
QPointF QCPItemRect::anchorPixelPoint(int anchorId) const {
22886
  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22887
  switch (anchorId) {
22888
    case aiTop:
22889
      return (rect.topLeft() + rect.topRight()) * 0.5;
22890
    case aiTopRight:
22891
      return rect.topRight();
22892
    case aiRight:
22893
      return (rect.topRight() + rect.bottomRight()) * 0.5;
22894
    case aiBottom:
22895
      return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
22896
    case aiBottomLeft:
22897
      return rect.bottomLeft();
22898
    case aiLeft:
22899
      return (rect.topLeft() + rect.bottomLeft()) * 0.5;
22900
  }
22901
22902
  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22903
  return QPointF();
22904
}
22905
22906
/*! \internal
22907
22908
  Returns the pen that should be used for drawing lines. Returns mPen when the
22909
  item is not selected and mSelectedPen when it is.
22910
*/
22911
QPen QCPItemRect::mainPen() const { return mSelected ? mSelectedPen : mPen; }
22912
22913
/*! \internal
22914
22915
  Returns the brush that should be used for drawing fills of the item. Returns
22916
  mBrush when the item is not selected and mSelectedBrush when it is.
22917
*/
22918
QBrush QCPItemRect::mainBrush() const {
22919
  return mSelected ? mSelectedBrush : mBrush;
22920
}
22921
22922
////////////////////////////////////////////////////////////////////////////////////////////////////
22923
//////////////////// QCPItemText
22924
////////////////////////////////////////////////////////////////////////////////////////////////////
22925
22926
/*! \class QCPItemText
22927
  \brief A text label
22928
22929
  \image html QCPItemText.png "Text example. Blue dotted circles are anchors,
22930
  solid blue discs are positions."
22931
22932
  Its position is defined by the member \a position and the setting of \ref
22933
  setPositionAlignment. The latter controls which part of the text rect shall be
22934
  aligned with \a position.
22935
22936
  The text alignment itself (i.e. left, center, right) can be controlled with
22937
  \ref setTextAlignment.
22938
22939
  The text may be rotated around the \a position point with \ref setRotation.
22940
*/
22941
22942
/*!
22943
  Creates a text item and sets default values.
22944
22945
  The constructed item can be added to the plot with QCustomPlot::addItem.
22946
*/
22947
QCPItemText::QCPItemText(QCustomPlot *parentPlot)
22948
    : QCPAbstractItem(parentPlot),
22949
      position(createPosition(QLatin1String("position"))),
22950
      topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
22951
      top(createAnchor(QLatin1String("top"), aiTop)),
22952
      topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22953
      right(createAnchor(QLatin1String("right"), aiRight)),
22954
      bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
22955
      bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22956
      bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22957
      left(createAnchor(QLatin1String("left"), aiLeft)) {
22958
  position->setCoords(0, 0);
22959
22960
  setRotation(0);
22961
  setTextAlignment(Qt::AlignTop | Qt::AlignHCenter);
22962
  setPositionAlignment(Qt::AlignCenter);
22963
  setText(QLatin1String("text"));
22964
22965
  setPen(Qt::NoPen);
22966
  setSelectedPen(Qt::NoPen);
22967
  setBrush(Qt::NoBrush);
22968
  setSelectedBrush(Qt::NoBrush);
22969
  setColor(Qt::black);
22970
  setSelectedColor(Qt::blue);
22971
}
22972
22973
QCPItemText::~QCPItemText() {}
22974
22975
/*!
22976
  Sets the color of the text.
22977
*/
22978
void QCPItemText::setColor(const QColor &color) { mColor = color; }
22979
22980
/*!
22981
  Sets the color of the text that will be used when the item is selected.
22982
*/
22983
void QCPItemText::setSelectedColor(const QColor &color) {
22984
  mSelectedColor = color;
22985
}
22986
22987
/*!
22988
  Sets the pen that will be used do draw a rectangular border around the text.
22989
  To disable the border, set \a pen to Qt::NoPen.
22990
22991
  \see setSelectedPen, setBrush, setPadding
22992
*/
22993
void QCPItemText::setPen(const QPen &pen) { mPen = pen; }
22994
22995
/*!
22996
  Sets the pen that will be used do draw a rectangular border around the text,
22997
  when the item is selected. To disable the border, set \a pen to Qt::NoPen.
22998
22999
  \see setPen
23000
*/
23001
void QCPItemText::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
23002
23003
/*!
23004
  Sets the brush that will be used do fill the background of the text. To
23005
  disable the background, set \a brush to Qt::NoBrush.
23006
23007
  \see setSelectedBrush, setPen, setPadding
23008
*/
23009
void QCPItemText::setBrush(const QBrush &brush) { mBrush = brush; }
23010
23011
/*!
23012
  Sets the brush that will be used do fill the background of the text, when the
23013
  item is selected. To disable the background, set \a brush to Qt::NoBrush.
23014
23015
  \see setBrush
23016
*/
23017
void QCPItemText::setSelectedBrush(const QBrush &brush) {
23018
  mSelectedBrush = brush;
23019
}
23020
23021
/*!
23022
  Sets the font of the text.
23023
23024
  \see setSelectedFont, setColor
23025
*/
23026
void QCPItemText::setFont(const QFont &font) { mFont = font; }
23027
23028
/*!
23029
  Sets the font of the text that will be used when the item is selected.
23030
23031
  \see setFont
23032
*/
23033
void QCPItemText::setSelectedFont(const QFont &font) { mSelectedFont = font; }
23034
23035
/*!
23036
  Sets the text that will be displayed. Multi-line texts are supported by
23037
  inserting a line break character, e.g. '\n'.
23038
23039
  \see setFont, setColor, setTextAlignment
23040
*/
23041
void QCPItemText::setText(const QString &text) { mText = text; }
23042
23043
/*!
23044
  Sets which point of the text rect shall be aligned with \a position.
23045
23046
  Examples:
23047
  \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will
23048
  be positioned such that the top of the text rect will be horizontally centered
23049
  on \a position. \li If \a alignment is <tt>Qt::AlignLeft |
23050
  Qt::AlignBottom</tt>, \a position will indicate the bottom left corner of the
23051
  text rect.
23052
23053
  If you want to control the alignment of (multi-lined) text within the text
23054
  rect, use \ref setTextAlignment.
23055
*/
23056
void QCPItemText::setPositionAlignment(Qt::Alignment alignment) {
23057
  mPositionAlignment = alignment;
23058
}
23059
23060
/*!
23061
  Controls how (multi-lined) text is aligned inside the text rect (typically
23062
  Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
23063
*/
23064
void QCPItemText::setTextAlignment(Qt::Alignment alignment) {
23065
  mTextAlignment = alignment;
23066
}
23067
23068
/*!
23069
  Sets the angle in degrees by which the text (and the text rectangle, if
23070
  visible) will be rotated around \a position.
23071
*/
23072
void QCPItemText::setRotation(double degrees) { mRotation = degrees; }
23073
23074
/*!
23075
  Sets the distance between the border of the text rectangle and the text. The
23076
  appearance (and visibility) of the text rectangle can be controlled with \ref
23077
  setPen and \ref setBrush.
23078
*/
23079
void QCPItemText::setPadding(const QMargins &padding) { mPadding = padding; }
23080
23081
/* inherits documentation from base class */
23082
double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable,
23083
                               QVariant *details) const {
23084
  Q_UNUSED(details)
23085
  if (onlySelectable && !mSelectable) return -1;
23086
23087
  // The rect may be rotated, so we transform the actual clicked pos to the
23088
  // rotated coordinate system, so we can use the normal rectSelectTest function
23089
  // for non-rotated rects:
23090
  QPointF positionPixels(position->pixelPoint());
23091
  QTransform inputTransform;
23092
  inputTransform.translate(positionPixels.x(), positionPixels.y());
23093
  inputTransform.rotate(-mRotation);
23094
  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
23095
  QPointF rotatedPos = inputTransform.map(pos);
23096
  QFontMetrics fontMetrics(mFont);
23097
  QRect textRect = fontMetrics.boundingRect(
23098
      0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
23099
  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
23100
                                        mPadding.right(), mPadding.bottom());
23101
  QPointF textPos =
23102
      getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
23103
  textBoxRect.moveTopLeft(textPos.toPoint());
23104
23105
  return rectSelectTest(textBoxRect, rotatedPos, true);
23106
}
23107
23108
/* inherits documentation from base class */
23109
void QCPItemText::draw(QCPPainter *painter) {
23110
  QPointF pos(position->pixelPoint());
23111
  QTransform transform = painter->transform();
23112
  transform.translate(pos.x(), pos.y());
23113
  if (!qFuzzyIsNull(mRotation)) transform.rotate(mRotation);
23114
  painter->setFont(mainFont());
23115
  QRect textRect = painter->fontMetrics().boundingRect(
23116
      0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
23117
  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
23118
                                        mPadding.right(), mPadding.bottom());
23119
  QPointF textPos = getTextDrawPoint(
23120
      QPointF(0, 0), textBoxRect,
23121
      mPositionAlignment);  // 0, 0 because the transform does the translation
23122
  textRect.moveTopLeft(textPos.toPoint() +
23123
                       QPoint(mPadding.left(), mPadding.top()));
23124
  textBoxRect.moveTopLeft(textPos.toPoint());
23125
  double clipPad = mainPen().widthF();
23126
  QRect boundingRect =
23127
      textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
23128
  if (transform.mapRect(boundingRect)
23129
          .intersects(painter->transform().mapRect(clipRect()))) {
23130
    painter->setTransform(transform);
23131
    if ((mainBrush().style() != Qt::NoBrush &&
23132
         mainBrush().color().alpha() != 0) ||
23133
        (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)) {
23134
      painter->setPen(mainPen());
23135
      painter->setBrush(mainBrush());
23136
      painter->drawRect(textBoxRect);
23137
    }
23138
    painter->setBrush(Qt::NoBrush);
23139
    painter->setPen(QPen(mainColor()));
23140
    painter->drawText(textRect, Qt::TextDontClip | mTextAlignment, mText);
23141
  }
23142
}
23143
23144
/* inherits documentation from base class */
23145
QPointF QCPItemText::anchorPixelPoint(int anchorId) const {
23146
  // get actual rect points (pretty much copied from draw function):
23147
  QPointF pos(position->pixelPoint());
23148
  QTransform transform;
23149
  transform.translate(pos.x(), pos.y());
23150
  if (!qFuzzyIsNull(mRotation)) transform.rotate(mRotation);
23151
  QFontMetrics fontMetrics(mainFont());
23152
  QRect textRect = fontMetrics.boundingRect(
23153
      0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
23154
  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
23155
                                         mPadding.right(), mPadding.bottom());
23156
  QPointF textPos = getTextDrawPoint(
23157
      QPointF(0, 0), textBoxRect,
23158
      mPositionAlignment);  // 0, 0 because the transform does the translation
23159
  textBoxRect.moveTopLeft(textPos.toPoint());
23160
  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
23161
23162
  switch (anchorId) {
23163
    case aiTopLeft:
23164
      return rectPoly.at(0);
23165
    case aiTop:
23166
      return (rectPoly.at(0) + rectPoly.at(1)) * 0.5;
23167
    case aiTopRight:
23168
      return rectPoly.at(1);
23169
    case aiRight:
23170
      return (rectPoly.at(1) + rectPoly.at(2)) * 0.5;
23171
    case aiBottomRight:
23172
      return rectPoly.at(2);
23173
    case aiBottom:
23174
      return (rectPoly.at(2) + rectPoly.at(3)) * 0.5;
23175
    case aiBottomLeft:
23176
      return rectPoly.at(3);
23177
    case aiLeft:
23178
      return (rectPoly.at(3) + rectPoly.at(0)) * 0.5;
23179
  }
23180
23181
  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23182
  return QPointF();
23183
}
23184
23185
/*! \internal
23186
23187
  Returns the point that must be given to the QPainter::drawText function (which
23188
  expects the top left point of the text rect), according to the position \a
23189
  pos, the text bounding box \a rect and the requested \a positionAlignment.
23190
23191
  For example, if \a positionAlignment is <tt>Qt::AlignLeft |
23192
  Qt::AlignBottom</tt> the returned point will be shifted upward by the height
23193
  of \a rect, starting from \a pos. So if the text is finally drawn at that
23194
  point, the lower left corner of the resulting text rect is at \a pos.
23195
*/
23196
QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect,
23197
                                      Qt::Alignment positionAlignment) const {
23198
  if (positionAlignment == 0 ||
23199
      positionAlignment == (Qt::AlignLeft | Qt::AlignTop))
23200
    return pos;
23201
23202
  QPointF result = pos;  // start at top left
23203
  if (positionAlignment.testFlag(Qt::AlignHCenter))
23204
    result.rx() -= rect.width() / 2.0;
23205
  else if (positionAlignment.testFlag(Qt::AlignRight))
23206
    result.rx() -= rect.width();
23207
  if (positionAlignment.testFlag(Qt::AlignVCenter))
23208
    result.ry() -= rect.height() / 2.0;
23209
  else if (positionAlignment.testFlag(Qt::AlignBottom))
23210
    result.ry() -= rect.height();
23211
  return result;
23212
}
23213
23214
/*! \internal
23215
23216
  Returns the font that should be used for drawing text. Returns mFont when the
23217
  item is not selected and mSelectedFont when it is.
23218
*/
23219
QFont QCPItemText::mainFont() const {
23220
  return mSelected ? mSelectedFont : mFont;
23221
}
23222
23223
/*! \internal
23224
23225
  Returns the color that should be used for drawing text. Returns mColor when
23226
  the item is not selected and mSelectedColor when it is.
23227
*/
23228
QColor QCPItemText::mainColor() const {
23229
  return mSelected ? mSelectedColor : mColor;
23230
}
23231
23232
/*! \internal
23233
23234
  Returns the pen that should be used for drawing lines. Returns mPen when the
23235
  item is not selected and mSelectedPen when it is.
23236
*/
23237
QPen QCPItemText::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23238
23239
/*! \internal
23240
23241
  Returns the brush that should be used for drawing fills of the item. Returns
23242
  mBrush when the item is not selected and mSelectedBrush when it is.
23243
*/
23244
QBrush QCPItemText::mainBrush() const {
23245
  return mSelected ? mSelectedBrush : mBrush;
23246
}
23247
23248
////////////////////////////////////////////////////////////////////////////////////////////////////
23249
//////////////////// QCPItemEllipse
23250
////////////////////////////////////////////////////////////////////////////////////////////////////
23251
23252
/*! \class QCPItemEllipse
23253
  \brief An ellipse
23254
23255
  \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are
23256
  anchors, solid blue discs are positions."
23257
23258
  It has two positions, \a topLeft and \a bottomRight, which define the rect the
23259
  ellipse will be drawn in.
23260
*/
23261
23262
/*!
23263
  Creates an ellipse item and sets default values.
23264
23265
  The constructed item can be added to the plot with QCustomPlot::addItem.
23266
*/
23267
QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot)
23268
    : QCPAbstractItem(parentPlot),
23269
      topLeft(createPosition(QLatin1String("topLeft"))),
23270
      bottomRight(createPosition(QLatin1String("bottomRight"))),
23271
      topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
23272
      top(createAnchor(QLatin1String("top"), aiTop)),
23273
      topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
23274
      right(createAnchor(QLatin1String("right"), aiRight)),
23275
      bottomRightRim(
23276
          createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
23277
      bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
23278
      bottomLeftRim(
23279
          createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
23280
      left(createAnchor(QLatin1String("left"), aiLeft)),
23281
      center(createAnchor(QLatin1String("center"), aiCenter)) {
23282
  topLeft->setCoords(0, 1);
23283
  bottomRight->setCoords(1, 0);
23284
23285
  setPen(QPen(Qt::black));
23286
  setSelectedPen(QPen(Qt::blue, 2));
23287
  setBrush(Qt::NoBrush);
23288
  setSelectedBrush(Qt::NoBrush);
23289
}
23290
23291
QCPItemEllipse::~QCPItemEllipse() {}
23292
23293
/*!
23294
  Sets the pen that will be used to draw the line of the ellipse
23295
23296
  \see setSelectedPen, setBrush
23297
*/
23298
void QCPItemEllipse::setPen(const QPen &pen) { mPen = pen; }
23299
23300
/*!
23301
  Sets the pen that will be used to draw the line of the ellipse when selected
23302
23303
  \see setPen, setSelected
23304
*/
23305
void QCPItemEllipse::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
23306
23307
/*!
23308
  Sets the brush that will be used to fill the ellipse. To disable filling, set
23309
  \a brush to Qt::NoBrush.
23310
23311
  \see setSelectedBrush, setPen
23312
*/
23313
void QCPItemEllipse::setBrush(const QBrush &brush) { mBrush = brush; }
23314
23315
/*!
23316
  Sets the brush that will be used to fill the ellipse when selected. To disable
23317
  filling, set \a brush to Qt::NoBrush.
23318
23319
  \see setBrush
23320
*/
23321
void QCPItemEllipse::setSelectedBrush(const QBrush &brush) {
23322
  mSelectedBrush = brush;
23323
}
23324
23325
/* inherits documentation from base class */
23326
double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable,
23327
                                  QVariant *details) const {
23328
  Q_UNUSED(details)
23329
  if (onlySelectable && !mSelectable) return -1;
23330
23331
  double result = -1;
23332
  QPointF p1 = topLeft->pixelPoint();
23333
  QPointF p2 = bottomRight->pixelPoint();
23334
  QPointF center((p1 + p2) / 2.0);
23335
  double a = qAbs(p1.x() - p2.x()) / 2.0;
23336
  double b = qAbs(p1.y() - p2.y()) / 2.0;
23337
  double x = pos.x() - center.x();
23338
  double y = pos.y() - center.y();
23339
23340
  // distance to border:
23341
  double c = 1.0 / qSqrt(x * x / (a * a) + y * y / (b * b));
23342
  result = qAbs(c - 1) * qSqrt(x * x + y * y);
23343
  // filled ellipse, allow click inside to count as hit:
23344
  if (result > mParentPlot->selectionTolerance() * 0.99 &&
23345
      mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0) {
23346
    if (x * x / (a * a) + y * y / (b * b) <= 1)
23347
      result = mParentPlot->selectionTolerance() * 0.99;
23348
  }
23349
  return result;
23350
}
23351
23352
/* inherits documentation from base class */
23353
void QCPItemEllipse::draw(QCPPainter *painter) {
23354
  QPointF p1 = topLeft->pixelPoint();
23355
  QPointF p2 = bottomRight->pixelPoint();
23356
  if (p1.toPoint() == p2.toPoint()) return;
23357
  QRectF ellipseRect = QRectF(p1, p2).normalized();
23358
  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
23359
                                   mainPen().widthF(), mainPen().widthF());
23360
  if (ellipseRect.intersects(clip))  // only draw if bounding rect of ellipse is
23361
                                     // visible in cliprect
23362
  {
23363
    painter->setPen(mainPen());
23364
    painter->setBrush(mainBrush());
23365
#ifdef __EXCEPTIONS
23366
    try  // drawEllipse sometimes throws exceptions if ellipse is too big
23367
    {
23368
#endif
23369
      painter->drawEllipse(ellipseRect);
23370
#ifdef __EXCEPTIONS
23371
    } catch (...) {
23372
      qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
23373
      setVisible(false);
23374
    }
23375
#endif
23376
  }
23377
}
23378
23379
/* inherits documentation from base class */
23380
QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const {
23381
  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
23382
  switch (anchorId) {
23383
    case aiTopLeftRim:
23384
      return rect.center() + (rect.topLeft() - rect.center()) * 1 / qSqrt(2);
23385
    case aiTop:
23386
      return (rect.topLeft() + rect.topRight()) * 0.5;
23387
    case aiTopRightRim:
23388
      return rect.center() + (rect.topRight() - rect.center()) * 1 / qSqrt(2);
23389
    case aiRight:
23390
      return (rect.topRight() + rect.bottomRight()) * 0.5;
23391
    case aiBottomRightRim:
23392
      return rect.center() +
23393
             (rect.bottomRight() - rect.center()) * 1 / qSqrt(2);
23394
    case aiBottom:
23395
      return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
23396
    case aiBottomLeftRim:
23397
      return rect.center() + (rect.bottomLeft() - rect.center()) * 1 / qSqrt(2);
23398
    case aiLeft:
23399
      return (rect.topLeft() + rect.bottomLeft()) * 0.5;
23400
    case aiCenter:
23401
      return (rect.topLeft() + rect.bottomRight()) * 0.5;
23402
  }
23403
23404
  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23405
  return QPointF();
23406
}
23407
23408
/*! \internal
23409
23410
  Returns the pen that should be used for drawing lines. Returns mPen when the
23411
  item is not selected and mSelectedPen when it is.
23412
*/
23413
QPen QCPItemEllipse::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23414
23415
/*! \internal
23416
23417
  Returns the brush that should be used for drawing fills of the item. Returns
23418
  mBrush when the item is not selected and mSelectedBrush when it is.
23419
*/
23420
QBrush QCPItemEllipse::mainBrush() const {
23421
  return mSelected ? mSelectedBrush : mBrush;
23422
}
23423
23424
////////////////////////////////////////////////////////////////////////////////////////////////////
23425
//////////////////// QCPItemPixmap
23426
////////////////////////////////////////////////////////////////////////////////////////////////////
23427
23428
/*! \class QCPItemPixmap
23429
  \brief An arbitrary pixmap
23430
23431
  \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are
23432
  anchors, solid blue discs are positions."
23433
23434
  It has two positions, \a topLeft and \a bottomRight, which define the
23435
  rectangle the pixmap will be drawn in. Depending on the scale setting (\ref
23436
  setScaled), the pixmap will be either scaled to fit the rectangle or be drawn
23437
  aligned to the topLeft position.
23438
23439
  If scaling is enabled and \a topLeft is further to the bottom/right than \a
23440
  bottomRight (as shown on the right side of the example image), the pixmap will
23441
  be flipped in the respective orientations.
23442
*/
23443
23444
/*!
23445
  Creates a rectangle item and sets default values.
23446
23447
  The constructed item can be added to the plot with QCustomPlot::addItem.
23448
*/
23449
QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot)
23450
    : QCPAbstractItem(parentPlot),
23451
      topLeft(createPosition(QLatin1String("topLeft"))),
23452
      bottomRight(createPosition(QLatin1String("bottomRight"))),
23453
      top(createAnchor(QLatin1String("top"), aiTop)),
23454
      topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
23455
      right(createAnchor(QLatin1String("right"), aiRight)),
23456
      bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
23457
      bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
23458
      left(createAnchor(QLatin1String("left"), aiLeft)),
23459
      mScaledPixmapInvalidated(true) {
23460
  topLeft->setCoords(0, 1);
23461
  bottomRight->setCoords(1, 0);
23462
23463
  setPen(Qt::NoPen);
23464
  setSelectedPen(QPen(Qt::blue));
23465
  setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
23466
}
23467
23468
QCPItemPixmap::~QCPItemPixmap() {}
23469
23470
/*!
23471
  Sets the pixmap that will be displayed.
23472
*/
23473
void QCPItemPixmap::setPixmap(const QPixmap &pixmap) {
23474
  mPixmap = pixmap;
23475
  mScaledPixmapInvalidated = true;
23476
  if (mPixmap.isNull()) qDebug() << Q_FUNC_INFO << "pixmap is null";
23477
}
23478
23479
/*!
23480
  Sets whether the pixmap will be scaled to fit the rectangle defined by the \a
23481
  topLeft and \a bottomRight positions.
23482
*/
23483
void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode,
23484
                              Qt::TransformationMode transformationMode) {
23485
  mScaled = scaled;
23486
  mAspectRatioMode = aspectRatioMode;
23487
  mTransformationMode = transformationMode;
23488
  mScaledPixmapInvalidated = true;
23489
}
23490
23491
/*!
23492
  Sets the pen that will be used to draw a border around the pixmap.
23493
23494
  \see setSelectedPen, setBrush
23495
*/
23496
void QCPItemPixmap::setPen(const QPen &pen) { mPen = pen; }
23497
23498
/*!
23499
  Sets the pen that will be used to draw a border around the pixmap when
23500
  selected
23501
23502
  \see setPen, setSelected
23503
*/
23504
void QCPItemPixmap::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
23505
23506
/* inherits documentation from base class */
23507
double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable,
23508
                                 QVariant *details) const {
23509
  Q_UNUSED(details)
23510
  if (onlySelectable && !mSelectable) return -1;
23511
23512
  return rectSelectTest(getFinalRect(), pos, true);
23513
}
23514
23515
/* inherits documentation from base class */
23516
void QCPItemPixmap::draw(QCPPainter *painter) {
23517
  bool flipHorz = false;
23518
  bool flipVert = false;
23519
  QRect rect = getFinalRect(&flipHorz, &flipVert);
23520
  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
23521
  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
23522
  if (boundingRect.intersects(clipRect())) {
23523
    updateScaledPixmap(rect, flipHorz, flipVert);
23524
    painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
23525
    QPen pen = mainPen();
23526
    if (pen.style() != Qt::NoPen) {
23527
      painter->setPen(pen);
23528
      painter->setBrush(Qt::NoBrush);
23529
      painter->drawRect(rect);
23530
    }
23531
  }
23532
}
23533
23534
/* inherits documentation from base class */
23535
QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const {
23536
  bool flipHorz;
23537
  bool flipVert;
23538
  QRect rect = getFinalRect(&flipHorz, &flipVert);
23539
  // we actually want denormal rects (negative width/height) here, so restore
23540
  // the flipped state:
23541
  if (flipHorz) rect.adjust(rect.width(), 0, -rect.width(), 0);
23542
  if (flipVert) rect.adjust(0, rect.height(), 0, -rect.height());
23543
23544
  switch (anchorId) {
23545
    case aiTop:
23546
      return (rect.topLeft() + rect.topRight()) * 0.5;
23547
    case aiTopRight:
23548
      return rect.topRight();
23549
    case aiRight:
23550
      return (rect.topRight() + rect.bottomRight()) * 0.5;
23551
    case aiBottom:
23552
      return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
23553
    case aiBottomLeft:
23554
      return rect.bottomLeft();
23555
    case aiLeft:
23556
      return (rect.topLeft() + rect.bottomLeft()) * 0.5;
23557
      ;
23558
  }
23559
23560
  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23561
  return QPointF();
23562
}
23563
23564
/*! \internal
23565
23566
  Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a
23567
  finalRect. The parameters \a flipHorz and \a flipVert control whether the
23568
  resulting image shall be flipped horizontally or vertically. (This is used
23569
  when \a topLeft is further to the bottom/right than \a bottomRight.)
23570
23571
  This function only creates the scaled pixmap when the buffered pixmap has a
23572
  different size than the expected result, so calling this function repeatedly,
23573
  e.g. in the \ref draw function, does not cause expensive rescaling every time.
23574
23575
  If scaling is disabled, sets mScaledPixmap to a null QPixmap.
23576
*/
23577
void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz,
23578
                                       bool flipVert) {
23579
  if (mPixmap.isNull()) return;
23580
23581
  if (mScaled) {
23582
    if (finalRect.isNull()) finalRect = getFinalRect(&flipHorz, &flipVert);
23583
    if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size()) {
23584
      mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode,
23585
                                     mTransformationMode);
23586
      if (flipHorz || flipVert)
23587
        mScaledPixmap = QPixmap::fromImage(
23588
            mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
23589
    }
23590
  } else if (!mScaledPixmap.isNull())
23591
    mScaledPixmap = QPixmap();
23592
  mScaledPixmapInvalidated = false;
23593
}
23594
23595
/*! \internal
23596
23597
  Returns the final (tight) rect the pixmap is drawn in, depending on the
23598
  current item positions and scaling settings.
23599
23600
  The output parameters \a flippedHorz and \a flippedVert return whether the
23601
  pixmap should be drawn flipped horizontally or vertically in the returned
23602
  rect. (The returned rect itself is always normalized, i.e. the top left corner
23603
  of the rect is actually further to the top/left than the bottom right corner).
23604
  This is the case when the item position \a topLeft is further to the
23605
  bottom/right than \a bottomRight.
23606
23607
  If scaling is disabled, returns a rect with size of the original pixmap and
23608
  the top left corner aligned with the item position \a topLeft. The position \a
23609
  bottomRight is ignored.
23610
*/
23611
QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const {
23612
  QRect result;
23613
  bool flipHorz = false;
23614
  bool flipVert = false;
23615
  QPoint p1 = topLeft->pixelPoint().toPoint();
23616
  QPoint p2 = bottomRight->pixelPoint().toPoint();
23617
  if (p1 == p2) return QRect(p1, QSize(0, 0));
23618
  if (mScaled) {
23619
    QSize newSize = QSize(p2.x() - p1.x(), p2.y() - p1.y());
23620
    QPoint topLeft = p1;
23621
    if (newSize.width() < 0) {
23622
      flipHorz = true;
23623
      newSize.rwidth() *= -1;
23624
      topLeft.setX(p2.x());
23625
    }
23626
    if (newSize.height() < 0) {
23627
      flipVert = true;
23628
      newSize.rheight() *= -1;
23629
      topLeft.setY(p2.y());
23630
    }
23631
    QSize scaledSize = mPixmap.size();
23632
    scaledSize.scale(newSize, mAspectRatioMode);
23633
    result = QRect(topLeft, scaledSize);
23634
  } else {
23635
    result = QRect(p1, mPixmap.size());
23636
  }
23637
  if (flippedHorz) *flippedHorz = flipHorz;
23638
  if (flippedVert) *flippedVert = flipVert;
23639
  return result;
23640
}
23641
23642
/*! \internal
23643
23644
  Returns the pen that should be used for drawing lines. Returns mPen when the
23645
  item is not selected and mSelectedPen when it is.
23646
*/
23647
QPen QCPItemPixmap::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23648
23649
////////////////////////////////////////////////////////////////////////////////////////////////////
23650
//////////////////// QCPItemTracer
23651
////////////////////////////////////////////////////////////////////////////////////////////////////
23652
23653
/*! \class QCPItemTracer
23654
  \brief Item that sticks to QCPGraph data points
23655
23656
  \image html QCPItemTracer.png "Tracer example. Blue dotted circles are
23657
  anchors, solid blue discs are positions."
23658
23659
  The tracer can be connected with a QCPGraph via \ref setGraph. Then it will
23660
  automatically adopt the coordinate axes of the graph and update its \a
23661
  position to be on the graph's data. This means the key stays controllable via
23662
  \ref setGraphKey, but the value will follow the graph data. If a QCPGraph is
23663
  connected, note that setting the coordinates of the tracer item directly via
23664
  \a position will have no effect because they will be overriden in the next
23665
  redraw (this is when the coordinate update happens).
23666
23667
  If the specified key in \ref setGraphKey is outside the key bounds of the
23668
  graph, the tracer will stay at the corresponding end of the graph.
23669
23670
  With \ref setInterpolating you may specify whether the tracer may only stay
23671
  exactly on data points or whether it interpolates data points linearly, if
23672
  given a key that lies between two data points of the graph.
23673
23674
  The tracer has different visual styles, see \ref setStyle. It is also possible
23675
  to make the tracer have no own visual appearance (set the style to \ref
23676
  tsNone), and just connect other item positions to the tracer \a position (used
23677
  as an anchor) via \ref QCPItemPosition::setParentAnchor.
23678
23679
  \note The tracer position is only automatically updated upon redraws. So when
23680
  the data of the graph changes and immediately afterwards (without a redraw)
23681
  the a position coordinates of the tracer are retrieved, they will not reflect
23682
  the updated data of the graph. In this case \ref updatePosition must be called
23683
  manually, prior to reading the tracer coordinates.
23684
*/
23685
23686
/*!
23687
  Creates a tracer item and sets default values.
23688
23689
  The constructed item can be added to the plot with QCustomPlot::addItem.
23690
*/
23691
QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot)
23692
    : QCPAbstractItem(parentPlot),
23693
      position(createPosition(QLatin1String("position"))),
23694
      mGraph(0) {
23695
  position->setCoords(0, 0);
23696
23697
  setBrush(Qt::NoBrush);
23698
  setSelectedBrush(Qt::NoBrush);
23699
  setPen(QPen(Qt::black));
23700
  setSelectedPen(QPen(Qt::blue, 2));
23701
  setStyle(tsCrosshair);
23702
  setSize(6);
23703
  setInterpolating(false);
23704
  setGraphKey(0);
23705
}
23706
23707
QCPItemTracer::~QCPItemTracer() {}
23708
23709
/*!
23710
  Sets the pen that will be used to draw the line of the tracer
23711
23712
  \see setSelectedPen, setBrush
23713
*/
23714
void QCPItemTracer::setPen(const QPen &pen) { mPen = pen; }
23715
23716
/*!
23717
  Sets the pen that will be used to draw the line of the tracer when selected
23718
23719
  \see setPen, setSelected
23720
*/
23721
void QCPItemTracer::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
23722
23723
/*!
23724
  Sets the brush that will be used to draw any fills of the tracer
23725
23726
  \see setSelectedBrush, setPen
23727
*/
23728
void QCPItemTracer::setBrush(const QBrush &brush) { mBrush = brush; }
23729
23730
/*!
23731
  Sets the brush that will be used to draw any fills of the tracer, when
23732
  selected.
23733
23734
  \see setBrush, setSelected
23735
*/
23736
void QCPItemTracer::setSelectedBrush(const QBrush &brush) {
23737
  mSelectedBrush = brush;
23738
}
23739
23740
/*!
23741
  Sets the size of the tracer in pixels, if the style supports setting a size
23742
  (e.g. \ref tsSquare does, \ref tsCrosshair does not).
23743
*/
23744
void QCPItemTracer::setSize(double size) { mSize = size; }
23745
23746
/*!
23747
  Sets the style/visual appearance of the tracer.
23748
23749
  If you only want to use the tracer \a position as an anchor for other items,
23750
  set \a style to \ref tsNone.
23751
*/
23752
void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style) {
23753
  mStyle = style;
23754
}
23755
23756
/*!
23757
  Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to
23758
  type QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a
23759
  graph.
23760
23761
  To free the tracer from any graph, set \a graph to 0. The tracer \a position
23762
  can then be placed freely like any other item position. This is the state the
23763
  tracer will assume when its graph gets deleted while still attached to it.
23764
23765
  \see setGraphKey
23766
*/
23767
void QCPItemTracer::setGraph(QCPGraph *graph) {
23768
  if (graph) {
23769
    if (graph->parentPlot() == mParentPlot) {
23770
      position->setType(QCPItemPosition::ptPlotCoords);
23771
      position->setAxes(graph->keyAxis(), graph->valueAxis());
23772
      mGraph = graph;
23773
      updatePosition();
23774
    } else
23775
      qDebug() << Q_FUNC_INFO
23776
               << "graph isn't in same QCustomPlot instance as this item";
23777
  } else {
23778
    mGraph = 0;
23779
  }
23780
}
23781
23782
/*!
23783
  Sets the key of the graph's data point the tracer will be positioned at. This
23784
  is the only free coordinate of a tracer when attached to a graph.
23785
23786
  Depending on \ref setInterpolating, the tracer will be either positioned on
23787
  the data point closest to \a key, or will stay exactly at \a key and
23788
  interpolate the value linearly.
23789
23790
  \see setGraph, setInterpolating
23791
*/
23792
void QCPItemTracer::setGraphKey(double key) { mGraphKey = key; }
23793
23794
/*!
23795
  Sets whether the value of the graph's data points shall be interpolated, when
23796
  positioning the tracer.
23797
23798
  If \a enabled is set to false and a key is given with \ref setGraphKey, the
23799
  tracer is placed on the data point of the graph which is closest to the key,
23800
  but which is not necessarily exactly there. If \a enabled is true, the tracer
23801
  will be positioned exactly at the specified key, and the appropriate value
23802
  will be interpolated from the graph's data points linearly.
23803
23804
  \see setGraph, setGraphKey
23805
*/
23806
void QCPItemTracer::setInterpolating(bool enabled) { mInterpolating = enabled; }
23807
23808
/* inherits documentation from base class */
23809
double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable,
23810
                                 QVariant *details) const {
23811
  Q_UNUSED(details)
23812
  if (onlySelectable && !mSelectable) return -1;
23813
23814
  QPointF center(position->pixelPoint());
23815
  double w = mSize / 2.0;
23816
  QRect clip = clipRect();
23817
  switch (mStyle) {
23818
    case tsNone:
23819
      return -1;
23820
    case tsPlus: {
23821
      if (clipRect().intersects(
23822
              QRectF(center - QPointF(w, w), center + QPointF(w, w)).toRect()))
23823
        return qSqrt(qMin(
23824
            distSqrToLine(center + QPointF(-w, 0), center + QPointF(w, 0), pos),
23825
            distSqrToLine(center + QPointF(0, -w), center + QPointF(0, w),
23826
                          pos)));
23827
      break;
23828
    }
23829
    case tsCrosshair: {
23830
      return qSqrt(
23831
          qMin(distSqrToLine(QPointF(clip.left(), center.y()),
23832
                             QPointF(clip.right(), center.y()), pos),
23833
               distSqrToLine(QPointF(center.x(), clip.top()),
23834
                             QPointF(center.x(), clip.bottom()), pos)));
23835
    }
23836
    case tsCircle: {
23837
      if (clip.intersects(QRectF(center - QPointF(w, w), center + QPointF(w, w))
23838
                              .toRect())) {
23839
        // distance to border:
23840
        double centerDist = QVector2D(center - pos).length();
23841
        double circleLine = w;
23842
        double result = qAbs(centerDist - circleLine);
23843
        // filled ellipse, allow click inside to count as hit:
23844
        if (result > mParentPlot->selectionTolerance() * 0.99 &&
23845
            mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0) {
23846
          if (centerDist <= circleLine)
23847
            result = mParentPlot->selectionTolerance() * 0.99;
23848
        }
23849
        return result;
23850
      }
23851
      break;
23852
    }
23853
    case tsSquare: {
23854
      if (clip.intersects(QRectF(center - QPointF(w, w), center + QPointF(w, w))
23855
                              .toRect())) {
23856
        QRectF rect = QRectF(center - QPointF(w, w), center + QPointF(w, w));
23857
        bool filledRect =
23858
            mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23859
        return rectSelectTest(rect, pos, filledRect);
23860
      }
23861
      break;
23862
    }
23863
  }
23864
  return -1;
23865
}
23866
23867
/* inherits documentation from base class */
23868
void QCPItemTracer::draw(QCPPainter *painter) {
23869
  updatePosition();
23870
  if (mStyle == tsNone) return;
23871
23872
  painter->setPen(mainPen());
23873
  painter->setBrush(mainBrush());
23874
  QPointF center(position->pixelPoint());
23875
  double w = mSize / 2.0;
23876
  QRect clip = clipRect();
23877
  switch (mStyle) {
23878
    case tsNone:
23879
      return;
23880
    case tsPlus: {
23881
      if (clip.intersects(QRectF(center - QPointF(w, w), center + QPointF(w, w))
23882
                              .toRect())) {
23883
        painter->drawLine(
23884
            QLineF(center + QPointF(-w, 0), center + QPointF(w, 0)));
23885
        painter->drawLine(
23886
            QLineF(center + QPointF(0, -w), center + QPointF(0, w)));
23887
      }
23888
      break;
23889
    }
23890
    case tsCrosshair: {
23891
      if (center.y() > clip.top() && center.y() < clip.bottom())
23892
        painter->drawLine(
23893
            QLineF(clip.left(), center.y(), clip.right(), center.y()));
23894
      if (center.x() > clip.left() && center.x() < clip.right())
23895
        painter->drawLine(
23896
            QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
23897
      break;
23898
    }
23899
    case tsCircle: {
23900
      if (clip.intersects(
23901
              QRectF(center - QPointF(w, w), center + QPointF(w, w)).toRect()))
23902
        painter->drawEllipse(center, w, w);
23903
      break;
23904
    }
23905
    case tsSquare: {
23906
      if (clip.intersects(
23907
              QRectF(center - QPointF(w, w), center + QPointF(w, w)).toRect()))
23908
        painter->drawRect(
23909
            QRectF(center - QPointF(w, w), center + QPointF(w, w)));
23910
      break;
23911
    }
23912
  }
23913
}
23914
23915
/*!
23916
  If the tracer is connected with a graph (\ref setGraph), this function updates
23917
  the tracer's \a position to reside on the graph data, depending on the
23918
  configured key (\ref setGraphKey).
23919
23920
  It is called automatically on every redraw and normally doesn't need to be
23921
  called manually. One exception is when you want to read the tracer coordinates
23922
  via \a position and are not sure that the graph's data (or the tracer key with
23923
  \ref setGraphKey) hasn't changed since the last redraw. In that situation,
23924
  call this function before accessing \a position, to make sure you don't get
23925
  out-of-date coordinates.
23926
23927
  If there is no graph set on this tracer, this function does nothing.
23928
*/
23929
void QCPItemTracer::updatePosition() {
23930
  if (mGraph) {
23931
    if (mParentPlot->hasPlottable(mGraph)) {
23932
      if (mGraph->data()->size() > 1) {
23933
        QCPDataMap::const_iterator first = mGraph->data()->constBegin();
23934
        QCPDataMap::const_iterator last = mGraph->data()->constEnd() - 1;
23935
        if (mGraphKey < first.key())
23936
          position->setCoords(first.key(), first.value().value);
23937
        else if (mGraphKey > last.key())
23938
          position->setCoords(last.key(), last.value().value);
23939
        else {
23940
          QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
23941
          if (it != first)  // mGraphKey is somewhere between iterators
23942
          {
23943
            QCPDataMap::const_iterator prevIt = it - 1;
23944
            if (mInterpolating) {
23945
              // interpolate between iterators around mGraphKey:
23946
              double slope = 0;
23947
              if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
23948
                slope = (it.value().value - prevIt.value().value) /
23949
                        (it.key() - prevIt.key());
23950
              position->setCoords(
23951
                  mGraphKey,
23952
                  (mGraphKey - prevIt.key()) * slope + prevIt.value().value);
23953
            } else {
23954
              // find iterator with key closest to mGraphKey:
23955
              if (mGraphKey < (prevIt.key() + it.key()) * 0.5) it = prevIt;
23956
              position->setCoords(it.key(), it.value().value);
23957
            }
23958
          } else  // mGraphKey is exactly on first iterator
23959
            position->setCoords(it.key(), it.value().value);
23960
        }
23961
      } else if (mGraph->data()->size() == 1) {
23962
        QCPDataMap::const_iterator it = mGraph->data()->constBegin();
23963
        position->setCoords(it.key(), it.value().value);
23964
      } else
23965
        qDebug() << Q_FUNC_INFO << "graph has no data";
23966
    } else
23967
      qDebug() << Q_FUNC_INFO
23968
               << "graph not contained in QCustomPlot instance (anymore)";
23969
  }
23970
}
23971
23972
/*! \internal
23973
23974
  Returns the pen that should be used for drawing lines. Returns mPen when the
23975
  item is not selected and mSelectedPen when it is.
23976
*/
23977
QPen QCPItemTracer::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23978
23979
/*! \internal
23980
23981
  Returns the brush that should be used for drawing fills of the item. Returns
23982
  mBrush when the item is not selected and mSelectedBrush when it is.
23983
*/
23984
QBrush QCPItemTracer::mainBrush() const {
23985
  return mSelected ? mSelectedBrush : mBrush;
23986
}
23987
23988
////////////////////////////////////////////////////////////////////////////////////////////////////
23989
//////////////////// QCPItemBracket
23990
////////////////////////////////////////////////////////////////////////////////////////////////////
23991
23992
/*! \class QCPItemBracket
23993
  \brief A bracket for referencing/highlighting certain parts in the plot.
23994
23995
  \image html QCPItemBracket.png "Bracket example. Blue dotted circles are
23996
  anchors, solid blue discs are positions."
23997
23998
  It has two positions, \a left and \a right, which define the span of the
23999
  bracket. If \a left is actually farther to the left than \a right, the bracket
24000
  is opened to the bottom, as shown in the example image.
24001
24002
  The bracket supports multiple styles via \ref setStyle. The length, i.e. how
24003
  far the bracket stretches away from the embraced span, can be controlled with
24004
  \ref setLength.
24005
24006
  \image html QCPItemBracket-length.png
24007
  <center>Demonstrating the effect of different values for \ref setLength, for
24008
  styles \ref bsCalligraphic and \ref bsSquare. Anchors and positions are
24009
  displayed for reference.</center>
24010
24011
  It provides an anchor \a center, to allow connection of other items, e.g. an
24012
  arrow (QCPItemLine or QCPItemCurve) or a text label (QCPItemText), to the
24013
  bracket.
24014
*/
24015
24016
/*!
24017
  Creates a bracket item and sets default values.
24018
24019
  The constructed item can be added to the plot with QCustomPlot::addItem.
24020
*/
24021
QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot)
24022
    : QCPAbstractItem(parentPlot),
24023
      left(createPosition(QLatin1String("left"))),
24024
      right(createPosition(QLatin1String("right"))),
24025
      center(createAnchor(QLatin1String("center"), aiCenter)) {
24026
  left->setCoords(0, 0);
24027
  right->setCoords(1, 1);
24028
24029
  setPen(QPen(Qt::black));
24030
  setSelectedPen(QPen(Qt::blue, 2));
24031
  setLength(8);
24032
  setStyle(bsCalligraphic);
24033
}
24034
24035
QCPItemBracket::~QCPItemBracket() {}
24036
24037
/*!
24038
  Sets the pen that will be used to draw the bracket.
24039
24040
  Note that when the style is \ref bsCalligraphic, only the color will be taken
24041
  from the pen, the stroke and width are ignored. To change the apparent stroke
24042
  width of a calligraphic bracket, use \ref setLength, which has a similar
24043
  effect.
24044
24045
  \see setSelectedPen
24046
*/
24047
void QCPItemBracket::setPen(const QPen &pen) { mPen = pen; }
24048
24049
/*!
24050
  Sets the pen that will be used to draw the bracket when selected
24051
24052
  \see setPen, setSelected
24053
*/
24054
void QCPItemBracket::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
24055
24056
/*!
24057
  Sets the \a length in pixels how far the bracket extends in the direction
24058
  towards the embraced span of the bracket (i.e. perpendicular to the
24059
  <i>left</i>-<i>right</i>-direction)
24060
24061
  \image html QCPItemBracket-length.png
24062
  <center>Demonstrating the effect of different values for \ref setLength, for
24063
  styles \ref bsCalligraphic and \ref bsSquare. Anchors and positions are
24064
  displayed for reference.</center>
24065
*/
24066
void QCPItemBracket::setLength(double length) { mLength = length; }
24067
24068
/*!
24069
  Sets the style of the bracket, i.e. the shape/visual appearance.
24070
24071
  \see setPen
24072
*/
24073
void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style) {
24074
  mStyle = style;
24075
}
24076
24077
/* inherits documentation from base class */
24078
double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable,
24079
                                  QVariant *details) const {
24080
  Q_UNUSED(details)
24081
  if (onlySelectable && !mSelectable) return -1;
24082
24083
  QVector2D leftVec(left->pixelPoint());
24084
  QVector2D rightVec(right->pixelPoint());
24085
  if (leftVec.toPoint() == rightVec.toPoint()) return -1;
24086
24087
  QVector2D widthVec = (rightVec - leftVec) * 0.5f;
24088
  QVector2D lengthVec(-widthVec.y(), widthVec.x());
24089
  lengthVec = lengthVec.normalized() * mLength;
24090
  QVector2D centerVec = (rightVec + leftVec) * 0.5f - lengthVec;
24091
24092
  switch (mStyle) {
24093
    case QCPItemBracket::bsSquare:
24094
    case QCPItemBracket::bsRound: {
24095
      double a = distSqrToLine((centerVec - widthVec).toPointF(),
24096
                               (centerVec + widthVec).toPointF(), pos);
24097
      double b = distSqrToLine((centerVec - widthVec + lengthVec).toPointF(),
24098
                               (centerVec - widthVec).toPointF(), pos);
24099
      double c = distSqrToLine((centerVec + widthVec + lengthVec).toPointF(),
24100
                               (centerVec + widthVec).toPointF(), pos);
24101
      return qSqrt(qMin(qMin(a, b), c));
24102
    }
24103
    case QCPItemBracket::bsCurly:
24104
    case QCPItemBracket::bsCalligraphic: {
24105
      double a = distSqrToLine(
24106
          (centerVec - widthVec * 0.75f + lengthVec * 0.15f).toPointF(),
24107
          (centerVec + lengthVec * 0.3f).toPointF(), pos);
24108
      double b = distSqrToLine(
24109
          (centerVec - widthVec + lengthVec * 0.7f).toPointF(),
24110
          (centerVec - widthVec * 0.75f + lengthVec * 0.15f).toPointF(), pos);
24111
      double c = distSqrToLine(
24112
          (centerVec + widthVec * 0.75f + lengthVec * 0.15f).toPointF(),
24113
          (centerVec + lengthVec * 0.3f).toPointF(), pos);
24114
      double d = distSqrToLine(
24115
          (centerVec + widthVec + lengthVec * 0.7f).toPointF(),
24116
          (centerVec + widthVec * 0.75f + lengthVec * 0.15f).toPointF(), pos);
24117
      return qSqrt(qMin(qMin(a, b), qMin(c, d)));
24118
    }
24119
  }
24120
  return -1;
24121
}
24122
24123
/* inherits documentation from base class */
24124
void QCPItemBracket::draw(QCPPainter *painter) {
24125
  QVector2D leftVec(left->pixelPoint());
24126
  QVector2D rightVec(right->pixelPoint());
24127
  if (leftVec.toPoint() == rightVec.toPoint()) return;
24128
24129
  QVector2D widthVec = (rightVec - leftVec) * 0.5f;
24130
  QVector2D lengthVec(-widthVec.y(), widthVec.x());
24131
  lengthVec = lengthVec.normalized() * mLength;
24132
  QVector2D centerVec = (rightVec + leftVec) * 0.5f - lengthVec;
24133
24134
  QPolygon boundingPoly;
24135
  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
24136
               << (rightVec - lengthVec).toPoint()
24137
               << (leftVec - lengthVec).toPoint();
24138
  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
24139
                                   mainPen().widthF(), mainPen().widthF());
24140
  if (clip.intersects(boundingPoly.boundingRect())) {
24141
    painter->setPen(mainPen());
24142
    switch (mStyle) {
24143
      case bsSquare: {
24144
        painter->drawLine((centerVec + widthVec).toPointF(),
24145
                          (centerVec - widthVec).toPointF());
24146
        painter->drawLine((centerVec + widthVec).toPointF(),
24147
                          (centerVec + widthVec + lengthVec).toPointF());
24148
        painter->drawLine((centerVec - widthVec).toPointF(),
24149
                          (centerVec - widthVec + lengthVec).toPointF());
24150
        break;
24151
      }
24152
      case bsRound: {
24153
        painter->setBrush(Qt::NoBrush);
24154
        QPainterPath path;
24155
        path.moveTo((centerVec + widthVec + lengthVec).toPointF());
24156
        path.cubicTo((centerVec + widthVec).toPointF(),
24157
                     (centerVec + widthVec).toPointF(), centerVec.toPointF());
24158
        path.cubicTo((centerVec - widthVec).toPointF(),
24159
                     (centerVec - widthVec).toPointF(),
24160
                     (centerVec - widthVec + lengthVec).toPointF());
24161
        painter->drawPath(path);
24162
        break;
24163
      }
24164
      case bsCurly: {
24165
        painter->setBrush(Qt::NoBrush);
24166
        QPainterPath path;
24167
        path.moveTo((centerVec + widthVec + lengthVec).toPointF());
24168
        path.cubicTo((centerVec + widthVec - lengthVec * 0.8f).toPointF(),
24169
                     (centerVec + 0.4f * widthVec + lengthVec).toPointF(),
24170
                     centerVec.toPointF());
24171
        path.cubicTo((centerVec - 0.4f * widthVec + lengthVec).toPointF(),
24172
                     (centerVec - widthVec - lengthVec * 0.8f).toPointF(),
24173
                     (centerVec - widthVec + lengthVec).toPointF());
24174
        painter->drawPath(path);
24175
        break;
24176
      }
24177
      case bsCalligraphic: {
24178
        painter->setPen(Qt::NoPen);
24179
        painter->setBrush(QBrush(mainPen().color()));
24180
        QPainterPath path;
24181
        path.moveTo((centerVec + widthVec + lengthVec).toPointF());
24182
24183
        path.cubicTo(
24184
            (centerVec + widthVec - lengthVec * 0.8f).toPointF(),
24185
            (centerVec + 0.4f * widthVec + 0.8f * lengthVec).toPointF(),
24186
            centerVec.toPointF());
24187
        path.cubicTo(
24188
            (centerVec - 0.4f * widthVec + 0.8f * lengthVec).toPointF(),
24189
            (centerVec - widthVec - lengthVec * 0.8f).toPointF(),
24190
            (centerVec - widthVec + lengthVec).toPointF());
24191
24192
        path.cubicTo(
24193
            (centerVec - widthVec - lengthVec * 0.5f).toPointF(),
24194
            (centerVec - 0.2f * widthVec + 1.2f * lengthVec).toPointF(),
24195
            (centerVec + lengthVec * 0.2f).toPointF());
24196
        path.cubicTo(
24197
            (centerVec + 0.2f * widthVec + 1.2f * lengthVec).toPointF(),
24198
            (centerVec + widthVec - lengthVec * 0.5f).toPointF(),
24199
            (centerVec + widthVec + lengthVec).toPointF());
24200
24201
        painter->drawPath(path);
24202
        break;
24203
      }
24204
    }
24205
  }
24206
}
24207
24208
/* inherits documentation from base class */
24209
QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const {
24210
  QVector2D leftVec(left->pixelPoint());
24211
  QVector2D rightVec(right->pixelPoint());
24212
  if (leftVec.toPoint() == rightVec.toPoint()) return leftVec.toPointF();
24213
24214
  QVector2D widthVec = (rightVec - leftVec) * 0.5f;
24215
  QVector2D lengthVec(-widthVec.y(), widthVec.x());
24216
  lengthVec = lengthVec.normalized() * mLength;
24217
  QVector2D centerVec = (rightVec + leftVec) * 0.5f - lengthVec;
24218
24219
  switch (anchorId) {
24220
    case aiCenter:
24221
      return centerVec.toPointF();
24222
  }
24223
  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
24224
  return QPointF();
24225
}
24226
24227
/*! \internal
24228
24229
  Returns the pen that should be used for drawing lines. Returns mPen when the
24230
  item is not selected and mSelectedPen when it is.
24231
*/
24232
QPen QCPItemBracket::mainPen() const { return mSelected ? mSelectedPen : mPen; }