File indexing completed on 2024-04-21 03:48:05

0001 /*
0002     File                 : CartesianPlotTest.cpp
0003     Project              : LabPlot
0004     Description          : Tests for cartesian plot
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2022 Stefan Gerlach <stefan.gerlach@uni.kn>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "CartesianPlotTest.h"
0012 #include "tests/CommonTest.h"
0013 
0014 #include "backend/core/Project.h"
0015 #include "backend/core/Workbook.h"
0016 #include "backend/lib/macros.h"
0017 #include "backend/matrix/Matrix.h"
0018 #include "backend/spreadsheet/Spreadsheet.h"
0019 #include "backend/worksheet/InfoElement.h"
0020 #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h"
0021 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
0022 #include "backend/worksheet/plots/cartesian/CartesianPlotPrivate.h"
0023 #include "backend/worksheet/plots/cartesian/Histogram.h"
0024 #include "backend/worksheet/plots/cartesian/XYCurve.h"
0025 #include "backend/worksheet/plots/cartesian/XYEquationCurve.h"
0026 #include "backend/worksheet/plots/cartesian/XYFitCurve.h"
0027 #include "commonfrontend/worksheet/WorksheetView.h"
0028 #include "kdefrontend/dockwidgets/XYFitCurveDock.h"
0029 
0030 #include <QAction>
0031 #include <QUndoStack>
0032 
0033 void CartesianPlotTest::initTestCase() {
0034     //  // needed in order to have the signals triggered by SignallingUndoCommand, see LabPlot.cpp
0035     //  //TODO: redesign/remove this
0036     qRegisterMetaType<const AbstractAspect*>("const AbstractAspect*");
0037     qRegisterMetaType<const AbstractColumn*>("const AbstractColumn*");
0038 }
0039 
0040 // ##############################################################################
0041 // #####################  import of LabPlot projects ############################
0042 // ##############################################################################
0043 
0044 #define LOAD_PROJECT_DATA_CHANGE                                                                                                                               \
0045     Project project;                                                                                                                                           \
0046     project.load(QFINDTESTDATA(QLatin1String("data/TestDataChange.lml")));                                                                                     \
0047                                                                                                                                                                \
0048     /* check the project tree for the imported project */                                                                                                      \
0049     /* Spreadsheet */                                                                                                                                          \
0050     auto* aspect = project.child<AbstractAspect>(0);                                                                                                           \
0051     QVERIFY(aspect != nullptr);                                                                                                                                \
0052     if (aspect)                                                                                                                                                \
0053         QCOMPARE(aspect->name(), QLatin1String("Spreadsheet"));                                                                                                \
0054     QVERIFY(aspect->type() == AspectType::Spreadsheet);                                                                                                        \
0055     auto s = dynamic_cast<Spreadsheet*>(aspect);                                                                                                               \
0056     if (!s)                                                                                                                                                    \
0057         return;                                                                                                                                                \
0058     auto* c1 = static_cast<Column*>(s->child<Column>(0));                                                                                                      \
0059     QVERIFY(c1 != nullptr);                                                                                                                                    \
0060     QCOMPARE(c1->name(), QLatin1String("1"));                                                                                                                  \
0061     QVERIFY(c1->columnMode() == AbstractColumn::ColumnMode::Double);                                                                                           \
0062     QVERIFY(c1->rowCount() == 100);                                                                                                                            \
0063     QVERIFY(c1->valueAt(0) == 1.);                                                                                                                             \
0064     QVERIFY(c1->valueAt(1) == 2.);                                                                                                                             \
0065     auto* c2 = static_cast<Column*>(s->child<Column>(1));                                                                                                      \
0066     QVERIFY(c2 != nullptr);                                                                                                                                    \
0067     QCOMPARE(c2->name(), QLatin1String("2"));                                                                                                                  \
0068     QVERIFY(c2->columnMode() == AbstractColumn::ColumnMode::Double);                                                                                           \
0069     QVERIFY(c2->rowCount() == 100);                                                                                                                            \
0070     QVERIFY(c2->valueAt(0) == 1.);                                                                                                                             \
0071     QVERIFY(c2->valueAt(1) == 2.);                                                                                                                             \
0072                                                                                                                                                                \
0073     /* Worksheet */                                                                                                                                            \
0074     aspect = project.child<AbstractAspect>(1);                                                                                                                 \
0075     QVERIFY(aspect != nullptr);                                                                                                                                \
0076     if (aspect)                                                                                                                                                \
0077         QCOMPARE(aspect->name(), QLatin1String("Worksheet - Spreadsheet"));                                                                                    \
0078     QVERIFY(aspect->type() == AspectType::Worksheet);                                                                                                          \
0079     auto* w = dynamic_cast<Worksheet*>(aspect);                                                                                                                \
0080     if (!w)                                                                                                                                                    \
0081         return;                                                                                                                                                \
0082                                                                                                                                                                \
0083     auto* plot = dynamic_cast<CartesianPlot*>(aspect->child<CartesianPlot>(0));                                                                                \
0084     QVERIFY(plot != nullptr);                                                                                                                                  \
0085     if (!plot)                                                                                                                                                 \
0086         return;                                                                                                                                                \
0087                                                                                                                                                                \
0088     /* curve */                                                                                                                                                \
0089     auto* curve = dynamic_cast<XYCurve*>(plot->child<XYCurve>(0));                                                                                             \
0090     QVERIFY(curve != nullptr);                                                                                                                                 \
0091     if (!curve)                                                                                                                                                \
0092         return;                                                                                                                                                \
0093     QCOMPARE(curve->name(), QStringLiteral("2"));                                                                                                              \
0094                                                                                                                                                                \
0095     CHECK_RANGE(plot, curve, Dimension::X, 1., 2.);                                                                                                            \
0096     CHECK_RANGE(plot, curve, Dimension::Y, 1., 2.);                                                                                                            \
0097                                                                                                                                                                \
0098     auto* xAxis = static_cast<Axis*>(plot->child<Axis>(0));                                                                                                    \
0099     QVERIFY(xAxis != nullptr);                                                                                                                                 \
0100     QCOMPARE(xAxis->orientation() == Axis::Orientation::Horizontal, true);                                                                                     \
0101                                                                                                                                                                \
0102     auto* yAxis = static_cast<Axis*>(plot->child<Axis>(2));                                                                                                    \
0103     QVERIFY(yAxis != nullptr);                                                                                                                                 \
0104     QCOMPARE(yAxis->orientation() == Axis::Orientation::Vertical, true);
0105 
0106 #define LOAD_PROJECT_HISTOGRAM_FIT_CURVE                                                                                                                       \
0107     Project project;                                                                                                                                           \
0108     project.load(QFINDTESTDATA(QLatin1String("data/histogram-fit-curve.lml")));                                                                                \
0109                                                                                                                                                                \
0110     /* TODO: check the project tree for the imported project */                                                                                                \
0111     /* Spreadsheet */                                                                                                                                          \
0112     auto* aspect = project.child<AbstractAspect>(0);                                                                                                           \
0113     QVERIFY(aspect != nullptr);                                                                                                                                \
0114     if (aspect)                                                                                                                                                \
0115         QCOMPARE(aspect->name(), QLatin1String("Spreadsheet"));                                                                                                \
0116     QVERIFY(aspect->type() == AspectType::Spreadsheet);                                                                                                        \
0117     auto s = dynamic_cast<Spreadsheet*>(aspect);                                                                                                               \
0118     if (!s)                                                                                                                                                    \
0119         return;                                                                                                                                                \
0120     auto* c1 = static_cast<Column*>(s->child<Column>(0));                                                                                                      \
0121     QVERIFY(c1 != nullptr);                                                                                                                                    \
0122     QCOMPARE(c1->name(), QLatin1String("Data"));                                                                                                               \
0123     QVERIFY(c1->columnMode() == AbstractColumn::ColumnMode::Double);                                                                                           \
0124     QVERIFY(c1->rowCount() == 10000);                                                                                                                          \
0125                                                                                                                                                                \
0126     /* Worksheet */                                                                                                                                            \
0127     aspect = project.child<AbstractAspect>(1);                                                                                                                 \
0128     QVERIFY(aspect != nullptr);                                                                                                                                \
0129     if (aspect)                                                                                                                                                \
0130         QCOMPARE(aspect->name(), QLatin1String("Worksheet - Spreadsheet"));                                                                                    \
0131     QVERIFY(aspect->type() == AspectType::Worksheet);                                                                                                          \
0132     auto* w = dynamic_cast<Worksheet*>(aspect);                                                                                                                \
0133     if (!w)                                                                                                                                                    \
0134         return;                                                                                                                                                \
0135                                                                                                                                                                \
0136     auto* plot = dynamic_cast<CartesianPlot*>(aspect->child<CartesianPlot>(0));                                                                                \
0137     QVERIFY(plot != nullptr);                                                                                                                                  \
0138     if (!plot)                                                                                                                                                 \
0139         return;                                                                                                                                                \
0140                                                                                                                                                                \
0141     auto* h = dynamic_cast<Histogram*>(plot->child<Histogram>(0));                                                                                             \
0142     QVERIFY(h != nullptr);                                                                                                                                     \
0143     if (!h)                                                                                                                                                    \
0144         return;                                                                                                                                                \
0145     QCOMPARE(h->name(), QStringLiteral("histogram"));                                                                                                          \
0146                                                                                                                                                                \
0147     /* curves */                                                                                                                                               \
0148     auto* curve1 = dynamic_cast<XYCurve*>(plot->child<XYCurve>(0));                                                                                            \
0149     QVERIFY(curve1 != nullptr);                                                                                                                                \
0150     if (!curve1)                                                                                                                                               \
0151         return;                                                                                                                                                \
0152     QCOMPARE(curve1->name(), QStringLiteral("fit"));                                                                                                           \
0153     auto* curve2 = dynamic_cast<XYCurve*>(plot->child<XYCurve>(1));                                                                                            \
0154     QVERIFY(curve2 != nullptr);                                                                                                                                \
0155     if (!curve2)                                                                                                                                               \
0156         return;                                                                                                                                                \
0157     QCOMPARE(curve2->name(), QStringLiteral("f(x)"));                                                                                                          \
0158                                                                                                                                                                \
0159     CHECK_RANGE(plot, curve1, Dimension::X, -4., 4.);                                                                                                          \
0160     CHECK_RANGE(plot, curve1, Dimension::Y, 0., 1.);
0161 
0162 #define SET_CARTESIAN_MOUSE_MODE(mode)                                                                                                                         \
0163     QAction a(nullptr);                                                                                                                                        \
0164     a.setData(static_cast<int>(mode));                                                                                                                         \
0165     view->cartesianPlotMouseModeChanged(&a);
0166 
0167 /*!
0168  * \brief CartesianPlotTest::changeData1: add data point
0169  *
0170  */
0171 
0172 void CartesianPlotTest::changeData1() {
0173     LOAD_PROJECT_DATA_CHANGE
0174 
0175     // insert data
0176     c1->setValueAt(2, 3.);
0177     c2->setValueAt(2, 3.);
0178 
0179     QVERIFY(c1->valueAt(2) == 3.);
0180     QVERIFY(c2->valueAt(2) == 3.);
0181 
0182     CHECK_RANGE(plot, curve, Dimension::X, 1., 3.);
0183     CHECK_RANGE(plot, curve, Dimension::Y, 1., 3.);
0184 }
0185 
0186 void CartesianPlotTest::changeData2() {
0187     LOAD_PROJECT_DATA_CHANGE
0188 
0189     // insert data
0190     c1->setValueAt(2, 3.);
0191     c2->setValueAt(2, 2.);
0192 
0193     QVERIFY(c1->valueAt(2) == 3.);
0194     QVERIFY(c2->valueAt(2) == 2.);
0195 
0196     CHECK_RANGE(plot, curve, Dimension::X, 1., 3.);
0197     CHECK_RANGE(plot, curve, Dimension::Y, 1., 2.);
0198 
0199     DEBUG_RANGE(plot, curve);
0200 }
0201 
0202 void CartesianPlotTest::changeData3() {
0203     LOAD_PROJECT_DATA_CHANGE
0204 
0205     // insert data
0206     c1->setValueAt(2, 2.);
0207     c2->setValueAt(2, 3.);
0208 
0209     QVERIFY(c1->valueAt(2) == 2.);
0210     QVERIFY(c2->valueAt(2) == 3.);
0211 
0212     CHECK_RANGE(plot, curve, Dimension::X, 1., 2.);
0213     CHECK_RANGE(plot, curve, Dimension::Y, 1., 3.);
0214 }
0215 
0216 void CartesianPlotTest::changeData4() {
0217     LOAD_PROJECT_DATA_CHANGE
0218 
0219     // insert data
0220     c2->setValueAt(2, 3.);
0221     c1->setValueAt(2, 3.);
0222 
0223     QVERIFY(c1->valueAt(2) == 3.);
0224     QVERIFY(c2->valueAt(2) == 3.);
0225 
0226     CHECK_RANGE(plot, curve, Dimension::X, 1., 3.);
0227     CHECK_RANGE(plot, curve, Dimension::Y, 1., 3.);
0228 }
0229 
0230 void CartesianPlotTest::changeData5() {
0231     LOAD_PROJECT_DATA_CHANGE
0232 
0233     // insert data
0234     c2->setValueAt(2, 2.);
0235     c1->setValueAt(2, 3.);
0236 
0237     QVERIFY(c1->valueAt(2) == 3.);
0238     QVERIFY(c2->valueAt(2) == 2.);
0239 
0240     CHECK_RANGE(plot, curve, Dimension::X, 1., 3.);
0241     CHECK_RANGE(plot, curve, Dimension::Y, 1., 2.);
0242 }
0243 
0244 void CartesianPlotTest::changeData6() {
0245     LOAD_PROJECT_DATA_CHANGE
0246 
0247     // insert data
0248     c2->setValueAt(2, 3.);
0249     c1->setValueAt(2, 2.);
0250 
0251     QVERIFY(c1->valueAt(2) == 2.);
0252     QVERIFY(c2->valueAt(2) == 3.);
0253 
0254     CHECK_RANGE(plot, curve, Dimension::X, 1., 2.);
0255     CHECK_RANGE(plot, curve, Dimension::Y, 1., 3.);
0256 }
0257 
0258 // check deleting curve
0259 
0260 void CartesianPlotTest::deleteCurveAutoscale() {
0261     LOAD_PROJECT_HISTOGRAM_FIT_CURVE
0262 
0263     // delete curve in plot
0264     plot->removeChild(curve2);
0265 
0266     CHECK_RANGE(plot, curve1, Dimension::X, -4., 4.);
0267     CHECK_RANGE(plot, curve1, Dimension::Y, 0., 0.45);
0268 }
0269 
0270 void CartesianPlotTest::deleteCurveNoAutoscale() {
0271     LOAD_PROJECT_HISTOGRAM_FIT_CURVE
0272     const auto cs = plot->coordinateSystem(curve2->coordinateSystemIndex());
0273     plot->enableAutoScale(Dimension::Y, cs->index(Dimension::Y), false, false);
0274 
0275     CHECK_RANGE(plot, curve1, Dimension::X, -4., 4.);
0276     CHECK_RANGE(plot, curve1, Dimension::Y, 0., 1.);
0277 
0278     // delete curve in plot
0279     plot->removeChild(curve2);
0280 
0281     CHECK_RANGE(plot, curve1, Dimension::X, -4., 4.);
0282     CHECK_RANGE(plot, curve1, Dimension::Y, 0., 1.);
0283 
0284     QCOMPARE(plot->autoScale(Dimension::Y, cs->index(Dimension::Y)), false);
0285 }
0286 
0287 void CartesianPlotTest::invisibleCurveAutoscale() {
0288     LOAD_PROJECT_HISTOGRAM_FIT_CURVE
0289 
0290     curve2->setVisible(false);
0291 
0292     CHECK_RANGE(plot, curve1, Dimension::X, -4., 4.);
0293     CHECK_RANGE(plot, curve1, Dimension::Y, 0., 0.45);
0294 }
0295 
0296 void CartesianPlotTest::invisibleCurveNoAutoscale() {
0297     LOAD_PROJECT_HISTOGRAM_FIT_CURVE
0298     const auto cs = plot->coordinateSystem(curve2->coordinateSystemIndex());
0299     plot->enableAutoScale(Dimension::Y, cs->index(Dimension::Y), false, false);
0300 
0301     CHECK_RANGE(plot, curve1, Dimension::X, -4., 4.);
0302     CHECK_RANGE(plot, curve1, Dimension::Y, 0., 1.);
0303 
0304     curve2->setVisible(false);
0305 
0306     CHECK_RANGE(plot, curve1, Dimension::X, -4., 4.);
0307     CHECK_RANGE(plot, curve1, Dimension::Y, 0., 1.);
0308 
0309     QCOMPARE(plot->autoScale(Dimension::Y, cs->index(Dimension::Y)), false);
0310 }
0311 
0312 void CartesianPlotTest::equationCurveEquationChangedAutoScale() {
0313     LOAD_PROJECT_HISTOGRAM_FIT_CURVE
0314     const auto cs = plot->coordinateSystem(curve2->coordinateSystemIndex());
0315 
0316     QCOMPARE(curve2->type(), AspectType::XYEquationCurve);
0317     auto eqc = static_cast<XYEquationCurve*>(curve2);
0318 
0319     auto equationData = eqc->equationData();
0320     equationData.max = QStringLiteral("10");
0321     eqc->setEquationData(equationData);
0322 
0323     CHECK_RANGE(plot, curve2, Dimension::X, -4., 10.);
0324     CHECK_RANGE(plot, curve2, Dimension::Y, 0., 10.);
0325 
0326     QCOMPARE(plot->autoScale(Dimension::Y, cs->index(Dimension::Y)), true);
0327 }
0328 
0329 void CartesianPlotTest::equationCurveEquationChangedNoAutoScale() {
0330     LOAD_PROJECT_HISTOGRAM_FIT_CURVE
0331     const auto cs = plot->coordinateSystem(curve2->coordinateSystemIndex());
0332     plot->enableAutoScale(Dimension::Y, cs->index(Dimension::Y), false, false);
0333 
0334     QCOMPARE(curve2->type(), AspectType::XYEquationCurve);
0335     auto eqc = static_cast<XYEquationCurve*>(curve2);
0336 
0337     auto equationData = eqc->equationData();
0338     equationData.max = QStringLiteral("10");
0339     eqc->setEquationData(equationData);
0340 
0341     CHECK_RANGE(plot, curve2, Dimension::X, -4., 10.);
0342     CHECK_RANGE(plot, curve2, Dimension::Y, 0., 1.);
0343 
0344     QCOMPARE(plot->autoScale(Dimension::Y, cs->index(Dimension::Y)), false);
0345 }
0346 
0347 void CartesianPlotTest::undoInfoElement() {
0348     auto* project = new Project();
0349     auto* worksheet = new Worksheet(QStringLiteral("ws"));
0350     project->addChild(worksheet);
0351 
0352     auto* plot = new CartesianPlot(QStringLiteral("plot"));
0353     worksheet->addChild(plot);
0354 
0355     auto* curve = new XYCurve(QStringLiteral("curve"));
0356     plot->addChild(curve);
0357 
0358     auto* info = new InfoElement(QStringLiteral("info"), plot, curve, 0.);
0359     plot->addChild(info);
0360 
0361     QCOMPARE(plot->childCount<InfoElement>(), 1);
0362 
0363     // undo the last step
0364     project->undoStack()->undo();
0365     QCOMPARE(plot->childCount<InfoElement>(), 0);
0366 
0367     // redo
0368     project->undoStack()->redo();
0369     QCOMPARE(plot->childCount<InfoElement>(), 1);
0370 }
0371 
0372 void CartesianPlotTest::axisFormat() {
0373     // testing #74
0374 
0375     QString savePath;
0376     {
0377         Project project;
0378 
0379         Spreadsheet* sheet = new Spreadsheet(QStringLiteral("Spreadsheet"), false);
0380         project.addChild(sheet);
0381 
0382         sheet->setColumnCount(2);
0383         sheet->setRowCount(3);
0384 
0385         sheet->column(0)->setColumnMode(AbstractColumn::ColumnMode::DateTime);
0386         sheet->column(1)->setColumnMode(AbstractColumn::ColumnMode::Integer);
0387 
0388         sheet->column(0)->setDateTimeAt(0, QDateTime::fromString(QStringLiteral("2022-02-03 12:23:00"), Qt::ISODate));
0389         sheet->column(0)->setDateTimeAt(1, QDateTime::fromString(QStringLiteral("2022-02-04 12:23:00"), Qt::ISODate));
0390         sheet->column(0)->setDateTimeAt(2, QDateTime::fromString(QStringLiteral("2022-02-05 12:23:00"), Qt::ISODate));
0391 
0392         QCOMPARE(sheet->column(0)->dateTimeAt(0), QDateTime::fromString(QStringLiteral("2022-02-03 12:23:00"), Qt::ISODate));
0393 
0394         sheet->column(1)->setValueAt(0, 0);
0395         sheet->column(1)->setValueAt(1, 1);
0396         sheet->column(1)->setValueAt(2, 2);
0397 
0398         auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
0399         project.addChild(worksheet);
0400 
0401         auto* plot = new CartesianPlot(QStringLiteral("plot"));
0402         worksheet->addChild(plot);
0403         plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0404 
0405         auto* curve = new XYCurve(QStringLiteral("curve"));
0406         plot->addChild(curve);
0407 
0408         curve->setXColumn(sheet->column(0));
0409         curve->setYColumn(sheet->column(1));
0410 
0411         auto* xAxis = static_cast<Axis*>(plot->child<Axis>(0));
0412         QVERIFY(xAxis);
0413         QCOMPARE(xAxis->name(), QStringLiteral("x"));
0414 
0415         const auto original = xAxis->labelsDateTimeFormat();
0416         const auto newFormat = QStringLiteral("yyyy-MM-dd hh:mm");
0417         QVERIFY(original != newFormat);
0418         xAxis->setLabelsDateTimeFormat(newFormat);
0419 
0420         SAVE_PROJECT("TestAxisDateTimeFormat.lml");
0421     }
0422 
0423     {
0424         Project project;
0425         project.load(savePath);
0426 
0427         /* check the project tree for the imported project */
0428         /* Spreadsheet */
0429         auto* aspect = project.child<AbstractAspect>(0);
0430         QVERIFY(aspect);
0431         QCOMPARE(aspect->name(), QLatin1String("Spreadsheet"));
0432         QVERIFY(aspect->type() == AspectType::Spreadsheet);
0433 
0434         /* Worksheet */
0435         aspect = project.child<AbstractAspect>(1);
0436         QVERIFY(aspect);
0437         QCOMPARE(aspect->name(), QLatin1String("Worksheet"));
0438         QVERIFY(aspect->type() == AspectType::Worksheet);
0439         auto* w = dynamic_cast<Worksheet*>(aspect);
0440         QVERIFY(w);
0441 
0442         auto* plot = dynamic_cast<CartesianPlot*>(aspect->child<CartesianPlot>(0));
0443         QVERIFY(plot);
0444 
0445         auto* xAxis = static_cast<Axis*>(plot->child<Axis>(0));
0446         QVERIFY(xAxis);
0447         QCOMPARE(xAxis->name(), QStringLiteral("x"));
0448         QCOMPARE(xAxis->labelsDateTimeFormat(), QStringLiteral("yyyy-MM-dd hh:mm"));
0449     }
0450 }
0451 
0452 void CartesianPlotTest::shiftLeftAutoScale() {
0453     Project project;
0454     auto* ws = new Worksheet(QStringLiteral("worksheet"));
0455     QVERIFY(ws != nullptr);
0456     project.addChild(ws);
0457 
0458     auto* p = new CartesianPlot(QStringLiteral("plot"));
0459     p->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0460     QVERIFY(p != nullptr);
0461     ws->addChild(p);
0462 
0463     auto* curve{new XYEquationCurve(QStringLiteral("f(x)"))};
0464     curve->setCoordinateSystemIndex(p->defaultCoordinateSystemIndex());
0465     p->addChild(curve);
0466 
0467     XYEquationCurve::EquationData data;
0468     data.min = QStringLiteral("1");
0469     data.max = QStringLiteral("2");
0470     data.count = 1000;
0471     data.expression1 = QStringLiteral("x");
0472     curve->setEquationData(data);
0473     curve->recalculate();
0474 
0475     CHECK_RANGE(p, curve, Dimension::X, 1., 2.);
0476     CHECK_RANGE(p, curve, Dimension::Y, 1., 2.);
0477 
0478     p->shiftLeftX();
0479 
0480     // Autoscale of the y range was done
0481     CHECK_RANGE(p, curve, Dimension::X, 1.1, 2.1);
0482     CHECK_RANGE(p, curve, Dimension::Y, 1.1, 2.); // changed range
0483 }
0484 
0485 void CartesianPlotTest::shiftRightAutoScale() {
0486     Project project;
0487     auto* ws = new Worksheet(QStringLiteral("worksheet"));
0488     QVERIFY(ws != nullptr);
0489     project.addChild(ws);
0490 
0491     auto* p = new CartesianPlot(QStringLiteral("plot"));
0492     p->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0493     QVERIFY(p != nullptr);
0494     ws->addChild(p);
0495 
0496     auto* curve{new XYEquationCurve(QStringLiteral("f(x)"))};
0497     curve->setCoordinateSystemIndex(p->defaultCoordinateSystemIndex());
0498     p->addChild(curve);
0499 
0500     XYEquationCurve::EquationData data;
0501     data.min = QStringLiteral("1");
0502     data.max = QStringLiteral("2");
0503     data.count = 1000;
0504     data.expression1 = QStringLiteral("x");
0505     curve->setEquationData(data);
0506     curve->recalculate();
0507 
0508     CHECK_RANGE(p, curve, Dimension::X, 1., 2.);
0509     CHECK_RANGE(p, curve, Dimension::Y, 1., 2.);
0510 
0511     p->shiftRightX();
0512 
0513     // Autoscale of the y range was done
0514     CHECK_RANGE(p, curve, Dimension::X, 0.9, 1.9);
0515     CHECK_RANGE(p, curve, Dimension::Y, 1., 1.9); // changed range
0516 }
0517 
0518 void CartesianPlotTest::shiftUpAutoScale() {
0519     Project project;
0520     auto* ws = new Worksheet(QStringLiteral("worksheet"));
0521     QVERIFY(ws != nullptr);
0522     project.addChild(ws);
0523 
0524     auto* p = new CartesianPlot(QStringLiteral("plot"));
0525     p->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0526     QVERIFY(p != nullptr);
0527     ws->addChild(p);
0528 
0529     auto* curve{new XYEquationCurve(QStringLiteral("f(x)"))};
0530     curve->setCoordinateSystemIndex(p->defaultCoordinateSystemIndex());
0531     p->addChild(curve);
0532 
0533     XYEquationCurve::EquationData data;
0534     data.min = QStringLiteral("1");
0535     data.max = QStringLiteral("2");
0536     data.count = 1000;
0537     data.expression1 = QStringLiteral("x");
0538     curve->setEquationData(data);
0539     curve->recalculate();
0540 
0541     CHECK_RANGE(p, curve, Dimension::X, 1., 2.);
0542     CHECK_RANGE(p, curve, Dimension::Y, 1., 2.);
0543 
0544     p->shiftUpY();
0545 
0546     // Autoscale of the y range was done
0547     CHECK_RANGE(p, curve, Dimension::X, 1., 1.9);
0548     CHECK_RANGE(p, curve, Dimension::Y, 0.9, 1.9); // changed range
0549 }
0550 
0551 void CartesianPlotTest::shiftDownAutoScale() {
0552     Project project;
0553     auto* ws = new Worksheet(QStringLiteral("worksheet"));
0554     QVERIFY(ws != nullptr);
0555     project.addChild(ws);
0556 
0557     auto* p = new CartesianPlot(QStringLiteral("plot"));
0558     p->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0559     QVERIFY(p != nullptr);
0560     ws->addChild(p);
0561 
0562     auto* curve{new XYEquationCurve(QStringLiteral("f(x)"))};
0563     curve->setCoordinateSystemIndex(p->defaultCoordinateSystemIndex());
0564     p->addChild(curve);
0565 
0566     XYEquationCurve::EquationData data;
0567     data.min = QStringLiteral("1");
0568     data.max = QStringLiteral("2");
0569     data.count = 1000;
0570     data.expression1 = QStringLiteral("x");
0571     curve->setEquationData(data);
0572     curve->recalculate();
0573 
0574     CHECK_RANGE(p, curve, Dimension::X, 1., 2.);
0575     CHECK_RANGE(p, curve, Dimension::Y, 1., 2.);
0576 
0577     p->shiftDownY();
0578 
0579     // Autoscale of the y range was done
0580     CHECK_RANGE(p, curve, Dimension::X, 1.1, 2.);
0581     CHECK_RANGE(p, curve, Dimension::Y, 1.1, 2.1); // changed range
0582 }
0583 
0584 void CartesianPlotTest::rangeFormatYDataChanged() {
0585     Project project;
0586 
0587     Spreadsheet* sheet = new Spreadsheet(QStringLiteral("Spreadsheet"), false);
0588     project.addChild(sheet);
0589 
0590     sheet->setColumnCount(2);
0591     sheet->setRowCount(3);
0592 
0593     sheet->column(0)->setColumnMode(AbstractColumn::ColumnMode::DateTime);
0594     sheet->column(1)->setColumnMode(AbstractColumn::ColumnMode::Integer);
0595 
0596     sheet->column(0)->setDateTimeAt(0, QDateTime::fromString(QStringLiteral("2022-02-03 12:23:00"), Qt::ISODate));
0597     sheet->column(0)->setDateTimeAt(1, QDateTime::fromString(QStringLiteral("2022-02-04 12:23:00"), Qt::ISODate));
0598     sheet->column(0)->setDateTimeAt(2, QDateTime::fromString(QStringLiteral("2022-02-05 12:23:00"), Qt::ISODate));
0599 
0600     QCOMPARE(sheet->column(0)->dateTimeAt(0), QDateTime::fromString(QStringLiteral("2022-02-03 12:23:00"), Qt::ISODate));
0601 
0602     sheet->column(1)->setValueAt(0, 0);
0603     sheet->column(1)->setValueAt(1, 1);
0604     sheet->column(1)->setValueAt(2, 2);
0605 
0606     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
0607     project.addChild(worksheet);
0608 
0609     auto* plot = new CartesianPlot(QStringLiteral("plot"));
0610     worksheet->addChild(plot);
0611     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0612 
0613     auto* curve = new XYCurve(QStringLiteral("curve"));
0614     plot->addChild(curve);
0615 
0616     curve->setXColumn(sheet->column(0));
0617     curve->setYColumn(sheet->column(1));
0618 
0619     QCOMPARE(plot->rangeFormat(Dimension::X, 0), RangeT::Format::DateTime);
0620     QCOMPARE(plot->rangeFormat(Dimension::Y, 0), RangeT::Format::Numeric);
0621 
0622     // simulate data change
0623     plot->dataChanged(curve, Dimension::Y);
0624 
0625     QCOMPARE(plot->rangeFormat(Dimension::X, 0), RangeT::Format::DateTime);
0626     QCOMPARE(plot->rangeFormat(Dimension::Y, 0), RangeT::Format::Numeric);
0627 }
0628 
0629 void CartesianPlotTest::rangeFormatXDataChanged() {
0630     Project project;
0631 
0632     Spreadsheet* sheet = new Spreadsheet(QStringLiteral("Spreadsheet"), false);
0633     project.addChild(sheet);
0634 
0635     sheet->setColumnCount(2);
0636     sheet->setRowCount(3);
0637 
0638     sheet->column(0)->setColumnMode(AbstractColumn::ColumnMode::DateTime);
0639     sheet->column(1)->setColumnMode(AbstractColumn::ColumnMode::Integer);
0640 
0641     sheet->column(0)->setDateTimeAt(0, QDateTime::fromString(QStringLiteral("2022-02-03 12:23:00"), Qt::ISODate));
0642     sheet->column(0)->setDateTimeAt(1, QDateTime::fromString(QStringLiteral("2022-02-04 12:23:00"), Qt::ISODate));
0643     sheet->column(0)->setDateTimeAt(2, QDateTime::fromString(QStringLiteral("2022-02-05 12:23:00"), Qt::ISODate));
0644 
0645     QCOMPARE(sheet->column(0)->dateTimeAt(0), QDateTime::fromString(QStringLiteral("2022-02-03 12:23:00"), Qt::ISODate));
0646 
0647     sheet->column(1)->setValueAt(0, 0);
0648     sheet->column(1)->setValueAt(1, 1);
0649     sheet->column(1)->setValueAt(2, 2);
0650 
0651     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
0652     project.addChild(worksheet);
0653 
0654     auto* plot = new CartesianPlot(QStringLiteral("plot"));
0655     worksheet->addChild(plot);
0656     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0657 
0658     auto* curve = new XYCurve(QStringLiteral("curve"));
0659     plot->addChild(curve);
0660 
0661     curve->setXColumn(sheet->column(1));
0662     curve->setYColumn(sheet->column(0));
0663 
0664     QCOMPARE(plot->rangeFormat(Dimension::X, 0), RangeT::Format::Numeric);
0665     QCOMPARE(plot->rangeFormat(Dimension::Y, 0), RangeT::Format::DateTime);
0666 
0667     // simulate data change
0668     plot->dataChanged(curve, Dimension::X);
0669 
0670     QCOMPARE(plot->rangeFormat(Dimension::X, 0), RangeT::Format::Numeric);
0671     QCOMPARE(plot->rangeFormat(Dimension::Y, 0), RangeT::Format::DateTime);
0672 }
0673 
0674 void CartesianPlotTest::rangeFormatNonDefaultRange() {
0675     Project project;
0676 
0677     Spreadsheet* sheet = new Spreadsheet(QStringLiteral("Spreadsheet"), false);
0678     project.addChild(sheet);
0679 
0680     sheet->setColumnCount(4);
0681     sheet->setRowCount(3);
0682 
0683     sheet->column(0)->setColumnMode(AbstractColumn::ColumnMode::Integer);
0684     sheet->column(1)->setColumnMode(AbstractColumn::ColumnMode::Integer);
0685     sheet->column(2)->setColumnMode(AbstractColumn::ColumnMode::DateTime);
0686     sheet->column(3)->setColumnMode(AbstractColumn::ColumnMode::Integer);
0687 
0688     sheet->column(0)->setValueAt(0, 0);
0689     sheet->column(0)->setValueAt(1, 1);
0690     sheet->column(0)->setValueAt(2, 2);
0691 
0692     sheet->column(1)->setValueAt(0, 5);
0693     sheet->column(1)->setValueAt(1, 6);
0694     sheet->column(1)->setValueAt(2, 7);
0695 
0696     sheet->column(2)->setDateTimeAt(0, QDateTime::fromString(QStringLiteral("2022-02-03 12:23:00"), Qt::ISODate));
0697     sheet->column(2)->setDateTimeAt(1, QDateTime::fromString(QStringLiteral("2022-02-04 12:23:00"), Qt::ISODate));
0698     sheet->column(2)->setDateTimeAt(2, QDateTime::fromString(QStringLiteral("2022-02-05 12:23:00"), Qt::ISODate));
0699 
0700     QCOMPARE(sheet->column(2)->dateTimeAt(0), QDateTime::fromString(QStringLiteral("2022-02-03 12:23:00"), Qt::ISODate));
0701 
0702     sheet->column(3)->setValueAt(0, 8);
0703     sheet->column(3)->setValueAt(1, 10);
0704     sheet->column(3)->setValueAt(2, 9);
0705 
0706     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
0707     project.addChild(worksheet);
0708 
0709     auto* plot = new CartesianPlot(QStringLiteral("plot"));
0710     worksheet->addChild(plot);
0711     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0712 
0713     // Create new cSystem
0714     Range<double> xRange;
0715     xRange.setFormat(RangeT::Format::Numeric);
0716     Range<double> yRange;
0717     yRange.setFormat(RangeT::Format::Numeric);
0718     plot->addXRange(xRange);
0719     plot->addYRange(yRange);
0720     CartesianCoordinateSystem* cSystem = new CartesianCoordinateSystem(plot);
0721     cSystem->setIndex(Dimension::X, 1);
0722     cSystem->setIndex(Dimension::Y, 1);
0723     plot->addCoordinateSystem(cSystem);
0724 
0725     auto* curve2 = new XYCurve(QStringLiteral("curve2"));
0726     plot->addChild(curve2);
0727 
0728     curve2->setCoordinateSystemIndex(1);
0729 
0730     QCOMPARE(plot->rangeFormat(Dimension::X, 0), RangeT::Format::Numeric);
0731     QCOMPARE(plot->rangeFormat(Dimension::Y, 0), RangeT::Format::Numeric);
0732     QCOMPARE(plot->rangeFormat(Dimension::X, 1), RangeT::Format::Numeric);
0733     QCOMPARE(plot->rangeFormat(Dimension::Y, 1), RangeT::Format::Numeric);
0734 
0735     curve2->setXColumn(sheet->column(2));
0736 
0737     QCOMPARE(plot->rangeFormat(Dimension::X, 0), RangeT::Format::Numeric);
0738     QCOMPARE(plot->rangeFormat(Dimension::Y, 0), RangeT::Format::Numeric);
0739     QCOMPARE(plot->rangeFormat(Dimension::X, 1), RangeT::Format::DateTime);
0740     QCOMPARE(plot->rangeFormat(Dimension::Y, 1), RangeT::Format::Numeric);
0741 
0742     curve2->setYColumn(sheet->column(3));
0743 
0744     QCOMPARE(plot->rangeFormat(Dimension::X, 0), RangeT::Format::Numeric);
0745     QCOMPARE(plot->rangeFormat(Dimension::Y, 0), RangeT::Format::Numeric);
0746     QCOMPARE(plot->rangeFormat(Dimension::X, 1), RangeT::Format::DateTime);
0747     QCOMPARE(plot->rangeFormat(Dimension::Y, 1), RangeT::Format::Numeric);
0748 
0749     plot->dataChanged(curve2, Dimension::X);
0750 
0751     QCOMPARE(plot->rangeFormat(Dimension::X, 0), RangeT::Format::Numeric);
0752     QCOMPARE(plot->rangeFormat(Dimension::Y, 0), RangeT::Format::Numeric);
0753     QCOMPARE(plot->rangeFormat(Dimension::X, 1), RangeT::Format::DateTime);
0754     QCOMPARE(plot->rangeFormat(Dimension::Y, 1), RangeT::Format::Numeric);
0755 }
0756 
0757 /*!
0758  * \brief CartesianPlotTest::invalidcSystem
0759  * Plot with 2 CoordinateSystems (with common x range), but the second has invalid start end (0, 0).
0760  * This scenario shall not destroy the x range when zooming in
0761  *
0762  */
0763 void CartesianPlotTest::invalidcSystem() {
0764     Project project;
0765 
0766     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
0767     project.addChild(worksheet);
0768     auto* view = dynamic_cast<WorksheetView*>(worksheet->view());
0769     QVERIFY(view != nullptr);
0770     view->initActions(); // needed by SET_CARTESIAN_MOUSE_MODE()
0771 
0772     auto* plot = new CartesianPlot(QStringLiteral("plot"));
0773     worksheet->addChild(plot);
0774     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0775     SET_CARTESIAN_MOUSE_MODE(CartesianPlot::MouseMode::ZoomXSelection) // must be set after the plot was added
0776 
0777     // Create new cSystem
0778     Range<double> yRange;
0779     yRange.setFormat(RangeT::Format::Numeric);
0780     plot->addYRange(yRange);
0781     plot->addCoordinateSystem();
0782     QCOMPARE(plot->coordinateSystemCount(), 2);
0783 
0784     auto* cSystem{plot->coordinateSystem(1)};
0785     cSystem->setIndex(Dimension::Y, 1);
0786     plot->setRangeDirty(Dimension::Y, 1, true);
0787     plot->retransformScale(Dimension::Y, 1);
0788 
0789     {
0790         CHECK_RANGE_CSYSTEMINDEX(plot, 0, Dimension::X, 0., 1.);
0791         CHECK_RANGE_CSYSTEMINDEX(plot, 0, Dimension::Y, 0., 1.);
0792         CHECK_RANGE_CSYSTEMINDEX(plot, 1, Dimension::X, 0., 1.);
0793         CHECK_RANGE_CSYSTEMINDEX(plot, 1, Dimension::Y, 0., 1.);
0794         const Range<double> plotSceneRangeX = {plot->dataRect().x(), plot->dataRect().x() + plot->dataRect().width()};
0795         const Range<double> plotSceneRangeY = {plot->dataRect().y() + plot->dataRect().height(), plot->dataRect().y()};
0796 
0797         double bx = plotSceneRangeX.size() / (1 - 0);
0798         double ax = plotSceneRangeX.start() - bx * 0;
0799 
0800         double by = plotSceneRangeY.size() / (1 - 0);
0801         double ay = plotSceneRangeY.start() - by * 0;
0802 
0803         CHECK_SCALE_PLOT(plot, 0, Dimension::X, ax, bx, 0);
0804         CHECK_SCALE_PLOT(plot, 0, Dimension::Y, ay, by, 0);
0805         CHECK_SCALE_PLOT(plot, 1, Dimension::X, ax, bx, 0);
0806         CHECK_SCALE_PLOT(plot, 1, Dimension::Y, ay, by, 0);
0807     }
0808 
0809     // Set range of the unused y range
0810     Range<double> range;
0811     range.setStart(0); // Both are set to the same value
0812     range.setEnd(0); // Both are set to the same value
0813     range.setFormat(RangeT::Format::Numeric);
0814     range.setAutoScale(false);
0815     range.setScale(RangeT::Scale::Linear);
0816 
0817     {
0818         // plot->setRange(Dimension::Y, 1, range); // does not work
0819         // Implementation of setRange() must be used, because setRange() uses check to check if
0820         // the range is valid, which it isn't in this test. To test neverthless and not removing a test
0821         // use directly the implementation
0822         int index = 1;
0823         auto dimension = Dimension::Y;
0824         auto otherValue = range;
0825         auto plotPrivate = plot->d_func();
0826         plotPrivate->setRangeDirty(dimension, index, true);
0827         auto tmp = plotPrivate->rangeConst(dimension, index);
0828         plotPrivate->setRange(dimension, index, otherValue);
0829         otherValue = tmp;
0830         plotPrivate->retransformScale(dimension, index, true);
0831         Dimension dim_other = Dimension::Y;
0832         if (dimension == Dimension::Y)
0833             dim_other = Dimension::X;
0834 
0835         QVector<int> scaledIndices;
0836         for (int i = 0; i < plotPrivate->q->coordinateSystemCount(); i++) {
0837             auto cs = plotPrivate->q->coordinateSystem(i);
0838             auto index_other = cs->index(dim_other);
0839             if (cs->index(dimension) == index && scaledIndices.indexOf(index_other) == -1) {
0840                 scaledIndices << index_other;
0841                 if (plotPrivate->q->autoScale(dim_other, index_other) && plotPrivate->q->scaleAuto(dim_other, index_other, false))
0842                     plotPrivate->retransformScale(dim_other, index_other);
0843             }
0844         }
0845         plotPrivate->q->WorksheetElementContainer::retransform();
0846         Q_EMIT plotPrivate->q->rangeChanged(dimension, index, plotPrivate->rangeConst(dimension, index));
0847     }
0848 
0849     // Recalculate scales is triggered
0850     {
0851         QCOMPARE(plot->coordinateSystemCount(), 2);
0852 
0853         CHECK_RANGE_CSYSTEMINDEX(plot, 0, Dimension::X, 0., 1.);
0854         CHECK_RANGE_CSYSTEMINDEX(plot, 0, Dimension::Y, 0., 1.);
0855         CHECK_RANGE_CSYSTEMINDEX(plot, 1, Dimension::X, 0., 1.);
0856         CHECK_RANGE_CSYSTEMINDEX(plot, 1, Dimension::Y, 0., 0.);
0857         const Range<double> plotSceneRangeX = {plot->dataRect().x(), plot->dataRect().x() + plot->dataRect().width()};
0858         const Range<double> plotSceneRangeY = {plot->dataRect().y() + plot->dataRect().height(), plot->dataRect().y()};
0859 
0860         double bx = plotSceneRangeX.size() / (1 - 0);
0861         double ax = plotSceneRangeX.start() - bx * 0;
0862 
0863         double by = plotSceneRangeY.size() / (1 - 0);
0864         double ay = plotSceneRangeY.start() - by * 0;
0865 
0866         CHECK_SCALE_PLOT(plot, 0, Dimension::X, ax, bx, 0);
0867         CHECK_SCALE_PLOT(plot, 0, Dimension::Y, ay, by, 0);
0868         // Don't care what the second cSystem has, because it is invalid
0869         // CHECK_SCALE_PLOT(plot, 1, Dimension::X, ax, bx, 0);
0870         // CHECK_SCALE_PLOT(plot, 1, Dimension::Y, ay, by, 0);
0871     }
0872 
0873     QCOMPARE(plot->mouseMode(), CartesianPlot::MouseMode::ZoomXSelection);
0874     plot->mousePressZoomSelectionMode(QPointF(0.2, -150), -1);
0875     plot->mouseMoveZoomSelectionMode(QPointF(0.6, 100), -1);
0876     plot->mouseReleaseZoomSelectionMode(-1);
0877 
0878     {
0879         QCOMPARE(plot->coordinateSystemCount(), 2);
0880 
0881         CHECK_RANGE_CSYSTEMINDEX(plot, 0, Dimension::X, 0.2, 0.6);
0882         CHECK_RANGE_CSYSTEMINDEX(plot, 0, Dimension::Y, 0., 1.);
0883         CHECK_RANGE_CSYSTEMINDEX(plot, 1, Dimension::X, 0.2, 0.6);
0884         CHECK_RANGE_CSYSTEMINDEX(plot, 1, Dimension::Y, 0., 0.);
0885         const Range<double> plotSceneRangeX = {plot->dataRect().x(), plot->dataRect().x() + plot->dataRect().width()};
0886         const Range<double> plotSceneRangeY = {plot->dataRect().y() + plot->dataRect().height(), plot->dataRect().y()};
0887 
0888         double bx = plotSceneRangeX.size() / (0.6 - 0.2);
0889         double ax = plotSceneRangeX.start() - bx * 0.2;
0890 
0891         double by = plotSceneRangeY.size() / (1 - 0);
0892         double ay = plotSceneRangeY.start() - by * 0;
0893 
0894         CHECK_SCALE_PLOT(plot, 0, Dimension::X, ax, bx, 0);
0895         CHECK_SCALE_PLOT(plot, 0, Dimension::Y, ay, by, 0);
0896         // Don't care what the second cSystem has, because it is invalid
0897         // CHECK_SCALE_PLOT(plot, 1, Dimension::X, ax, bx, 0);
0898         // CHECK_SCALE_PLOT(plot, 1, Dimension::Y, ay, by, 0);
0899     }
0900 }
0901 
0902 void CartesianPlotTest::autoScaleFitCurveCalculation() {
0903     Project project;
0904 
0905     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
0906     project.addChild(worksheet);
0907     auto* view = dynamic_cast<WorksheetView*>(worksheet->view());
0908     QVERIFY(view != nullptr);
0909     view->initActions(); // needed by SET_CARTESIAN_MOUSE_MODE()
0910 
0911     auto* plot = new CartesianPlot(QStringLiteral("plot"));
0912     worksheet->addChild(plot);
0913     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0914     plot->setNiceExtend(false);
0915 
0916     auto* equationCurve{new XYEquationCurve(QStringLiteral("f(x)"))};
0917     equationCurve->setCoordinateSystemIndex(plot->defaultCoordinateSystemIndex());
0918     plot->addChild(equationCurve);
0919 
0920     XYEquationCurve::EquationData data;
0921     data.min = QStringLiteral("0");
0922     data.max = QStringLiteral("1");
0923     data.count = 10;
0924     data.expression1 = QStringLiteral("x");
0925     equationCurve->setEquationData(data);
0926     equationCurve->recalculate();
0927 
0928     CHECK_RANGE(plot, equationCurve, Dimension::X, 0., 1.);
0929     CHECK_RANGE(plot, equationCurve, Dimension::Y, 0., 1.);
0930 
0931     auto* fitCurve = new XYFitCurve(QStringLiteral("Fit"));
0932     fitCurve->setCoordinateSystemIndex(plot->defaultCoordinateSystemIndex());
0933     plot->addChild(fitCurve);
0934 
0935     XYFitCurve::FitData f;
0936     f.autoRange = false;
0937     f.fitRange = Range<double>(0, 1);
0938     f.autoEvalRange = false;
0939     f.evalRange = Range<double>(0, 3); // larger than fit range
0940     f.modelCategory = nsl_fit_model_basic;
0941     f.modelType = nsl_fit_model_polynomial;
0942     f.degree = 1; // linear
0943     f.model = QStringLiteral("c0 + c1*x");
0944     fitCurve->initFitData(f); // Important, otherwise paramNames gets not filled
0945     fitCurve->setFitData(f);
0946     fitCurve->setDataSourceType(XYAnalysisCurve::DataSourceType::Curve);
0947     fitCurve->setDataSourceCurve(equationCurve);
0948     fitCurve->recalculate();
0949 
0950     CHECK_RANGE(plot, equationCurve, Dimension::X, 0., 3.);
0951     CHECK_RANGE(plot, equationCurve, Dimension::Y, 0., 3.);
0952 }
0953 
0954 void CartesianPlotTest::wheelEventCenterAxes() {
0955     Project project;
0956 
0957     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
0958     project.addChild(worksheet);
0959     auto* view = dynamic_cast<WorksheetView*>(worksheet->view());
0960     QVERIFY(view != nullptr);
0961     view->initMenus(); // needed by SET_CARTESIAN_MOUSE_MODE()
0962 
0963     auto* plot = new CartesianPlot(QStringLiteral("plot"));
0964     worksheet->addChild(plot);
0965     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
0966     plot->setNiceExtend(false);
0967 
0968     auto* equationCurve{new XYEquationCurve(QStringLiteral("f(x)"))};
0969     equationCurve->setCoordinateSystemIndex(plot->defaultCoordinateSystemIndex());
0970     plot->addChild(equationCurve);
0971 
0972     XYEquationCurve::EquationData data;
0973     data.min = QStringLiteral("0");
0974     data.max = QStringLiteral("10");
0975     data.count = 10;
0976     data.expression1 = QStringLiteral("x");
0977     equationCurve->setEquationData(data);
0978     equationCurve->recalculate();
0979 
0980     CHECK_RANGE(plot, equationCurve, Dimension::X, 0., 10.);
0981     CHECK_RANGE(plot, equationCurve, Dimension::Y, 0., 10.);
0982 
0983     plot->m_zoomFactor = 2;
0984     plot->setNiceExtend(false);
0985     const auto& rect = plot->dataRect();
0986 
0987     int signalEmittedCounter = 0;
0988     connect(plot,
0989             &CartesianPlot::wheelEventSignal,
0990             [&signalEmittedCounter](const QPointF& sceneRelPos, int delta, int xIndex, int yIndex, bool considerDimension, Dimension dim) {
0991                 signalEmittedCounter++;
0992                 if (signalEmittedCounter == 1) {
0993                     QCOMPARE(sceneRelPos.x(), 0.5);
0994                     QCOMPARE(sceneRelPos.y(), 0.5);
0995                     QVERIFY(delta > 0);
0996                     QCOMPARE(xIndex, 0);
0997                     QCOMPARE(yIndex, 0);
0998                     QCOMPARE(considerDimension, true);
0999                     QCOMPARE(dim, Dimension::X);
1000                 } else {
1001                     QCOMPARE(sceneRelPos.x(), 0.5);
1002                     QCOMPARE(sceneRelPos.y(), 0.5);
1003                     QVERIFY(delta < 0);
1004                     QCOMPARE(xIndex, 0);
1005                     QCOMPARE(yIndex, 0);
1006                     QCOMPARE(considerDimension, true);
1007                     QCOMPARE(dim, Dimension::Y);
1008                 }
1009             });
1010 
1011     const auto& axes = plot->children<Axis>();
1012     QCOMPARE(axes.length(), 2);
1013     QCOMPARE(axes.at(0)->name(), QStringLiteral("x"));
1014     QCOMPARE(axes.at(1)->name(), QStringLiteral("y"));
1015     auto* xAxis = axes.at(0);
1016     auto* yAxis = axes.at(1);
1017 
1018     plot->enableAutoScale(Dimension::X, 0, false);
1019     plot->enableAutoScale(Dimension::Y, 0, false);
1020 
1021     xAxis->setSelected(true);
1022     QGraphicsSceneWheelEvent event;
1023     event.setPos(QPointF(rect.center().x(), rect.center().y()));
1024     event.setDelta(10); // value not important, only sign
1025     plot->d_func()->wheelEvent(&event);
1026     CHECK_RANGE(plot, equationCurve, Dimension::X, 2.5, 7.5);
1027     CHECK_RANGE(plot, equationCurve, Dimension::Y, 0., 10.);
1028     QCOMPARE(signalEmittedCounter, 1);
1029 
1030     xAxis->setSelected(false);
1031     yAxis->setSelected(true);
1032     event.setPos(QPointF(rect.center().x(), rect.center().y()));
1033     event.setDelta(-10); // value not important, only sign
1034     plot->d_func()->wheelEvent(&event);
1035     CHECK_RANGE(plot, equationCurve, Dimension::X, 2.5, 7.5);
1036     CHECK_RANGE(plot, equationCurve, Dimension::Y, -5., 15.0);
1037     QCOMPARE(signalEmittedCounter, 2);
1038 }
1039 
1040 void CartesianPlotTest::wheelEventNotCenter() {
1041     Project project;
1042 
1043     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
1044     project.addChild(worksheet);
1045     auto* view = dynamic_cast<WorksheetView*>(worksheet->view());
1046     QVERIFY(view != nullptr);
1047     view->initMenus(); // needed by SET_CARTESIAN_MOUSE_MODE()
1048 
1049     auto* plot = new CartesianPlot(QStringLiteral("plot"));
1050     worksheet->addChild(plot);
1051     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
1052     plot->setNiceExtend(false);
1053 
1054     auto* equationCurve{new XYEquationCurve(QStringLiteral("f(x)"))};
1055     equationCurve->setCoordinateSystemIndex(plot->defaultCoordinateSystemIndex());
1056     plot->addChild(equationCurve);
1057 
1058     XYEquationCurve::EquationData data;
1059     data.min = QStringLiteral("0");
1060     data.max = QStringLiteral("10");
1061     data.count = 10;
1062     data.expression1 = QStringLiteral("x");
1063     equationCurve->setEquationData(data);
1064     equationCurve->recalculate();
1065 
1066     CHECK_RANGE(plot, equationCurve, Dimension::X, 0., 10.);
1067     CHECK_RANGE(plot, equationCurve, Dimension::Y, 0., 10.);
1068 
1069     plot->m_zoomFactor = 2;
1070     plot->setNiceExtend(false);
1071     const auto& rect = plot->dataRect();
1072 
1073     const auto& axes = plot->children<Axis>();
1074     QCOMPARE(axes.length(), 2);
1075     QCOMPARE(axes.at(0)->name(), QStringLiteral("x"));
1076     QCOMPARE(axes.at(1)->name(), QStringLiteral("y"));
1077     auto* xAxis = axes.at(0);
1078     //  const auto* yAxis = axes.at(1);
1079 
1080     int signalEmittedCounter = 0;
1081     connect(plot,
1082             &CartesianPlot::wheelEventSignal,
1083             [&signalEmittedCounter](const QPointF& sceneRelPos, int delta, int xIndex, int yIndex, bool considerDimension, Dimension dim) {
1084                 signalEmittedCounter++;
1085                 QCOMPARE(sceneRelPos.x(), 0.75);
1086                 QCOMPARE(sceneRelPos.y(), 0.2);
1087                 QVERIFY(delta > 0);
1088                 QCOMPARE(xIndex, 0);
1089                 QCOMPARE(yIndex, 0);
1090                 QCOMPARE(considerDimension, true);
1091                 QCOMPARE(dim, Dimension::X);
1092             });
1093 
1094     xAxis->setSelected(true);
1095     QGraphicsSceneWheelEvent event;
1096     event.setPos(QPointF(rect.left() + rect.width() * 3 / 4, rect.top() + rect.height() * 0.8));
1097     event.setDelta(10); // value not important, only sign
1098     plot->d_func()->wheelEvent(&event);
1099     CHECK_RANGE(plot, equationCurve, Dimension::X, 3.75, 8.75);
1100     CHECK_RANGE(plot, equationCurve, Dimension::Y, 10. / 3., 8.8888888888888888);
1101     QCOMPARE(signalEmittedCounter, 1);
1102 }
1103 
1104 void CartesianPlotTest::wheelEventOutsideTopLeft() {
1105     Project project;
1106 
1107     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
1108     project.addChild(worksheet);
1109     auto* view = dynamic_cast<WorksheetView*>(worksheet->view());
1110     QVERIFY(view != nullptr);
1111     view->initMenus(); // needed by SET_CARTESIAN_MOUSE_MODE()
1112 
1113     auto* plot = new CartesianPlot(QStringLiteral("plot"));
1114     worksheet->addChild(plot);
1115     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
1116     plot->setNiceExtend(false);
1117 
1118     auto* equationCurve{new XYEquationCurve(QStringLiteral("f(x)"))};
1119     equationCurve->setCoordinateSystemIndex(plot->defaultCoordinateSystemIndex());
1120     plot->addChild(equationCurve);
1121 
1122     XYEquationCurve::EquationData data;
1123     data.min = QStringLiteral("0");
1124     data.max = QStringLiteral("10");
1125     data.count = 10;
1126     data.expression1 = QStringLiteral("x");
1127     equationCurve->setEquationData(data);
1128     equationCurve->recalculate();
1129 
1130     CHECK_RANGE(plot, equationCurve, Dimension::X, 0., 10.);
1131     CHECK_RANGE(plot, equationCurve, Dimension::Y, 0., 10.);
1132 
1133     plot->m_zoomFactor = 2;
1134     plot->setNiceExtend(false);
1135     const auto& rect = plot->dataRect();
1136 
1137     int signalEmittedCounter = 0;
1138     connect(plot,
1139             &CartesianPlot::wheelEventSignal,
1140             [&signalEmittedCounter](const QPointF& sceneRelPos, int delta, int xIndex, int yIndex, bool considerDimension, Dimension dim) {
1141                 signalEmittedCounter++;
1142                 QCOMPARE(sceneRelPos.x(), -0.2);
1143                 QCOMPARE(sceneRelPos.y(), 1.3);
1144                 QVERIFY(delta > 0);
1145                 Q_UNUSED(xIndex);
1146                 Q_UNUSED(yIndex);
1147                 QCOMPARE(considerDimension, false);
1148                 Q_UNUSED(dim);
1149             });
1150 
1151     QGraphicsSceneWheelEvent event;
1152     event.setPos(QPointF(rect.left() - 140, rect.top() - 210));
1153     event.setDelta(10); // value not important, only sign
1154     plot->d_func()->wheelEvent(&event);
1155     CHECK_RANGE(plot, equationCurve, Dimension::X, -1., 4.);
1156     CHECK_RANGE(plot, equationCurve, Dimension::Y, 6.5, 11.5);
1157     QCOMPARE(signalEmittedCounter, 1);
1158 }
1159 
1160 void CartesianPlotTest::wheelEventOutsideBottomRight() {
1161     Project project;
1162 
1163     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
1164     project.addChild(worksheet);
1165     auto* view = dynamic_cast<WorksheetView*>(worksheet->view());
1166     QVERIFY(view != nullptr);
1167     view->initMenus(); // needed by SET_CARTESIAN_MOUSE_MODE()
1168 
1169     auto* plot = new CartesianPlot(QStringLiteral("plot"));
1170     worksheet->addChild(plot);
1171     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
1172     plot->setNiceExtend(false);
1173 
1174     auto* equationCurve{new XYEquationCurve(QStringLiteral("f(x)"))};
1175     equationCurve->setCoordinateSystemIndex(plot->defaultCoordinateSystemIndex());
1176     plot->addChild(equationCurve);
1177 
1178     XYEquationCurve::EquationData data;
1179     data.min = QStringLiteral("0");
1180     data.max = QStringLiteral("10");
1181     data.count = 10;
1182     data.expression1 = QStringLiteral("x");
1183     equationCurve->setEquationData(data);
1184     equationCurve->recalculate();
1185 
1186     CHECK_RANGE(plot, equationCurve, Dimension::X, 0., 10.);
1187     CHECK_RANGE(plot, equationCurve, Dimension::Y, 0., 10.);
1188 
1189     plot->m_zoomFactor = 2;
1190     plot->setNiceExtend(false);
1191     const auto& rect = plot->dataRect();
1192 
1193     int signalEmittedCounter = 0;
1194     connect(plot,
1195             &CartesianPlot::wheelEventSignal,
1196             [&signalEmittedCounter](const QPointF& sceneRelPos, int delta, int xIndex, int yIndex, bool considerDimension, Dimension dim) {
1197                 signalEmittedCounter++;
1198                 QCOMPARE(sceneRelPos.x(), 1.4);
1199                 QCOMPARE(sceneRelPos.y(), -0.7);
1200                 QVERIFY(delta > 0);
1201                 Q_UNUSED(xIndex);
1202                 Q_UNUSED(yIndex);
1203                 QCOMPARE(considerDimension, false);
1204                 Q_UNUSED(dim);
1205             });
1206 
1207     QGraphicsSceneWheelEvent event;
1208     event.setPos(QPointF(rect.right() + 280, rect.bottom() + 490));
1209     event.setDelta(10); // value not important, only sign
1210     plot->d_func()->wheelEvent(&event);
1211     CHECK_RANGE(plot, equationCurve, Dimension::X, 7., 12.);
1212     CHECK_RANGE(plot, equationCurve, Dimension::Y, -3.5, 1.5);
1213     QCOMPARE(signalEmittedCounter, 1);
1214 }
1215 
1216 void CartesianPlotTest::spreadsheetRemoveRows() {
1217     Project project;
1218 
1219     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
1220     project.addChild(worksheet);
1221 
1222     auto* plot = new CartesianPlot(QStringLiteral("plot"));
1223     worksheet->addChild(plot);
1224     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
1225     plot->setNiceExtend(false);
1226 
1227     auto* sheet = new Spreadsheet(QStringLiteral("data"), false);
1228     project.addChild(sheet);
1229     sheet->setColumnCount(2);
1230 
1231     const auto& columns = sheet->children<Column>();
1232     QCOMPARE(columns.count(), 2);
1233     Column* xColumn = columns.at(0);
1234     Column* yColumn = columns.at(1);
1235 
1236     xColumn->replaceValues(-1,
1237                            {
1238                                1.,
1239                                2.,
1240                                3.,
1241                                4.,
1242                                5.,
1243                            });
1244     yColumn->replaceValues(-1,
1245                            {
1246                                0.,
1247                                4.,
1248                                6.,
1249                                8.,
1250                                10.,
1251                            });
1252 
1253     auto* curve = new XYCurve(QStringLiteral("curve"));
1254     plot->addChild(curve);
1255     curve->setXColumn(xColumn);
1256     curve->setYColumn(yColumn);
1257 
1258     CHECK_RANGE(plot, curve, Dimension::X, 1., 5.);
1259     CHECK_RANGE(plot, curve, Dimension::Y, 0., 10.);
1260 
1261     sheet->removeRows(4, 1);
1262 
1263     CHECK_RANGE(plot, curve, Dimension::X, 1., 4.);
1264     CHECK_RANGE(plot, curve, Dimension::Y, 0., 8.);
1265 
1266     sheet->undoStack()->undo();
1267 
1268     CHECK_RANGE(plot, curve, Dimension::X, 1., 5.);
1269     CHECK_RANGE(plot, curve, Dimension::Y, 0., 10.);
1270 }
1271 
1272 void CartesianPlotTest::spreadsheetInsertRows() {
1273     Project project;
1274 
1275     auto* worksheet = new Worksheet(QStringLiteral("Worksheet"));
1276     project.addChild(worksheet);
1277 
1278     auto* plot = new CartesianPlot(QStringLiteral("plot"));
1279     worksheet->addChild(plot);
1280     plot->setType(CartesianPlot::Type::TwoAxes); // Otherwise no axis are created
1281     plot->setNiceExtend(false);
1282 
1283     auto* sheet = new Spreadsheet(QStringLiteral("data"), false);
1284     project.addChild(sheet);
1285     sheet->setColumnCount(2);
1286 
1287     const auto& columns = sheet->children<Column>();
1288     QCOMPARE(columns.count(), 2);
1289     Column* xColumn = columns.at(0);
1290     Column* yColumn = columns.at(1);
1291 
1292     xColumn->replaceValues(-1,
1293                            {
1294                                1.,
1295                                2.,
1296                                3.,
1297                                4.,
1298                                5.,
1299                            });
1300     yColumn->replaceValues(-1,
1301                            {
1302                                0.,
1303                                4.,
1304                                6.,
1305                                8.,
1306                                10.,
1307                            });
1308 
1309     auto* curve = new XYCurve(QStringLiteral("curve"));
1310     plot->addChild(curve);
1311     curve->setXColumn(xColumn);
1312     curve->setYColumn(yColumn);
1313 
1314     CHECK_RANGE(plot, curve, Dimension::X, 1., 5.);
1315     CHECK_RANGE(plot, curve, Dimension::Y, 0., 10.);
1316 
1317     sheet->insertRows(4, 1);
1318     QCOMPARE(xColumn->rowCount(), 6);
1319     QCOMPARE(yColumn->rowCount(), 6);
1320 
1321     xColumn->replaceValues(5, {13});
1322     yColumn->replaceValues(5, {25});
1323 
1324     CHECK_RANGE(plot, curve, Dimension::X, 1., 13.);
1325     CHECK_RANGE(plot, curve, Dimension::Y, 0., 25.);
1326 
1327     sheet->undoStack()->undo(); // xColumn replace values
1328     sheet->undoStack()->undo(); // yColumn replace values
1329     sheet->undoStack()->undo(); // spreadsheet insertRows
1330 
1331     CHECK_RANGE(plot, curve, Dimension::X, 1., 5.);
1332     CHECK_RANGE(plot, curve, Dimension::Y, 0., 10.);
1333 }
1334 
1335 QTEST_MAIN(CartesianPlotTest)