@@ -383,6 +383,10 public: | |||
|
383 | 383 | return std::make_pair(minIt, maxIt); |
|
384 | 384 | } |
|
385 | 385 | |
|
386 | /// @return the y-axis associated to the data series | |
|
387 | /// @todo pass getter as protected and use iterators to access the y-axis data | |
|
388 | OptionalAxis yAxis() const { return m_YAxis; } | |
|
389 | ||
|
386 | 390 | // /////// // |
|
387 | 391 | // Mutexes // |
|
388 | 392 | // /////// // |
@@ -438,9 +442,6 protected: | |||
|
438 | 442 | // necessary to call the sort method here ('other' is sorted) |
|
439 | 443 | } |
|
440 | 444 | |
|
441 | /// @return the y-axis associated to the data series | |
|
442 | OptionalAxis yAxis() const { return m_YAxis; } | |
|
443 | ||
|
444 | 445 | /// Assignment operator |
|
445 | 446 | template <int D> |
|
446 | 447 | DataSeries &operator=(DataSeries<D> other) |
@@ -41,6 +41,10 public: | |||
|
41 | 41 | /// @return gets the data at the index passed in parameter, NaN if the index is outside the |
|
42 | 42 | /// bounds of the axis, or if the axis is undefined |
|
43 | 43 | double at(int index) const; |
|
44 | ||
|
45 | ///@return the min and max values of the data on the axis, NaN values if there is no data | |
|
46 | std::pair<double, double> bounds() const; | |
|
47 | ||
|
44 | 48 | /// @return the number of data on the axis, 0 if the axis is not defined |
|
45 | 49 | int size() const; |
|
46 | 50 | /// @return the unit of the axis, an empty unit if the axis is not defined |
@@ -44,6 +44,31 double OptionalAxis::at(int index) const | |||
|
44 | 44 | } |
|
45 | 45 | } |
|
46 | 46 | |
|
47 | std::pair<double, double> OptionalAxis::bounds() const | |
|
48 | { | |
|
49 | if (!m_Defined || m_Data->size() == 0) { | |
|
50 | return std::make_pair(std::numeric_limits<double>::quiet_NaN(), | |
|
51 | std::numeric_limits<double>::quiet_NaN()); | |
|
52 | } | |
|
53 | else { | |
|
54 | ||
|
55 | auto minIt = std::min_element( | |
|
56 | m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) { | |
|
57 | return SortUtils::minCompareWithNaN(it1.first(), it2.first()); | |
|
58 | }); | |
|
59 | ||
|
60 | // Gets the iterator on the max of all values data | |
|
61 | auto maxIt = std::max_element( | |
|
62 | m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) { | |
|
63 | return SortUtils::maxCompareWithNaN(it1.first(), it2.first()); | |
|
64 | }); | |
|
65 | ||
|
66 | auto pair = std::make_pair(minIt->first(), maxIt->first()); | |
|
67 | ||
|
68 | return std::make_pair(minIt->first(), maxIt->first()); | |
|
69 | } | |
|
70 | } | |
|
71 | ||
|
47 | 72 | int OptionalAxis::size() const |
|
48 | 73 | { |
|
49 | 74 | return m_Defined ? m_Data->size() : 0; |
@@ -33,7 +33,6 private slots: | |||
|
33 | 33 | void testMerge_data(); |
|
34 | 34 | void testMerge(); |
|
35 | 35 | |
|
36 | /// @todo ALX: test subdataseries | |
|
37 | 36 | /// Tests get subdata of a spectrogram series |
|
38 | 37 | void testSubDataSeries_data(); |
|
39 | 38 | void testSubDataSeries(); |
@@ -34,6 +34,8 struct VisualizationGraphHelper { | |||
|
34 | 34 | |
|
35 | 35 | static void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries, |
|
36 | 36 | const SqpRange &dateTime); |
|
37 | ||
|
38 | static void setYAxisRange(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept; | |
|
37 | 39 | }; |
|
38 | 40 | |
|
39 | 41 | #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H |
@@ -46,7 +46,8 public: | |||
|
46 | 46 | /// Returns the list of all variables used in the graph |
|
47 | 47 | QList<std::shared_ptr<Variable> > variables() const; |
|
48 | 48 | |
|
49 | void setYRange(const SqpRange &range); | |
|
49 | /// Sets the y-axis range based on the data of a variable | |
|
50 | void setYRange(std::shared_ptr<Variable> variable); | |
|
50 | 51 | SqpRange graphRange() const noexcept; |
|
51 | 52 | void setGraphRange(const SqpRange &range); |
|
52 | 53 |
@@ -4,6 +4,7 | |||
|
4 | 4 | #include <Common/ColorUtils.h> |
|
5 | 5 | |
|
6 | 6 | #include <Data/ScalarSeries.h> |
|
7 | #include <Data/SpectrogramSeries.h> | |
|
7 | 8 | #include <Data/VectorSeries.h> |
|
8 | 9 | |
|
9 | 10 | #include <Variable/Variable.h> |
@@ -68,6 +69,24 struct PlottablesCreator<T, | |||
|
68 | 69 | }; |
|
69 | 70 | |
|
70 | 71 | /** |
|
72 | * Specialization of PlottablesCreator for spectrograms | |
|
73 | * @sa SpectrogramSeries | |
|
74 | */ | |
|
75 | template <typename T> | |
|
76 | struct PlottablesCreator<T, | |
|
77 | typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > { | |
|
78 | static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot) | |
|
79 | { | |
|
80 | PlottablesMap result{}; | |
|
81 | result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}}); | |
|
82 | ||
|
83 | plot.replot(); | |
|
84 | ||
|
85 | return result; | |
|
86 | } | |
|
87 | }; | |
|
88 | ||
|
89 | /** | |
|
71 | 90 | * Struct used to update plottables, depending on the type of the data series from which to update |
|
72 | 91 | * them |
|
73 | 92 | * @tparam T the data series' type |
@@ -75,9 +94,15 struct PlottablesCreator<T, | |||
|
75 | 94 | */ |
|
76 | 95 | template <typename T, typename Enabled = void> |
|
77 | 96 | struct PlottablesUpdater { |
|
97 | static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &) | |
|
98 | { | |
|
99 | qCCritical(LOG_VisualizationGraphHelper()) | |
|
100 | << QObject::tr("Can't set plot y-axis range: unmanaged data series type"); | |
|
101 | } | |
|
102 | ||
|
78 | 103 | static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool) |
|
79 | 104 | { |
|
80 |
qCCritical(LOG_ |
|
|
105 | qCCritical(LOG_VisualizationGraphHelper()) | |
|
81 | 106 | << QObject::tr("Can't update plottables: unmanaged data series type"); |
|
82 | 107 | } |
|
83 | 108 | }; |
@@ -91,6 +116,24 template <typename T> | |||
|
91 | 116 | struct PlottablesUpdater<T, |
|
92 | 117 | typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value |
|
93 | 118 | or std::is_base_of<VectorSeries, T>::value> > { |
|
119 | static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot) | |
|
120 | { | |
|
121 | auto minValue = 0., maxValue = 0.; | |
|
122 | ||
|
123 | dataSeries.lockRead(); | |
|
124 | auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd); | |
|
125 | auto end = dataSeries.cend(); | |
|
126 | if (valuesBounds.first != end && valuesBounds.second != end) { | |
|
127 | auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; }; | |
|
128 | ||
|
129 | minValue = rangeValue(valuesBounds.first->minValue()); | |
|
130 | maxValue = rangeValue(valuesBounds.second->maxValue()); | |
|
131 | } | |
|
132 | dataSeries.unlock(); | |
|
133 | ||
|
134 | plot.yAxis->setRange(QCPRange{minValue, maxValue}); | |
|
135 | } | |
|
136 | ||
|
94 | 137 | static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range, |
|
95 | 138 | bool rescaleAxes) |
|
96 | 139 | { |
@@ -134,11 +177,85 struct PlottablesUpdater<T, | |||
|
134 | 177 | }; |
|
135 | 178 | |
|
136 | 179 | /** |
|
180 | * Specialization of PlottablesUpdater for spectrograms | |
|
181 | * @sa SpectrogramSeries | |
|
182 | */ | |
|
183 | template <typename T> | |
|
184 | struct PlottablesUpdater<T, | |
|
185 | typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > { | |
|
186 | static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot) | |
|
187 | { | |
|
188 | double min, max; | |
|
189 | /// @todo ALX: use iterators here | |
|
190 | std::tie(min, max) = dataSeries.yAxis().bounds(); | |
|
191 | ||
|
192 | if (!std::isnan(min) && !std::isnan(max)) { | |
|
193 | plot.yAxis->setRange(QCPRange{min, max}); | |
|
194 | } | |
|
195 | } | |
|
196 | ||
|
197 | static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range, | |
|
198 | bool rescaleAxes) | |
|
199 | { | |
|
200 | if (plottables.empty()) { | |
|
201 | qCDebug(LOG_VisualizationGraphHelper()) | |
|
202 | << QObject::tr("Can't update spectrogram: no colormap has been associated"); | |
|
203 | return; | |
|
204 | } | |
|
205 | ||
|
206 | // Gets the colormap to update (normally there is only one colormap) | |
|
207 | Q_ASSERT(plottables.size() == 1); | |
|
208 | auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0)); | |
|
209 | Q_ASSERT(colormap != nullptr); | |
|
210 | ||
|
211 | dataSeries.lockRead(); | |
|
212 | ||
|
213 | auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd); | |
|
214 | /// @todo ALX: use iterators here | |
|
215 | auto yAxis = dataSeries.yAxis(); | |
|
216 | ||
|
217 | // Gets properties of x-axis and y-axis to set size and range of the colormap | |
|
218 | auto nbX = std::distance(its.first, its.second); | |
|
219 | auto xMin = nbX != 0 ? its.first->x() : 0.; | |
|
220 | auto xMax = nbX != 0 ? (its.second - 1)->x() : 0.; | |
|
221 | ||
|
222 | auto nbY = yAxis.size(); | |
|
223 | auto yMin = 0., yMax = 0.; | |
|
224 | if (nbY != 0) { | |
|
225 | std::tie(yMin, yMax) = yAxis.bounds(); | |
|
226 | } | |
|
227 | ||
|
228 | colormap->data()->setSize(nbX, nbY); | |
|
229 | colormap->data()->setRange(QCPRange{xMin, xMax}, QCPRange{yMin, yMax}); | |
|
230 | ||
|
231 | // Sets values | |
|
232 | auto xIndex = 0; | |
|
233 | for (auto it = its.first; it != its.second; ++it, ++xIndex) { | |
|
234 | for (auto yIndex = 0; yIndex < nbY; ++yIndex) { | |
|
235 | colormap->data()->setCell(xIndex, yIndex, it->value(yIndex)); | |
|
236 | } | |
|
237 | } | |
|
238 | ||
|
239 | dataSeries.unlock(); | |
|
240 | ||
|
241 | // Rescales axes | |
|
242 | auto plot = colormap->parentPlot(); | |
|
243 | ||
|
244 | if (rescaleAxes) { | |
|
245 | plot->rescaleAxes(); | |
|
246 | } | |
|
247 | ||
|
248 | plot->replot(); | |
|
249 | } | |
|
250 | }; | |
|
251 | ||
|
252 | /** | |
|
137 | 253 | * Helper used to create/update plottables |
|
138 | 254 | */ |
|
139 | 255 | struct IPlottablesHelper { |
|
140 | 256 | virtual ~IPlottablesHelper() noexcept = default; |
|
141 | 257 | virtual PlottablesMap create(QCustomPlot &plot) const = 0; |
|
258 | virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0; | |
|
142 | 259 | virtual void update(PlottablesMap &plottables, const SqpRange &range, |
|
143 | 260 | bool rescaleAxes = false) const = 0; |
|
144 | 261 | }; |
@@ -161,6 +278,11 struct PlottablesHelper : public IPlottablesHelper { | |||
|
161 | 278 | PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes); |
|
162 | 279 | } |
|
163 | 280 | |
|
281 | void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override | |
|
282 | { | |
|
283 | return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot); | |
|
284 | } | |
|
285 | ||
|
164 | 286 | T &m_DataSeries; |
|
165 | 287 | }; |
|
166 | 288 | |
@@ -170,6 +292,9 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dat | |||
|
170 | 292 | if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) { |
|
171 | 293 | return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries); |
|
172 | 294 | } |
|
295 | else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) { | |
|
296 | return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries); | |
|
297 | } | |
|
173 | 298 | else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) { |
|
174 | 299 | return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries); |
|
175 | 300 | } |
@@ -195,6 +320,19 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variabl | |||
|
195 | 320 | } |
|
196 | 321 | } |
|
197 | 322 | |
|
323 | void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable, | |
|
324 | QCustomPlot &plot) noexcept | |
|
325 | { | |
|
326 | if (variable) { | |
|
327 | auto helper = createHelper(variable->dataSeries()); | |
|
328 | helper->setYAxisRange(variable->range(), plot); | |
|
329 | } | |
|
330 | else { | |
|
331 | qCDebug(LOG_VisualizationGraphHelper()) | |
|
332 | << QObject::tr("Can't set y-axis range of plot: the variable is null"); | |
|
333 | } | |
|
334 | } | |
|
335 | ||
|
198 | 336 | void VisualizationGraphHelper::updateData(PlottablesMap &plottables, |
|
199 | 337 | std::shared_ptr<IDataSeries> dataSeries, |
|
200 | 338 | const SqpRange &dateTime) |
@@ -45,7 +45,6 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { | |||
|
45 | 45 | std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap; |
|
46 | 46 | bool m_DoAcquisition; |
|
47 | 47 | bool m_IsCalibration; |
|
48 | QCPItemTracer *m_TextTracer; | |
|
49 | 48 | /// Delegate used to attach rendering features to the plot |
|
50 | 49 | std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate; |
|
51 | 50 | }; |
@@ -177,9 +176,14 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const | |||
|
177 | 176 | return variables; |
|
178 | 177 | } |
|
179 | 178 | |
|
180 |
void VisualizationGraphWidget::setYRange( |
|
|
179 | void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable) | |
|
181 | 180 | { |
|
182 | ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd); | |
|
181 | if (!variable) { | |
|
182 | qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null"; | |
|
183 | return; | |
|
184 | } | |
|
185 | ||
|
186 | VisualizationGraphHelper::setYAxisRange(variable, *ui->widget); | |
|
183 | 187 | } |
|
184 | 188 | |
|
185 | 189 | SqpRange VisualizationGraphWidget::graphRange() const noexcept |
@@ -271,23 +271,7 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<V | |||
|
271 | 271 | this->insertGraph(index, graphWidget); |
|
272 | 272 | |
|
273 | 273 | graphWidget->addVariable(variable, range); |
|
274 | ||
|
275 | // get y using variable range | |
|
276 | if (auto dataSeries = variable->dataSeries()) { | |
|
277 | dataSeries->lockRead(); | |
|
278 | auto valuesBounds | |
|
279 | = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd); | |
|
280 | auto end = dataSeries->cend(); | |
|
281 | if (valuesBounds.first != end && valuesBounds.second != end) { | |
|
282 | auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; }; | |
|
283 | ||
|
284 | auto minValue = rangeValue(valuesBounds.first->minValue()); | |
|
285 | auto maxValue = rangeValue(valuesBounds.second->maxValue()); | |
|
286 | ||
|
287 | graphWidget->setYRange(SqpRange{minValue, maxValue}); | |
|
288 | } | |
|
289 | dataSeries->unlock(); | |
|
290 | } | |
|
274 | graphWidget->setYRange(variable); | |
|
291 | 275 | |
|
292 | 276 | return graphWidget; |
|
293 | 277 | } |
General Comments 0
You need to be logged in to leave comments.
Login now