##// END OF EJS Templates
Merge pull request 233 from SCIQLOP-Initialisation develop...
leroux -
r619:3ec53f32541f merge
parent child
Show More
@@ -1,116 +1,117
1 #ifndef SCIQLOP_SORTUTILS_H
1 #ifndef SCIQLOP_SORTUTILS_H
2 #define SCIQLOP_SORTUTILS_H
2 #define SCIQLOP_SORTUTILS_H
3
3
4 #include <algorithm>
4 #include <algorithm>
5 #include <cmath>
5 #include <numeric>
6 #include <numeric>
6 #include <vector>
7 #include <vector>
7
8
8 /**
9 /**
9 * Utility class with methods for sorting data
10 * Utility class with methods for sorting data
10 */
11 */
11 struct SortUtils {
12 struct SortUtils {
12 /**
13 /**
13 * Generates a vector representing the index of insertion of each data of a container if this
14 * Generates a vector representing the index of insertion of each data of a container if this
14 * one had to be sorted according to a comparison function.
15 * one had to be sorted according to a comparison function.
15 *
16 *
16 * For example:
17 * For example:
17 * If the container is a vector {1; 4; 2; 5; 3} and the comparison function is std::less, the
18 * If the container is a vector {1; 4; 2; 5; 3} and the comparison function is std::less, the
18 * result would be : {0; 3; 1; 4; 2}
19 * result would be : {0; 3; 1; 4; 2}
19 *
20 *
20 * @tparam Container the type of the container.
21 * @tparam Container the type of the container.
21 * @tparam Compare the type of the comparison function
22 * @tparam Compare the type of the comparison function
22 * @param container the container from which to generate the result. The container must have a
23 * @param container the container from which to generate the result. The container must have a
23 * at() method that returns a value associated to an index
24 * at() method that returns a value associated to an index
24 * @param compare the comparison function
25 * @param compare the comparison function
25 */
26 */
26 template <typename Container, typename Compare>
27 template <typename Container, typename Compare>
27 static std::vector<int> sortPermutation(const Container &container, const Compare &compare)
28 static std::vector<int> sortPermutation(const Container &container, const Compare &compare)
28 {
29 {
29 auto permutation = std::vector<int>{};
30 auto permutation = std::vector<int>{};
30 permutation.resize(container.size());
31 permutation.resize(container.size());
31
32
32 std::iota(permutation.begin(), permutation.end(), 0);
33 std::iota(permutation.begin(), permutation.end(), 0);
33 std::sort(permutation.begin(), permutation.end(),
34 std::sort(permutation.begin(), permutation.end(),
34 [&](int i, int j) { return compare(container.at(i), container.at(j)); });
35 [&](int i, int j) { return compare(container.at(i), container.at(j)); });
35 return permutation;
36 return permutation;
36 }
37 }
37
38
38 /**
39 /**
39 * Sorts a container according to indices passed in parameter
40 * Sorts a container according to indices passed in parameter
40 * @param container the container sorted
41 * @param container the container sorted
41 * @param sortPermutation the indices used to sort the container
42 * @param sortPermutation the indices used to sort the container
42 * @return the container sorted
43 * @return the container sorted
43 * @warning no verification is made on validity of sortPermutation (i.e. the vector has unique
44 * @warning no verification is made on validity of sortPermutation (i.e. the vector has unique
44 * indices and its range is [0 ; vector.size()[ )
45 * indices and its range is [0 ; vector.size()[ )
45 */
46 */
46 template <typename Container>
47 template <typename Container>
47 static Container sort(const Container &container, const std::vector<int> &sortPermutation)
48 static Container sort(const Container &container, const std::vector<int> &sortPermutation)
48 {
49 {
49 if (container.size() != sortPermutation.size()) {
50 if (container.size() != sortPermutation.size()) {
50 return Container{};
51 return Container{};
51 }
52 }
52
53
53 // Inits result
54 // Inits result
54 auto sortedData = Container{};
55 auto sortedData = Container{};
55 sortedData.resize(container.size());
56 sortedData.resize(container.size());
56
57
57 std::transform(sortPermutation.cbegin(), sortPermutation.cend(), sortedData.begin(),
58 std::transform(sortPermutation.cbegin(), sortPermutation.cend(), sortedData.begin(),
58 [&container](int i) { return container.at(i); });
59 [&container](int i) { return container.at(i); });
59
60
60 return sortedData;
61 return sortedData;
61 }
62 }
62
63
63 /**
64 /**
64 * Compares two values that can be NaN. This method is intended to be used as a compare function
65 * Compares two values that can be NaN. This method is intended to be used as a compare function
65 * for searching min value by excluding NaN values.
66 * for searching min value by excluding NaN values.
66 *
67 *
67 * Examples of use:
68 * Examples of use:
68 * - f({1, 3, 2, 4, 5}) will return 1
69 * - f({1, 3, 2, 4, 5}) will return 1
69 * - f({NaN, 3, 2, 4, 5}) will return 2 (NaN is excluded)
70 * - f({NaN, 3, 2, 4, 5}) will return 2 (NaN is excluded)
70 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
71 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
71 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
72 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
72 *
73 *
73 * @param v1 first value
74 * @param v1 first value
74 * @param v2 second value
75 * @param v2 second value
75 * @return true if v1 < v2, false otherwise
76 * @return true if v1 < v2, false otherwise
76 * @sa std::min_element
77 * @sa std::min_element
77 */
78 */
78 template <typename T>
79 template <typename T>
79 static bool minCompareWithNaN(const T &v1, const T &v2)
80 static bool minCompareWithNaN(const T &v1, const T &v2)
80 {
81 {
81 // Table used with NaN values:
82 // Table used with NaN values:
82 // NaN < v2 -> false
83 // NaN < v2 -> false
83 // v1 < NaN -> true
84 // v1 < NaN -> true
84 // NaN < NaN -> false
85 // NaN < NaN -> false
85 // v1 < v2 -> v1 < v2
86 // v1 < v2 -> v1 < v2
86 return std::isnan(v1) ? false : std::isnan(v2) || (v1 < v2);
87 return std::isnan(v1) ? false : std::isnan(v2) || (v1 < v2);
87 }
88 }
88
89
89 /**
90 /**
90 * Compares two values that can be NaN. This method is intended to be used as a compare function
91 * Compares two values that can be NaN. This method is intended to be used as a compare function
91 * for searching max value by excluding NaN values.
92 * for searching max value by excluding NaN values.
92 *
93 *
93 * Examples of use:
94 * Examples of use:
94 * - f({1, 3, 2, 4, 5}) will return 5
95 * - f({1, 3, 2, 4, 5}) will return 5
95 * - f({1, 3, 2, 4, NaN}) will return 4 (NaN is excluded)
96 * - f({1, 3, 2, 4, NaN}) will return 4 (NaN is excluded)
96 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
97 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
97 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
98 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
98 *
99 *
99 * @param v1 first value
100 * @param v1 first value
100 * @param v2 second value
101 * @param v2 second value
101 * @return true if v1 < v2, false otherwise
102 * @return true if v1 < v2, false otherwise
102 * @sa std::max_element
103 * @sa std::max_element
103 */
104 */
104 template <typename T>
105 template <typename T>
105 static bool maxCompareWithNaN(const T &v1, const T &v2)
106 static bool maxCompareWithNaN(const T &v1, const T &v2)
106 {
107 {
107 // Table used with NaN values:
108 // Table used with NaN values:
108 // NaN < v2 -> true
109 // NaN < v2 -> true
109 // v1 < NaN -> false
110 // v1 < NaN -> false
110 // NaN < NaN -> false
111 // NaN < NaN -> false
111 // v1 < v2 -> v1 < v2
112 // v1 < v2 -> v1 < v2
112 return std::isnan(v1) ? true : !std::isnan(v2) && (v1 < v2);
113 return std::isnan(v1) ? true : !std::isnan(v2) && (v1 < v2);
113 }
114 }
114 };
115 };
115
116
116 #endif // SCIQLOP_SORTUTILS_H
117 #endif // SCIQLOP_SORTUTILS_H
@@ -1,520 +1,522
1 #include "Data/DataSeries.h"
1 #include "Data/DataSeries.h"
2 #include "Data/ScalarSeries.h"
2 #include "Data/ScalarSeries.h"
3 #include "Data/VectorSeries.h"
3 #include "Data/VectorSeries.h"
4
4
5 #include <cmath>
6
5 #include <QObject>
7 #include <QObject>
6 #include <QtTest>
8 #include <QtTest>
7
9
8 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
9 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
10
12
11 class TestDataSeries : public QObject {
13 class TestDataSeries : public QObject {
12 Q_OBJECT
14 Q_OBJECT
13 private:
15 private:
14 template <typename T>
16 template <typename T>
15 void testValuesBoundsStructure()
17 void testValuesBoundsStructure()
16 {
18 {
17 // ////////////// //
19 // ////////////// //
18 // Test structure //
20 // Test structure //
19 // ////////////// //
21 // ////////////// //
20
22
21 // Data series to get values bounds
23 // Data series to get values bounds
22 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
24 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
23
25
24 // x-axis range
26 // x-axis range
25 QTest::addColumn<double>("minXAxis");
27 QTest::addColumn<double>("minXAxis");
26 QTest::addColumn<double>("maxXAxis");
28 QTest::addColumn<double>("maxXAxis");
27
29
28 // Expected results
30 // Expected results
29 QTest::addColumn<bool>(
31 QTest::addColumn<bool>(
30 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
32 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
31 QTest::addColumn<double>("expectedMinValue");
33 QTest::addColumn<double>("expectedMinValue");
32 QTest::addColumn<double>("expectedMaxValue");
34 QTest::addColumn<double>("expectedMaxValue");
33 }
35 }
34
36
35 template <typename T>
37 template <typename T>
36 void testValuesBounds()
38 void testValuesBounds()
37 {
39 {
38 QFETCH(std::shared_ptr<T>, dataSeries);
40 QFETCH(std::shared_ptr<T>, dataSeries);
39 QFETCH(double, minXAxis);
41 QFETCH(double, minXAxis);
40 QFETCH(double, maxXAxis);
42 QFETCH(double, maxXAxis);
41
43
42 QFETCH(bool, expectedOK);
44 QFETCH(bool, expectedOK);
43 QFETCH(double, expectedMinValue);
45 QFETCH(double, expectedMinValue);
44 QFETCH(double, expectedMaxValue);
46 QFETCH(double, expectedMaxValue);
45
47
46 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
48 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
47 auto end = dataSeries->cend();
49 auto end = dataSeries->cend();
48
50
49 // Checks iterators with expected result
51 // Checks iterators with expected result
50 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
52 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
51
53
52 if (expectedOK) {
54 if (expectedOK) {
53 auto compare = [](const auto &v1, const auto &v2) {
55 auto compare = [](const auto &v1, const auto &v2) {
54 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
56 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
55 };
57 };
56
58
57 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
59 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
58 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
60 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
59 }
61 }
60 }
62 }
61
63
62 private slots:
64 private slots:
63 /// Input test data
65 /// Input test data
64 /// @sa testCtor()
66 /// @sa testCtor()
65 void testCtor_data();
67 void testCtor_data();
66
68
67 /// Tests construction of a data series
69 /// Tests construction of a data series
68 void testCtor();
70 void testCtor();
69
71
70 /// Input test data
72 /// Input test data
71 /// @sa testMerge()
73 /// @sa testMerge()
72 void testMerge_data();
74 void testMerge_data();
73
75
74 /// Tests merge of two data series
76 /// Tests merge of two data series
75 void testMerge();
77 void testMerge();
76
78
77 /// Input test data
79 /// Input test data
78 /// @sa testMinXAxisData()
80 /// @sa testMinXAxisData()
79 void testMinXAxisData_data();
81 void testMinXAxisData_data();
80
82
81 /// Tests get min x-axis data of a data series
83 /// Tests get min x-axis data of a data series
82 void testMinXAxisData();
84 void testMinXAxisData();
83
85
84 /// Input test data
86 /// Input test data
85 /// @sa testMaxXAxisData()
87 /// @sa testMaxXAxisData()
86 void testMaxXAxisData_data();
88 void testMaxXAxisData_data();
87
89
88 /// Tests get max x-axis data of a data series
90 /// Tests get max x-axis data of a data series
89 void testMaxXAxisData();
91 void testMaxXAxisData();
90
92
91 /// Input test data
93 /// Input test data
92 /// @sa testXAxisRange()
94 /// @sa testXAxisRange()
93 void testXAxisRange_data();
95 void testXAxisRange_data();
94
96
95 /// Tests get x-axis range of a data series
97 /// Tests get x-axis range of a data series
96 void testXAxisRange();
98 void testXAxisRange();
97
99
98 /// Input test data
100 /// Input test data
99 /// @sa testValuesBoundsScalar()
101 /// @sa testValuesBoundsScalar()
100 void testValuesBoundsScalar_data();
102 void testValuesBoundsScalar_data();
101
103
102 /// Tests get values bounds of a scalar series
104 /// Tests get values bounds of a scalar series
103 void testValuesBoundsScalar();
105 void testValuesBoundsScalar();
104
106
105 /// Input test data
107 /// Input test data
106 /// @sa testValuesBoundsVector()
108 /// @sa testValuesBoundsVector()
107 void testValuesBoundsVector_data();
109 void testValuesBoundsVector_data();
108
110
109 /// Tests get values bounds of a vector series
111 /// Tests get values bounds of a vector series
110 void testValuesBoundsVector();
112 void testValuesBoundsVector();
111 };
113 };
112
114
113 void TestDataSeries::testCtor_data()
115 void TestDataSeries::testCtor_data()
114 {
116 {
115 // ////////////// //
117 // ////////////// //
116 // Test structure //
118 // Test structure //
117 // ////////////// //
119 // ////////////// //
118
120
119 // x-axis data
121 // x-axis data
120 QTest::addColumn<QVector<double> >("xAxisData");
122 QTest::addColumn<QVector<double> >("xAxisData");
121 // values data
123 // values data
122 QTest::addColumn<QVector<double> >("valuesData");
124 QTest::addColumn<QVector<double> >("valuesData");
123
125
124 // expected x-axis data
126 // expected x-axis data
125 QTest::addColumn<QVector<double> >("expectedXAxisData");
127 QTest::addColumn<QVector<double> >("expectedXAxisData");
126 // expected values data
128 // expected values data
127 QTest::addColumn<QVector<double> >("expectedValuesData");
129 QTest::addColumn<QVector<double> >("expectedValuesData");
128
130
129 // ////////// //
131 // ////////// //
130 // Test cases //
132 // Test cases //
131 // ////////// //
133 // ////////// //
132
134
133 QTest::newRow("invalidData (different sizes of vectors)")
135 QTest::newRow("invalidData (different sizes of vectors)")
134 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 200., 300.}
136 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 200., 300.}
135 << QVector<double>{} << QVector<double>{};
137 << QVector<double>{} << QVector<double>{};
136
138
137 QTest::newRow("sortedData") << QVector<double>{1., 2., 3., 4., 5.}
139 QTest::newRow("sortedData") << QVector<double>{1., 2., 3., 4., 5.}
138 << QVector<double>{100., 200., 300., 400., 500.}
140 << QVector<double>{100., 200., 300., 400., 500.}
139 << QVector<double>{1., 2., 3., 4., 5.}
141 << QVector<double>{1., 2., 3., 4., 5.}
140 << QVector<double>{100., 200., 300., 400., 500.};
142 << QVector<double>{100., 200., 300., 400., 500.};
141
143
142 QTest::newRow("unsortedData") << QVector<double>{5., 4., 3., 2., 1.}
144 QTest::newRow("unsortedData") << QVector<double>{5., 4., 3., 2., 1.}
143 << QVector<double>{100., 200., 300., 400., 500.}
145 << QVector<double>{100., 200., 300., 400., 500.}
144 << QVector<double>{1., 2., 3., 4., 5.}
146 << QVector<double>{1., 2., 3., 4., 5.}
145 << QVector<double>{500., 400., 300., 200., 100.};
147 << QVector<double>{500., 400., 300., 200., 100.};
146
148
147 QTest::newRow("unsortedData2")
149 QTest::newRow("unsortedData2")
148 << QVector<double>{1., 4., 3., 5., 2.} << QVector<double>{100., 200., 300., 400., 500.}
150 << QVector<double>{1., 4., 3., 5., 2.} << QVector<double>{100., 200., 300., 400., 500.}
149 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 500., 300., 200., 400.};
151 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 500., 300., 200., 400.};
150 }
152 }
151
153
152 void TestDataSeries::testCtor()
154 void TestDataSeries::testCtor()
153 {
155 {
154 // Creates series
156 // Creates series
155 QFETCH(QVector<double>, xAxisData);
157 QFETCH(QVector<double>, xAxisData);
156 QFETCH(QVector<double>, valuesData);
158 QFETCH(QVector<double>, valuesData);
157
159
158 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
160 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
159 Unit{}, Unit{});
161 Unit{}, Unit{});
160
162
161 // Validates results : we check that the data series is sorted on its x-axis data
163 // Validates results : we check that the data series is sorted on its x-axis data
162 QFETCH(QVector<double>, expectedXAxisData);
164 QFETCH(QVector<double>, expectedXAxisData);
163 QFETCH(QVector<double>, expectedValuesData);
165 QFETCH(QVector<double>, expectedValuesData);
164
166
165 auto seriesXAxisData = series->xAxisData()->data();
167 auto seriesXAxisData = series->xAxisData()->data();
166 auto seriesValuesData = series->valuesData()->data();
168 auto seriesValuesData = series->valuesData()->data();
167
169
168 QVERIFY(
170 QVERIFY(
169 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
171 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
170 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
172 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
171 seriesValuesData.cbegin()));
173 seriesValuesData.cbegin()));
172 }
174 }
173
175
174 namespace {
176 namespace {
175
177
176 std::shared_ptr<ScalarSeries> createScalarSeries(QVector<double> xAxisData,
178 std::shared_ptr<ScalarSeries> createScalarSeries(QVector<double> xAxisData,
177 QVector<double> valuesData)
179 QVector<double> valuesData)
178 {
180 {
179 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
181 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
180 Unit{});
182 Unit{});
181 }
183 }
182
184
183 std::shared_ptr<VectorSeries> createVectorSeries(QVector<double> xAxisData,
185 std::shared_ptr<VectorSeries> createVectorSeries(QVector<double> xAxisData,
184 QVector<double> xValuesData,
186 QVector<double> xValuesData,
185 QVector<double> yValuesData,
187 QVector<double> yValuesData,
186 QVector<double> zValuesData)
188 QVector<double> zValuesData)
187 {
189 {
188 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
190 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
189 std::move(yValuesData), std::move(zValuesData), Unit{},
191 std::move(yValuesData), std::move(zValuesData), Unit{},
190 Unit{});
192 Unit{});
191 }
193 }
192
194
193 } // namespace
195 } // namespace
194
196
195 void TestDataSeries::testMerge_data()
197 void TestDataSeries::testMerge_data()
196 {
198 {
197 // ////////////// //
199 // ////////////// //
198 // Test structure //
200 // Test structure //
199 // ////////////// //
201 // ////////////// //
200
202
201 // Data series to merge
203 // Data series to merge
202 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
204 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
203 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
205 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
204
206
205 // Expected values in the first data series after merge
207 // Expected values in the first data series after merge
206 QTest::addColumn<QVector<double> >("expectedXAxisData");
208 QTest::addColumn<QVector<double> >("expectedXAxisData");
207 QTest::addColumn<QVector<double> >("expectedValuesData");
209 QTest::addColumn<QVector<double> >("expectedValuesData");
208
210
209 // ////////// //
211 // ////////// //
210 // Test cases //
212 // Test cases //
211 // ////////// //
213 // ////////// //
212
214
213 QTest::newRow("sortedMerge")
215 QTest::newRow("sortedMerge")
214 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
216 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
215 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
217 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
216 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
218 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
217 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
219 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
218
220
219 QTest::newRow("unsortedMerge")
221 QTest::newRow("unsortedMerge")
220 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
222 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
221 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
223 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
222 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
224 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
223 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
225 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
224
226
225 QTest::newRow("unsortedMerge2")
227 QTest::newRow("unsortedMerge2")
226 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 300., 400., 500.})
228 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 300., 400., 500.})
227 << createScalarSeries({3., 4., 5., 6., 7.}, {600., 700., 800., 900., 1000.})
229 << createScalarSeries({3., 4., 5., 6., 7.}, {600., 700., 800., 900., 1000.})
228 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
230 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
229 << QVector<double>{100., 200., 600., 700., 800., 900., 1000., 300., 400., 500.};
231 << QVector<double>{100., 200., 600., 700., 800., 900., 1000., 300., 400., 500.};
230
232
231 QTest::newRow("unsortedMerge3")
233 QTest::newRow("unsortedMerge3")
232 << createScalarSeries({3., 5., 8., 7., 2}, {100., 200., 300., 400., 500.})
234 << createScalarSeries({3., 5., 8., 7., 2}, {100., 200., 300., 400., 500.})
233 << createScalarSeries({6., 4., 9., 10., 1.}, {600., 700., 800., 900., 1000.})
235 << createScalarSeries({6., 4., 9., 10., 1.}, {600., 700., 800., 900., 1000.})
234 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
236 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
235 << QVector<double>{1000., 500., 100., 700., 200., 600., 400., 300., 800., 900.};
237 << QVector<double>{1000., 500., 100., 700., 200., 600., 400., 300., 800., 900.};
236 }
238 }
237
239
238 void TestDataSeries::testMerge()
240 void TestDataSeries::testMerge()
239 {
241 {
240 // Merges series
242 // Merges series
241 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
243 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
242 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
244 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
243
245
244 dataSeries->merge(dataSeries2.get());
246 dataSeries->merge(dataSeries2.get());
245
247
246 // Validates results : we check that the merge is valid and the data series is sorted on its
248 // Validates results : we check that the merge is valid and the data series is sorted on its
247 // x-axis data
249 // x-axis data
248 QFETCH(QVector<double>, expectedXAxisData);
250 QFETCH(QVector<double>, expectedXAxisData);
249 QFETCH(QVector<double>, expectedValuesData);
251 QFETCH(QVector<double>, expectedValuesData);
250
252
251 auto seriesXAxisData = dataSeries->xAxisData()->data();
253 auto seriesXAxisData = dataSeries->xAxisData()->data();
252 auto seriesValuesData = dataSeries->valuesData()->data();
254 auto seriesValuesData = dataSeries->valuesData()->data();
253
255
254 QVERIFY(
256 QVERIFY(
255 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
257 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
256 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
258 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
257 seriesValuesData.cbegin()));
259 seriesValuesData.cbegin()));
258 }
260 }
259
261
260 void TestDataSeries::testMinXAxisData_data()
262 void TestDataSeries::testMinXAxisData_data()
261 {
263 {
262 // ////////////// //
264 // ////////////// //
263 // Test structure //
265 // Test structure //
264 // ////////////// //
266 // ////////////// //
265
267
266 // Data series to get min data
268 // Data series to get min data
267 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
269 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
268
270
269 // Min data
271 // Min data
270 QTest::addColumn<double>("min");
272 QTest::addColumn<double>("min");
271
273
272 // Expected results
274 // Expected results
273 QTest::addColumn<bool>(
275 QTest::addColumn<bool>(
274 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
276 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
275 QTest::addColumn<double>(
277 QTest::addColumn<double>(
276 "expectedMin"); // Expected value when method doesn't return end iterator
278 "expectedMin"); // Expected value when method doesn't return end iterator
277
279
278 // ////////// //
280 // ////////// //
279 // Test cases //
281 // Test cases //
280 // ////////// //
282 // ////////// //
281
283
282 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
284 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
283 {100., 200., 300., 400., 500.})
285 {100., 200., 300., 400., 500.})
284 << 0. << true << 1.;
286 << 0. << true << 1.;
285 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
287 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
286 {100., 200., 300., 400., 500.})
288 {100., 200., 300., 400., 500.})
287 << 1. << true << 1.;
289 << 1. << true << 1.;
288 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
290 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
289 {100., 200., 300., 400., 500.})
291 {100., 200., 300., 400., 500.})
290 << 1.1 << true << 2.;
292 << 1.1 << true << 2.;
291 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
293 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
292 {100., 200., 300., 400., 500.})
294 {100., 200., 300., 400., 500.})
293 << 5. << true << 5.;
295 << 5. << true << 5.;
294 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
296 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
295 {100., 200., 300., 400., 500.})
297 {100., 200., 300., 400., 500.})
296 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
298 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
297 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
299 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
298 << std::numeric_limits<double>::quiet_NaN();
300 << std::numeric_limits<double>::quiet_NaN();
299 }
301 }
300
302
301 void TestDataSeries::testMinXAxisData()
303 void TestDataSeries::testMinXAxisData()
302 {
304 {
303 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
305 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
304 QFETCH(double, min);
306 QFETCH(double, min);
305
307
306 QFETCH(bool, expectedOK);
308 QFETCH(bool, expectedOK);
307 QFETCH(double, expectedMin);
309 QFETCH(double, expectedMin);
308
310
309 auto it = dataSeries->minXAxisData(min);
311 auto it = dataSeries->minXAxisData(min);
310
312
311 QCOMPARE(expectedOK, it != dataSeries->cend());
313 QCOMPARE(expectedOK, it != dataSeries->cend());
312
314
313 // If the method doesn't return a end iterator, checks with expected value
315 // If the method doesn't return a end iterator, checks with expected value
314 if (expectedOK) {
316 if (expectedOK) {
315 QCOMPARE(expectedMin, it->x());
317 QCOMPARE(expectedMin, it->x());
316 }
318 }
317 }
319 }
318
320
319 void TestDataSeries::testMaxXAxisData_data()
321 void TestDataSeries::testMaxXAxisData_data()
320 {
322 {
321 // ////////////// //
323 // ////////////// //
322 // Test structure //
324 // Test structure //
323 // ////////////// //
325 // ////////////// //
324
326
325 // Data series to get max data
327 // Data series to get max data
326 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
328 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
327
329
328 // Max data
330 // Max data
329 QTest::addColumn<double>("max");
331 QTest::addColumn<double>("max");
330
332
331 // Expected results
333 // Expected results
332 QTest::addColumn<bool>(
334 QTest::addColumn<bool>(
333 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
335 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
334 QTest::addColumn<double>(
336 QTest::addColumn<double>(
335 "expectedMax"); // Expected value when method doesn't return end iterator
337 "expectedMax"); // Expected value when method doesn't return end iterator
336
338
337 // ////////// //
339 // ////////// //
338 // Test cases //
340 // Test cases //
339 // ////////// //
341 // ////////// //
340
342
341 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
343 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
342 {100., 200., 300., 400., 500.})
344 {100., 200., 300., 400., 500.})
343 << 6. << true << 5.;
345 << 6. << true << 5.;
344 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
346 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
345 {100., 200., 300., 400., 500.})
347 {100., 200., 300., 400., 500.})
346 << 5. << true << 5.;
348 << 5. << true << 5.;
347 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
349 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
348 {100., 200., 300., 400., 500.})
350 {100., 200., 300., 400., 500.})
349 << 4.9 << true << 4.;
351 << 4.9 << true << 4.;
350 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
352 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
351 {100., 200., 300., 400., 500.})
353 {100., 200., 300., 400., 500.})
352 << 1.1 << true << 1.;
354 << 1.1 << true << 1.;
353 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
355 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
354 {100., 200., 300., 400., 500.})
356 {100., 200., 300., 400., 500.})
355 << 1. << true << 1.;
357 << 1. << true << 1.;
356 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
358 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
357 << std::numeric_limits<double>::quiet_NaN();
359 << std::numeric_limits<double>::quiet_NaN();
358 }
360 }
359
361
360 void TestDataSeries::testMaxXAxisData()
362 void TestDataSeries::testMaxXAxisData()
361 {
363 {
362 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
364 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
363 QFETCH(double, max);
365 QFETCH(double, max);
364
366
365 QFETCH(bool, expectedOK);
367 QFETCH(bool, expectedOK);
366 QFETCH(double, expectedMax);
368 QFETCH(double, expectedMax);
367
369
368 auto it = dataSeries->maxXAxisData(max);
370 auto it = dataSeries->maxXAxisData(max);
369
371
370 QCOMPARE(expectedOK, it != dataSeries->cend());
372 QCOMPARE(expectedOK, it != dataSeries->cend());
371
373
372 // If the method doesn't return a end iterator, checks with expected value
374 // If the method doesn't return a end iterator, checks with expected value
373 if (expectedOK) {
375 if (expectedOK) {
374 QCOMPARE(expectedMax, it->x());
376 QCOMPARE(expectedMax, it->x());
375 }
377 }
376 }
378 }
377
379
378 void TestDataSeries::testXAxisRange_data()
380 void TestDataSeries::testXAxisRange_data()
379 {
381 {
380 // ////////////// //
382 // ////////////// //
381 // Test structure //
383 // Test structure //
382 // ////////////// //
384 // ////////////// //
383
385
384 // Data series to get x-axis range
386 // Data series to get x-axis range
385 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
387 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
386
388
387 // Min/max values
389 // Min/max values
388 QTest::addColumn<double>("min");
390 QTest::addColumn<double>("min");
389 QTest::addColumn<double>("max");
391 QTest::addColumn<double>("max");
390
392
391 // Expected values
393 // Expected values
392 QTest::addColumn<QVector<double> >("expectedXAxisData");
394 QTest::addColumn<QVector<double> >("expectedXAxisData");
393 QTest::addColumn<QVector<double> >("expectedValuesData");
395 QTest::addColumn<QVector<double> >("expectedValuesData");
394
396
395 // ////////// //
397 // ////////// //
396 // Test cases //
398 // Test cases //
397 // ////////// //
399 // ////////// //
398
400
399 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
401 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
400 {100., 200., 300., 400., 500.})
402 {100., 200., 300., 400., 500.})
401 << -1. << 3.2 << QVector<double>{1., 2., 3.}
403 << -1. << 3.2 << QVector<double>{1., 2., 3.}
402 << QVector<double>{100., 200., 300.};
404 << QVector<double>{100., 200., 300.};
403 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
405 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
404 {100., 200., 300., 400., 500.})
406 {100., 200., 300., 400., 500.})
405 << 1. << 4. << QVector<double>{1., 2., 3., 4.}
407 << 1. << 4. << QVector<double>{1., 2., 3., 4.}
406 << QVector<double>{100., 200., 300., 400.};
408 << QVector<double>{100., 200., 300., 400.};
407 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
409 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
408 {100., 200., 300., 400., 500.})
410 {100., 200., 300., 400., 500.})
409 << 1. << 3.9 << QVector<double>{1., 2., 3.}
411 << 1. << 3.9 << QVector<double>{1., 2., 3.}
410 << QVector<double>{100., 200., 300.};
412 << QVector<double>{100., 200., 300.};
411 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
413 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
412 {100., 200., 300., 400., 500.})
414 {100., 200., 300., 400., 500.})
413 << 0. << 0.9 << QVector<double>{} << QVector<double>{};
415 << 0. << 0.9 << QVector<double>{} << QVector<double>{};
414 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
416 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
415 {100., 200., 300., 400., 500.})
417 {100., 200., 300., 400., 500.})
416 << 0. << 1. << QVector<double>{1.} << QVector<double>{100.};
418 << 0. << 1. << QVector<double>{1.} << QVector<double>{100.};
417 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
419 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
418 {100., 200., 300., 400., 500.})
420 {100., 200., 300., 400., 500.})
419 << 2.1 << 6. << QVector<double>{3., 4., 5.}
421 << 2.1 << 6. << QVector<double>{3., 4., 5.}
420 << QVector<double>{300., 400., 500.};
422 << QVector<double>{300., 400., 500.};
421 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
423 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
422 {100., 200., 300., 400., 500.})
424 {100., 200., 300., 400., 500.})
423 << 6. << 9. << QVector<double>{} << QVector<double>{};
425 << 6. << 9. << QVector<double>{} << QVector<double>{};
424 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
426 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
425 {100., 200., 300., 400., 500.})
427 {100., 200., 300., 400., 500.})
426 << 5. << 9. << QVector<double>{5.} << QVector<double>{500.};
428 << 5. << 9. << QVector<double>{5.} << QVector<double>{500.};
427 }
429 }
428
430
429 void TestDataSeries::testXAxisRange()
431 void TestDataSeries::testXAxisRange()
430 {
432 {
431 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
433 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
432 QFETCH(double, min);
434 QFETCH(double, min);
433 QFETCH(double, max);
435 QFETCH(double, max);
434
436
435 QFETCH(QVector<double>, expectedXAxisData);
437 QFETCH(QVector<double>, expectedXAxisData);
436 QFETCH(QVector<double>, expectedValuesData);
438 QFETCH(QVector<double>, expectedValuesData);
437
439
438 auto bounds = dataSeries->xAxisRange(min, max);
440 auto bounds = dataSeries->xAxisRange(min, max);
439 QVERIFY(std::equal(bounds.first, bounds.second, expectedXAxisData.cbegin(),
441 QVERIFY(std::equal(bounds.first, bounds.second, expectedXAxisData.cbegin(),
440 expectedXAxisData.cend(),
442 expectedXAxisData.cend(),
441 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
443 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
442 QVERIFY(std::equal(
444 QVERIFY(std::equal(
443 bounds.first, bounds.second, expectedValuesData.cbegin(), expectedValuesData.cend(),
445 bounds.first, bounds.second, expectedValuesData.cbegin(), expectedValuesData.cend(),
444 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
446 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
445 }
447 }
446
448
447 void TestDataSeries::testValuesBoundsScalar_data()
449 void TestDataSeries::testValuesBoundsScalar_data()
448 {
450 {
449 testValuesBoundsStructure<ScalarSeries>();
451 testValuesBoundsStructure<ScalarSeries>();
450
452
451 // ////////// //
453 // ////////// //
452 // Test cases //
454 // Test cases //
453 // ////////// //
455 // ////////// //
454 auto nan = std::numeric_limits<double>::quiet_NaN();
456 auto nan = std::numeric_limits<double>::quiet_NaN();
455
457
456 QTest::newRow("scalarBounds1")
458 QTest::newRow("scalarBounds1")
457 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
459 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
458 << true << 100. << 500.;
460 << true << 100. << 500.;
459 QTest::newRow("scalarBounds2")
461 QTest::newRow("scalarBounds2")
460 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
462 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
461 << true << 200. << 400.;
463 << true << 200. << 400.;
462 QTest::newRow("scalarBounds3")
464 QTest::newRow("scalarBounds3")
463 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
465 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
464 << false << nan << nan;
466 << false << nan << nan;
465 QTest::newRow("scalarBounds4")
467 QTest::newRow("scalarBounds4")
466 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
468 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
467 << false << nan << nan;
469 << false << nan << nan;
468 QTest::newRow("scalarBounds5")
470 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
469 << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100. << 100.;
471 << 100.;
470 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
472 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
471
473
472 // Tests with NaN values: NaN values are not included in min/max search
474 // Tests with NaN values: NaN values are not included in min/max search
473 QTest::newRow("scalarBounds7")
475 QTest::newRow("scalarBounds7")
474 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
476 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
475 << true << 200. << 400.;
477 << true << 200. << 400.;
476 QTest::newRow("scalarBounds8")
478 QTest::newRow("scalarBounds8")
477 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
479 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
478 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
480 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
479 }
481 }
480
482
481 void TestDataSeries::testValuesBoundsScalar()
483 void TestDataSeries::testValuesBoundsScalar()
482 {
484 {
483 testValuesBounds<ScalarSeries>();
485 testValuesBounds<ScalarSeries>();
484 }
486 }
485
487
486 void TestDataSeries::testValuesBoundsVector_data()
488 void TestDataSeries::testValuesBoundsVector_data()
487 {
489 {
488 testValuesBoundsStructure<VectorSeries>();
490 testValuesBoundsStructure<VectorSeries>();
489
491
490 // ////////// //
492 // ////////// //
491 // Test cases //
493 // Test cases //
492 // ////////// //
494 // ////////// //
493 auto nan = std::numeric_limits<double>::quiet_NaN();
495 auto nan = std::numeric_limits<double>::quiet_NaN();
494
496
495 QTest::newRow("vectorBounds1")
497 QTest::newRow("vectorBounds1")
496 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
498 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
497 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
499 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
498 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
500 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
499 QTest::newRow("vectorBounds2")
501 QTest::newRow("vectorBounds2")
500 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
502 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
501 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
503 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
502 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
504 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
503 QTest::newRow("vectorBounds3")
505 QTest::newRow("vectorBounds3")
504 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
506 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
505 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
507 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
506 << 2. << 3. << true << 10. << 24.;
508 << 2. << 3. << true << 10. << 24.;
507
509
508 // Tests with NaN values: NaN values are not included in min/max search
510 // Tests with NaN values: NaN values are not included in min/max search
509 QTest::newRow("vectorBounds4")
511 QTest::newRow("vectorBounds4")
510 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
512 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
511 << nan << nan;
513 << nan << nan;
512 }
514 }
513
515
514 void TestDataSeries::testValuesBoundsVector()
516 void TestDataSeries::testValuesBoundsVector()
515 {
517 {
516 testValuesBounds<VectorSeries>();
518 testValuesBounds<VectorSeries>();
517 }
519 }
518
520
519 QTEST_MAIN(TestDataSeries)
521 QTEST_MAIN(TestDataSeries)
520 #include "TestDataSeries.moc"
522 #include "TestDataSeries.moc"
@@ -1,256 +1,257
1 #include "Visualization/VisualizationZoneWidget.h"
1 #include "Visualization/VisualizationZoneWidget.h"
2
2
3
3
4 #include "Visualization/IVisualizationWidgetVisitor.h"
4 #include "Visualization/IVisualizationWidgetVisitor.h"
5 #include "Visualization/VisualizationGraphWidget.h"
5 #include "Visualization/VisualizationGraphWidget.h"
6 #include "ui_VisualizationZoneWidget.h"
6 #include "ui_VisualizationZoneWidget.h"
7
7
8 #include <Data/SqpRange.h>
8 #include <Data/SqpRange.h>
9 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
10 #include <Variable/VariableController.h>
10 #include <Variable/VariableController.h>
11
11
12 #include <cmath>
12 #include <QUuid>
13 #include <QUuid>
13 #include <SqpApplication.h>
14 #include <SqpApplication.h>
14
15
15 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
16 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
16
17
17 namespace {
18 namespace {
18
19
19 /// Minimum height for graph added in zones (in pixels)
20 /// Minimum height for graph added in zones (in pixels)
20 const auto GRAPH_MINIMUM_HEIGHT = 300;
21 const auto GRAPH_MINIMUM_HEIGHT = 300;
21
22
22 /// Generates a default name for a new graph, according to the number of graphs already displayed in
23 /// Generates a default name for a new graph, according to the number of graphs already displayed in
23 /// the zone
24 /// the zone
24 QString defaultGraphName(const QLayout &layout)
25 QString defaultGraphName(const QLayout &layout)
25 {
26 {
26 auto count = 0;
27 auto count = 0;
27 for (auto i = 0; i < layout.count(); ++i) {
28 for (auto i = 0; i < layout.count(); ++i) {
28 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
29 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
29 count++;
30 count++;
30 }
31 }
31 }
32 }
32
33
33 return QObject::tr("Graph %1").arg(count + 1);
34 return QObject::tr("Graph %1").arg(count + 1);
34 }
35 }
35
36
36 } // namespace
37 } // namespace
37
38
38 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
39 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
39
40
40 explicit VisualizationZoneWidgetPrivate() : m_SynchronisationGroupId{QUuid::createUuid()} {}
41 explicit VisualizationZoneWidgetPrivate() : m_SynchronisationGroupId{QUuid::createUuid()} {}
41 QUuid m_SynchronisationGroupId;
42 QUuid m_SynchronisationGroupId;
42 };
43 };
43
44
44 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
45 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
45 : QWidget{parent},
46 : QWidget{parent},
46 ui{new Ui::VisualizationZoneWidget},
47 ui{new Ui::VisualizationZoneWidget},
47 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
48 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
48 {
49 {
49 ui->setupUi(this);
50 ui->setupUi(this);
50
51
51 ui->zoneNameLabel->setText(name);
52 ui->zoneNameLabel->setText(name);
52
53
53 // 'Close' options : widget is deleted when closed
54 // 'Close' options : widget is deleted when closed
54 setAttribute(Qt::WA_DeleteOnClose);
55 setAttribute(Qt::WA_DeleteOnClose);
55 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
56 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
56 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
57 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
57
58
58 // Synchronisation id
59 // Synchronisation id
59 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
60 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
60 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
61 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
61 }
62 }
62
63
63 VisualizationZoneWidget::~VisualizationZoneWidget()
64 VisualizationZoneWidget::~VisualizationZoneWidget()
64 {
65 {
65 delete ui;
66 delete ui;
66 }
67 }
67
68
68 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
69 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
69 {
70 {
70 ui->visualizationZoneFrame->layout()->addWidget(graphWidget);
71 ui->visualizationZoneFrame->layout()->addWidget(graphWidget);
71 }
72 }
72
73
73 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
74 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
74 {
75 {
75 auto graphWidget = new VisualizationGraphWidget{
76 auto graphWidget = new VisualizationGraphWidget{
76 defaultGraphName(*ui->visualizationZoneFrame->layout()), this};
77 defaultGraphName(*ui->visualizationZoneFrame->layout()), this};
77
78
78
79
79 // Set graph properties
80 // Set graph properties
80 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
81 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
81 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
82 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
82
83
83
84
84 // Lambda to synchronize zone widget
85 // Lambda to synchronize zone widget
85 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
86 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
86 const SqpRange &oldGraphRange) {
87 const SqpRange &oldGraphRange) {
87
88
88 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
89 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
89 auto frameLayout = ui->visualizationZoneFrame->layout();
90 auto frameLayout = ui->visualizationZoneFrame->layout();
90 for (auto i = 0; i < frameLayout->count(); ++i) {
91 for (auto i = 0; i < frameLayout->count(); ++i) {
91 auto graphChild
92 auto graphChild
92 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
93 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
93 if (graphChild && (graphChild != graphWidget)) {
94 if (graphChild && (graphChild != graphWidget)) {
94
95
95 auto graphChildRange = graphChild->graphRange();
96 auto graphChildRange = graphChild->graphRange();
96 switch (zoomType) {
97 switch (zoomType) {
97 case AcquisitionZoomType::ZoomIn: {
98 case AcquisitionZoomType::ZoomIn: {
98 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
99 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
99 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
100 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
100 graphChildRange.m_TStart += deltaLeft;
101 graphChildRange.m_TStart += deltaLeft;
101 graphChildRange.m_TEnd -= deltaRight;
102 graphChildRange.m_TEnd -= deltaRight;
102 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
103 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
103 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
104 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
104 << deltaLeft;
105 << deltaLeft;
105 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
106 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
106 << deltaRight;
107 << deltaRight;
107 qCCritical(LOG_VisualizationZoneWidget())
108 qCCritical(LOG_VisualizationZoneWidget())
108 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
109 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
109
110
110 break;
111 break;
111 }
112 }
112
113
113 case AcquisitionZoomType::ZoomOut: {
114 case AcquisitionZoomType::ZoomOut: {
114 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
115 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
115 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
116 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
116 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
117 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
117 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
118 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
118 << deltaLeft;
119 << deltaLeft;
119 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
120 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
120 << deltaRight;
121 << deltaRight;
121 qCCritical(LOG_VisualizationZoneWidget())
122 qCCritical(LOG_VisualizationZoneWidget())
122 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
123 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
123 graphChildRange.m_TStart -= deltaLeft;
124 graphChildRange.m_TStart -= deltaLeft;
124 graphChildRange.m_TEnd += deltaRight;
125 graphChildRange.m_TEnd += deltaRight;
125 break;
126 break;
126 }
127 }
127 case AcquisitionZoomType::PanRight: {
128 case AcquisitionZoomType::PanRight: {
128 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
129 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
129 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
130 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
130 graphChildRange.m_TStart += deltaRight;
131 graphChildRange.m_TStart += deltaRight;
131 graphChildRange.m_TEnd += deltaRight;
132 graphChildRange.m_TEnd += deltaRight;
132 qCCritical(LOG_VisualizationZoneWidget())
133 qCCritical(LOG_VisualizationZoneWidget())
133 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
134 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
134 break;
135 break;
135 }
136 }
136 case AcquisitionZoomType::PanLeft: {
137 case AcquisitionZoomType::PanLeft: {
137 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
138 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
138 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
139 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
139 graphChildRange.m_TStart -= deltaLeft;
140 graphChildRange.m_TStart -= deltaLeft;
140 graphChildRange.m_TEnd -= deltaLeft;
141 graphChildRange.m_TEnd -= deltaLeft;
141 break;
142 break;
142 }
143 }
143 case AcquisitionZoomType::Unknown: {
144 case AcquisitionZoomType::Unknown: {
144 qCCritical(LOG_VisualizationZoneWidget())
145 qCCritical(LOG_VisualizationZoneWidget())
145 << tr("Impossible to synchronize: zoom type unknown");
146 << tr("Impossible to synchronize: zoom type unknown");
146 break;
147 break;
147 }
148 }
148 default:
149 default:
149 qCCritical(LOG_VisualizationZoneWidget())
150 qCCritical(LOG_VisualizationZoneWidget())
150 << tr("Impossible to synchronize: zoom type not take into account");
151 << tr("Impossible to synchronize: zoom type not take into account");
151 // No action
152 // No action
152 break;
153 break;
153 }
154 }
154 graphChild->enableAcquisition(false);
155 graphChild->enableAcquisition(false);
155 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
156 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
156 << graphChild->graphRange();
157 << graphChild->graphRange();
157 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
158 qCCritical(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
158 << graphChildRange;
159 << graphChildRange;
159 qCCritical(LOG_VisualizationZoneWidget())
160 qCCritical(LOG_VisualizationZoneWidget())
160 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
161 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
161 graphChild->setGraphRange(graphChildRange);
162 graphChild->setGraphRange(graphChildRange);
162 graphChild->enableAcquisition(true);
163 graphChild->enableAcquisition(true);
163 }
164 }
164 }
165 }
165 };
166 };
166
167
167 // connection for synchronization
168 // connection for synchronization
168 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
169 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
169 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
170 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
170 &VisualizationZoneWidget::onVariableAdded);
171 &VisualizationZoneWidget::onVariableAdded);
171
172
172 auto range = SqpRange{};
173 auto range = SqpRange{};
173
174
174 // Apply visitor to graph children
175 // Apply visitor to graph children
175 auto layout = ui->visualizationZoneFrame->layout();
176 auto layout = ui->visualizationZoneFrame->layout();
176 if (layout->count() > 0) {
177 if (layout->count() > 0) {
177 // Case of a new graph in a existant zone
178 // Case of a new graph in a existant zone
178 if (auto visualizationGraphWidget
179 if (auto visualizationGraphWidget
179 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
180 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
180 range = visualizationGraphWidget->graphRange();
181 range = visualizationGraphWidget->graphRange();
181 }
182 }
182 }
183 }
183 else {
184 else {
184 // Case of a new graph as the first of the zone
185 // Case of a new graph as the first of the zone
185 range = variable->range();
186 range = variable->range();
186 }
187 }
187
188
188 this->addGraph(graphWidget);
189 this->addGraph(graphWidget);
189
190
190 graphWidget->addVariable(variable, range);
191 graphWidget->addVariable(variable, range);
191
192
192 // get y using variable range
193 // get y using variable range
193 if (auto dataSeries = variable->dataSeries()) {
194 if (auto dataSeries = variable->dataSeries()) {
194 auto valuesBounds = dataSeries->valuesBounds(range.m_TStart, range.m_TEnd);
195 auto valuesBounds = dataSeries->valuesBounds(range.m_TStart, range.m_TEnd);
195 auto end = dataSeries->cend();
196 auto end = dataSeries->cend();
196 if (valuesBounds.first != end && valuesBounds.second != end) {
197 if (valuesBounds.first != end && valuesBounds.second != end) {
197 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
198 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
198
199
199 auto minValue = rangeValue(valuesBounds.first->minValue());
200 auto minValue = rangeValue(valuesBounds.first->minValue());
200 auto maxValue = rangeValue(valuesBounds.second->maxValue());
201 auto maxValue = rangeValue(valuesBounds.second->maxValue());
201
202
202 graphWidget->setYRange(SqpRange{minValue, maxValue});
203 graphWidget->setYRange(SqpRange{minValue, maxValue});
203 }
204 }
204 }
205 }
205
206
206 return graphWidget;
207 return graphWidget;
207 }
208 }
208
209
209 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
210 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
210 {
211 {
211 if (visitor) {
212 if (visitor) {
212 visitor->visitEnter(this);
213 visitor->visitEnter(this);
213
214
214 // Apply visitor to graph children
215 // Apply visitor to graph children
215 auto layout = ui->visualizationZoneFrame->layout();
216 auto layout = ui->visualizationZoneFrame->layout();
216 for (auto i = 0; i < layout->count(); ++i) {
217 for (auto i = 0; i < layout->count(); ++i) {
217 if (auto item = layout->itemAt(i)) {
218 if (auto item = layout->itemAt(i)) {
218 // Widgets different from graphs are not visited (no action)
219 // Widgets different from graphs are not visited (no action)
219 if (auto visualizationGraphWidget
220 if (auto visualizationGraphWidget
220 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
221 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
221 visualizationGraphWidget->accept(visitor);
222 visualizationGraphWidget->accept(visitor);
222 }
223 }
223 }
224 }
224 }
225 }
225
226
226 visitor->visitLeave(this);
227 visitor->visitLeave(this);
227 }
228 }
228 else {
229 else {
229 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
230 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
230 }
231 }
231 }
232 }
232
233
233 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
234 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
234 {
235 {
235 // A tab can always accomodate a variable
236 // A tab can always accomodate a variable
236 Q_UNUSED(variable);
237 Q_UNUSED(variable);
237 return true;
238 return true;
238 }
239 }
239
240
240 bool VisualizationZoneWidget::contains(const Variable &variable) const
241 bool VisualizationZoneWidget::contains(const Variable &variable) const
241 {
242 {
242 Q_UNUSED(variable);
243 Q_UNUSED(variable);
243 return false;
244 return false;
244 }
245 }
245
246
246 QString VisualizationZoneWidget::name() const
247 QString VisualizationZoneWidget::name() const
247 {
248 {
248 return ui->zoneNameLabel->text();
249 return ui->zoneNameLabel->text();
249 }
250 }
250
251
251 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
252 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
252 {
253 {
253 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
254 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
254 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
255 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
255 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
256 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
256 }
257 }
General Comments 0
You need to be logged in to leave comments. Login now