labplot  2.8.2
About: LabPlot is an application for plotting and analysis of 2D and 3D functions and data. It is a complete rewrite of LabPlot1 and lacks in the first release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior.
  Fossies Dox: labplot-2.8.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

XYCurve.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  File : XYCurve.cpp
3  Project : LabPlot
4  Description : A xy-curve
5  --------------------------------------------------------------------
6  Copyright : (C) 2010-2020 Alexander Semke (alexander.semke@web.de)
7  Copyright : (C) 2013-2020 Stefan Gerlach (stefan.gerlach@uni.kn)
8 
9  ***************************************************************************/
10 
11 /***************************************************************************
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * *
18  * This program is distributed in the hope that it will be useful, *
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21  * GNU General Public License for more details. *
22  * *
23  * You should have received a copy of the GNU General Public License *
24  * along with this program; if not, write to the Free Software *
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
26  * Boston, MA 02110-1301 USA *
27  * *
28  ***************************************************************************/
29 
30 /*!
31  \class XYCurve
32  \brief A 2D-curve, provides an interface for editing many properties of the curve.
33 
34  \ingroup worksheet
35 */
36 
37 #include "XYCurve.h"
38 #include "XYCurvePrivate.h"
43 #include "backend/core/Project.h"
47 #include "backend/lib/macros.h"
48 #include "backend/lib/trace.h"
49 #include "backend/gsl/errors.h"
50 #include "tools/ImageTools.h"
51 
52 #include <QPainter>
53 #include <QGraphicsSceneContextMenuEvent>
54 #include <QMenu>
55 #include <QDesktopWidget>
56 
57 #include <KConfig>
58 #include <KLocalizedString>
59 
60 extern "C" {
61 #include <gsl/gsl_math.h>
62 #include <gsl/gsl_spline.h>
63 #include <gsl/gsl_errno.h>
64 }
65 
66 XYCurve::XYCurve(const QString &name, AspectType type)
67  : WorksheetElement(name, type), d_ptr(new XYCurvePrivate(this)) {
68 
69  init();
70 }
71 
72 XYCurve::XYCurve(const QString& name, XYCurvePrivate* dd, AspectType type)
73  : WorksheetElement(name, type), d_ptr(dd) {
74 
75  init();
76 }
77 
78 //no need to delete the d-pointer here - it inherits from QGraphicsItem
79 //and is deleted during the cleanup in QGraphicsScene
80 XYCurve::~XYCurve() = default;
81 
83  Q_D(XYCurve);
84  d->plot = static_cast<const CartesianPlot*>(parentAspect());
85  d->cSystem = static_cast<const CartesianCoordinateSystem*>(d->plot->coordinateSystem());
86 }
87 
88 void XYCurve::init() {
89  Q_D(XYCurve);
90 
91  KConfig config;
92  KConfigGroup group = config.group("XYCurve");
93 
94  d->lineType = (LineType) group.readEntry("LineType", static_cast<int>(LineType::Line));
95  d->lineIncreasingXOnly = group.readEntry("LineIncreasingXOnly", false);
96  d->lineSkipGaps = group.readEntry("SkipLineGaps", false);
97  d->lineInterpolationPointsCount = group.readEntry("LineInterpolationPointsCount", 1);
98  d->linePen.setStyle( (Qt::PenStyle) group.readEntry("LineStyle", (int)Qt::SolidLine) );
99  d->linePen.setColor( group.readEntry("LineColor", QColor(Qt::black)) );
100  d->linePen.setWidthF( group.readEntry("LineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)) );
101  d->lineOpacity = group.readEntry("LineOpacity", 1.0);
102 
103  d->dropLineType = (DropLineType) group.readEntry("DropLineType", static_cast<int>(LineType::NoLine));
104  d->dropLinePen.setStyle( (Qt::PenStyle) group.readEntry("DropLineStyle", (int)Qt::SolidLine) );
105  d->dropLinePen.setColor( group.readEntry("DropLineColor", QColor(Qt::black)));
106  d->dropLinePen.setWidthF( group.readEntry("DropLineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)) );
107  d->dropLineOpacity = group.readEntry("DropLineOpacity", 1.0);
108 
109  d->symbolsStyle = (Symbol::Style)group.readEntry("SymbolStyle", static_cast<int>(Symbol::Style::NoSymbols));
110  d->symbolsSize = group.readEntry("SymbolSize", Worksheet::convertToSceneUnits(5, Worksheet::Unit::Point));
111  d->symbolsRotationAngle = group.readEntry("SymbolRotation", 0.0);
112  d->symbolsOpacity = group.readEntry("SymbolOpacity", 1.0);
113  d->symbolsBrush.setStyle( (Qt::BrushStyle)group.readEntry("SymbolFillingStyle", (int)Qt::SolidPattern) );
114  d->symbolsBrush.setColor( group.readEntry("SymbolFillingColor", QColor(Qt::black)) );
115  d->symbolsPen.setStyle( (Qt::PenStyle)group.readEntry("SymbolBorderStyle", (int)Qt::SolidLine) );
116  d->symbolsPen.setColor( group.readEntry("SymbolBorderColor", QColor(Qt::black)) );
117  d->symbolsPen.setWidthF( group.readEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(0.0, Worksheet::Unit::Point)) );
118 
119  d->valuesType = (ValuesType) group.readEntry("ValuesType", static_cast<int>(ValuesType::NoValues));
120  d->valuesPosition = (ValuesPosition) group.readEntry("ValuesPosition", static_cast<int>(ValuesPosition::Above));
121  d->valuesDistance = group.readEntry("ValuesDistance", Worksheet::convertToSceneUnits(5, Worksheet::Unit::Point));
122  d->valuesRotationAngle = group.readEntry("ValuesRotation", 0.0);
123  d->valuesOpacity = group.readEntry("ValuesOpacity", 1.0);
124  d->valuesNumericFormat = group.readEntry("ValuesNumericFormat", "f").at(0).toLatin1();
125  d->valuesPrecision = group.readEntry("ValuesNumericFormat", 2);
126  d->valuesDateTimeFormat = group.readEntry("ValuesDateTimeFormat", "yyyy-MM-dd");
127  d->valuesPrefix = group.readEntry("ValuesPrefix", "");
128  d->valuesSuffix = group.readEntry("ValuesSuffix", "");
129  d->valuesFont = group.readEntry("ValuesFont", QFont());
130  d->valuesFont.setPixelSize( Worksheet::convertToSceneUnits(8, Worksheet::Unit::Point) );
131  d->valuesColor = group.readEntry("ValuesColor", QColor(Qt::black));
132 
133  d->fillingPosition = (FillingPosition) group.readEntry("FillingPosition", static_cast<int>(FillingPosition::NoFilling));
134  d->fillingType = (PlotArea::BackgroundType) group.readEntry("FillingType", static_cast<int>(PlotArea::BackgroundType::Color));
135  d->fillingColorStyle = (PlotArea::BackgroundColorStyle) group.readEntry("FillingColorStyle", static_cast<int>(PlotArea::BackgroundColorStyle::SingleColor));
136  d->fillingImageStyle = (PlotArea::BackgroundImageStyle) group.readEntry("FillingImageStyle", static_cast<int>(PlotArea::BackgroundImageStyle::Scaled));
137  d->fillingBrushStyle = (Qt::BrushStyle) group.readEntry("FillingBrushStyle", static_cast<int>(Qt::SolidPattern));
138  d->fillingFileName = group.readEntry("FillingFileName", QString());
139  d->fillingFirstColor = group.readEntry("FillingFirstColor", QColor(Qt::white));
140  d->fillingSecondColor = group.readEntry("FillingSecondColor", QColor(Qt::black));
141  d->fillingOpacity = group.readEntry("FillingOpacity", 1.0);
142 
143  d->xErrorType = (ErrorType) group.readEntry("XErrorType", static_cast<int>(ErrorType::NoError));
144  d->yErrorType = (ErrorType) group.readEntry("YErrorType", static_cast<int>(ErrorType::NoError));
145  d->errorBarsType = (ErrorBarsType) group.readEntry("ErrorBarsType", static_cast<int>(ErrorBarsType::Simple));
146  d->errorBarsCapSize = group.readEntry( "ErrorBarsCapSize", Worksheet::convertToSceneUnits(10, Worksheet::Unit::Point) );
147  d->errorBarsPen.setStyle( (Qt::PenStyle)group.readEntry("ErrorBarsStyle", (int)Qt::SolidLine) );
148  d->errorBarsPen.setColor( group.readEntry("ErrorBarsColor", QColor(Qt::black)) );
149  d->errorBarsPen.setWidthF( group.readEntry("ErrorBarsWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)) );
150  d->errorBarsOpacity = group.readEntry("ErrorBarsOpacity", 1.0);
151 }
152 
154  visibilityAction = new QAction(QIcon::fromTheme("view-visible"), i18n("Visible"), this);
155  visibilityAction->setCheckable(true);
156  connect(visibilityAction, SIGNAL(triggered(bool)), this, SLOT(visibilityChanged()));
157 
158  navigateToAction = new QAction(QIcon::fromTheme("go-next-view"), QString(), this);
159  connect(navigateToAction, SIGNAL(triggered(bool)), this, SLOT(navigateTo()));
160 
161  m_menusInitialized = true;
162 }
163 
165  if (!m_menusInitialized)
166  initActions();
167 
168  QMenu* menu = WorksheetElement::createContextMenu();
169  QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action"
170  visibilityAction->setChecked(isVisible());
171  menu->insertAction(firstAction, visibilityAction);
172 
173  //"data analysis" menu
174  auto* plot = static_cast<CartesianPlot*>(parentAspect());
175  menu->insertMenu(visibilityAction, plot->analysisMenu());
176  menu->insertSeparator(visibilityAction);
177 
178  //"Navigate to spreadsheet"-action, show only if x- or y-columns have data from a spreadsheet
179  AbstractAspect* parentSpreadsheet = nullptr;
180  if (xColumn() && dynamic_cast<Spreadsheet*>(xColumn()->parentAspect()) )
181  parentSpreadsheet = xColumn()->parentAspect();
182  else if (yColumn() && dynamic_cast<Spreadsheet*>(yColumn()->parentAspect()) )
183  parentSpreadsheet = yColumn()->parentAspect();
184 
185  if (parentSpreadsheet) {
186  navigateToAction->setText(i18n("Navigate to \"%1\"", parentSpreadsheet->name()));
187  navigateToAction->setData(parentSpreadsheet->path());
188  menu->insertAction(visibilityAction, navigateToAction);
189  menu->insertSeparator(visibilityAction);
190  }
191 
192  //if the context menu is called on an item that is not selected yet, select it
193  if (!graphicsItem()->isSelected())
194  graphicsItem()->setSelected(true);
195 
196  return menu;
197 }
198 
199 /*!
200  Returns an icon to be used in the project explorer.
201 */
202 QIcon XYCurve::icon() const {
203  return QIcon::fromTheme("labplot-xy-curve");
204 }
205 
206 QGraphicsItem* XYCurve::graphicsItem() const {
207  return d_ptr;
208 }
209 
210 STD_SWAP_METHOD_SETTER_CMD_IMPL(XYCurve, SetVisible, bool, swapVisible)
211 void XYCurve::setVisible(bool on) {
212  Q_D(XYCurve);
213  exec(new XYCurveSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible")));
214 }
215 
216 bool XYCurve::isVisible() const {
217  Q_D(const XYCurve);
218  return d->isVisible();
219 }
220 
221 void XYCurve::setPrinting(bool on) {
222  Q_D(XYCurve);
223  d->setPrinting(on);
224 }
225 
226 /*!
227  * \brief XYCurve::activateCurve
228  * Checks if the mousepos distance to the curve is less than @p maxDist
229  * \p mouseScenePos
230  * \p maxDist Maximum distance the point lies away from the curve
231  * \return Returns true if the distance is smaller than maxDist.
232  */
233 bool XYCurve::activateCurve(QPointF mouseScenePos, double maxDist) {
234  Q_D(XYCurve);
235  return d->activateCurve(mouseScenePos, maxDist);
236 }
237 
238 /*!
239  * \brief XYCurve::setHover
240  * Will be called in CartesianPlot::hoverMoveEvent()
241  * See d->setHover(on) for more documentation
242  * \p on
243  */
244 void XYCurve::setHover(bool on) {
245  Q_D(XYCurve);
246  d->setHover(on);
247 }
248 
249 //##############################################################################
250 //########################## getter methods ##################################
251 //##############################################################################
252 
253 //data source
254 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xColumn, xColumn)
255 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yColumn, yColumn)
256 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, xColumnPath, xColumnPath)
257 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, yColumnPath, yColumnPath)
258 
259 //line
261 BASIC_SHARED_D_READER_IMPL(XYCurve, bool, lineSkipGaps, lineSkipGaps)
262 BASIC_SHARED_D_READER_IMPL(XYCurve, bool, lineIncreasingXOnly, lineIncreasingXOnly)
263 BASIC_SHARED_D_READER_IMPL(XYCurve, int, lineInterpolationPointsCount, lineInterpolationPointsCount)
264 CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, linePen, linePen)
265 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, lineOpacity, lineOpacity)
266 
267 //droplines
268 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::DropLineType, dropLineType, dropLineType)
269 CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, dropLinePen, dropLinePen)
270 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, dropLineOpacity, dropLineOpacity)
271 
272 //symbols
273 BASIC_SHARED_D_READER_IMPL(XYCurve, Symbol::Style, symbolsStyle, symbolsStyle)
274 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsOpacity, symbolsOpacity)
275 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsRotationAngle, symbolsRotationAngle)
276 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsSize, symbolsSize)
277 CLASS_SHARED_D_READER_IMPL(XYCurve, QBrush, symbolsBrush, symbolsBrush)
278 CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, symbolsPen, symbolsPen)
279 
280 //values
282 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn *, valuesColumn, valuesColumn)
283 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesColumnPath, valuesColumnPath)
284 
285 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ValuesPosition, valuesPosition, valuesPosition)
286 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesDistance, valuesDistance)
287 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesRotationAngle, valuesRotationAngle)
288 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesOpacity, valuesOpacity)
289 CLASS_SHARED_D_READER_IMPL(XYCurve, char, valuesNumericFormat, valuesNumericFormat)
290 BASIC_SHARED_D_READER_IMPL(XYCurve, int, valuesPrecision, valuesPrecision)
291 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesDateTimeFormat, valuesDateTimeFormat)
292 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesPrefix, valuesPrefix)
293 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesSuffix, valuesSuffix)
294 CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, valuesColor, valuesColor)
295 CLASS_SHARED_D_READER_IMPL(XYCurve, QFont, valuesFont, valuesFont)
296 
297 //filling
298 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::FillingPosition, fillingPosition, fillingPosition)
300 BASIC_SHARED_D_READER_IMPL(XYCurve, PlotArea::BackgroundColorStyle, fillingColorStyle, fillingColorStyle)
301 BASIC_SHARED_D_READER_IMPL(XYCurve, PlotArea::BackgroundImageStyle, fillingImageStyle, fillingImageStyle)
302 CLASS_SHARED_D_READER_IMPL(XYCurve, Qt::BrushStyle, fillingBrushStyle, fillingBrushStyle)
303 CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, fillingFirstColor, fillingFirstColor)
304 CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, fillingSecondColor, fillingSecondColor)
305 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, fillingFileName, fillingFileName)
306 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, fillingOpacity, fillingOpacity)
307 
308 //error bars
309 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorType, xErrorType, xErrorType)
310 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xErrorPlusColumn, xErrorPlusColumn)
311 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xErrorMinusColumn, xErrorMinusColumn)
312 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorType, yErrorType, yErrorType)
313 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yErrorPlusColumn, yErrorPlusColumn)
314 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yErrorMinusColumn, yErrorMinusColumn)
315 
316 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, xErrorPlusColumnPath, xErrorPlusColumnPath)
317 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, xErrorMinusColumnPath, xErrorMinusColumnPath)
318 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, yErrorPlusColumnPath, yErrorPlusColumnPath)
319 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, yErrorMinusColumnPath, yErrorMinusColumnPath)
320 
321 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorBarsType, errorBarsType, errorBarsType)
322 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, errorBarsCapSize, errorBarsCapSize)
323 CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, errorBarsPen, errorBarsPen)
324 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, errorBarsOpacity, errorBarsOpacity)
325 
326 /*!
327  * return \c true if the data in the source columns (x, y) used in the analysis curves, \c false otherwise
328  */
329 bool XYCurve::isSourceDataChangedSinceLastRecalc() const {
330  Q_D(const XYCurve);
331  return d->sourceDataChangedSinceLastRecalc;
332 }
333 
334 //##############################################################################
335 //################# setter methods and undo commands ##########################
336 //##############################################################################
337 
338 // 1) add XYCurveSetXColumnCmd as friend class to XYCurve
339 // 2) add XYCURVE_COLUMN_CONNECT(x) as private method to XYCurve
340 // 3) define all missing slots
341 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(X, x, recalcLogicalPoints)
342 void XYCurve::setXColumn(const AbstractColumn* column) {
343  Q_D(XYCurve);
344  if (column != d->xColumn)
345  exec(new XYCurveSetXColumnCmd(d, column, ki18n("%1: x-data source changed")));
346 }
347 
348 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(Y, y, recalcLogicalPoints)
349 void XYCurve::setYColumn(const AbstractColumn* column) {
350  Q_D(XYCurve);
351  if (column != d->yColumn)
352  exec(new XYCurveSetYColumnCmd(d, column, ki18n("%1: y-data source changed")));
353 }
354 
355 void XYCurve::setXColumnPath(const QString& path) {
356  Q_D(XYCurve);
357  d->xColumnPath = path;
358 }
359 
360 void XYCurve::setYColumnPath(const QString& path) {
361  Q_D(XYCurve);
362  d->yColumnPath = path;
363 }
364 
365 //Line
366 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineType, XYCurve::LineType, lineType, updateLines)
367 void XYCurve::setLineType(LineType type) {
368  Q_D(XYCurve);
369  if (type != d->lineType)
370  exec(new XYCurveSetLineTypeCmd(d, type, ki18n("%1: line type changed")));
371 }
372 
373 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineSkipGaps, bool, lineSkipGaps, updateLines)
374 void XYCurve::setLineSkipGaps(bool skip) {
375  Q_D(XYCurve);
376  if (skip != d->lineSkipGaps)
377  exec(new XYCurveSetLineSkipGapsCmd(d, skip, ki18n("%1: set skip line gaps")));
378 }
379 
380 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineIncreasingXOnly, bool, lineIncreasingXOnly, updateLines)
381 void XYCurve::setLineIncreasingXOnly(bool incr) {
382  Q_D(XYCurve);
383  if (incr != d->lineIncreasingXOnly)
384  exec(new XYCurveSetLineIncreasingXOnlyCmd(d, incr, ki18n("%1: set increasing X")));
385 }
386 
387 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineInterpolationPointsCount, int, lineInterpolationPointsCount, updateLines)
388 void XYCurve::setLineInterpolationPointsCount(int count) {
389  Q_D(XYCurve);
390  if (count != d->lineInterpolationPointsCount)
391  exec(new XYCurveSetLineInterpolationPointsCountCmd(d, count, ki18n("%1: set the number of interpolation points")));
392 }
393 
394 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLinePen, QPen, linePen, recalcShapeAndBoundingRect)
395 void XYCurve::setLinePen(const QPen &pen) {
396  Q_D(XYCurve);
397  if (pen != d->linePen)
398  exec(new XYCurveSetLinePenCmd(d, pen, ki18n("%1: set line style")));
399 }
400 
401 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineOpacity, qreal, lineOpacity, updatePixmap);
402 void XYCurve::setLineOpacity(qreal opacity) {
403  Q_D(XYCurve);
404  if (opacity != d->lineOpacity)
405  exec(new XYCurveSetLineOpacityCmd(d, opacity, ki18n("%1: set line opacity")));
406 }
407 
408 //Drop lines
409 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLineType, XYCurve::DropLineType, dropLineType, updateDropLines)
410 void XYCurve::setDropLineType(DropLineType type) {
411  Q_D(XYCurve);
412  if (type != d->dropLineType)
413  exec(new XYCurveSetDropLineTypeCmd(d, type, ki18n("%1: drop line type changed")));
414 }
415 
416 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLinePen, QPen, dropLinePen, recalcShapeAndBoundingRect)
417 void XYCurve::setDropLinePen(const QPen &pen) {
418  Q_D(XYCurve);
419  if (pen != d->dropLinePen)
420  exec(new XYCurveSetDropLinePenCmd(d, pen, ki18n("%1: set drop line style")));
421 }
422 
423 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLineOpacity, qreal, dropLineOpacity, updatePixmap)
424 void XYCurve::setDropLineOpacity(qreal opacity) {
425  Q_D(XYCurve);
426  if (opacity != d->dropLineOpacity)
427  exec(new XYCurveSetDropLineOpacityCmd(d, opacity, ki18n("%1: set drop line opacity")));
428 }
429 
430 // Symbols-Tab
431 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsStyle, Symbol::Style, symbolsStyle, retransform)
432 void XYCurve::setSymbolsStyle(Symbol::Style style) {
433  Q_D(XYCurve);
434  if (style != d->symbolsStyle)
435  exec(new XYCurveSetSymbolsStyleCmd(d, style, ki18n("%1: set symbol style")));
436 }
437 
438 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsSize, qreal, symbolsSize, updateSymbols)
439 void XYCurve::setSymbolsSize(qreal size) {
440  Q_D(XYCurve);
441  if (!qFuzzyCompare(1 + size, 1 + d->symbolsSize))
442  exec(new XYCurveSetSymbolsSizeCmd(d, size, ki18n("%1: set symbol size")));
443 }
444 
445 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsRotationAngle, qreal, symbolsRotationAngle, updateSymbols)
446 void XYCurve::setSymbolsRotationAngle(qreal angle) {
447  Q_D(XYCurve);
448  if (!qFuzzyCompare(1 + angle, 1 + d->symbolsRotationAngle))
449  exec(new XYCurveSetSymbolsRotationAngleCmd(d, angle, ki18n("%1: rotate symbols")));
450 }
451 
452 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsBrush, QBrush, symbolsBrush, updatePixmap)
453 void XYCurve::setSymbolsBrush(const QBrush &brush) {
454  Q_D(XYCurve);
455  if (brush != d->symbolsBrush)
456  exec(new XYCurveSetSymbolsBrushCmd(d, brush, ki18n("%1: set symbol filling")));
457 }
458 
459 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsPen, QPen, symbolsPen, updateSymbols)
460 void XYCurve::setSymbolsPen(const QPen &pen) {
461  Q_D(XYCurve);
462  if (pen != d->symbolsPen)
463  exec(new XYCurveSetSymbolsPenCmd(d, pen, ki18n("%1: set symbol outline style")));
464 }
465 
466 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsOpacity, qreal, symbolsOpacity, updatePixmap)
467 void XYCurve::setSymbolsOpacity(qreal opacity) {
468  Q_D(XYCurve);
469  if (opacity != d->symbolsOpacity)
470  exec(new XYCurveSetSymbolsOpacityCmd(d, opacity, ki18n("%1: set symbols opacity")));
471 }
472 
473 //Values-Tab
474 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesType, XYCurve::ValuesType, valuesType, updateValues)
475 void XYCurve::setValuesType(XYCurve::ValuesType type) {
476  Q_D(XYCurve);
477  if (type != d->valuesType)
478  exec(new XYCurveSetValuesTypeCmd(d, type, ki18n("%1: set values type")));
479 }
480 
481 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(Values, values, updateValues)
482 void XYCurve::setValuesColumn(const AbstractColumn* column) {
483  Q_D(XYCurve);
484  if (column != d->valuesColumn) {
485  exec(new XYCurveSetValuesColumnCmd(d, column, ki18n("%1: set values column")));
486  if (column)
487  connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(updateValues()));
488  }
489 }
490 
491 void XYCurve::setValuesColumnPath(const QString& path) {
492  Q_D(XYCurve);
493  d->valuesColumnPath = path;
494 }
495 
496 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesPosition, XYCurve::ValuesPosition, valuesPosition, updateValues)
497 void XYCurve::setValuesPosition(ValuesPosition position) {
498  Q_D(XYCurve);
499  if (position != d->valuesPosition)
500  exec(new XYCurveSetValuesPositionCmd(d, position, ki18n("%1: set values position")));
501 }
502 
503 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesDistance, qreal, valuesDistance, updateValues)
504 void XYCurve::setValuesDistance(qreal distance) {
505  Q_D(XYCurve);
506  if (distance != d->valuesDistance)
507  exec(new XYCurveSetValuesDistanceCmd(d, distance, ki18n("%1: set values distance")));
508 }
509 
510 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesRotationAngle, qreal, valuesRotationAngle, updateValues)
511 void XYCurve::setValuesRotationAngle(qreal angle) {
512  Q_D(XYCurve);
513  if (!qFuzzyCompare(1 + angle, 1 + d->valuesRotationAngle))
514  exec(new XYCurveSetValuesRotationAngleCmd(d, angle, ki18n("%1: rotate values")));
515 }
516 
517 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesOpacity, qreal, valuesOpacity, updatePixmap)
518 void XYCurve::setValuesOpacity(qreal opacity) {
519  Q_D(XYCurve);
520  if (opacity != d->valuesOpacity)
521  exec(new XYCurveSetValuesOpacityCmd(d, opacity, ki18n("%1: set values opacity")));
522 }
523 
524 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesNumericFormat, char, valuesNumericFormat, updateValues)
525 void XYCurve::setValuesNumericFormat(char format) {
526  Q_D(XYCurve);
527  if (format != d->valuesNumericFormat)
528  exec(new XYCurveSetValuesNumericFormatCmd(d, format, ki18n("%1: set values numeric format")));
529 }
530 
531 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesPrecision, int, valuesPrecision, updateValues)
532 void XYCurve::setValuesPrecision(int precision) {
533  Q_D(XYCurve);
534  if (precision != d->valuesPrecision)
535  exec(new XYCurveSetValuesPrecisionCmd(d, precision, ki18n("%1: set values precision")));
536 }
537 
538 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesDateTimeFormat, QString, valuesDateTimeFormat, updateValues)
539 void XYCurve::setValuesDateTimeFormat(const QString& format) {
540  Q_D(XYCurve);
541  if (format != d->valuesDateTimeFormat)
542  exec(new XYCurveSetValuesDateTimeFormatCmd(d, format, ki18n("%1: set values datetime format")));
543 }
544 
545 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesPrefix, QString, valuesPrefix, updateValues)
546 void XYCurve::setValuesPrefix(const QString& prefix) {
547  Q_D(XYCurve);
548  if (prefix != d->valuesPrefix)
549  exec(new XYCurveSetValuesPrefixCmd(d, prefix, ki18n("%1: set values prefix")));
550 }
551 
552 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesSuffix, QString, valuesSuffix, updateValues)
553 void XYCurve::setValuesSuffix(const QString& suffix) {
554  Q_D(XYCurve);
555  if (suffix != d->valuesSuffix)
556  exec(new XYCurveSetValuesSuffixCmd(d, suffix, ki18n("%1: set values suffix")));
557 }
558 
559 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesFont, QFont, valuesFont, updateValues)
560 void XYCurve::setValuesFont(const QFont& font) {
561  Q_D(XYCurve);
562  if (font != d->valuesFont)
563  exec(new XYCurveSetValuesFontCmd(d, font, ki18n("%1: set values font")));
564 }
565 
566 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesColor, QColor, valuesColor, updatePixmap)
567 void XYCurve::setValuesColor(const QColor& color) {
568  Q_D(XYCurve);
569  if (color != d->valuesColor)
570  exec(new XYCurveSetValuesColorCmd(d, color, ki18n("%1: set values color")));
571 }
572 
573 //Filling
574 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingPosition, XYCurve::FillingPosition, fillingPosition, updateFilling)
575 void XYCurve::setFillingPosition(FillingPosition position) {
576  Q_D(XYCurve);
577  if (position != d->fillingPosition)
578  exec(new XYCurveSetFillingPositionCmd(d, position, ki18n("%1: filling position changed")));
579 }
580 
581 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingType, PlotArea::BackgroundType, fillingType, updatePixmap)
582 void XYCurve::setFillingType(PlotArea::BackgroundType type) {
583  Q_D(XYCurve);
584  if (type != d->fillingType)
585  exec(new XYCurveSetFillingTypeCmd(d, type, ki18n("%1: filling type changed")));
586 }
587 
588 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingColorStyle, PlotArea::BackgroundColorStyle, fillingColorStyle, updatePixmap)
589 void XYCurve::setFillingColorStyle(PlotArea::BackgroundColorStyle style) {
590  Q_D(XYCurve);
591  if (style != d->fillingColorStyle)
592  exec(new XYCurveSetFillingColorStyleCmd(d, style, ki18n("%1: filling color style changed")));
593 }
594 
595 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingImageStyle, PlotArea::BackgroundImageStyle, fillingImageStyle, updatePixmap)
596 void XYCurve::setFillingImageStyle(PlotArea::BackgroundImageStyle style) {
597  Q_D(XYCurve);
598  if (style != d->fillingImageStyle)
599  exec(new XYCurveSetFillingImageStyleCmd(d, style, ki18n("%1: filling image style changed")));
600 }
601 
602 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingBrushStyle, Qt::BrushStyle, fillingBrushStyle, updatePixmap)
603 void XYCurve::setFillingBrushStyle(Qt::BrushStyle style) {
604  Q_D(XYCurve);
605  if (style != d->fillingBrushStyle)
606  exec(new XYCurveSetFillingBrushStyleCmd(d, style, ki18n("%1: filling brush style changed")));
607 }
608 
609 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingFirstColor, QColor, fillingFirstColor, updatePixmap)
610 void XYCurve::setFillingFirstColor(const QColor& color) {
611  Q_D(XYCurve);
612  if (color != d->fillingFirstColor)
613  exec(new XYCurveSetFillingFirstColorCmd(d, color, ki18n("%1: set filling first color")));
614 }
615 
616 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingSecondColor, QColor, fillingSecondColor, updatePixmap)
617 void XYCurve::setFillingSecondColor(const QColor& color) {
618  Q_D(XYCurve);
619  if (color != d->fillingSecondColor)
620  exec(new XYCurveSetFillingSecondColorCmd(d, color, ki18n("%1: set filling second color")));
621 }
622 
623 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingFileName, QString, fillingFileName, updatePixmap)
624 void XYCurve::setFillingFileName(const QString& fileName) {
625  Q_D(XYCurve);
626  if (fileName != d->fillingFileName)
627  exec(new XYCurveSetFillingFileNameCmd(d, fileName, ki18n("%1: set filling image")));
628 }
629 
630 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingOpacity, qreal, fillingOpacity, updatePixmap)
631 void XYCurve::setFillingOpacity(qreal opacity) {
632  Q_D(XYCurve);
633  if (opacity != d->fillingOpacity)
634  exec(new XYCurveSetFillingOpacityCmd(d, opacity, ki18n("%1: set filling opacity")));
635 }
636 
637 //Error bars
638 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetXErrorType, XYCurve::ErrorType, xErrorType, updateErrorBars)
639 void XYCurve::setXErrorType(ErrorType type) {
640  Q_D(XYCurve);
641  if (type != d->xErrorType)
642  exec(new XYCurveSetXErrorTypeCmd(d, type, ki18n("%1: x-error type changed")));
643 }
644 
645 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(XErrorPlus, xErrorPlus, updateErrorBars)
646 void XYCurve::setXErrorPlusColumn(const AbstractColumn* column) {
647  Q_D(XYCurve);
648  if (column != d->xErrorPlusColumn) {
649  exec(new XYCurveSetXErrorPlusColumnCmd(d, column, ki18n("%1: set x-error column")));
650  if (column) {
651  connect(column, &AbstractColumn::dataChanged, this, &XYCurve::updateErrorBars);
652  //in the macro we connect to recalcLogicalPoints which is not needed for error columns
653  disconnect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints);
654  }
655  }
656 }
657 
658 void XYCurve::setXErrorPlusColumnPath(const QString& path) {
659  Q_D(XYCurve);
660  d->xErrorPlusColumnPath = path;
661 }
662 
663 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(XErrorMinus, xErrorMinus, updateErrorBars)
664 void XYCurve::setXErrorMinusColumn(const AbstractColumn* column) {
665  Q_D(XYCurve);
666  if (column != d->xErrorMinusColumn) {
667  exec(new XYCurveSetXErrorMinusColumnCmd(d, column, ki18n("%1: set x-error column")));
668  if (column) {
669  connect(column, &AbstractColumn::dataChanged, this, &XYCurve::updateErrorBars);
670  //in the macro we connect to recalcLogicalPoints which is not needed for error columns
671  disconnect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints);
672  }
673  }
674 }
675 
676 void XYCurve::setXErrorMinusColumnPath(const QString& path) {
677  Q_D(XYCurve);
678  d->xErrorMinusColumnPath = path;
679 }
680 
681 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetYErrorType, XYCurve::ErrorType, yErrorType, updateErrorBars)
682 void XYCurve::setYErrorType(ErrorType type) {
683  Q_D(XYCurve);
684  if (type != d->yErrorType)
685  exec(new XYCurveSetYErrorTypeCmd(d, type, ki18n("%1: y-error type changed")));
686 }
687 
688 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(YErrorPlus, yErrorPlus, updateErrorBars)
689 void XYCurve::setYErrorPlusColumn(const AbstractColumn* column) {
690  Q_D(XYCurve);
691  if (column != d->yErrorPlusColumn) {
692  exec(new XYCurveSetYErrorPlusColumnCmd(d, column, ki18n("%1: set y-error column")));
693  if (column) {
694  connect(column, &AbstractColumn::dataChanged, this, &XYCurve::updateErrorBars);
695  //in the macro we connect to recalcLogicalPoints which is not needed for error columns
696  disconnect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints);
697  }
698  }
699 }
700 
701 void XYCurve::setYErrorPlusColumnPath(const QString& path) {
702  Q_D(XYCurve);
703  d->yErrorPlusColumnPath = path;
704 }
705 
706 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(YErrorMinus, yErrorMinus, updateErrorBars)
707 void XYCurve::setYErrorMinusColumn(const AbstractColumn* column) {
708  Q_D(XYCurve);
709  if (column != d->yErrorMinusColumn) {
710  exec(new XYCurveSetYErrorMinusColumnCmd(d, column, ki18n("%1: set y-error column")));
711  if (column) {
712  connect(column, &AbstractColumn::dataChanged, this, &XYCurve::updateErrorBars);
713  //in the macro we connect to recalcLogicalPoints which is not needed for error columns
714  disconnect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints);
715  }
716  }
717 }
718 
719 void XYCurve::setYErrorMinusColumnPath(const QString& path) {
720  Q_D(XYCurve);
721  d->yErrorMinusColumnPath = path;
722 }
723 
724 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsCapSize, qreal, errorBarsCapSize, updateErrorBars)
725 void XYCurve::setErrorBarsCapSize(qreal size) {
726  Q_D(XYCurve);
727  if (size != d->errorBarsCapSize)
728  exec(new XYCurveSetErrorBarsCapSizeCmd(d, size, ki18n("%1: set error bar cap size")));
729 }
730 
731 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsType, XYCurve::ErrorBarsType, errorBarsType, updateErrorBars)
732 void XYCurve::setErrorBarsType(ErrorBarsType type) {
733  Q_D(XYCurve);
734  if (type != d->errorBarsType)
735  exec(new XYCurveSetErrorBarsTypeCmd(d, type, ki18n("%1: error bar type changed")));
736 }
737 
738 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsPen, QPen, errorBarsPen, recalcShapeAndBoundingRect)
739 void XYCurve::setErrorBarsPen(const QPen& pen) {
740  Q_D(XYCurve);
741  if (pen != d->errorBarsPen)
742  exec(new XYCurveSetErrorBarsPenCmd(d, pen, ki18n("%1: set error bar style")));
743 }
744 
745 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsOpacity, qreal, errorBarsOpacity, updatePixmap)
746 void XYCurve::setErrorBarsOpacity(qreal opacity) {
747  Q_D(XYCurve);
748  if (opacity != d->errorBarsOpacity)
749  exec(new XYCurveSetErrorBarsOpacityCmd(d, opacity, ki18n("%1: set error bar opacity")));
750 }
751 
753  Q_D(XYCurve);
754  d->suppressRetransform(b);
755 }
756 
757 //##############################################################################
758 //################################# SLOTS ####################################
759 //##############################################################################
761  Q_D(XYCurve);
762  d->retransform();
763 }
764 
766  Q_D(XYCurve);
767  d->recalcLogicalPoints();
768 }
769 
771  Q_D(XYCurve);
772  d->updateValues();
773 }
774 
776  Q_D(XYCurve);
777  d->updateErrorBars();
778 }
779 
780 //TODO
781 void XYCurve::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) {
782  Q_UNUSED(pageResize);
783  Q_D(const XYCurve);
784 
785  setSymbolsSize(d->symbolsSize * horizontalRatio);
786 
787  QPen pen = d->symbolsPen;
788  pen.setWidthF(pen.widthF() * (horizontalRatio + verticalRatio) / 2.);
789  setSymbolsPen(pen);
790 
791  pen = d->linePen;
792  pen.setWidthF(pen.widthF() * (horizontalRatio + verticalRatio) / 2.);
793  setLinePen(pen);
794 
795  //setValuesDistance(d->distance*);
796  QFont font = d->valuesFont;
797  font.setPointSizeF(font.pointSizeF()*horizontalRatio);
798  setValuesFont(font);
799 }
800 
802  Q_D(XYCurve);
803  if (aspect == d->xColumn) {
804  disconnect(aspect, nullptr, this, nullptr);
805  d->xColumn = nullptr;
806  d->retransform();
807  }
808 }
809 
811  Q_D(XYCurve);
812  if (aspect == d->yColumn) {
813  disconnect(aspect, nullptr, this, nullptr);
814  d->yColumn = nullptr;
815  d->retransform();
816  }
817 }
818 
820  Q_D(XYCurve);
821  if (aspect == d->valuesColumn) {
822  disconnect(aspect, nullptr, this, nullptr);
823  d->valuesColumn = nullptr;
824  d->updateValues();
825  }
826 }
827 
829  Q_D(XYCurve);
830  if (aspect == d->xErrorPlusColumn) {
831  disconnect(aspect, nullptr, this, nullptr);
832  d->xErrorPlusColumn = nullptr;
833  d->updateErrorBars();
834  }
835 }
836 
838  Q_D(XYCurve);
839  if (aspect == d->xErrorMinusColumn) {
840  disconnect(aspect, nullptr, this, nullptr);
841  d->xErrorMinusColumn = nullptr;
842  d->updateErrorBars();
843  }
844 }
845 
847  Q_D(XYCurve);
848  if (aspect == d->yErrorPlusColumn) {
849  disconnect(aspect, nullptr, this, nullptr);
850  d->yErrorPlusColumn = nullptr;
851  d->updateErrorBars();
852  }
853 }
854 
856  Q_D(XYCurve);
857  if (aspect == d->yErrorMinusColumn) {
858  disconnect(aspect, nullptr, this, nullptr);
859  d->yErrorMinusColumn = nullptr;
860  d->updateErrorBars();
861  }
862 }
863 
865  Q_D(XYCurve);
866  setXColumnPath(d->xColumn->path());
867 }
868 
870  Q_D(XYCurve);
871  setYColumnPath(d->yColumn->path());
872 }
873 
875  Q_D(XYCurve);
876  setXErrorPlusColumnPath(d->xErrorPlusColumn->path());
877 }
878 
880  Q_D(XYCurve);
881  setXErrorMinusColumnPath(d->xErrorMinusColumn->path());
882 }
883 
885  Q_D(XYCurve);
886  setYErrorPlusColumnPath(d->yErrorPlusColumn->path());
887 }
888 
890  Q_D(XYCurve);
891  setYErrorMinusColumnPath(d->yErrorMinusColumn->path());
892 }
893 
895  Q_D(XYCurve);
896  setValuesColumnPath(d->valuesColumn->path());
897 }
898 
899 //##############################################################################
900 //###### SLOTs for changes triggered via QActions in the context menu ########
901 //##############################################################################
903  Q_D(const XYCurve);
904  this->setVisible(!d->isVisible());
905 }
906 
908  project()->navigateTo(navigateToAction->data().toString());
909 }
910 
911 //##############################################################################
912 //######################### Private implementation #############################
913 //##############################################################################
915  setFlag(QGraphicsItem::ItemIsSelectable, true);
916  setAcceptHoverEvents(false);
917 }
918 
919 QString XYCurvePrivate::name() const {
920  return q->name();
921 }
922 
924  return boundingRectangle;
925 }
926 
927 /*!
928  Returns the shape of the XYCurve as a QPainterPath in local coordinates
929 */
930 QPainterPath XYCurvePrivate::shape() const {
931  return curveShape;
932 }
933 
934 void XYCurvePrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
935  if (q->activateCurve(event->pos())) {
936  q->createContextMenu()->exec(event->screenPos());
937  return;
938  }
939  QGraphicsItem::contextMenuEvent(event);
940 }
941 
943  bool oldValue = isVisible();
944 
945  //When making a graphics item invisible, it gets deselected in the scene.
946  //In this case we don't want to deselect the item in the project explorer.
947  //We need to supress the deselection in the view.
948  auto* worksheet = static_cast<Worksheet*>(q->parent(AspectType::Worksheet));
949  worksheet->suppressSelectionChangedEvent(true);
950  setVisible(on);
951  worksheet->suppressSelectionChangedEvent(false);
952 
953  emit q->visibilityChanged(on);
954  retransform();
955  return oldValue;
956 }
957 
958 /*!
959  called when the size of the plot or its data ranges (manual changes, zooming, etc.) were changed.
960  recalculates the position of the scene points to be drawn.
961  triggers the update of lines, drop lines, symbols etc.
962 */
964  if (!isVisible())
965  return;
966 
967  DEBUG("\n" << Q_FUNC_INFO << ", name = " << STDSTRING(name()) << ", m_suppressRetransform = " << m_suppressRetransform);
968  if (m_suppressRetransform || !plot)
969  return;
970 
971  {
972 #ifdef PERFTRACE_CURVES
973  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform()");
974 #endif
975 
976  m_scenePoints.clear();
977 
978  if (!xColumn || !yColumn) {
979  DEBUG(Q_FUNC_INFO << ", xColumn or yColumn not available");
980  linePath = QPainterPath();
981  dropLinePath = QPainterPath();
982  symbolsPath = QPainterPath();
983  valuesPath = QPainterPath();
984  errorBarsPath = QPainterPath();
985  curveShape = QPainterPath();
986  m_lines.clear();
987  m_valuePoints.clear();
988  m_valueStrings.clear();
989  m_fillPolygons.clear();
991  return;
992  }
993 
994  if (!plot->isPanningActive())
995  WAIT_CURSOR;
996 
997  //calculate the scene coordinates
998  // This condition cannot be used, because m_logicalPoints is also used in updateErrorBars(), updateDropLines() and in updateFilling()
999  // TODO: check updateErrorBars() and updateDropLines() and if they aren't available don't calculate this part
1000  //if (symbolsStyle != Symbol::Style::NoSymbols || valuesType != XYCurve::NoValues ) {
1001  {
1002 #ifdef PERFTRACE_CURVES
1003  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform(), map logical points to scene coordinates");
1004 #endif
1005 
1006  const int numberOfPoints = m_logicalPoints.size();
1007  DEBUG(Q_FUNC_INFO << ", number of logical points = " << numberOfPoints)
1008  if (numberOfPoints > 0) {
1009  // this is the old method considering DPI
1010  DEBUG(Q_FUNC_INFO << ", plot->dataRect() width/height = " << plot->dataRect().width() << '/' << plot->dataRect().height());
1011  //const double widthDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().width(), Worksheet::Unit::Inch);
1012  //const double heightDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().height(), Worksheet::Unit::Inch);
1013  //DEBUG(Q_FUNC_INFO << ", widthDatarectInch/heightDatarectInch = " << widthDatarectInch << '/' << heightDatarectInch)
1014  DEBUG(Q_FUNC_INFO << ", logical DPI X/Y = " << QApplication::desktop()->logicalDpiX() << '/' << QApplication::desktop()->logicalDpiY())
1015  DEBUG(Q_FUNC_INFO << ", physical DPI X/Y = " << QApplication::desktop()->physicalDpiX() << '/' << QApplication::desktop()->physicalDpiY())
1016  //const int numberOfPixelX = ceil(widthDatarectInch * QApplication::desktop()->physicalDpiX());
1017  //const int numberOfPixelY = ceil(heightDatarectInch * QApplication::desktop()->physicalDpiY());
1018 
1019  // new method
1020  const int numberOfPixelX = plot->dataRect().width();
1021  const int numberOfPixelY = plot->dataRect().height();
1022 
1023  if (numberOfPixelX <= 0 || numberOfPixelY <= 0) {
1024  DEBUG(Q_FUNC_INFO << ", number of pixel X <= 0 or number of pixel Y <= 0!")
1025  RESET_CURSOR;
1026  return;
1027  }
1028 
1029  DEBUG(Q_FUNC_INFO << ", numberOfPixelX/numberOfPixelY = " << numberOfPixelX << '/' << numberOfPixelY)
1030  //TODO: not needed with new method
1031  const double minLogicalDiffX = plot->dataRect().width()/numberOfPixelX;
1032  const double minLogicalDiffY = plot->dataRect().height()/numberOfPixelY;
1033  DEBUG(Q_FUNC_INFO << ", -> minLogicalDiffX/Y = " << minLogicalDiffX << '/' << minLogicalDiffY)
1034 
1035  // eliminate multiple scene points (size (numberOfPixelX + 1) * (numberOfPixelY + 1))
1036  QVector<QVector<bool>> scenePointsUsed(numberOfPixelX + 1);
1037  for (auto& col: scenePointsUsed)
1038  col.resize(numberOfPixelY + 1);
1039 
1040  const auto columnProperties = xColumn->properties();
1041  int startIndex, endIndex;
1042  if (columnProperties == AbstractColumn::Properties::MonotonicDecreasing ||
1044  DEBUG(Q_FUNC_INFO << ", column monotonic")
1045  double xMin = cSystem->mapSceneToLogical(plot->dataRect().topLeft()).x();
1046  double xMax = cSystem->mapSceneToLogical(plot->dataRect().bottomRight()).x();
1047  DEBUG(Q_FUNC_INFO << ", xMin/xMax = " << xMin << '/' << xMax)
1048 
1049  startIndex = Column::indexForValue(xMin, m_logicalPoints, columnProperties);
1050  endIndex = Column::indexForValue(xMax, m_logicalPoints, columnProperties);
1051 
1052  if (startIndex > endIndex && endIndex >= 0)
1053  std::swap(startIndex, endIndex);
1054 
1055  if (startIndex < 0)
1056  startIndex = 0;
1057  if (endIndex < 0)
1058  endIndex = numberOfPoints - 1;
1059 
1060  } else {
1061  DEBUG(Q_FUNC_INFO << ", column not monotonic")
1062  startIndex = 0;
1063  endIndex = numberOfPoints - 1;
1064  }
1065 
1066  m_pointVisible.clear();
1067  m_pointVisible.resize(numberOfPoints);
1068  cSystem->mapLogicalToScene(startIndex, endIndex, m_logicalPoints, m_scenePoints,
1069  m_pointVisible, scenePointsUsed, minLogicalDiffX, minLogicalDiffY);
1070  }
1071  }
1072  //} // (symbolsStyle != Symbol::Style::NoSymbols || valuesType != XYCurve::NoValues )
1073 
1074  m_suppressRecalc = true;
1075  updateLines();
1076  updateDropLines();
1077  updateSymbols();
1078  updateValues();
1079  m_suppressRecalc = false;
1080  updateErrorBars();
1081 
1082  RESET_CURSOR;
1083  }
1084 }
1085 
1086 /*!
1087  * called if the x- or y-data was changed.
1088  * copies the valid data points from the x- and y-columns into the internal container
1089  */
1091  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::recalcLogicalPoints()");
1092 
1093  m_pointVisible.clear();
1094  m_logicalPoints.clear();
1095  connectedPointsLogical.clear();
1096  validPointsIndicesLogical.clear();
1097 
1098  if (!xColumn || !yColumn)
1099  return;
1100 
1101  auto xColMode = xColumn->columnMode();
1102  auto yColMode = yColumn->columnMode();
1103  const int rows = xColumn->rowCount();
1104  m_logicalPoints.reserve(rows);
1105 
1106  //take only valid and non masked points
1107  for (int row{0}; row < rows; row++) {
1108  if ( xColumn->isValid(row) && yColumn->isValid(row)
1109  && (!xColumn->isMasked(row)) && (!yColumn->isMasked(row)) ) {
1110  QPointF tempPoint;
1111 
1112  switch (xColMode) {
1116  tempPoint.setX(xColumn->valueAt(row));
1117  break;
1119  tempPoint.setX(xColumn->dateTimeAt(row).toMSecsSinceEpoch());
1120  break;
1124  break;
1125  }
1126 
1127  switch (yColMode) {
1131  tempPoint.setY(yColumn->valueAt(row));
1132  break;
1134  tempPoint.setY(yColumn->dateTimeAt(row).toMSecsSinceEpoch());
1135  break;
1139  break;
1140  }
1141 
1142  m_logicalPoints.append(tempPoint);
1143  //TODO: append, resize-reserve
1144  connectedPointsLogical.push_back(true);
1145  validPointsIndicesLogical.push_back(row);
1146  } else {
1147  if (!connectedPointsLogical.empty())
1148  connectedPointsLogical[connectedPointsLogical.size() - 1] = false;
1149  }
1150  }
1151 
1152  m_pointVisible.resize(m_logicalPoints.size());
1153 }
1154 
1155 /*!
1156  * Adds a line, which connects two points, but only if they don't lie on the same xAxis pixel.
1157  * If they lie on the same x pixel, draw a vertical line between the minimum and maximum y value. So all points are included
1158  * This function is only valid for linear x Axis scale!
1159  * @param p0 first point
1160  * @param p1 second point
1161  * @param overlap if at the previous call was an overlap between the previous two points
1162  * @param minLogicalDiffX logical difference between two pixels
1163  * @param pixelDiff x pixel distance between two points
1164  */
1165 void XYCurvePrivate::addLinearLine(QPointF p0, QPointF p1, QPointF& lastPoint, double minLogicalDiffX, qint64& pixelDiff) {
1166  pixelDiff = qRound64(p1.x() / minLogicalDiffX) - qRound64(p0.x() / minLogicalDiffX);
1167  //QDEBUG(Q_FUNC_INFO << ", " << p0 << " -> " << p1 << "p0.x*minLogicalDiffX =" << p0.x()*minLogicalDiffX << ", p1.x*minLogicalDiffX =" << p1.x()*minLogicalDiffX << ", pixelDiff =" << pixelDiff);
1168 
1169  addUniqueLine(p0, p1, lastPoint, pixelDiff);
1170 }
1171 
1172 /*!
1173  * Adds a line, which connects two points, but only if they don't lie on the same xAxis pixel.
1174  * If they lie on the same x pixel, draw a vertical line between the minimum and maximum y value. So all points are included
1175  * This function can be used for all axis scalings (linear, log, sqrt, ...). For the linear case use the function above, because it's optimized for the linear case
1176  * @param p0 first point
1177  * @param p1 second point
1178  * @param lastPoint remember last point in case of overlap
1179  * @param pixelDiff x pixel distance between two points
1180  * @param pixelCount pixel count
1181  */
1182 void XYCurvePrivate::addLine(QPointF p0, QPointF p1, QPointF& lastPoint, qint64& pixelDiff, int numberOfPixelX) {
1183  DEBUG(Q_FUNC_INFO)
1184 
1185  if (plot->xScale() == CartesianPlot::Scale::Linear) {
1186  double minLogicalDiffX = (plot->xMax() - plot->xMin())/numberOfPixelX;
1187  //DEBUG(" plot->xMax() - plot->xMin() = " << plot->xMax() - plot->xMin())
1188  //DEBUG(" plot->dataRect().width() = " << plot->dataRect().width())
1189  //DEBUG(" -> minLogicalDiffX = " << minLogicalDiffX)
1190  addLinearLine(p0, p1, lastPoint, minLogicalDiffX, pixelDiff);
1191  } else {
1192  // for nonlinear scaling the pixel distance must be calculated for every point pair
1195 
1196  // if the point is not valid, don't create a line
1197  //if (std::isnan(p0Scene.x()) || std::isnan(p0Scene.y()))
1198  if ((p0Scene.x() == 0 && p0Scene.y() == 0) || (p1Scene.x() == 0 && p1Scene.y() == 0)) { // not possible to create line
1199  DEBUG(Q_FUNC_INFO << ", not possible to create a line between " << p0Scene.x() << ',' << p0Scene.y() << " and "<< p1Scene.x() << ',' << p1Scene.y())
1200  return;
1201  }
1202 
1203  // using only the difference between the points is not sufficient, because p0 is updated always
1204  // if new line is added or not
1205  qint64 p0Pixel = qRound64((p0Scene.x() - plot->dataRect().x()) / (double)plot->dataRect().width() * numberOfPixelX);
1206  qint64 p1Pixel = qRound64((p1Scene.x() - plot->dataRect().x()) / (double)plot->dataRect().width() * numberOfPixelX);
1207  //DEBUG(Q_FUNC_INFO << ", p0Pixel/p1Pixel = " << p0Pixel << ' ' << p1Pixel)
1208  pixelDiff = p1Pixel - p0Pixel;
1209  addUniqueLine(p0, p1, lastPoint, pixelDiff);
1210  }
1211 }
1212 
1213 /*!
1214  * \brief XYCurvePrivate::addUniqueLine
1215  * This function is called from the other two addLine() functions to avoid duplication
1216  * @param p0 first point
1217  * @param p1 second point
1218  * @param lastPoint remember last point in case of overlap
1219  * @param pixelDiff x pixel distance between two points
1220  */
1221 void XYCurvePrivate::addUniqueLine(QPointF p0, QPointF p1, QPointF& lastPoint, qint64& pixelDiff) {
1222  //QDEBUG(Q_FUNC_INFO << " :" << p0 << " ->" << p1 << ", lastPoint =" << lastPoint << ", pixelDiff =" << pixelDiff)
1223  if (pixelDiff == 0) {
1224  //QDEBUG(" pixelDiff == 0!")
1225  if (isnan(lastPoint.x())) // save last point
1226  lastPoint = p0;
1227  } else { // pixelDiff > 0
1228  //QDEBUG(" pixelDiff =" << pixelDiff << ", last point : " << lastPoint)
1229  if (!isnan(lastPoint.x())) { // when previously lastPoint, draw a line
1230  //QDEBUG(" REDUCED LINE from " << lastPoint << " to " << p0)
1231  //TODO: only when line in scene
1232  //if ((p0.x() >= plot->xMin() && p0.x() <= plot->xMax()) || (p1.x() >= plot->xMin() && p1.x() <= plot->xMax()))
1233  // || (p0.x() < plot->xMin() && p1.x() > plot->xMax()) || (p0.x() > plot->xMax() && p1.x() < plot->xMin())
1234  // same for y
1235  m_lines.append(QLineF(lastPoint, p0));
1236 
1237  lastPoint.setX(NAN);
1238  }
1239 
1240  //QDEBUG(" LINE " << p0 << ' ' << p1)
1241  //TODO only when line in scene (s.a.)
1242  m_lines.append(QLineF(p0, p1));
1243  }
1244 }
1245 
1246 /*!
1247  recalculates the painter path for the lines connecting the data points.
1248  Called each time when the type of this connection is changed.
1249 TODO: At the moment also the points which are outside of the scene are added. This algorithm can be improved by omitting lines
1250  lines not visible in plot
1251 */
1253 #ifdef PERFTRACE_CURVES
1254  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines()");
1255 #endif
1256  linePath = QPainterPath();
1257  m_lines.clear();
1259  DEBUG(Q_FUNC_INFO << ", nothing to do, since line type is XYCurve::LineType::NoLine");
1260  updateFilling();
1262  return;
1263  }
1264 
1265  int numberOfPoints{m_logicalPoints.size()};
1266  if (numberOfPoints <= 1) {
1267  DEBUG(Q_FUNC_INFO << ", nothing to do, since not enough data points available");
1269  return;
1270  }
1271 
1272  const QRectF pageRect = plot->dataRect();
1273  // old method using DPI
1274  //const double widthDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().width(), Worksheet::Unit::Inch);
1275  //float heightDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().height(), Worksheet::Unit::Inch); // unsed
1276  //const int countPixelX = ceil(widthDatarectInch * QApplication::desktop()->physicalDpiX());
1277  //int countPixelY = ceil(heightDatarectInch*QApplication::desktop()->physicalDpiY()); // unused
1278  // new method
1279  const int numberOfPixelX = pageRect.width();
1280 
1281  // only valid for linear scale
1282  //double minLogicalDiffX = 1/((plot->xMax()-plot->xMin())/countPixelX); // unused
1283  //double minLogicalDiffY = 1/((plot->yMax()-plot->yMin())/countPixelY); // unused
1284 
1285  //calculate the lines connecting the data points
1286  {
1287 #ifdef PERFTRACE_CURVES
1288  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), calculate the lines connecting the data points");
1289 #endif
1290 
1291  // find index for xMin and xMax to not loop through all values
1292  int startIndex, endIndex;
1293  auto columnProperties = q->xColumn()->properties();
1294  if (columnProperties == AbstractColumn::Properties::MonotonicDecreasing ||
1296  DEBUG(Q_FUNC_INFO << ", monotonic")
1297  const double xMin = cSystem->mapSceneToLogical(pageRect.topLeft()).x();
1298  const double xMax = cSystem->mapSceneToLogical(pageRect.bottomRight()).x();
1299 
1300  startIndex = Column::indexForValue(xMin, m_logicalPoints, columnProperties);
1301  endIndex = Column::indexForValue(xMax, m_logicalPoints, columnProperties);
1302 
1303  if (startIndex > endIndex)
1304  std::swap(startIndex, endIndex);
1305 
1306  startIndex--; // use one value before
1307  endIndex++;
1308  if (startIndex < 0)
1309  startIndex = 0;
1310  if (endIndex < 0 || endIndex >= numberOfPoints)
1311  endIndex = numberOfPoints - 1;
1312 
1313  numberOfPoints = endIndex - startIndex + 1;
1314  } else {
1315  DEBUG(Q_FUNC_INFO << ", non monotonic")
1316  startIndex = 0;
1317  endIndex = numberOfPoints - 1;
1318  }
1319  DEBUG(Q_FUNC_INFO << ", start/endIndex = " << startIndex << '/' << endIndex)
1320 
1321  QPointF tempPoint1, tempPoint2; // used as temporaryPoints to interpolate datapoints if set
1322  if (columnProperties == AbstractColumn::Properties::Constant) {
1323  DEBUG(Q_FUNC_INFO << ", CONSTANT column")
1324  tempPoint1 = QPointF(plot->xMin(), plot->yMin());
1325  tempPoint2 = QPointF(plot->xMin(), plot->yMax());
1326  m_lines.append(QLineF(tempPoint1, tempPoint2));
1327  } else {
1328  QPointF lastPoint{NAN, NAN}; // last x value
1329  qint64 pixelDiff;
1330  QPointF p0, p1;
1331 
1332  switch (lineType) {
1334  break;
1335  case XYCurve::LineType::Line: {
1336  for (int i{startIndex}; i < endIndex; i++) {
1337  if (!lineSkipGaps && !connectedPointsLogical.at(i))
1338  continue;
1339  p0 = m_logicalPoints.at(i);
1340  p1 = m_logicalPoints.at(i+1);
1341  if (lineIncreasingXOnly && (p1.x() < p0.x())) // skip points
1342  continue;
1343  addLine(p0, p1, lastPoint, pixelDiff, numberOfPixelX);
1344  }
1345 
1346  if (!isnan(lastPoint.x())) // last line
1347  m_lines.append(QLineF(lastPoint, p1));
1348 
1349  break;
1350  }
1352  for (int i{startIndex}; i < endIndex; i++) {
1354  continue;
1355  p0 = m_logicalPoints.at(i);
1356  p1 = m_logicalPoints.at(i+1);
1357  if (lineIncreasingXOnly && (p1.x() < p0.x()))
1358  continue;
1359 
1360  tempPoint1 = QPointF(p1.x(), p0.y());
1361  addLine(p0, tempPoint1, lastPoint, pixelDiff, numberOfPixelX);
1362  addLine(tempPoint1, p1, lastPoint, pixelDiff, numberOfPixelX);
1363  }
1364  if (!isnan(lastPoint.x())) // last line
1365  m_lines.append(QLineF(lastPoint, p1));
1366 
1367  break;
1368  }
1370  for (int i{startIndex}; i < endIndex; i++) {
1371  if (!lineSkipGaps && !connectedPointsLogical.at(i))
1372  continue;
1373  p0 = m_logicalPoints.at(i);
1374  p1 = m_logicalPoints.at(i+1);
1375  if (lineIncreasingXOnly && (p1.x() < p0.x()))
1376  continue;
1377  tempPoint1 = QPointF(p0.x(), p1.y());
1378  addLine(p0, tempPoint1, lastPoint, pixelDiff, numberOfPixelX);
1379  addLine(tempPoint1, p1, lastPoint, pixelDiff, numberOfPixelX);
1380  }
1381  if (!isnan(lastPoint.x())) // last line
1382  m_lines.append(QLineF(lastPoint, p1));
1383 
1384  break;
1385  }
1387  for (int i{startIndex}; i < endIndex; i++) {
1389  continue;
1390 
1391  p0 = m_logicalPoints.at(i);
1392  p1 = m_logicalPoints.at(i+1);
1393  if (lineIncreasingXOnly && (p1.x() < p0.x()))
1394  continue;
1395  tempPoint1 = QPointF(p0.x() + (p1.x()-p0.x())/2., p0.y());
1396  tempPoint2 = QPointF(p0.x() + (p1.x()-p0.x())/2., p1.y());
1397  addLine(p0, tempPoint1, lastPoint, pixelDiff, numberOfPixelX);
1398  addLine(tempPoint1, tempPoint2, lastPoint, pixelDiff, numberOfPixelX);
1399  addLine(tempPoint2, p1, lastPoint, pixelDiff, numberOfPixelX);
1400  }
1401  if (!isnan(lastPoint.x())) // last line
1402  m_lines.append(QLineF(lastPoint, p1));
1403 
1404  break;
1405  }
1407  for (int i{startIndex}; i < endIndex; i++) {
1409  continue;
1410 
1411  p0 = m_logicalPoints.at(i);
1412  p1 = m_logicalPoints.at(i+1);
1413  if (lineIncreasingXOnly && (p1.x() < p0.x()))
1414  continue;
1415  tempPoint1 = QPointF(p0.x(), p0.y() + (p1.y()-p0.y())/2.);
1416  tempPoint2 = QPointF(p1.x(), p0.y() + (p1.y()-p0.y())/2.);
1417  addLine(p0, tempPoint1, lastPoint, pixelDiff, numberOfPixelX);
1418  addLine(tempPoint1, tempPoint2, lastPoint, pixelDiff, numberOfPixelX);
1419  addLine(tempPoint2, p1, lastPoint, pixelDiff, numberOfPixelX);
1420  }
1421  if (!isnan(lastPoint.x())) // last line
1422  m_lines.append(QLineF(lastPoint, p1));
1423 
1424  break;
1425  }
1427  int skip{0};
1428  for (int i{startIndex}; i < endIndex; i++) {
1429  p0 = m_logicalPoints.at(i);
1430  p1 = m_logicalPoints.at(i+1);
1431  if (skip != 1) {
1432  if ( (!lineSkipGaps && !connectedPointsLogical[i])
1433  || (lineIncreasingXOnly && (p1.x() < p0.x())) ) {
1434  skip = 0;
1435  continue;
1436  }
1437  addLine(p0, p1, lastPoint, pixelDiff, numberOfPixelX);
1438  skip++;
1439  } else {
1440  skip = 0;
1441  if (!isnan(lastPoint.x())) {
1442  lastPoint.setX(NAN);
1443  m_lines.append(QLineF(lastPoint, p1));
1444  }
1445  }
1446  }
1447  if (!isnan(lastPoint.x())) // last line
1448  m_lines.append(QLineF(m_logicalPoints.at(endIndex - 1), m_logicalPoints.at(endIndex)));
1449 
1450  break;
1451  }
1453  int skip{0};
1454  for (int i{startIndex}; i < endIndex; i++) {
1455  if (skip != 2) {
1456  p0 = m_logicalPoints.at(i);
1457  p1 = m_logicalPoints.at(i+1);
1458  if ( (!lineSkipGaps && !connectedPointsLogical[i])
1459  || (lineIncreasingXOnly && (p1.x() < p0.x())) ) {
1460  skip = 0;
1461  continue;
1462  }
1463  addLine(p0, p1, lastPoint, pixelDiff, numberOfPixelX);
1464  skip++;
1465  } else {
1466  skip = 0;
1467  if (!isnan(lastPoint.x())) {
1468  lastPoint.setX(NAN);
1469  m_lines.append(QLineF(lastPoint, p1));
1470  }
1471  if (!isnan(lastPoint.x())) // last line
1472  m_lines.append(QLineF(m_logicalPoints[endIndex-1], m_logicalPoints[endIndex]));
1473 
1474  break;
1475  }
1476  }
1477  if (!isnan(lastPoint.x())) // last line
1478  m_lines.append(QLineF(m_logicalPoints.at(endIndex - 1), m_logicalPoints.at(endIndex)));
1479 
1480  break;
1481  }
1486  std::unique_ptr<double[]> x(new double[numberOfPoints]());
1487  std::unique_ptr<double[]> y(new double[numberOfPoints]());
1488  for (int i{0}; i < numberOfPoints; i++) { // TODO: interpolating only between the visible points?
1489  x[i] = m_logicalPoints.at(i+startIndex).x();
1490  y[i] = m_logicalPoints.at(i+startIndex).y();
1491  }
1492 
1493  gsl_interp_accel *acc = gsl_interp_accel_alloc();
1494  gsl_spline *spline{nullptr};
1495  gsl_set_error_handler_off();
1496  switch (lineType) {
1498  spline = gsl_spline_alloc(gsl_interp_cspline, numberOfPoints);
1499  break;
1501  spline = gsl_spline_alloc(gsl_interp_cspline_periodic, numberOfPoints);
1502  break;
1504  spline = gsl_spline_alloc(gsl_interp_akima, numberOfPoints);
1505  break;
1507  spline = gsl_spline_alloc(gsl_interp_akima_periodic, numberOfPoints);
1508  break;
1517  break;
1518  }
1519 
1520  if (!spline) {
1521  QString msg;
1523  msg = i18n("Error: Akima spline interpolation requires a minimum of 5 points.");
1524  else
1525  msg = i18n("Error: Could not initialize the spline function.");
1526  emit q->info(msg);
1527 
1529  gsl_interp_accel_free(acc);
1530  return;
1531  }
1532 
1533  int status = gsl_spline_init(spline, x.get(), y.get(), numberOfPoints);
1534  if (status != 0) {
1535  //TODO: check in gsl/interp.c when GSL_EINVAL is thrown
1536  QString gslError;
1537  if (status == GSL_EINVAL)
1538  gslError = i18n("x values must be monotonically increasing.");
1539  else
1540  gslError = gslErrorToString(status);
1541  emit q->info( i18n("Error: %1", gslError) );
1542 
1544  gsl_spline_free(spline);
1545  gsl_interp_accel_free(acc);
1546  return;
1547  }
1548 
1549  //create interpolating points
1550  //TODO: QVector
1551  std::vector<double> xinterp, yinterp;
1552  for (int i{0}; i < numberOfPoints - 1; i++) {
1553  const double x1 = x[i];
1554  const double x2 = x[i+1];
1555  const double step = std::abs(x2 - x1)/(lineInterpolationPointsCount + 1);
1556 
1557  for (int j{0}; j < (lineInterpolationPointsCount + 1); j++) {
1558  const double xi = x1 + j*step;
1559  const double yi = gsl_spline_eval(spline, xi, acc);
1560  xinterp.push_back(xi);
1561  yinterp.push_back(yi);
1562  }
1563  }
1564 
1565  if (!xinterp.empty()) {
1566  for (unsigned int i{0}; i < xinterp.size() - 1; i++) {
1567  p0 = QPointF(xinterp[i], yinterp[i]);
1568  p1 = QPointF(xinterp[i + 1], yinterp[i + 1]);
1569  addLine(p0, p1, lastPoint, pixelDiff, numberOfPixelX);
1570  }
1571 
1572  addLine(QPointF(xinterp[xinterp.size() - 1], yinterp[yinterp.size() - 1]), QPointF(x[numberOfPoints - 1], y[numberOfPoints - 1]),
1573  lastPoint, pixelDiff, numberOfPixelX);
1574 
1575  // add last line
1576  if (!isnan(lastPoint.x()))
1577  m_lines.append(QLineF(QPointF(xinterp[xinterp.size() - 1], yinterp[yinterp.size() - 1]),
1578  QPointF(x[numberOfPoints - 1], y[numberOfPoints - 1])));
1579  }
1580 
1581  gsl_spline_free(spline);
1582  gsl_interp_accel_free(acc);
1583  break;
1584  }
1585  }
1586  }
1587  }
1588 
1589  //map the lines to scene coordinates
1590  {
1591 #ifdef PERFTRACE_CURVES
1592  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), map lines to scene coordinates");
1593 #endif
1595  }
1596 
1597  {
1598 #ifdef PERFTRACE_CURVES
1599  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), calculate new line path");
1600 #endif
1601  //new line path
1602  for (const auto& line : qAsConst(m_lines)) {
1603  linePath.moveTo(line.p1());
1604  linePath.lineTo(line.p2());
1605  }
1606  }
1607 
1608  updateFilling();
1610 }
1611 
1612 /*!
1613  recalculates the painter path for the drop lines.
1614  Called each time when the type of the drop lines is changed.
1615 */
1617  dropLinePath = QPainterPath();
1620  return;
1621  }
1622 
1623  //calculate drop lines
1624  QVector<QLineF> dlines;
1625  const double xMin = plot->xMin();
1626  const double yMin = plot->yMin();
1627 
1628  //don't skip the invisible points, we still need to calculate
1629  //the drop lines falling into the plot region
1630  switch (dropLineType) {
1632  break;
1634  for (const auto& point: qAsConst(m_logicalPoints)) {
1635  dlines.append(QLineF(point, QPointF(point.x(), yMin)));
1636  }
1637  break;
1639  for (const auto& point: qAsConst(m_logicalPoints)) {
1640  dlines.append(QLineF(point, QPointF(xMin, point.y())));
1641  }
1642  break;
1644  for (const auto& point: qAsConst(m_logicalPoints)) {
1645  dlines.append(QLineF(point, QPointF(point.x(), yMin)));
1646  dlines.append(QLineF(point, QPointF(xMin, point.y())));
1647  }
1648  break;
1650  for (const auto& point: qAsConst(m_logicalPoints)) {
1651  dlines.append(QLineF(point, QPointF(point.x(), 0)));
1652  }
1653  break;
1655  for (const auto& point: qAsConst(m_logicalPoints)) {
1656  dlines.append(QLineF(point, QPointF(point.x(), yColumn->minimum())));
1657  }
1658  break;
1660  for (const auto& point: qAsConst(m_logicalPoints)) {
1661  dlines.append(QLineF(point, QPointF(point.x(), yColumn->maximum())));
1662  }
1663  break;
1664  }
1665 
1666  //map the drop lines to scene coordinates
1667  dlines = cSystem->mapLogicalToScene(dlines);
1668 
1669  //new painter path for the drop lines
1670  for (const auto& line : qAsConst(dlines)) {
1671  dropLinePath.moveTo(line.p1());
1672  dropLinePath.lineTo(line.p2());
1673  }
1674 
1676 }
1677 
1679 #ifdef PERFTRACE_CURVES
1680  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateSymbols()");
1681 #endif
1682  symbolsPath = QPainterPath();
1684  QPainterPath path = Symbol::pathFromStyle(symbolsStyle);
1685 
1686  QTransform trafo;
1687  trafo.scale(symbolsSize, symbolsSize);
1688  path = trafo.map(path);
1689  trafo.reset();
1690 
1691  if (symbolsRotationAngle != 0) {
1692  trafo.rotate(symbolsRotationAngle);
1693  path = trafo.map(path);
1694  }
1695 
1696  for (const auto& point : qAsConst(m_scenePoints)) {
1697  trafo.reset();
1698  trafo.translate(point.x(), point.y());
1699  symbolsPath.addPath(trafo.map(path));
1700  }
1701  }
1702 
1704 }
1705 
1706 /*!
1707  recreates the value strings to be shown and recalculates their draw position.
1708 */
1710 #ifdef PERFTRACE_CURVES
1711  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateValues()");
1712 #endif
1713  valuesPath = QPainterPath();
1714  m_valuePoints.clear();
1715  m_valueStrings.clear();
1716 
1717  const int numberOfPoints = m_logicalPoints.size();
1718  if (valuesType == XYCurve::ValuesType::NoValues || numberOfPoints == 0) {
1720  return;
1721  }
1722  m_valuePoints.reserve(numberOfPoints);
1723  m_valueStrings.reserve(numberOfPoints);
1724 
1725  //determine the value string for all points that are currently visible in the plot
1726  int i{0};
1728  switch (valuesType) {
1730  case XYCurve::ValuesType::X: {
1731  CartesianPlot::RangeFormat rangeFormat = plot->xRangeFormat();
1732  int precision = valuesPrecision;
1734  precision = 0;
1735  for (const auto& point : qAsConst(m_logicalPoints)) {
1736  if (!m_pointVisible.at(i++)) continue;
1737  QString value;
1738  if (rangeFormat == CartesianPlot::RangeFormat::Numeric)
1739  value = numberLocale.toString(point.x(), valuesNumericFormat, precision);
1740  else
1741  value = QDateTime::fromMSecsSinceEpoch(point.x()).toString(valuesDateTimeFormat);
1743  }
1744  break;
1745  }
1746  case XYCurve::ValuesType::Y: {
1747  CartesianPlot::RangeFormat rangeFormat = plot->yRangeFormat();
1748  int precision = valuesPrecision;
1750  precision = 0;
1751  for (const auto& point : qAsConst(m_logicalPoints)) {
1752  if (!m_pointVisible.at(i++)) continue;
1753  QString value;
1754  if (rangeFormat == CartesianPlot::RangeFormat::Numeric)
1755  value = numberLocale.toString(point.y(), valuesNumericFormat, precision);
1756  else
1757  value = QDateTime::fromMSecsSinceEpoch(point.y()).toString(valuesDateTimeFormat);
1759  }
1760  break;
1761  }
1764  CartesianPlot::RangeFormat xRangeFormat = plot->xRangeFormat();
1765  CartesianPlot::RangeFormat yRangeFormat = plot->yRangeFormat();
1766 
1767  int xPrecision = valuesPrecision;
1769  xPrecision = 0;
1770 
1771  int yPrecision = valuesPrecision;
1773  yPrecision = 0;
1774 
1775  for (const auto& point : qAsConst(m_logicalPoints)) {
1776  if (!m_pointVisible.at(i++)) continue;
1777  QString value;
1779  value = '(';
1780  if (xRangeFormat == CartesianPlot::RangeFormat::Numeric)
1781  value += numberLocale.toString(point.x(), valuesNumericFormat, xPrecision);
1782  else
1783  value += QDateTime::fromMSecsSinceEpoch(point.x()).toString(valuesDateTimeFormat);
1784 
1785  if (yRangeFormat == CartesianPlot::RangeFormat::Numeric)
1786  value += ',' + numberLocale.toString(point.y(), valuesNumericFormat, yPrecision);
1787  else
1788  value += ',' + QDateTime::fromMSecsSinceEpoch(point.y()).toString(valuesDateTimeFormat);
1789 
1791  value += ')';
1792 
1794  }
1795  break;
1796  }
1798  if (!valuesColumn) {
1800  return;
1801  }
1802 
1803  const int endRow{qMin(numberOfPoints, valuesColumn->rowCount())};
1804  auto xColMode{valuesColumn->columnMode()};
1805  for (int i = 0; i < endRow; ++i) {
1806  if (!m_pointVisible[i]) continue;
1807 
1808  if ( !valuesColumn->isValid(i) || valuesColumn->isMasked(i) )
1809  continue;
1810 
1811  switch (xColMode) {
1814  break;
1817  m_valueStrings << valuesPrefix + numberLocale.toString(valuesColumn->valueAt(i)) + valuesSuffix;
1818  break;
1821  break;
1826  break;
1827  }
1828  }
1829  }
1830  }
1831  m_valueStrings.squeeze();
1832 
1833  //Calculate the coordinates where to paint the value strings.
1834  //The coordinates depend on the actual size of the string.
1835  QPointF tempPoint;
1836  QFontMetrics fm(valuesFont);
1837  const int h{fm.ascent()};
1838 
1839  i = 0;
1840  for (const auto& string : qAsConst(m_valueStrings)) {
1841  const int w{fm.boundingRect(string).width()};
1842  const double x{m_scenePoints.at(i).x()};
1843  const double y{m_scenePoints.at(i).y()};
1844  i++;
1845 
1846  switch (valuesPosition) {
1848  tempPoint = QPointF(x - w/2., y - valuesDistance);
1849  break;
1851  tempPoint = QPointF(x - w/2., y + valuesDistance + h/2.);
1852  break;
1854  tempPoint = QPointF(x - valuesDistance - w - 1., y);
1855  break;
1857  tempPoint = QPointF(x + valuesDistance - 1., y);
1858  break;
1859  }
1860  m_valuePoints.append(tempPoint);
1861  }
1862  m_valuePoints.squeeze();
1863 
1864  QTransform trafo;
1865  QPainterPath path;
1866  i = 0;
1867  for (const auto& point : qAsConst(m_valuePoints)) {
1868  path = QPainterPath();
1869  path.addText(QPoint(0, 0), valuesFont, m_valueStrings.at(i++));
1870 
1871  trafo.reset();
1872  trafo.translate(point.x(), point.y());
1873  if (valuesRotationAngle != 0)
1874  trafo.rotate(-valuesRotationAngle);
1875 
1876  valuesPath.addPath(trafo.map(path));
1877  }
1878 
1880 }
1881 
1884  return;
1885 
1886  m_fillPolygons.clear();
1887 
1888  //don't try to calculate the filling polygons if
1889  // - no filling was enabled
1890  // - the number of visible points on the scene is too high
1891  // - no scene points available, everything outside of the plot region or no scene points calculated yet
1892  if (fillingPosition == XYCurve::FillingPosition::NoFilling || m_scenePoints.size() > 1000 || m_scenePoints.isEmpty()) {
1894  return;
1895  }
1896 
1897  QVector<QLineF> fillLines;
1898 
1899  //if there're no interpolation lines available (XYCurve::NoLine selected), create line-interpolation,
1900  //use already available lines otherwise.
1901  if (!m_lines.isEmpty())
1902  fillLines = m_lines;
1903  else {
1904  for (int i = 0; i < m_logicalPoints.size() - 1; i++) {
1905  if (!lineSkipGaps && !connectedPointsLogical[i]) continue;
1906  fillLines.append(QLineF(m_logicalPoints.at(i), m_logicalPoints.at(i+1)));
1907  }
1908 
1909  //no lines available (no points), nothing to do
1910  if (fillLines.isEmpty())
1911  return;
1912 
1913  fillLines = cSystem->mapLogicalToScene(fillLines);
1914 
1915  //no lines available (no points) after mapping, nothing to do
1916  if (fillLines.isEmpty())
1917  return;
1918  }
1919 
1920  //create polygon(s):
1921  //1. Depending on the current zoom-level, only a subset of the curve may be visible in the plot
1922  //and more of the filling area should be shown than the area defined by the start and end points of the currently visible points.
1923  //We check first whether the curve crosses the boundaries of the plot and determine new start and end points and put them to the boundaries.
1924  //2. Furthermore, depending on the current filling type we determine the end point (x- or y-coordinate) where all polygons are closed at the end.
1925  QPolygonF pol;
1926  QPointF start = fillLines.at(0).p1(); //starting point of the current polygon, initialize with the first visible point
1927  QPointF end = fillLines.at(fillLines.size()-1).p2(); //end point of the current polygon, initialize with the last visible point
1928  const QPointF& first = m_logicalPoints.at(0); //first point of the curve, may not be visible currently
1929  const QPointF& last = m_logicalPoints.at(m_logicalPoints.size()-1);//last point of the curve, may not be visible currently
1930  QPointF edge;
1931  double xEnd{0.}, yEnd{0.};
1933  edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin()));
1934 
1935  //start point
1936  if (nsl_math_essentially_equal(start.y(), edge.y())) {
1937  if (first.x() < plot->xMin())
1938  start = edge;
1939  else if (first.x() > plot->xMax())
1940  start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
1941  else
1942  start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMin()));
1943  }
1944 
1945  //end point
1946  if (nsl_math_essentially_equal(end.y(), edge.y())) {
1947  if (last.x() < plot->xMin())
1948  end = edge;
1949  else if (last.x() > plot->xMax())
1950  end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
1951  else
1952  end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMin()));
1953  }
1954 
1955  //coordinate at which to close all polygons
1956  yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())).y();
1958  edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax()));
1959 
1960  //start point
1961  if (nsl_math_essentially_equal(start.y(), edge.y())) {
1962  if (first.x() < plot->xMin())
1963  start = edge;
1964  else if (first.x() > plot->xMax())
1965  start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
1966  else
1967  start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMax()));
1968  }
1969 
1970  //end point
1971  if (nsl_math_essentially_equal(end.y(), edge.y())) {
1972  if (last.x() < plot->xMin())
1973  end = edge;
1974  else if (last.x() > plot->xMax())
1975  end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
1976  else
1977  end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMax()));
1978  }
1979 
1980  //coordinate at which to close all polygons
1981  yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())).y();
1983  edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax()));
1984 
1985  //start point
1986  if (nsl_math_essentially_equal(start.y(), edge.y())) {
1987  if (plot->yMax() > 0) {
1988  if (first.x() < plot->xMin())
1989  start = edge;
1990  else if (first.x() > plot->xMax())
1991  start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
1992  else
1993  start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMax()));
1994  } else {
1995  if (first.x() < plot->xMin())
1996  start = edge;
1997  else if (first.x() > plot->xMax())
1998  start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
1999  else
2000  start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMin()));
2001  }
2002  }
2003 
2004  //end point
2005  if (nsl_math_essentially_equal(end.y(), edge.y())) {
2006  if (plot->yMax() > 0) {
2007  if (last.x() < plot->xMin())
2008  end = edge;
2009  else if (last.x() > plot->xMax())
2010  end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
2011  else
2012  end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMax()));
2013  } else {
2014  if (last.x() < plot->xMin())
2015  end = edge;
2016  else if (last.x() > plot->xMax())
2017  end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
2018  else
2019  end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMin()));
2020  }
2021  }
2022 
2023  yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin() > 0 ? plot->yMin() : 0)).y();
2025  edge = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
2026 
2027  //start point
2028  if (nsl_math_essentially_equal(start.x(), edge.x())) {
2029  if (first.y() < plot->yMin())
2030  start = edge;
2031  else if (first.y() > plot->yMax())
2032  start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
2033  else
2034  start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), first.y()));
2035  }
2036 
2037  //end point
2038  if (nsl_math_essentially_equal(end.x(), edge.x())) {
2039  if (last.y() < plot->yMin())
2040  end = edge;
2041  else if (last.y() > plot->yMax())
2042  end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
2043  else
2044  end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), last.y()));
2045  }
2046 
2047  //coordinate at which to close all polygons
2048  xEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())).x();
2049  } else { //FillingRight
2050  edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin()));
2051 
2052  //start point
2053  if (nsl_math_essentially_equal(start.x(), edge.x())) {
2054  if (first.y() < plot->yMin())
2055  start = edge;
2056  else if (first.y() > plot->yMax())
2057  start = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax()));
2058  else
2059  start = cSystem->mapLogicalToScene(QPointF(plot->xMin(), first.y()));
2060  }
2061 
2062  //end point
2063  if (nsl_math_essentially_equal(end.x(), edge.x())) {
2064  if (last.y() < plot->yMin())
2065  end = edge;
2066  else if (last.y() > plot->yMax())
2067  end = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax()));
2068  else
2069  end = cSystem->mapLogicalToScene(QPointF(plot->xMin(), last.y()));
2070  }
2071 
2072  //coordinate at which to close all polygons
2073  xEnd = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())).x();
2074  }
2075 
2076  if (start != fillLines.at(0).p1())
2077  pol << start;
2078 
2079  QPointF p1, p2;
2080  for (int i = 0; i < fillLines.size(); ++i) {
2081  const QLineF& line = fillLines.at(i);
2082  p1 = line.p1();
2083  p2 = line.p2();
2084  if (i != 0 && p1 != fillLines.at(i-1).p2()) {
2085  //the first point of the current line is not equal to the last point of the previous line
2086  //->check whether we have a break in between.
2087  const bool gap = false; //TODO
2088  if (!gap) {
2089  //-> we have no break in the curve -> connect the points by a horizontal/vertical line
2090  pol << fillLines.at(i-1).p2() << p1;
2091  } else {
2092  //-> we have a break in the curve -> close the polygon, add it to the polygon list and start a new polygon
2094  pol << QPointF(fillLines.at(i-1).p2().x(), yEnd);
2095  pol << QPointF(start.x(), yEnd);
2096  } else {
2097  pol << QPointF(xEnd, fillLines.at(i-1).p2().y());
2098  pol << QPointF(xEnd, start.y());
2099  }
2100 
2101  m_fillPolygons << pol;
2102  pol.clear();
2103  start = p1;
2104  }
2105  }
2106  pol << p1 << p2;
2107  }
2108 
2109  if (p2 != end)
2110  pol << end;
2111 
2112  //close the last polygon
2114  pol << QPointF(end.x(), yEnd);
2115  pol << QPointF(start.x(), yEnd);
2116  } else {
2117  pol << QPointF(xEnd, end.y());
2118  pol << QPointF(xEnd, start.y());
2119  }
2120 
2121  m_fillPolygons << pol;
2123 }
2124 
2125  /*!
2126  * Find y value which corresponds to a @p x . @p valueFound indicates, if value was found.
2127  * When monotonic increasing or decreasing a different algorithm will be used, which needs less steps (mean) (log_2(rowCount)) to find the value.
2128  * @param x
2129  * @param valueFound
2130  * @return
2131  */
2132 double XYCurve::y(double x, bool &valueFound) const {
2133  if (!yColumn() || !xColumn()) {
2134  valueFound = false;
2135  return NAN;
2136  }
2137 
2138  auto yColumnMode = yColumn()->columnMode();
2139  const int index = xColumn()->indexForValue(x);
2140  if (index < 0) {
2141  valueFound = false;
2142  return NAN;
2143  }
2144 
2145  valueFound = true;
2146  if (yColumnMode == AbstractColumn::ColumnMode::Numeric || yColumnMode == AbstractColumn::ColumnMode::Integer ||
2147  yColumnMode == AbstractColumn::ColumnMode::BigInt) {
2148  return yColumn()->valueAt(index);
2149  } else {
2150  valueFound = false;
2151  return NAN;
2152  }
2153 }
2154 
2155 /*!
2156 * Find y DateTime which corresponds to a @p x . @p valueFound indicates, if value was found.
2157 * When monotonic increasing or decreasing a different algorithm will be used, which needs less steps (mean) (log_2(rowCount)) to find the value.
2158 * @param x
2159 * @param valueFound
2160 * @return Return found value
2161 */
2162 QDateTime XYCurve::yDateTime(double x, bool &valueFound) const {
2163  if (!yColumn() || !xColumn()) {
2164  valueFound = false;
2165  return QDateTime();
2166  }
2167 
2168  auto yColumnMode = yColumn()->columnMode();
2169  const int index = xColumn()->indexForValue(x);
2170  if (index < 0) {
2171  valueFound = false;
2172  return QDateTime();
2173  }
2174 
2175  valueFound = true;
2176  if (yColumnMode == AbstractColumn::ColumnMode::Day ||
2177  yColumnMode == AbstractColumn::ColumnMode::Month ||
2178  yColumnMode == AbstractColumn::ColumnMode::DateTime)
2179  return yColumn()->dateTimeAt(index);
2180 
2181  valueFound = false;
2182  return QDateTime();
2183 }
2184 
2185 bool XYCurve::minMaxY(int indexMin, int indexMax, double& yMin, double& yMax, bool includeErrorBars) const {
2186  return minMax(yColumn(), xColumn(), yErrorType(), yErrorPlusColumn(), yErrorMinusColumn(), indexMin, indexMax, yMin, yMax, includeErrorBars);
2187 }
2188 
2189 bool XYCurve::minMaxX(int indexMin, int indexMax, double& xMin, double& xMax, bool includeErrorBars) const {
2190  return minMax(xColumn(), yColumn(), xErrorType(), xErrorPlusColumn(), xErrorMinusColumn(), indexMin, indexMax, xMin, xMax, includeErrorBars);
2191 }
2192 
2193 /*!
2194  * Calculates the minimum \p min and maximum \p max of a curve with optionally respecting the error bars
2195  * This function does not check if the values are out of range
2196  * \p indexMax is not included
2197  * \p column
2198  * \p errorType
2199  * \p errorPlusColumn
2200  * \p errorMinusColumn
2201  * \p indexMin
2202  * \p indexMax
2203  * \p min
2204  * \p max
2205  * \ includeErrorBars If true respect the error bars in the min/max calculation
2206  */
2207 bool XYCurve::minMax(const AbstractColumn* column1, const AbstractColumn* column2, const ErrorType errorType, const AbstractColumn* errorPlusColumn, const AbstractColumn* errorMinusColumn, int indexMin, int indexMax, double& min, double& max, bool includeErrorBars) const {
2208  // when property is increasing or decreasing there is a benefit in finding minimum and maximum
2209  // for property == AbstractColumn::Properties::No it must be iterated over all values so it does not matter if this function or the below one is used
2210  // if the property of the second column is not AbstractColumn::Properties::No means, that all values are valid and not masked
2211  if ((!includeErrorBars || errorType == ErrorType::NoError) && column1->properties() != AbstractColumn::Properties::No && column2 && column2->properties() != AbstractColumn::Properties::No) {
2212  min = column1->minimum(indexMin, indexMax);
2213  max = column1->maximum(indexMin, indexMax);
2214  return true;
2215  }
2216 
2217  if (column1->rowCount() == 0)
2218  return false;
2219 
2220  min = INFINITY;
2221  max = -INFINITY;
2222 
2223  for (int i = indexMin; i < indexMax; ++i) {
2224  if (!column1->isValid(i) || column1->isMasked(i) || (column2 && (!column2->isValid(i) || column2->isMasked(i))))
2225  continue;
2226 
2227  if ( (errorPlusColumn && i >= errorPlusColumn->rowCount())
2228  || (errorMinusColumn && i >= errorMinusColumn->rowCount()) )
2229  continue;
2230 
2231  double value;
2234  value = column1->valueAt(i);
2235  else if (column1->columnMode() == AbstractColumn::ColumnMode::DateTime ||
2238  value = column1->dateTimeAt(i).toMSecsSinceEpoch();
2239  } else
2240  return false;
2241 
2242  if (errorType == ErrorType::NoError) {
2243  if (value < min)
2244  min = value;
2245 
2246  if (value > max)
2247  max = value;
2248  } else {
2249  //determine the values for the errors
2250  double errorPlus, errorMinus;
2251  if (errorPlusColumn && errorPlusColumn->isValid(i) && !errorPlusColumn->isMasked(i))
2252  if (errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::Numeric ||
2253  errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::Integer ||
2254  errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::BigInt)
2255  errorPlus = errorPlusColumn->valueAt(i);
2256  else if (errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::DateTime ||
2257  errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::Month ||
2258  errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::Day)
2259  errorPlus = errorPlusColumn->dateTimeAt(i).toMSecsSinceEpoch();
2260  else
2261  return false;
2262  else
2263  errorPlus = 0;
2264 
2265  if (errorType == ErrorType::Symmetric)
2266  errorMinus = errorPlus;
2267  else {
2268  if (errorMinusColumn && errorMinusColumn->isValid(i) && !errorMinusColumn->isMasked(i))
2269  if (errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::Numeric ||
2270  errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::Integer ||
2271  errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::BigInt)
2272  errorMinus = errorMinusColumn->valueAt(i);
2273  else if (errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::DateTime ||
2274  errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::Month ||
2275  errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::Day)
2276  errorMinus = errorMinusColumn->dateTimeAt(i).toMSecsSinceEpoch();
2277  else
2278  return false;
2279  else
2280  errorMinus = 0;
2281  }
2282 
2283  if (value - errorMinus < min)
2284  min = value - errorMinus;
2285 
2286  if (value + errorPlus > max)
2287  max = value + errorPlus;
2288  }
2289  }
2290  return true;
2291 }
2292 
2293 bool XYCurvePrivate::activateCurve(QPointF mouseScenePos, double maxDist) {
2294  if (!isVisible())
2295  return false;
2296 
2297  int rowCount{0};
2299  rowCount = m_lines.count();
2301  rowCount = m_scenePoints.size();
2302  else
2303  return false;
2304 
2305  if (rowCount == 0)
2306  return false;
2307 
2308  if (maxDist < 0)
2309  maxDist = (linePen.width() < 10) ? 10. : linePen.width();
2310 
2311  auto properties{q->xColumn()->properties()};
2312  if (properties == AbstractColumn::Properties::No) {
2313  // assumption: points exist if no line. otherwise previously returned false
2315  QPointF curvePosPrevScene = m_scenePoints.at(0);
2316  QPointF curvePosScene = curvePosPrevScene;
2317  for (int row = 0; row < rowCount; row ++) {
2318  if (gsl_hypot(mouseScenePos.x() - curvePosScene.x(), mouseScenePos.y() - curvePosScene.y()) <= maxDist)
2319  return true;
2320 
2321  curvePosPrevScene = curvePosScene;
2322  curvePosScene = m_scenePoints.at(row);
2323  }
2324  } else {
2325  for (int row = 0; row < rowCount; row++) {
2326  QLineF line = m_lines.at(row);
2327  if (pointLiesNearLine(line.p1(), line.p2(), mouseScenePos, maxDist))
2328  return true;
2329  }
2330  }
2331 
2332  } else if (properties == AbstractColumn::Properties::MonotonicIncreasing ||
2334 
2335  bool increase{true};
2337  increase = false;
2338 
2339  double x{mouseScenePos.x() - maxDist};
2340  int index{0};
2341 
2342  QPointF curvePosScene;
2343  QPointF curvePosPrevScene;
2344 
2346  curvePosScene = m_scenePoints.at(index);
2347  curvePosPrevScene = curvePosScene;
2348  index = Column::indexForValue(x, m_scenePoints, static_cast<AbstractColumn::Properties>(properties));
2349  } else
2350  index = Column::indexForValue(x, m_lines, static_cast<AbstractColumn::Properties>(properties));
2351 
2352  if (index >= 1)
2353  index --; // use one before so it is secured that I'm before point.x()
2354  else if (index == -1)
2355  return false;
2356 
2357  const double xMax{mouseScenePos.x() + maxDist};
2358  bool stop{false};
2359  while (true) {
2360  // assumption: points exist if no line. otherwise previously returned false
2361  if (lineType == XYCurve::LineType::NoLine) {// check points only if no line otherwise check only the lines
2362  if (curvePosScene.x() > xMax)
2363  stop = true; // one more time if bigger
2364  if (gsl_hypot(mouseScenePos.x()- curvePosScene.x(), mouseScenePos.y()-curvePosScene.y()) <= maxDist)
2365  return true;
2366  } else {
2367  if (m_lines.at(index).p1().x() > xMax)
2368  stop = true; // one more time if bigger
2369 
2370  QLineF line = m_lines.at(index);
2371  if (pointLiesNearLine(line.p1(), line.p2(), mouseScenePos, maxDist))
2372  return true;
2373  }
2374 
2375  if (stop || (index >= rowCount - 1 && increase) || (index <= 0 && !increase))
2376  break;
2377 
2378  if (increase)
2379  index++;
2380  else
2381  index--;
2382 
2384  curvePosPrevScene = curvePosScene;
2385  curvePosScene = m_scenePoints.at(index);
2386  }
2387  }
2388  }
2389 
2390  return false;
2391 }
2392 
2393 /*!
2394  * \brief XYCurve::pointLiesNearLine
2395  * Calculates if a point \p pos lies near than maxDist to the line created by the points \p p1 and \p p2
2396  * https://stackoverflow.com/questions/11604680/point-laying-near-line
2397  * \p p1 first point of the line
2398  * \p p2 second point of the line
2399  * \p pos Position to check
2400  * \p maxDist Maximal distance away from the curve, which is valid
2401  * \return Return true if point lies next to the line
2402  */
2403 bool XYCurvePrivate::pointLiesNearLine(const QPointF p1, const QPointF p2, const QPointF pos, const double maxDist) const {
2404  const double dx12{p2.x() - p1.x()};
2405  const double dy12{p2.y() - p1.y()};
2406  const double vecLength{gsl_hypot(dx12, dy12)};
2407 
2408  const double dx1m{pos.x() - p1.x()};
2409  const double dy1m{pos.y() - p1.y()};
2410  if (vecLength == 0) {
2411  if (gsl_hypot(dx1m, dy1m) <= maxDist)
2412  return true;
2413  return false;
2414  }
2415  QPointF unitvec(dx12/vecLength, dy12/vecLength);
2416 
2417  const double dist_segm{std::abs(dx1m*unitvec.y() - dy1m*unitvec.x())};
2418  const double scalarProduct{dx1m*unitvec.x() + dy1m*unitvec.y()};
2419 
2420  if (scalarProduct > 0) {
2421  if (scalarProduct < vecLength && dist_segm < maxDist)
2422  return true;
2423  }
2424  return false;
2425 }
2426 
2427 // TODO: curvePosScene.x() >= mouseScenePos.x() &&
2428 // curvePosPrevScene.x() < mouseScenePos.x()
2429 // should not be here
2430 bool XYCurvePrivate::pointLiesNearCurve(const QPointF mouseScenePos, const QPointF curvePosPrevScene, const QPointF curvePosScene, const int index, const double maxDist) const {
2431  if (q->lineType() != XYCurve::LineType::NoLine &&
2432  curvePosScene.x() >= mouseScenePos.x() &&
2433  curvePosPrevScene.x() < mouseScenePos.x()) {
2434 
2435  if (q->lineType() == XYCurve::LineType::Line) {
2436  // point is not in the near of the point, but it can be in the near of the connection line of two points
2437  if (pointLiesNearLine(curvePosPrevScene,curvePosScene, mouseScenePos, maxDist))
2438  return true;
2439  } else if (q->lineType() == XYCurve::LineType::StartHorizontal) {
2440  QPointF tempPoint = curvePosPrevScene;
2441  tempPoint.setX(curvePosScene.x());
2442  if (pointLiesNearLine(curvePosPrevScene,tempPoint, mouseScenePos, maxDist))
2443  return true;
2444  if (pointLiesNearLine(tempPoint,curvePosScene, mouseScenePos, maxDist))
2445  return true;
2446  } else if (q->lineType() == XYCurve::LineType::StartVertical) {
2447  QPointF tempPoint = curvePosPrevScene;
2448  tempPoint.setY(curvePosScene.y());
2449  if (pointLiesNearLine(curvePosPrevScene,tempPoint, mouseScenePos, maxDist))
2450  return true;
2451  if (pointLiesNearLine(tempPoint,curvePosScene, mouseScenePos, maxDist))
2452  return true;
2453  } else if (q->lineType() == XYCurve::LineType::MidpointHorizontal) {
2454  QPointF tempPoint = curvePosPrevScene;
2455  tempPoint.setX(curvePosPrevScene.x()+(curvePosScene.x()-curvePosPrevScene.x())/2);
2456  if (pointLiesNearLine(curvePosPrevScene,tempPoint, mouseScenePos, maxDist))
2457  return true;
2458  QPointF tempPoint2(tempPoint.x(), curvePosScene.y());
2459  if (pointLiesNearLine(tempPoint,tempPoint2, mouseScenePos, maxDist))
2460  return true;
2461 
2462  if (pointLiesNearLine(tempPoint2,curvePosScene, mouseScenePos, maxDist))
2463  return true;
2464  } else if (q->lineType() == XYCurve::LineType::MidpointVertical) {
2465  QPointF tempPoint = curvePosPrevScene;
2466  tempPoint.setY(curvePosPrevScene.y()+(curvePosScene.y()-curvePosPrevScene.y())/2);
2467  if (pointLiesNearLine(curvePosPrevScene,tempPoint, mouseScenePos, maxDist))
2468  return true;
2469  QPointF tempPoint2(tempPoint.y(), curvePosScene.x());
2470  if (pointLiesNearLine(tempPoint,tempPoint2, mouseScenePos, maxDist))
2471  return true;
2472 
2473  if (pointLiesNearLine(tempPoint2,curvePosScene, mouseScenePos, maxDist))
2474  return true;
2475  } else if (q->lineType() == XYCurve::LineType::SplineAkimaNatural ||
2476  q->lineType() == XYCurve::LineType::SplineCubicNatural ||
2477  q->lineType() == XYCurve::LineType::SplineAkimaPeriodic ||
2478  q->lineType() == XYCurve::LineType::SplineCubicPeriodic) {
2479  for (int i = 0; i < q->lineInterpolationPointsCount() + 1; i++) {
2480  QLineF line = m_lines.at(index*(q->lineInterpolationPointsCount()+1)+i);
2481  QPointF p1{line.p1()}; //cSystem->mapLogicalToScene(line.p1());
2482  QPointF p2{line.p2()}; //cSystem->mapLogicalToScene(line.p2());
2483  if (pointLiesNearLine(p1, p2, mouseScenePos, maxDist))
2484  return true;
2485  }
2486  } else {
2487  // point is not in the near of the point, but it can be in the near of the connection line of two points
2488  if (pointLiesNearLine(curvePosPrevScene, curvePosScene, mouseScenePos, maxDist))
2489  return true;
2490  }
2491  }
2492  return false;
2493 }
2494 
2496  errorBarsPath = QPainterPath();
2499  return;
2500  }
2501 
2502  QVector<QLineF> elines;
2503  QVector<QPointF> pointsErrorBarAnchorX;
2504  QVector<QPointF> pointsErrorBarAnchorY;
2505 
2506  for (int i = 0; i < m_logicalPoints.size(); ++i) {
2507  if (!m_pointVisible.at(i))
2508  continue;
2509 
2510  const QPointF& point{m_logicalPoints.at(i)};
2511  const int index{validPointsIndicesLogical.at(i)};
2512  double errorPlus, errorMinus;
2513 
2514  //error bars for x
2516  //determine the values for the errors
2518  errorPlus = xErrorPlusColumn->valueAt(index);
2519  else
2520  errorPlus = 0;
2521 
2523  errorMinus = errorPlus;
2524  else {
2526  errorMinus = xErrorMinusColumn->valueAt(index);
2527  else
2528  errorMinus = 0;
2529  }
2530 
2531  //draw the error bars
2532  if (errorMinus != 0 || errorPlus != 0)
2533  elines.append(QLineF(QPointF(point.x()-errorMinus, point.y()),
2534  QPointF(point.x()+errorPlus, point.y())));
2535 
2536  //determine the end points of the errors bars in logical coordinates to draw later the cap
2538  if (errorMinus != 0)
2539  pointsErrorBarAnchorX << QPointF(point.x() - errorMinus, point.y());
2540  if (errorPlus != 0)
2541  pointsErrorBarAnchorX << QPointF(point.x() + errorPlus, point.y());
2542  }
2543  }
2544 
2545  //error bars for y
2547  //determine the values for the errors
2549  errorPlus = yErrorPlusColumn->valueAt(index);
2550  else
2551  errorPlus = 0;
2552 
2554  errorMinus = errorPlus;
2555  else {
2557  errorMinus = yErrorMinusColumn->valueAt(index);
2558  else
2559  errorMinus = 0;
2560  }
2561 
2562  //draw the error bars
2563  if (errorMinus != 0 || errorPlus != 0)
2564  elines.append(QLineF(QPointF(point.x(), point.y() + errorPlus),
2565  QPointF(point.x(), point.y() - errorMinus)));
2566 
2567  //determine the end points of the errors bars in logical coordinates to draw later the cap
2569  if (errorMinus != 0)
2570  pointsErrorBarAnchorY << QPointF(point.x(), point.y() + errorPlus);
2571  if (errorPlus != 0)
2572  pointsErrorBarAnchorY << QPointF(point.x(), point.y() - errorMinus);
2573  }
2574  }
2575  }
2576 
2577  //map the error bars to scene coordinates
2578  elines = cSystem->mapLogicalToScene(elines);
2579 
2580  //new painter path for the error bars
2581  for (const auto& line : qAsConst(elines)) {
2582  errorBarsPath.moveTo(line.p1());
2583  errorBarsPath.lineTo(line.p2());
2584  }
2585 
2586  //add caps for x error bars
2587  if (!pointsErrorBarAnchorX.isEmpty()) {
2588  pointsErrorBarAnchorX = cSystem->mapLogicalToScene(pointsErrorBarAnchorX);
2589  for (const auto& point : qAsConst(pointsErrorBarAnchorX)) {
2590  errorBarsPath.moveTo(QPointF(point.x(), point.y() - errorBarsCapSize/2.));
2591  errorBarsPath.lineTo(QPointF(point.x(), point.y() + errorBarsCapSize/2.));
2592  }
2593  }
2594 
2595  //add caps for y error bars
2596  if (!pointsErrorBarAnchorY.isEmpty()) {
2597  pointsErrorBarAnchorY = cSystem->mapLogicalToScene(pointsErrorBarAnchorY);
2598  for (const auto& point : qAsConst(pointsErrorBarAnchorY)) {
2599  errorBarsPath.moveTo(QPointF(point.x() - errorBarsCapSize/2., point.y()));
2600  errorBarsPath.lineTo(QPointF(point.x() + errorBarsCapSize/2., point.y()));
2601  }
2602  }
2603 
2605 }
2606 
2607 /*!
2608  recalculates the outer bounds and the shape of the curve.
2609 */
2611  DEBUG(Q_FUNC_INFO << ", m_suppressRecalc = " << m_suppressRecalc);
2612  if (m_suppressRecalc)
2613  return;
2614 
2615 #ifdef PERFTRACE_CURVES
2616  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::recalcShapeAndBoundingRect()");
2617 #endif
2618 
2619  prepareGeometryChange();
2620  curveShape = QPainterPath();
2623 
2626 
2628  curveShape.addPath(symbolsPath);
2629 
2631  curveShape.addPath(valuesPath);
2632 
2635 
2636  boundingRectangle = curveShape.boundingRect();
2637 
2638  for (const auto& pol : qAsConst(m_fillPolygons))
2639  boundingRectangle = boundingRectangle.united(pol.boundingRect());
2640 
2641  //TODO: when the selection is painted, line intersections are visible.
2642  //simplified() removes those artifacts but is horrible slow for curves with large number of points.
2643  //search for an alternative.
2644  //curveShape = curveShape.simplified();
2645 
2646  updatePixmap();
2647 }
2648 
2649 void XYCurvePrivate::draw(QPainter* painter) {
2650 #ifdef PERFTRACE_CURVES
2651  PERFTRACE(name().toLatin1() + ", XYCurvePrivate::draw()");
2652 #endif
2653 
2654  //draw filling
2656  painter->setOpacity(fillingOpacity);
2657  painter->setPen(Qt::SolidLine);
2658  drawFilling(painter);
2659  }
2660 
2661  //draw lines
2663  painter->setOpacity(lineOpacity);
2664  painter->setPen(linePen);
2665  painter->setBrush(Qt::NoBrush);
2666  painter->drawPath(linePath);
2667  }
2668 
2669  //draw drop lines
2671  painter->setOpacity(dropLineOpacity);
2672  painter->setPen(dropLinePen);
2673  painter->setBrush(Qt::NoBrush);
2674  painter->drawPath(dropLinePath);
2675  }
2676 
2677  //draw error bars
2679  painter->setOpacity(errorBarsOpacity);
2680  painter->setPen(errorBarsPen);
2681  painter->setBrush(Qt::NoBrush);
2682  painter->drawPath(errorBarsPath);
2683  }
2684 
2685  //draw symbols
2687  painter->setOpacity(symbolsOpacity);
2688  painter->setPen(symbolsPen);
2689  painter->setBrush(symbolsBrush);
2690  drawSymbols(painter);
2691  }
2692 
2693  //draw values
2695  painter->setOpacity(valuesOpacity);
2696  painter->setPen(QPen(valuesColor));
2697  painter->setFont(valuesFont);
2698  drawValues(painter);
2699  }
2700 }
2701 
2703  DEBUG(Q_FUNC_INFO << ", m_suppressRecalc = " << m_suppressRecalc);
2704  if (m_suppressRecalc)
2705  return;
2706 
2707  WAIT_CURSOR;
2708 
2711  if (boundingRectangle.width() == 0 || boundingRectangle.height() == 0) {
2712  DEBUG(Q_FUNC_INFO << ", boundingRectangle.width() or boundingRectangle.height() == 0");
2713  m_pixmap = QPixmap();
2714  RESET_CURSOR;
2715  return;
2716  }
2717  QPixmap pixmap(ceil(boundingRectangle.width()), ceil(boundingRectangle.height()));
2718  pixmap.fill(Qt::transparent);
2719  QPainter painter(&pixmap);
2720  painter.setRenderHint(QPainter::Antialiasing, true);
2721  painter.translate(-boundingRectangle.topLeft());
2722 
2723  draw(&painter);
2724  painter.end();
2725  m_pixmap = pixmap;
2726 
2727  update();
2728  RESET_CURSOR;
2729 }
2730 
2731 /*!
2732  Reimplementation of QGraphicsItem::paint(). This function does the actual painting of the curve.
2733  \sa QGraphicsItem::paint().
2734 */
2735 void XYCurvePrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
2736  Q_UNUSED(option);
2737  Q_UNUSED(widget);
2738  if (!isVisible())
2739  return;
2740 
2741  painter->setPen(Qt::NoPen);
2742  painter->setBrush(Qt::NoBrush);
2743  painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
2744 
2745  if ( !m_printing && KSharedConfig::openConfig()->group("Settings_Worksheet").readEntry<bool>("DoubleBuffering", true) )
2746  painter->drawPixmap(boundingRectangle.topLeft(), m_pixmap); //draw the cached pixmap (fast)
2747  else
2748  draw(painter); //draw directly again (slow)
2749 
2750 
2751  if (m_hovered && !isSelected() && !m_printing) {
2753  QPixmap pix = m_pixmap;
2754  QPainter p(&pix);
2755  p.setCompositionMode(QPainter::CompositionMode_SourceIn); // source (shadow) pixels merged with the alpha channel of the destination (m_pixmap)
2756  p.fillRect(pix.rect(), QApplication::palette().color(QPalette::Shadow));
2757  p.end();
2758 
2759  m_hoverEffectImage = ImageTools::blurred(pix.toImage(), m_pixmap.rect(), 5);
2760  m_hoverEffectImageIsDirty = false;
2761  }
2762 
2763  painter->drawImage(boundingRectangle.topLeft(), m_hoverEffectImage, m_pixmap.rect());
2764  return;
2765  }
2766 
2767  if (isSelected() && !m_printing) {
2769  QPixmap pix = m_pixmap;
2770  QPainter p(&pix);
2771  p.setCompositionMode(QPainter::CompositionMode_SourceIn);
2772  p.fillRect(pix.rect(), QApplication::palette().color(QPalette::Highlight));
2773  p.end();
2774 
2775  m_selectionEffectImage = ImageTools::blurred(pix.toImage(), m_pixmap.rect(), 5);
2777  }
2778 
2779  painter->drawImage(boundingRectangle.topLeft(), m_selectionEffectImage, m_pixmap.rect());
2780  }
2781 }
2782 
2783 /*!
2784  Drawing of symbolsPath is very slow, so we draw every symbol in the loop which is much faster (factor 10)
2785 */
2786 void XYCurvePrivate::drawSymbols(QPainter* painter) {
2787  QPainterPath path = Symbol::pathFromStyle(symbolsStyle);
2788 
2789  QTransform trafo;
2790  trafo.scale(symbolsSize, symbolsSize);
2791 
2792  if (symbolsRotationAngle != 0)
2793  trafo.rotate(-symbolsRotationAngle);
2794 
2795  path = trafo.map(path);
2796 
2797  for (const auto& point : qAsConst(m_scenePoints)) {
2798  trafo.reset();
2799  trafo.translate(point.x(), point.y());
2800  painter->drawPath(trafo.map(path));
2801  }
2802 }
2803 
2804 void XYCurvePrivate::drawValues(QPainter* painter) {
2805  int i = 0;
2806  for (const auto& point : qAsConst(m_valuePoints)) {
2807  painter->translate(point);
2808  if (valuesRotationAngle != 0)
2809  painter->rotate(-valuesRotationAngle);
2810 
2811  painter->drawText(QPoint(0, 0), m_valueStrings.at(i++));
2812 
2813  if (valuesRotationAngle != 0)
2814  painter->rotate(valuesRotationAngle);
2815  painter->translate(-point);
2816  }
2817 }
2818 
2819 void XYCurvePrivate::drawFilling(QPainter* painter) {
2820  for (const auto& pol : qAsConst(m_fillPolygons)) {
2821  QRectF rect = pol.boundingRect();
2823  switch (fillingColorStyle) {
2825  painter->setBrush(QBrush(fillingFirstColor));
2826  break;
2827  }
2829  QLinearGradient linearGrad(rect.topLeft(), rect.topRight());
2830  linearGrad.setColorAt(0, fillingFirstColor);
2831  linearGrad.setColorAt(1, fillingSecondColor);
2832  painter->setBrush(QBrush(linearGrad));
2833  break;
2834  }
2836  QLinearGradient linearGrad(rect.topLeft(), rect.bottomLeft());
2837  linearGrad.setColorAt(0, fillingFirstColor);
2838  linearGrad.setColorAt(1, fillingSecondColor);
2839  painter->setBrush(QBrush(linearGrad));
2840  break;
2841  }
2843  QLinearGradient linearGrad(rect.topLeft(), rect.bottomRight());
2844  linearGrad.setColorAt(0, fillingFirstColor);
2845  linearGrad.setColorAt(1, fillingSecondColor);
2846  painter->setBrush(QBrush(linearGrad));
2847  break;
2848  }
2850  QLinearGradient linearGrad(rect.bottomLeft(), rect.topRight());
2851  linearGrad.setColorAt(0, fillingFirstColor);
2852  linearGrad.setColorAt(1, fillingSecondColor);
2853  painter->setBrush(QBrush(linearGrad));
2854  break;
2855  }
2857  QRadialGradient radialGrad(rect.center(), rect.width()/2);
2858  radialGrad.setColorAt(0, fillingFirstColor);
2859  radialGrad.setColorAt(1, fillingSecondColor);
2860  painter->setBrush(QBrush(radialGrad));
2861  break;
2862  }
2863  }
2865  if ( !fillingFileName.trimmed().isEmpty() ) {
2866  QPixmap pix(fillingFileName);
2867  switch (fillingImageStyle) {
2869  pix = pix.scaled(rect.size().toSize(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
2870  painter->setBrush(QBrush(pix));
2871  painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2);
2872  break;
2874  pix = pix.scaled(rect.size().toSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2875  painter->setBrush(QBrush(pix));
2876  painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2);
2877  break;
2879  pix = pix.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
2880  painter->setBrush(QBrush(pix));
2881  painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2);
2882  break;
2884  QPixmap backpix(rect.size().toSize());
2885  backpix.fill();
2886  QPainter p(&backpix);
2887  p.drawPixmap(QPointF(0, 0), pix);
2888  p.end();
2889  painter->setBrush(QBrush(backpix));
2890  painter->setBrushOrigin(-pix.size().width()/2, -pix.size().height()/2);
2891  break;
2892  }
2894  painter->setBrush(QBrush(pix));
2895  break;
2897  painter->setBrush(QBrush(pix));
2898  painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2);
2899  }
2900  }
2902  painter->setBrush(QBrush(fillingFirstColor, fillingBrushStyle));
2903 
2904  painter->drawPolygon(pol);
2905  }
2906 }
2907 
2909  m_printing = on;
2910 }
2911 
2913  m_suppressRetransform = on;
2914  m_suppressRecalc = on;
2915 }
2916 
2917 /*!
2918  * checks if the mousePress event was done near the histogram shape
2919  * and selects the graphics item if it is the case.
2920  * \p event
2921  */
2922 void XYCurvePrivate::mousePressEvent(QGraphicsSceneMouseEvent* event) {
2924  event->ignore();
2925  return QGraphicsItem::mousePressEvent(event);
2926  }
2927 
2928  if(q->activateCurve(event->pos())){
2929  setSelected(true);
2930  return;
2931  }
2932 
2933  event->ignore();
2934  setSelected(false);
2935  QGraphicsItem::mousePressEvent(event);
2936 }
2937 
2938 /*!
2939  * Is called in CartesianPlot::hoverMoveEvent where it is determined which curve to hover.
2940  * \p on
2941  */
2943  if(on == m_hovered)
2944  return; // don't update if state not changed
2945 
2946  m_hovered = on;
2947  on ? emit q->hovered() : emit q->unhovered();
2948  update();
2949 }
2950 
2951 //##############################################################################
2952 //################## Serialization/Deserialization ###########################
2953 //##############################################################################
2954 //! Save as XML
2955 void XYCurve::save(QXmlStreamWriter* writer) const {
2956  Q_D(const XYCurve);
2957 
2958  writer->writeStartElement( "xyCurve" );
2959  writeBasicAttributes( writer );
2960  writeCommentElement( writer );
2961 
2962  //general
2963  writer->writeStartElement( "general" );
2964  WRITE_COLUMN(d->xColumn, xColumn);
2965  WRITE_COLUMN(d->yColumn, yColumn);
2966  writer->writeAttribute( "visible", QString::number(d->isVisible()) );
2967  writer->writeEndElement();
2968 
2969  //Line
2970  writer->writeStartElement( "lines" );
2971  writer->writeAttribute( "type", QString::number(static_cast<int>(d->lineType)) );
2972  writer->writeAttribute( "skipGaps", QString::number(d->lineSkipGaps) );
2973  writer->writeAttribute( "increasingXOnly", QString::number(d->lineIncreasingXOnly) );
2974  writer->writeAttribute( "interpolationPointsCount", QString::number(d->lineInterpolationPointsCount) );
2975  WRITE_QPEN(d->linePen);
2976  writer->writeAttribute( "opacity", QString::number(d->lineOpacity) );
2977  writer->writeEndElement();
2978 
2979  //Drop lines
2980  writer->writeStartElement( "dropLines" );
2981  writer->writeAttribute( "type", QString::number(static_cast<int>(d->dropLineType)) );
2982  WRITE_QPEN(d->dropLinePen);
2983  writer->writeAttribute( "opacity", QString::number(d->dropLineOpacity) );
2984  writer->writeEndElement();
2985 
2986  //Symbols
2987  writer->writeStartElement( "symbols" );
2988  writer->writeAttribute( "symbolsStyle", QString::number(static_cast<int>(d->symbolsStyle)) );
2989  writer->writeAttribute( "opacity", QString::number(d->symbolsOpacity) );
2990  writer->writeAttribute( "rotation", QString::number(d->symbolsRotationAngle) );
2991  writer->writeAttribute( "size", QString::number(d->symbolsSize) );
2992  WRITE_QBRUSH(d->symbolsBrush);
2993  WRITE_QPEN(d->symbolsPen);
2994  writer->writeEndElement();
2995 
2996  //Values
2997  writer->writeStartElement( "values" );
2998  writer->writeAttribute( "type", QString::number(static_cast<int>(d->valuesType)) );
2999  WRITE_COLUMN(d->valuesColumn, valuesColumn);
3000  writer->writeAttribute( "position", QString::number(static_cast<int>(d->valuesPosition)) );
3001  writer->writeAttribute( "distance", QString::number(d->valuesDistance) );
3002  writer->writeAttribute( "rotation", QString::number(d->valuesRotationAngle) );
3003  writer->writeAttribute( "opacity", QString::number(d->valuesOpacity) );
3004  writer->writeAttribute("numericFormat", QString(d->valuesNumericFormat));
3005  writer->writeAttribute("dateTimeFormat", d->valuesDateTimeFormat);
3006  writer->writeAttribute( "precision", QString::number(d->valuesPrecision) );
3007  writer->writeAttribute( "prefix", d->valuesPrefix );
3008  writer->writeAttribute( "suffix", d->valuesSuffix );
3009  WRITE_QCOLOR(d->valuesColor);
3010  WRITE_QFONT(d->valuesFont);
3011  writer->writeEndElement();
3012 
3013  //Filling
3014  writer->writeStartElement( "filling" );
3015  writer->writeAttribute( "position", QString::number(static_cast<int>(d->fillingPosition)) );
3016  writer->writeAttribute( "type", QString::number(static_cast<int>(d->fillingType)) );
3017  writer->writeAttribute( "colorStyle", QString::number(static_cast<int>(d->fillingColorStyle)) );
3018  writer->writeAttribute( "imageStyle", QString::number(static_cast<int>(d->fillingImageStyle)) );
3019  writer->writeAttribute( "brushStyle", QString::number(d->fillingBrushStyle) );
3020  writer->writeAttribute( "firstColor_r", QString::number(d->fillingFirstColor.red()) );
3021  writer->writeAttribute( "firstColor_g", QString::number(d->fillingFirstColor.green()) );
3022  writer->writeAttribute( "firstColor_b", QString::number(d->fillingFirstColor.blue()) );
3023  writer->writeAttribute( "secondColor_r", QString::number(d->fillingSecondColor.red()) );
3024  writer->writeAttribute( "secondColor_g", QString::number(d->fillingSecondColor.green()) );
3025  writer->writeAttribute( "secondColor_b", QString::number(d->fillingSecondColor.blue()) );
3026  writer->writeAttribute( "fileName", d->fillingFileName );
3027  writer->writeAttribute( "opacity", QString::number(d->fillingOpacity) );
3028  writer->writeEndElement();
3029 
3030  //Error bars
3031  writer->writeStartElement( "errorBars" );
3032  writer->writeAttribute( "xErrorType", QString::number(static_cast<int>(d->xErrorType)) );
3033  WRITE_COLUMN(d->xErrorPlusColumn, xErrorPlusColumn);
3034  WRITE_COLUMN(d->xErrorMinusColumn, xErrorMinusColumn);
3035  writer->writeAttribute( "yErrorType", QString::number(static_cast<int>(d->yErrorType)) );
3036  WRITE_COLUMN(d->yErrorPlusColumn, yErrorPlusColumn);
3037  WRITE_COLUMN(d->yErrorMinusColumn, yErrorMinusColumn);
3038  writer->writeAttribute( "type", QString::number(static_cast<int>(d->errorBarsType)) );
3039  writer->writeAttribute( "capSize", QString::number(d->errorBarsCapSize) );
3040  WRITE_QPEN(d->errorBarsPen);
3041  writer->writeAttribute( "opacity", QString::number(d->errorBarsOpacity) );
3042  writer->writeEndElement();
3043 
3044  writer->writeEndElement(); //close "xyCurve" section
3045 }
3046 
3047 //! Load from XML
3048 bool XYCurve::load(XmlStreamReader* reader, bool preview) {
3049  Q_D(XYCurve);
3050 
3051  if (!readBasicAttributes(reader))
3052  return false;
3053 
3054  KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
3055  QXmlStreamAttributes attribs;
3056  QString str;
3057 
3058  while (!reader->atEnd()) {
3059  reader->readNext();
3060  if (reader->isEndElement() && reader->name() == "xyCurve")
3061  break;
3062 
3063  if (!reader->isStartElement())
3064  continue;
3065 
3066  if (reader->name() == "comment") {
3067  if (!readCommentElement(reader)) return false;
3068  } else if (reader->name() == "general") {
3069  attribs = reader->attributes();
3070  READ_COLUMN(xColumn);
3071  READ_COLUMN(yColumn);
3072 
3073  str = attribs.value("visible").toString();
3074  if (str.isEmpty())
3075  reader->raiseWarning(attributeWarning.subs("visible").toString());
3076  else
3077  d->setVisible(str.toInt());
3078  } else if (!preview && reader->name() == "lines") {
3079  attribs = reader->attributes();
3080 
3081  READ_INT_VALUE("type", lineType, LineType);
3082  READ_INT_VALUE("skipGaps", lineSkipGaps, bool);
3083  READ_INT_VALUE("increasingXOnly", lineIncreasingXOnly, bool);
3084  READ_INT_VALUE("interpolationPointsCount", lineInterpolationPointsCount, int);
3085  READ_QPEN(d->linePen);
3086  READ_DOUBLE_VALUE("opacity", lineOpacity);
3087  } else if (!preview && reader->name() == "dropLines") {
3088  attribs = reader->attributes();
3089 
3090  READ_INT_VALUE("type", dropLineType, DropLineType);
3091  READ_QPEN(d->dropLinePen);
3092  READ_DOUBLE_VALUE("opacity", dropLineOpacity);
3093 
3094  } else if (!preview && reader->name() == "symbols") {
3095  attribs = reader->attributes();
3096 
3097  READ_INT_VALUE("symbolsStyle", symbolsStyle, Symbol::Style);
3098  READ_DOUBLE_VALUE("opacity", symbolsOpacity);
3099  READ_DOUBLE_VALUE("rotation", symbolsRotationAngle);
3100  READ_DOUBLE_VALUE("size", symbolsSize);
3101 
3102  READ_QBRUSH(d->symbolsBrush);
3103  READ_QPEN(d->symbolsPen);
3104  } else if (!preview && reader->name() == "values") {
3105  attribs = reader->attributes();
3106 
3107  READ_INT_VALUE("type", valuesType, ValuesType);
3108  READ_COLUMN(valuesColumn);
3109 
3110  READ_INT_VALUE("position", valuesPosition, ValuesPosition);
3111  READ_DOUBLE_VALUE("distance", valuesDistance);
3112  READ_DOUBLE_VALUE("rotation", valuesRotationAngle);
3113  READ_DOUBLE_VALUE("opacity", valuesOpacity);
3114 
3115  str = attribs.value("numericFormat").toString();
3116  if (str.isEmpty())
3117  reader->raiseWarning(attributeWarning.subs("numericFormat").toString());
3118  else
3119  d->valuesNumericFormat = *(str.toLatin1().data());
3120 
3121  READ_STRING_VALUE("dateTimeFormat", valuesDateTimeFormat);
3122  READ_INT_VALUE("precision", valuesPrecision, int);
3123 
3124  //don't produce any warning if no prefix or suffix is set (empty string is allowed here in xml)
3125  d->valuesPrefix = attribs.value("prefix").toString();
3126  d->valuesSuffix = attribs.value("suffix").toString();
3127 
3128  READ_QCOLOR(d->valuesColor);
3129  READ_QFONT(d->valuesFont);
3130  } else if (!preview && reader->name() == "filling") {
3131  attribs = reader->attributes();
3132 
3133  READ_INT_VALUE("position", fillingPosition, FillingPosition);
3134  READ_INT_VALUE("type", fillingType, PlotArea::BackgroundType);
3135  READ_INT_VALUE("colorStyle", fillingColorStyle, PlotArea::BackgroundColorStyle);
3136  READ_INT_VALUE("imageStyle", fillingImageStyle, PlotArea::BackgroundImageStyle );
3137  READ_INT_VALUE("brushStyle", fillingBrushStyle, Qt::BrushStyle);
3138 
3139  str = attribs.value("firstColor_r").toString();
3140  if (str.isEmpty())
3141  reader->raiseWarning(attributeWarning.subs("firstColor_r").toString());
3142  else
3143  d->fillingFirstColor.setRed(str.toInt());
3144 
3145  str = attribs.value("firstColor_g").toString();
3146  if (str.isEmpty())
3147  reader->raiseWarning(attributeWarning.subs("firstColor_g").toString());
3148  else
3149  d->fillingFirstColor.setGreen(str.toInt());
3150 
3151  str = attribs.value("firstColor_b").toString();
3152  if (str.isEmpty())
3153  reader->raiseWarning(attributeWarning.subs("firstColor_b").toString());
3154  else
3155  d->fillingFirstColor.setBlue(str.toInt());
3156 
3157  str = attribs.value("secondColor_r").toString();
3158  if (str.isEmpty())
3159  reader->raiseWarning(attributeWarning.subs("secondColor_r").toString());
3160  else
3161  d->fillingSecondColor.setRed(str.toInt());
3162 
3163  str = attribs.value("secondColor_g").toString();
3164  if (str.isEmpty())
3165  reader->raiseWarning(attributeWarning.subs("secondColor_g").toString());
3166  else
3167  d->fillingSecondColor.setGreen(str.toInt());
3168 
3169  str = attribs.value("secondColor_b").toString();
3170  if (str.isEmpty())
3171  reader->raiseWarning(attributeWarning.subs("secondColor_b").toString());
3172  else
3173  d->fillingSecondColor.setBlue(str.toInt());
3174 
3175  READ_STRING_VALUE("fileName", fillingFileName);
3176  READ_DOUBLE_VALUE("opacity", fillingOpacity);
3177  } else if (!preview && reader->name() == "errorBars") {
3178  attribs = reader->attributes();
3179 
3180  READ_INT_VALUE("xErrorType", xErrorType, ErrorType);
3181  READ_COLUMN(xErrorPlusColumn);
3182  READ_COLUMN(xErrorMinusColumn);
3183 
3184  READ_INT_VALUE("yErrorType", yErrorType, ErrorType);
3185  READ_COLUMN(yErrorPlusColumn);
3186  READ_COLUMN(yErrorMinusColumn);
3187 
3188  READ_INT_VALUE("type", errorBarsType, ErrorBarsType);
3189  READ_DOUBLE_VALUE("capSize", errorBarsCapSize);
3190 
3191  READ_QPEN(d->errorBarsPen);
3192 
3193  READ_DOUBLE_VALUE("opacity", errorBarsOpacity);
3194  }
3195  }
3196 
3197  return true;
3198 }
3199 
3200 //##############################################################################
3201 //######################### Theme management ##################################
3202 //##############################################################################
3203 void XYCurve::loadThemeConfig(const KConfig& config) {
3204  KConfigGroup group = config.group("XYCurve");
3205 
3206  int index = parentAspect()->indexOfChild<XYCurve>(this);
3207  const auto* plot = dynamic_cast<const CartesianPlot*>(parentAspect());
3208  QColor themeColor;
3209  if (index<plot->themeColorPalette().size())
3210  themeColor = plot->themeColorPalette().at(index);
3211  else {
3212  if (plot->themeColorPalette().size())
3213  themeColor = plot->themeColorPalette().last();
3214  }
3215 
3216  QPen p;
3217 
3218  Q_D(XYCurve);
3219  d->m_suppressRecalc = true;
3220 
3221  //Line
3222  p.setStyle((Qt::PenStyle)group.readEntry("LineStyle", (int)Qt::SolidLine));
3223  p.setWidthF(group.readEntry("LineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
3224  p.setColor(themeColor);
3225  this->setLinePen(p);
3226  this->setLineOpacity(group.readEntry("LineOpacity", 1.0));
3227 
3228  //Drop line
3229  p.setStyle((Qt::PenStyle)group.readEntry("DropLineStyle", (int)Qt::SolidLine));
3230  p.setWidthF(group.readEntry("DropLineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
3231  p.setColor(themeColor);
3232  this->setDropLinePen(p);
3233  this->setDropLineOpacity(group.readEntry("DropLineOpacity", 1.0));
3234 
3235  //Symbol
3236  this->setSymbolsOpacity(group.readEntry("SymbolOpacity", 1.0));
3237  QBrush brush;
3238  brush.setStyle((Qt::BrushStyle)group.readEntry("SymbolFillingStyle", (int)Qt::SolidPattern));
3239  brush.setColor(themeColor);
3240  this->setSymbolsBrush(brush);
3241  p.setStyle((Qt::PenStyle)group.readEntry("SymbolBorderStyle", (int)Qt::SolidLine));
3242  p.setColor(themeColor);
3243  p.setWidthF(group.readEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(0.0, Worksheet::Unit::Point)));
3244  this->setSymbolsPen(p);
3245 
3246  //Values
3247  this->setValuesOpacity(group.readEntry("ValuesOpacity", 1.0));
3248  this->setValuesColor(group.readEntry("ValuesColor", themeColor));
3249 
3250  //Filling
3251  this->setFillingBrushStyle((Qt::BrushStyle)group.readEntry("FillingBrushStyle", (int)Qt::SolidPattern));
3252  this->setFillingColorStyle((PlotArea::BackgroundColorStyle)group.readEntry("FillingColorStyle", static_cast<int>(PlotArea::BackgroundColorStyle::SingleColor)));
3253  this->setFillingOpacity(group.readEntry("FillingOpacity", 1.0));
3254  this->setFillingPosition((FillingPosition)group.readEntry("FillingPosition", static_cast<int>(FillingPosition::NoFilling)));
3255  this->setFillingFirstColor(themeColor);
3256  this->setFillingSecondColor(group.readEntry("FillingSecondColor", QColor(Qt::black)));
3257  this->setFillingType((PlotArea::BackgroundType)group.readEntry("FillingType", static_cast<int>(PlotArea::BackgroundType::Color)));
3258 
3259  //Error Bars
3260  p.setStyle((Qt::PenStyle)group.readEntry("ErrorBarsStyle", (int)Qt::SolidLine));
3261  p.setWidthF(group.readEntry("ErrorBarsWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
3262  p.setColor(themeColor);
3263  this->setErrorBarsPen(p);
3264  this->setErrorBarsOpacity(group.readEntry("ErrorBarsOpacity", 1.0));
3265 
3266  d->m_suppressRecalc = false;
3267  d->recalcShapeAndBoundingRect();
3268 }
3269 
3270 void XYCurve::saveThemeConfig(const KConfig& config) {
3271  KConfigGroup group = config.group("XYCurve");
3272 
3273  //Drop line
3274  group.writeEntry("DropLineColor",(QColor) this->dropLinePen().color());
3275  group.writeEntry("DropLineStyle",(int) this->dropLinePen().style());
3276  group.writeEntry("DropLineWidth", this->dropLinePen().widthF());
3277  group.writeEntry("DropLineOpacity",this->dropLineOpacity());
3278 
3279  //Error Bars
3280  group.writeEntry("ErrorBarsCapSize",this->errorBarsCapSize());
3281  group.writeEntry("ErrorBarsOpacity",this->errorBarsOpacity());
3282  group.writeEntry("ErrorBarsColor",(QColor) this->errorBarsPen().color());
3283  group.writeEntry("ErrorBarsStyle",(int) this->errorBarsPen().style());
3284  group.writeEntry("ErrorBarsWidth", this->errorBarsPen().widthF());
3285 
3286  //Filling
3287  group.writeEntry("FillingBrushStyle",(int) this->fillingBrushStyle());
3288  group.writeEntry("FillingColorStyle",(int) this->fillingColorStyle());
3289  group.writeEntry("FillingOpacity", this->fillingOpacity());
3290  group.writeEntry("FillingPosition",(int) this->fillingPosition());
3291  group.writeEntry("FillingSecondColor",(QColor) this->fillingSecondColor());
3292  group.writeEntry("FillingType",(int) this->fillingType());
3293 
3294  //Line
3295  group.writeEntry("LineOpacity", this->lineOpacity());
3296  group.writeEntry("LineStyle",(int) this->linePen().style());
3297  group.writeEntry("LineWidth", this->linePen().widthF());
3298 
3299  //Symbol
3300  group.writeEntry("SymbolOpacity", this->symbolsOpacity());
3301 
3302  //Values
3303  group.writeEntry("ValuesOpacity", this->valuesOpacity());
3304  group.writeEntry("ValuesColor", (QColor) this->valuesColor());
3305  group.writeEntry("ValuesFont", this->valuesFont());
3306 
3307  int index = parentAspect()->indexOfChild<XYCurve>(this);
3308  if (index < 5) {
3309  KConfigGroup themeGroup = config.group("Theme");
3310  for (int i = index; i < 5; i++) {
3311  QString s = "ThemePaletteColor" + QString::number(i+1);
3312  themeGroup.writeEntry(s, (QColor)this->linePen().color());
3313  }
3314  }
3315 }
AspectType
static const QRgb white
Definition: ImageEditor.cpp:37
static const QRgb black
Definition: ImageEditor.cpp:38
STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineOpacity, qreal, lineOpacity, updatePixmap)
Base class of all persistent objects in a Project.
void info(const QString &text)
Implementations should call this whenever status information should be given to the user.
AspectType type() const
bool readCommentElement(XmlStreamReader *)
Load comment from an XML element.
int indexOfChild(const AbstractAspect *child, ChildIndexFlags flags={}) const
Return (0 based) index of child in the list of children inheriting from class T.
AbstractAspect * parent(AspectType type) const
In the parent-child hierarchy, return the first parent of type.
QString name() const
void writeBasicAttributes(QXmlStreamWriter *) const
Save name and creation time to XML.
AbstractAspect * parentAspect() const
Return my parent Aspect or 0 if I currently don't have one.
virtual QString path() const
Return the path that leads from the top-most Aspect (usually a Project) to me.
void exec(QUndoCommand *)
Execute the given command, pushing it on the undoStack() if available.
bool readBasicAttributes(XmlStreamReader *)
Load name and creation time from XML.
void writeCommentElement(QXmlStreamWriter *) const
Save the comment to XML.
virtual Project * project()
Return the Project this Aspect belongs to, or 0 if it is currently not part of one.
AbstractAspectPrivate * d
Interface definition for data with column logic.
virtual double valueAt(int row) const
Return the double value in row 'row'.
virtual Properties properties() const
bool isMasked(int row) const
Return whether a certain row is masked.
virtual QString textAt(int row) const
Return the content of row 'row'.
virtual int rowCount() const =0
Return the data vector size.
virtual double maximum(int count=0) const
virtual double minimum(int count=0) const
virtual QDateTime dateTimeAt(int row) const
Return the QDateTime in row 'row'.
void dataChanged(const AbstractColumn *source)
Data of the column has changed.
bool isValid(int row) const
Convenience method for mode-independent testing of validity.
virtual ColumnMode columnMode() const =0
Return the column mode.
Cartesian coordinate system for plots.
QVector< QPointF > mapLogicalToScene(const QVector< QPointF > &, MappingFlags flags=MappingFlag::DefaultMapping) const override
QVector< QPointF > mapSceneToLogical(const QVector< QPointF > &, MappingFlags flags=MappingFlag::DefaultMapping) const override
A xy-plot.
Definition: CartesianPlot.h:58
bool isPanningActive() const
QRectF dataRect() const
MouseMode mouseMode() const
static int indexForValue(double x, QVector< double > &column, Properties properties=Properties::No)
Definition: Column.cpp:1831
static QImage blurred(const QImage &image, QRect rect, int radius, bool alphaOnly=false)
Definition: ImageTools.cpp:35
BackgroundColorStyle
Definition: PlotArea.h:46
BackgroundImageStyle
Definition: PlotArea.h:48
BackgroundType
Definition: PlotArea.h:45
void navigateTo(const QString &path)
Definition: Project.cpp:344
Aspect providing a spreadsheet table with column logic.
Definition: Spreadsheet.h:40
static QPainterPath pathFromStyle(Symbol::Style)
Definition: Symbol.cpp:43
Style
Definition: Symbol.h:38
Base class for all Worksheet children.
static QPainterPath shapeFromPath(const QPainterPath &, const QPen &)
QMenu * createContextMenu() override
Return a new context menu.
Top-level container for worksheet elements like plot, labels, etc.
Definition: Worksheet.h:46
void suppressSelectionChangedEvent(bool)
Definition: Worksheet.cpp:408
static double convertToSceneUnits(const double value, const Worksheet::Unit unit)
Definition: Worksheet.cpp:113
QPainterPath linePath
double errorBarsCapSize
QRectF boundingRectangle
void retransform()
Definition: XYCurve.cpp:963
int lineInterpolationPointsCount
void updateSymbols()
Definition: XYCurve.cpp:1678
void recalcShapeAndBoundingRect()
Definition: XYCurve.cpp:2610
PlotArea::BackgroundType fillingType
const AbstractColumn * xErrorMinusColumn
bool activateCurve(QPointF mouseScenePos, double maxDist)
Definition: XYCurve.cpp:2293
std::vector< bool > connectedPointsLogical
std::vector< int > validPointsIndicesLogical
void updateErrorBars()
Definition: XYCurve.cpp:2495
XYCurve::ErrorBarsType errorBarsType
XYCurvePrivate(XYCurve *)
Definition: XYCurve.cpp:914
QPainterPath shape() const override
Definition: XYCurve.cpp:930
void updateDropLines()
Definition: XYCurve.cpp:1616
bool pointLiesNearCurve(const QPointF mouseScenePos, const QPointF curvePosPrevScene, const QPointF curvePosScene, const int index, const double maxDist) const
Definition: XYCurve.cpp:2430
void updateValues()
Definition: XYCurve.cpp:1709
QVector< QString > m_valueStrings
PlotArea::BackgroundImageStyle fillingImageStyle
QVector< QPointF > m_scenePoints
XYCurve::ErrorType xErrorType
void contextMenuEvent(QGraphicsSceneContextMenuEvent *) override
Definition: XYCurve.cpp:934
XYCurve::ValuesType valuesType
QPainterPath dropLinePath
QRectF boundingRect() const override
Definition: XYCurve.cpp:923
qreal symbolsRotationAngle
QImage m_selectionEffectImage
QVector< QPointF > m_logicalPoints
const AbstractColumn * xColumn
bool swapVisible(bool)
Definition: XYCurve.cpp:942
void drawValues(QPainter *)
Definition: XYCurve.cpp:2804
XYCurve::ValuesPosition valuesPosition
Qt::BrushStyle fillingBrushStyle
QColor fillingSecondColor
void drawFilling(QPainter *)
Definition: XYCurve.cpp:2819
QVector< QPolygonF > m_fillPolygons
void addUniqueLine(QPointF p0, QPointF p1, QPointF &lastPoint, qint64 &pixelDiff)
XYCurvePrivate::addUniqueLine This function is called from the other two addLine() functions to avoid...
Definition: XYCurve.cpp:1221
void updatePixmap()
Definition: XYCurve.cpp:2702
bool lineIncreasingXOnly
QString valuesDateTimeFormat
QColor fillingFirstColor
const CartesianPlot * plot
void drawSymbols(QPainter *)
Definition: XYCurve.cpp:2786
void setPrinting(bool)
Definition: XYCurve.cpp:2908
QImage m_hoverEffectImage
XYCurve::FillingPosition fillingPosition
void recalcLogicalPoints()
Definition: XYCurve.cpp:1090
QVector< bool > m_pointVisible
XYCurve *const q
const CartesianCoordinateSystem * cSystem
QPainterPath errorBarsPath
const AbstractColumn * yErrorPlusColumn
void updateLines()
Definition: XYCurve.cpp:1252
QPainterPath valuesPath
XYCurve::ErrorType yErrorType
QString fillingFileName
bool m_hoverEffectImageIsDirty
void draw(QPainter *)
Definition: XYCurve.cpp:2649
QVector< QPointF > m_valuePoints
const AbstractColumn * xErrorPlusColumn
bool pointLiesNearLine(const QPointF p1, const QPointF p2, const QPointF pos, const double maxDist) const
XYCurve::pointLiesNearLine Calculates if a point pos lies near than maxDist to the line created by th...
Definition: XYCurve.cpp:2403
const AbstractColumn * valuesColumn
void mousePressEvent(QGraphicsSceneMouseEvent *) override
Definition: XYCurve.cpp:2922
bool m_suppressRetransform
QString valuesPrefix
void updateFilling()
Definition: XYCurve.cpp:1882
void addLine(QPointF p0, QPointF p1, QPointF &lastPoint, qint64 &pixelDiff, int numberOfPixelX)
Definition: XYCurve.cpp:1182
const AbstractColumn * yErrorMinusColumn
void setHover(bool on)
Definition: XYCurve.cpp:2942
QPainterPath symbolsPath
qreal valuesRotationAngle
const AbstractColumn * yColumn
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *widget=nullptr) override
Definition: XYCurve.cpp:2735
XYCurve::DropLineType dropLineType
void suppressRetransform(bool)
Definition: XYCurve.cpp:2912
QString name() const
Definition: XYCurve.cpp:919
QVector< QLineF > m_lines
QString valuesSuffix
QPainterPath curveShape
void addLinearLine(QPointF p0, QPointF p1, QPointF &lastPoint, double minLogicalDiffX, qint64 &pixelDiff)
Definition: XYCurve.cpp:1165
XYCurve::LineType lineType
PlotArea::BackgroundColorStyle fillingColorStyle
bool m_selectionEffectImageIsDirty
Symbol::Style symbolsStyle
A 2D-curve, provides an interface for editing many properties of the curve.
Definition: XYCurve.h:46
void yColumnAboutToBeRemoved(const AbstractAspect *)
Definition: XYCurve.cpp:810
bool isVisible() const override
Return whether the element is (at least) partially visible.
Definition: XYCurve.cpp:216
void setPrinting(bool on) override
Switches the printing mode on/off.
Definition: XYCurve.cpp:221
friend class XYCurveSetValuesColumnCmd
Definition: XYCurve.h:56
QIcon icon() const override
Definition: XYCurve.cpp:202
bool activateCurve(QPointF mouseScenePos, double maxDist=-1) override
XYCurve::activateCurve Checks if the mousepos distance to the curve is less than maxDist mouseScenePo...
Definition: XYCurve.cpp:233
void xErrorMinusColumnAboutToBeRemoved(const AbstractAspect *)
Definition: XYCurve.cpp:837
void navigateTo()
Definition: XYCurve.cpp:907
QAction * visibilityAction
Definition: XYCurve.h:200
void finalizeAdd() override
Definition: XYCurve.cpp:82
void valuesColumnNameChanged()
Definition: XYCurve.cpp:894
LineType
Definition: XYCurve.h:57
void dataChanged()
void handleResize(double horizontalRatio, double verticalRatio, bool pageResize) override
Definition: XYCurve.cpp:781
void xErrorPlusColumnAboutToBeRemoved(const AbstractAspect *)
Definition: XYCurve.cpp:828
ErrorType
Definition: XYCurve.h:63
bool minMax(const AbstractColumn *column1, const AbstractColumn *column2, const ErrorType errorType, const AbstractColumn *errorPlusColumn, const AbstractColumn *errorMinusColumn, int indexMin, int indexMax, double &yMin, double &yMax, bool includeErrorBars) const
Definition: XYCurve.cpp:2207
ValuesType
Definition: XYCurve.h:61
QMenu * createContextMenu() override
Return a new context menu.
Definition: XYCurve.cpp:164
bool minMaxY(int indexMin, int indexMax, double &yMin, double &yMax, bool includeErrorBars=true) const
Definition: XYCurve.cpp:2185
void setVisible(bool on) override
Show/hide the element.
Definition: XYCurve.cpp:211
void visibilityChanged()
Definition: XYCurve.cpp:902
void xColumnNameChanged()
Definition: XYCurve.cpp:864
QGraphicsItem * graphicsItem() const override
Return the graphics item representing this element.
Definition: XYCurve.cpp:206
friend class XYCurveSetXColumnCmd
Definition: XYCurve.h:50
friend class XYCurveSetYErrorPlusColumnCmd
Definition: XYCurve.h:54
void updateErrorBars()
Definition: XYCurve.cpp:775
friend class XYCurveSetXErrorMinusColumnCmd
Definition: XYCurve.h:53
friend class XYCurveSetYColumnCmd
Definition: XYCurve.h:51
void yErrorMinusColumnAboutToBeRemoved(const AbstractAspect *)
Definition: XYCurve.cpp:855
void yErrorPlusColumnNameChanged()
Definition: XYCurve.cpp:884
~XYCurve() override
void saveThemeConfig(const KConfig &) override
Definition: XYCurve.cpp:3270
bool load(XmlStreamReader *, bool preview) override
Load from XML.
Definition: XYCurve.cpp:3048
void retransform() override
Tell the element to newly transform its graphics item into its coordinate system.
Definition: XYCurve.cpp:760
void initActions()
Definition: XYCurve.cpp:153
void yErrorMinusColumnNameChanged()
Definition: XYCurve.cpp:889
ValuesPosition
Definition: XYCurve.h:62
bool minMaxX(int indexMin, int indexMax, double &yMin, double &yMax, bool includeErrorBars=true) const
Definition: XYCurve.cpp:2189
QAction * navigateToAction
Definition: XYCurve.h:201
friend class XYCurveSetXErrorPlusColumnCmd
Definition: XYCurve.h:52
bool m_menusInitialized
Definition: XYCurve.h:202
void xErrorMinusColumnNameChanged()
Definition: XYCurve.cpp:879
void save(QXmlStreamWriter *) const override
Save as XML.
Definition: XYCurve.cpp:2955
QDateTime yDateTime(double x, bool &valueFound) const
Definition: XYCurve.cpp:2162
void suppressRetransform(bool)
Definition: XYCurve.cpp:752
void valuesColumnAboutToBeRemoved(const AbstractAspect *)
Definition: XYCurve.cpp:819
double y(double x, bool &valueFound) const
Definition: XYCurve.cpp:2132
void yErrorPlusColumnAboutToBeRemoved(const AbstractAspect *)
Definition: XYCurve.cpp:846
void init()
Definition: XYCurve.cpp:88
XYCurvePrivate *const d_ptr
Definition: XYCurve.h:186
XYCurve(const QString &name, AspectType type=AspectType::XYCurve)
Definition: XYCurve.cpp:66
void loadThemeConfig(const KConfig &) override
Definition: XYCurve.cpp:3203
FillingPosition
Definition: XYCurve.h:64
void setHover(bool on) override
XYCurve::setHover Will be called in CartesianPlot::hoverMoveEvent() See d->setHover(on) for more docu...
Definition: XYCurve.cpp:244
void xErrorPlusColumnNameChanged()
Definition: XYCurve.cpp:874
ErrorBarsType
Definition: XYCurve.h:65
void yColumnNameChanged()
Definition: XYCurve.cpp:869
void xColumnAboutToBeRemoved(const AbstractAspect *)
Definition: XYCurve.cpp:801
friend class XYCurveSetYErrorMinusColumnCmd
Definition: XYCurve.h:55
void recalcLogicalPoints()
Definition: XYCurve.cpp:765
DropLineType
Definition: XYCurve.h:60
void updateValues()
Definition: XYCurve.cpp:770
XML stream parser that supports errors as well as warnings. This class also adds line and column numb...
void raiseWarning(const QString &)
#define XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(cmd_name, prefix, finalize_method)
Definition: macrosXYCurve.h:70
#define WAIT_CURSOR
Definition: macros.h:63
#define READ_DOUBLE_VALUE(name, var)
Definition: macros.h:475
#define STDSTRING(qstr)
Definition: macros.h:67
#define WRITE_QCOLOR(color)
Definition: macros.h:286
#define CLASS_SHARED_D_READER_IMPL(classname, type, method, var)
Definition: macros.h:154
#define RESET_CURSOR
Definition: macros.h:64
#define DEBUG(x)
Definition: macros.h:50
#define SET_NUMBER_LOCALE
Definition: macros.h:75
#define READ_COLUMN(columnName)
Definition: macros.h:462
#define READ_INT_VALUE(name, var, type)
Definition: macros.h:468
constexpr std::add_const_t< T > & qAsConst(T &t) noexcept
Definition: macros.h:58
#define READ_QPEN(pen)
Definition: macros.h:324
#define WRITE_QPEN(pen)
Definition: macros.h:315
#define READ_QCOLOR(color)
Definition: macros.h:293
#define READ_QBRUSH(brush)
Definition: macros.h:418
#define STD_SWAP_METHOD_SETTER_CMD_IMPL(class_name, cmd_name, value_type, method_name)
Definition: macros.h:250
#define BASIC_SHARED_D_READER_IMPL(classname, type, method, var)
Definition: macros.h:127
#define READ_STRING_VALUE(name, var)
Definition: macros.h:482
#define WRITE_QBRUSH(brush)
Definition: macros.h:410
#define WRITE_COLUMN(column, columnName)
Definition: macros.h:451
#define WRITE_QFONT(font)
Definition: macros.h:361
#define READ_QFONT(font)
Definition: macros.h:370
@ Shadow
Definition: OriginObj.h:71
#define i18n(m)
Definition: nsl_common.h:38
bool nsl_math_essentially_equal(double a, double b)
Definition: nsl_math.c:40
#define PERFTRACE(msg)
Definition: trace.h:57