##// END OF EJS Templates
Mesh generation for QColorMap (3)...
Alexandre Leroux -
r968:6a9d119e7c40
parent child
Show More
@@ -1,161 +1,196
1 1 #include "Data/DataSeriesUtils.h"
2 2
3 3 #include <cmath>
4 4
5 5 Q_LOGGING_CATEGORY(LOG_DataSeriesUtils, "DataSeriesUtils")
6 6
7 7 void DataSeriesUtils::fillDataHoles(std::vector<double> &xAxisData, std::vector<double> &valuesData,
8 8 double resolution, double fillValue, double minBound,
9 9 double maxBound)
10 10 {
11 11 if (resolution == 0. || std::isnan(resolution)) {
12 12 qCWarning(LOG_DataSeriesUtils())
13 13 << "Can't fill data holes with a null resolution, no changes will be made";
14 14 return;
15 15 }
16 16
17 17 if (xAxisData.empty()) {
18 18 qCWarning(LOG_DataSeriesUtils())
19 19 << "Can't fill data holes for empty data, no changes will be made";
20 20 return;
21 21 }
22 22
23 23 // Gets the number of values per x-axis data
24 24 auto nbComponents = valuesData.size() / xAxisData.size();
25 25
26 26 // Generates fill values that will be used to complete values data
27 27 std::vector<double> fillValues(nbComponents, fillValue);
28 28
29 29 // Checks if there are data holes on the beginning of the data and generates the hole at the
30 30 // extremity if it's the case
31 31 auto minXAxisData = xAxisData.front();
32 32 if (!std::isnan(minBound) && minBound < minXAxisData) {
33 33 auto holeSize = static_cast<int>((minXAxisData - minBound) / resolution);
34 34 if (holeSize > 0) {
35 35 xAxisData.insert(xAxisData.begin(), minXAxisData - holeSize * resolution);
36 36 valuesData.insert(valuesData.begin(), fillValues.begin(), fillValues.end());
37 37 }
38 38 }
39 39
40 40 // Same for the end of the data
41 41 auto maxXAxisData = xAxisData.back();
42 42 if (!std::isnan(maxBound) && maxBound > maxXAxisData) {
43 43 auto holeSize = static_cast<int>((maxBound - maxXAxisData) / resolution);
44 44 if (holeSize > 0) {
45 45 xAxisData.insert(xAxisData.end(), maxXAxisData + holeSize * resolution);
46 46 valuesData.insert(valuesData.end(), fillValues.begin(), fillValues.end());
47 47 }
48 48 }
49 49
50 50 // Generates other data holes
51 51 auto xAxisIt = xAxisData.begin();
52 52 while (xAxisIt != xAxisData.end()) {
53 53 // Stops at first value which has a gap greater than resolution with the value next to it
54 54 xAxisIt = std::adjacent_find(
55 55 xAxisIt, xAxisData.end(),
56 56 [resolution](const auto &a, const auto &b) { return (b - a) > resolution; });
57 57
58 58 if (xAxisIt != xAxisData.end()) {
59 59 auto nextXAxisIt = xAxisIt + 1;
60 60
61 61 // Gets the values that has a gap greater than resolution between them
62 62 auto lowValue = *xAxisIt;
63 63 auto highValue = *nextXAxisIt;
64 64
65 65 // Completes holes between the two values by creating new values (according to the
66 66 // resolution)
67 67 for (auto i = lowValue + resolution; i < highValue; i += resolution) {
68 68 // Gets the iterator of values data from which to insert fill values
69 69 auto nextValuesIt = valuesData.begin()
70 70 + std::distance(xAxisData.begin(), nextXAxisIt) * nbComponents;
71 71
72 72 // New value is inserted before nextXAxisIt
73 73 nextXAxisIt = xAxisData.insert(nextXAxisIt, i) + 1;
74 74
75 75 // New values are inserted before nextValuesIt
76 76 valuesData.insert(nextValuesIt, fillValues.begin(), fillValues.end());
77 77 }
78 78
79 79 // Moves to the next value to continue loop on the x-axis data
80 80 xAxisIt = nextXAxisIt;
81 81 }
82 82 }
83 83 }
84 84
85 85 namespace {
86 86
87 87 /**
88 88 * Generates axis's mesh properties according to data and resolution
89 89 * @param begin the iterator pointing to the beginning of the data
90 90 * @param end the iterator pointing to the end of the data
91 91 * @param fun the function to retrieve data from the data iterators
92 92 * @param resolution the resolution to use for the axis' mesh
93 93 * @return a tuple representing the mesh properties : <nb values, min value, value step>
94 94 */
95 95 template <typename Iterator, typename IteratorFun>
96 96 std::tuple<int, double, double> meshProperties(Iterator begin, Iterator end, IteratorFun fun,
97 97 double resolution)
98 98 {
99 99 // Computes the gap between min and max data. This will be used to determinate the step between
100 100 // each data of the mesh
101 101 auto min = fun(begin);
102 102 auto max = fun(end - 1);
103 103 auto gap = max - min;
104 104
105 105 // Computes the step trying to use the fixed resolution. If the resolution doesn't separate the
106 106 // values evenly , it is recalculated.
107 107 // For example, for a resolution of 2.0:
108 108 // - for interval [0; 8] => resolution is valid, the generated mesh will be [0, 2, 4, 6, 8]
109 109 // - for interval [0; 9] => it's impossible to create a regular mesh with this resolution
110 110 // The resolution is recalculated and is worth 1.8. The generated mesh will be [0, 1.8, 3.6,
111 111 // 5.4, 7.2, 9]
112 112 auto nbVal = static_cast<int>(std::ceil(gap / resolution));
113 113 auto step = gap / nbVal;
114 114
115 115 // last data is included in the total number of values
116 116 return std::make_tuple(nbVal + 1, min, step);
117 117 }
118 118
119 119 } // namespace
120 120
121 121 DataSeriesUtils::Mesh DataSeriesUtils::regularMesh(DataSeriesIterator begin, DataSeriesIterator end,
122 122 Resolution xResolution, Resolution yResolution)
123 123 {
124 124 // Checks preconditions
125 125 if (xResolution.m_Val == 0. || std::isnan(xResolution.m_Val) || yResolution.m_Val == 0.
126 126 || std::isnan(yResolution.m_Val)) {
127 127 qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh with a null resolution";
128 128 return Mesh{};
129 129 }
130 130
131 131 if (xResolution.m_Logarithmic) {
132 132 qCWarning(LOG_DataSeriesUtils())
133 133 << "Can't generate mesh with a logarithmic x-axis resolution";
134 134 return Mesh{};
135 135 }
136 136
137 137 if (std::distance(begin, end) == 0) {
138 138 qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh for empty data";
139 139 return Mesh{};
140 140 }
141 141
142 142 auto yData = begin->y();
143 143 if (yData.empty()) {
144 144 qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh for data with no y-axis";
145 145 return Mesh{};
146 146 }
147 147
148 148 // Converts y-axis and its resolution to logarithmic values
149 149 if (yResolution.m_Logarithmic) {
150 150 std::for_each(yData.begin(), yData.end(), [](auto &val) { val = std::log10(val); });
151 151 }
152 152
153 153 // Computes mesh properties
154 154 int nbX, nbY;
155 155 double xMin, xStep, yMin, yStep;
156 156 std::tie(nbX, xMin, xStep)
157 157 = meshProperties(begin, end, [](const auto &it) { return it->x(); }, xResolution.m_Val);
158 158 std::tie(nbY, yMin, yStep) = meshProperties(
159 159 yData.begin(), yData.end(), [](const auto &it) { return *it; }, yResolution.m_Val);
160 160
161 // Generates mesh according to the x-axis and y-axis steps
162 Mesh result{nbX, xMin, xStep, nbY, yMin, yStep};
163
164 for (auto meshXIndex = 0; meshXIndex < nbX; ++meshXIndex) {
165 auto meshX = xMin + meshXIndex * xStep;
166 // According to current x-axis of the mesh, finds in the data series the interval in which
167 // the data is or gets closer (without exceeding it).
168 // An interval is defined by a value and extends to +/- 50% of the resolution. For example,
169 // for a value of 3 and a resolution of 1, the associated interval is [2.5, 3.5].
170 auto xIt = std::lower_bound(begin, end, meshX,
171 [xResolution](const auto &it, const auto &val) {
172 return it.x() - xResolution.m_Val / 2. < val;
173 })
174 - 1;
175
176 // When the corresponding entry of the data series is found, generates the values of the
177 // mesh by retrieving the values of the entry, for each y-axis value of the mesh
178 auto values = xIt->values();
179
180 for (auto meshYIndex = 0; meshYIndex < nbY; ++meshYIndex) {
181 auto meshY = yMin + meshYIndex * yStep;
182
183 auto yBegin = yData.begin();
184 auto yIt = std::lower_bound(yBegin, yData.end(), meshY,
185 [yResolution](const auto &it, const auto &val) {
186 return it - yResolution.m_Val / 2. < val;
187 })
188 - 1;
189
190 auto valueIndex = std::distance(yBegin, yIt);
191 result.m_Data[result.m_NbX * meshYIndex + meshXIndex] = values.at(valueIndex);
192 }
193 }
194
195 return result;
161 196 }
General Comments 0
You need to be logged in to leave comments. Login now