@@ -383,6 +383,10 public: | |||||
383 | return std::make_pair(minIt, maxIt); |
|
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 | // Mutexes // |
|
391 | // Mutexes // | |
388 | // /////// // |
|
392 | // /////// // | |
@@ -438,9 +442,6 protected: | |||||
438 | // necessary to call the sort method here ('other' is sorted) |
|
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 | /// Assignment operator |
|
445 | /// Assignment operator | |
445 | template <int D> |
|
446 | template <int D> | |
446 | DataSeries &operator=(DataSeries<D> other) |
|
447 | DataSeries &operator=(DataSeries<D> other) |
@@ -41,6 +41,10 public: | |||||
41 | /// @return gets the data at the index passed in parameter, NaN if the index is outside the |
|
41 | /// @return gets the data at the index passed in parameter, NaN if the index is outside the | |
42 | /// bounds of the axis, or if the axis is undefined |
|
42 | /// bounds of the axis, or if the axis is undefined | |
43 | double at(int index) const; |
|
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 | /// @return the number of data on the axis, 0 if the axis is not defined |
|
48 | /// @return the number of data on the axis, 0 if the axis is not defined | |
45 | int size() const; |
|
49 | int size() const; | |
46 | /// @return the unit of the axis, an empty unit if the axis is not defined |
|
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 | int OptionalAxis::size() const |
|
72 | int OptionalAxis::size() const | |
48 | { |
|
73 | { | |
49 | return m_Defined ? m_Data->size() : 0; |
|
74 | return m_Defined ? m_Data->size() : 0; |
@@ -33,7 +33,6 private slots: | |||||
33 | void testMerge_data(); |
|
33 | void testMerge_data(); | |
34 | void testMerge(); |
|
34 | void testMerge(); | |
35 |
|
35 | |||
36 | /// @todo ALX: test subdataseries |
|
|||
37 | /// Tests get subdata of a spectrogram series |
|
36 | /// Tests get subdata of a spectrogram series | |
38 | void testSubDataSeries_data(); |
|
37 | void testSubDataSeries_data(); | |
39 | void testSubDataSeries(); |
|
38 | void testSubDataSeries(); |
@@ -34,6 +34,8 struct VisualizationGraphHelper { | |||||
34 |
|
34 | |||
35 | static void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries, |
|
35 | static void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries, | |
36 | const SqpRange &dateTime); |
|
36 | const SqpRange &dateTime); | |
|
37 | ||||
|
38 | static void setYAxisRange(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept; | |||
37 | }; |
|
39 | }; | |
38 |
|
40 | |||
39 | #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H |
|
41 | #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H |
@@ -46,7 +46,8 public: | |||||
46 | /// Returns the list of all variables used in the graph |
|
46 | /// Returns the list of all variables used in the graph | |
47 | QList<std::shared_ptr<Variable> > variables() const; |
|
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 | SqpRange graphRange() const noexcept; |
|
51 | SqpRange graphRange() const noexcept; | |
51 | void setGraphRange(const SqpRange &range); |
|
52 | void setGraphRange(const SqpRange &range); | |
52 |
|
53 |
@@ -4,6 +4,7 | |||||
4 | #include <Common/ColorUtils.h> |
|
4 | #include <Common/ColorUtils.h> | |
5 |
|
5 | |||
6 | #include <Data/ScalarSeries.h> |
|
6 | #include <Data/ScalarSeries.h> | |
|
7 | #include <Data/SpectrogramSeries.h> | |||
7 | #include <Data/VectorSeries.h> |
|
8 | #include <Data/VectorSeries.h> | |
8 |
|
9 | |||
9 | #include <Variable/Variable.h> |
|
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 | * Struct used to update plottables, depending on the type of the data series from which to update |
|
90 | * Struct used to update plottables, depending on the type of the data series from which to update | |
72 | * them |
|
91 | * them | |
73 | * @tparam T the data series' type |
|
92 | * @tparam T the data series' type | |
@@ -75,9 +94,15 struct PlottablesCreator<T, | |||||
75 | */ |
|
94 | */ | |
76 | template <typename T, typename Enabled = void> |
|
95 | template <typename T, typename Enabled = void> | |
77 | struct PlottablesUpdater { |
|
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 | static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool) |
|
103 | static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool) | |
79 | { |
|
104 | { | |
80 |
qCCritical(LOG_ |
|
105 | qCCritical(LOG_VisualizationGraphHelper()) | |
81 | << QObject::tr("Can't update plottables: unmanaged data series type"); |
|
106 | << QObject::tr("Can't update plottables: unmanaged data series type"); | |
82 | } |
|
107 | } | |
83 | }; |
|
108 | }; | |
@@ -91,6 +116,24 template <typename T> | |||||
91 | struct PlottablesUpdater<T, |
|
116 | struct PlottablesUpdater<T, | |
92 | typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value |
|
117 | typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value | |
93 | or std::is_base_of<VectorSeries, T>::value> > { |
|
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 | static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range, |
|
137 | static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range, | |
95 | bool rescaleAxes) |
|
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 | * Helper used to create/update plottables |
|
253 | * Helper used to create/update plottables | |
138 | */ |
|
254 | */ | |
139 | struct IPlottablesHelper { |
|
255 | struct IPlottablesHelper { | |
140 | virtual ~IPlottablesHelper() noexcept = default; |
|
256 | virtual ~IPlottablesHelper() noexcept = default; | |
141 | virtual PlottablesMap create(QCustomPlot &plot) const = 0; |
|
257 | virtual PlottablesMap create(QCustomPlot &plot) const = 0; | |
|
258 | virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0; | |||
142 | virtual void update(PlottablesMap &plottables, const SqpRange &range, |
|
259 | virtual void update(PlottablesMap &plottables, const SqpRange &range, | |
143 | bool rescaleAxes = false) const = 0; |
|
260 | bool rescaleAxes = false) const = 0; | |
144 | }; |
|
261 | }; | |
@@ -161,6 +278,11 struct PlottablesHelper : public IPlottablesHelper { | |||||
161 | PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes); |
|
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 | T &m_DataSeries; |
|
286 | T &m_DataSeries; | |
165 | }; |
|
287 | }; | |
166 |
|
288 | |||
@@ -170,6 +292,9 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dat | |||||
170 | if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) { |
|
292 | if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) { | |
171 | return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries); |
|
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 | else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) { |
|
298 | else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) { | |
174 | return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries); |
|
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 | void VisualizationGraphHelper::updateData(PlottablesMap &plottables, |
|
336 | void VisualizationGraphHelper::updateData(PlottablesMap &plottables, | |
199 | std::shared_ptr<IDataSeries> dataSeries, |
|
337 | std::shared_ptr<IDataSeries> dataSeries, | |
200 | const SqpRange &dateTime) |
|
338 | const SqpRange &dateTime) |
@@ -45,7 +45,6 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { | |||||
45 | std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap; |
|
45 | std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap; | |
46 | bool m_DoAcquisition; |
|
46 | bool m_DoAcquisition; | |
47 | bool m_IsCalibration; |
|
47 | bool m_IsCalibration; | |
48 | QCPItemTracer *m_TextTracer; |
|
|||
49 | /// Delegate used to attach rendering features to the plot |
|
48 | /// Delegate used to attach rendering features to the plot | |
50 | std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate; |
|
49 | std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate; | |
51 | }; |
|
50 | }; | |
@@ -177,9 +176,14 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const | |||||
177 | return variables; |
|
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 | SqpRange VisualizationGraphWidget::graphRange() const noexcept |
|
189 | SqpRange VisualizationGraphWidget::graphRange() const noexcept |
@@ -271,23 +271,7 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<V | |||||
271 | this->insertGraph(index, graphWidget); |
|
271 | this->insertGraph(index, graphWidget); | |
272 |
|
272 | |||
273 | graphWidget->addVariable(variable, range); |
|
273 | graphWidget->addVariable(variable, range); | |
274 |
|
274 | graphWidget->setYRange(variable); | ||
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 | } |
|
|||
291 |
|
275 | |||
292 | return graphWidget; |
|
276 | return graphWidget; | |
293 | } |
|
277 | } |
General Comments 0
You need to be logged in to leave comments.
Login now