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