##// END OF EJS Templates
Sets differents colors for components of a vector
Alexandre Leroux -
r584:17624d030c39
parent child
Show More
@@ -0,0 +1,19
1 #ifndef SCIQLOP_COLORUTILS_H
2 #define SCIQLOP_COLORUTILS_H
3
4 #include <vector>
5
6 class QColor;
7
8 /**
9 * Utility class with methods for colors
10 */
11 struct ColorUtils {
12 /// Generates a color scale from min / max values and a number of colors.
13 /// The algorithm uses the HSV color model to generate color variations (see
14 /// http://doc.qt.io/qt-4.8/qcolor.html#the-hsv-color-model)
15 static std::vector<QColor> colors(const QColor &minColor, const QColor &maxColor,
16 int nbColors) noexcept;
17 };
18
19 #endif // SCIQLOP_COLORUTILS_H
@@ -0,0 +1,29
1 #include "Common/ColorUtils.h"
2
3 #include <QtGui/QColor>
4
5 std::vector<QColor> ColorUtils::colors(const QColor &minColor, const QColor &maxColor,
6 int nbColors) noexcept
7 {
8 auto result = std::vector<QColor>{};
9
10 if (nbColors == 1) {
11 result.push_back(minColor);
12 }
13 else if (nbColors > 0) {
14 const auto nbSteps = static_cast<double>(nbColors - 1);
15
16 const auto colorHStep = (maxColor.hue() - minColor.hue()) / nbSteps;
17 const auto colorSStep = (maxColor.saturation() - minColor.saturation()) / nbSteps;
18 const auto colorVStep = (maxColor.value() - minColor.value()) / nbSteps;
19 const auto colorAStep = (maxColor.alpha() - minColor.alpha()) / nbSteps;
20
21 for (auto i = 0; i < nbColors; ++i) {
22 result.push_back(QColor::fromHsv(
23 minColor.hue() + i * colorHStep, minColor.saturation() + i * colorSStep,
24 minColor.value() + i * colorVStep, minColor.alpha() + i * colorAStep));
25 }
26 }
27
28 return result;
29 }
@@ -1,234 +1,239
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Common/ColorUtils.h>
5
4 #include <Data/ScalarSeries.h>
6 #include <Data/ScalarSeries.h>
5 #include <Data/VectorSeries.h>
7 #include <Data/VectorSeries.h>
6
8
7 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
8
10
9 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
10
12
11 namespace {
13 namespace {
12
14
13 class SqpDataContainer : public QCPGraphDataContainer {
15 class SqpDataContainer : public QCPGraphDataContainer {
14 public:
16 public:
15 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
16 };
18 };
17
19
18
20
19 /// Format for datetimes on a axis
21 /// Format for datetimes on a axis
20 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
22 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
21
23
22 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
24 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
23 /// non-time data
25 /// non-time data
24 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
26 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
25 {
27 {
26 if (isTimeAxis) {
28 if (isTimeAxis) {
27 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
29 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
28 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
30 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
29 dateTicker->setDateTimeSpec(Qt::UTC);
31 dateTicker->setDateTimeSpec(Qt::UTC);
30
32
31 return dateTicker;
33 return dateTicker;
32 }
34 }
33 else {
35 else {
34 // default ticker
36 // default ticker
35 return QSharedPointer<QCPAxisTicker>::create();
37 return QSharedPointer<QCPAxisTicker>::create();
36 }
38 }
37 }
39 }
38
40
39 /// Sets axes properties according to the properties of a data series
41 /// Sets axes properties according to the properties of a data series
40 template <int Dim>
42 template <int Dim>
41 void setAxesProperties(const DataSeries<Dim> &dataSeries, QCustomPlot &plot) noexcept
43 void setAxesProperties(const DataSeries<Dim> &dataSeries, QCustomPlot &plot) noexcept
42 {
44 {
43 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
45 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
44 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
46 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
45 auto setAxisProperties = [](auto axis, const auto &unit) {
47 auto setAxisProperties = [](auto axis, const auto &unit) {
46 // label (unit name)
48 // label (unit name)
47 axis->setLabel(unit.m_Name);
49 axis->setLabel(unit.m_Name);
48
50
49 // ticker (depending on the type of unit)
51 // ticker (depending on the type of unit)
50 axis->setTicker(axisTicker(unit.m_TimeUnit));
52 axis->setTicker(axisTicker(unit.m_TimeUnit));
51 };
53 };
52 setAxisProperties(plot.xAxis, dataSeries.xAxisUnit());
54 setAxisProperties(plot.xAxis, dataSeries.xAxisUnit());
53 setAxisProperties(plot.yAxis, dataSeries.valuesUnit());
55 setAxisProperties(plot.yAxis, dataSeries.valuesUnit());
54 }
56 }
55
57
56 /**
58 /**
57 * Struct used to create plottables, depending on the type of the data series from which to create them
59 * Struct used to create plottables, depending on the type of the data series from which to create them
58 * @tparam T the data series' type
60 * @tparam T the data series' type
59 * @remarks Default implementation can't create plottables
61 * @remarks Default implementation can't create plottables
60 */
62 */
61 template <typename T, typename Enabled = void>
63 template <typename T, typename Enabled = void>
62 struct PlottablesCreator {
64 struct PlottablesCreator {
63 static PlottablesMap createPlottables(T &, QCustomPlot &)
65 static PlottablesMap createPlottables(T &, QCustomPlot &)
64 {
66 {
65 qCCritical(LOG_DataSeries())
67 qCCritical(LOG_DataSeries())
66 << QObject::tr("Can't create plottables: unmanaged data series type");
68 << QObject::tr("Can't create plottables: unmanaged data series type");
67 return {};
69 return {};
68 }
70 }
69 };
71 };
70
72
71 /**
73 /**
72 * Specialization of PlottablesCreator for scalars and vectors
74 * Specialization of PlottablesCreator for scalars and vectors
73 * @sa ScalarSeries
75 * @sa ScalarSeries
74 * @sa VectorSeries
76 * @sa VectorSeries
75 */
77 */
76 template <typename T>
78 template <typename T>
77 struct PlottablesCreator<T,
79 struct PlottablesCreator<T,
78 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
80 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
79 or std::is_base_of<VectorSeries, T>::value> > {
81 or std::is_base_of<VectorSeries, T>::value> > {
80 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
82 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
81 {
83 {
82 PlottablesMap result{};
84 PlottablesMap result{};
83
85
84 // Gets the number of components of the data series
86 // Gets the number of components of the data series
85 auto componentCount = dataSeries.valuesData()->componentCount();
87 auto componentCount = dataSeries.valuesData()->componentCount();
86
88
89 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
90
87 // For each component of the data series, creates a QCPGraph to add to the plot
91 // For each component of the data series, creates a QCPGraph to add to the plot
88 for (auto i = 0; i < componentCount; ++i) {
92 for (auto i = 0; i < componentCount; ++i) {
89 auto graph = plot.addGraph();
93 auto graph = plot.addGraph();
94 graph->setPen(QPen{colors.at(i)});
90
95
91 result.insert({i, graph});
96 result.insert({i, graph});
92 }
97 }
93
98
94 // Axes properties
99 // Axes properties
95 setAxesProperties(dataSeries, plot);
100 setAxesProperties(dataSeries, plot);
96
101
97 plot.replot();
102 plot.replot();
98
103
99 return result;
104 return result;
100 }
105 }
101 };
106 };
102
107
103 /**
108 /**
104 * Struct used to update plottables, depending on the type of the data series from which to update them
109 * Struct used to update plottables, depending on the type of the data series from which to update them
105 * @tparam T the data series' type
110 * @tparam T the data series' type
106 * @remarks Default implementation can't update plottables
111 * @remarks Default implementation can't update plottables
107 */
112 */
108 template <typename T, typename Enabled = void>
113 template <typename T, typename Enabled = void>
109 struct PlottablesUpdater {
114 struct PlottablesUpdater {
110 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
115 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
111 {
116 {
112 qCCritical(LOG_DataSeries())
117 qCCritical(LOG_DataSeries())
113 << QObject::tr("Can't update plottables: unmanaged data series type");
118 << QObject::tr("Can't update plottables: unmanaged data series type");
114 }
119 }
115 };
120 };
116
121
117 /**
122 /**
118 * Specialization of PlottablesUpdater for scalars and vectors
123 * Specialization of PlottablesUpdater for scalars and vectors
119 * @sa ScalarSeries
124 * @sa ScalarSeries
120 * @sa VectorSeries
125 * @sa VectorSeries
121 */
126 */
122 template <typename T>
127 template <typename T>
123 struct PlottablesUpdater<T,
128 struct PlottablesUpdater<T,
124 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
129 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
125 or std::is_base_of<VectorSeries, T>::value> > {
130 or std::is_base_of<VectorSeries, T>::value> > {
126 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
131 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
127 bool rescaleAxes)
132 bool rescaleAxes)
128 {
133 {
129 dataSeries.lockRead();
134 dataSeries.lockRead();
130
135
131 // For each plottable to update, resets its data
136 // For each plottable to update, resets its data
132 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
137 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
133 for (const auto &plottable : plottables) {
138 for (const auto &plottable : plottables) {
134 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
139 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
135 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
140 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
136 graph->setData(dataContainer);
141 graph->setData(dataContainer);
137
142
138 dataContainers.insert({plottable.first, dataContainer});
143 dataContainers.insert({plottable.first, dataContainer});
139 }
144 }
140 }
145 }
141
146
142 // - Gets the data of the series included in the current range
147 // - Gets the data of the series included in the current range
143 // - Updates each plottable by adding, for each data item, a point that takes x-axis data and value data. The correct value is retrieved according to the index of the component
148 // - Updates each plottable by adding, for each data item, a point that takes x-axis data and value data. The correct value is retrieved according to the index of the component
144 auto subDataIts = dataSeries.subData(range.m_TStart, range.m_TEnd);
149 auto subDataIts = dataSeries.subData(range.m_TStart, range.m_TEnd);
145 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
150 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
146 for (const auto &dataContainer : dataContainers) {
151 for (const auto &dataContainer : dataContainers) {
147 auto componentIndex = dataContainer.first;
152 auto componentIndex = dataContainer.first;
148 dataContainer.second->appendGraphData(
153 dataContainer.second->appendGraphData(
149 QCPGraphData(it->x(), it->value(componentIndex)));
154 QCPGraphData(it->x(), it->value(componentIndex)));
150 }
155 }
151 }
156 }
152
157
153 dataSeries.unlock();
158 dataSeries.unlock();
154
159
155 if (!plottables.empty()) {
160 if (!plottables.empty()) {
156 auto plot = plottables.begin()->second->parentPlot();
161 auto plot = plottables.begin()->second->parentPlot();
157
162
158 if (rescaleAxes) {
163 if (rescaleAxes) {
159 plot->rescaleAxes();
164 plot->rescaleAxes();
160 }
165 }
161
166
162 plot->replot();
167 plot->replot();
163 }
168 }
164 }
169 }
165 };
170 };
166
171
167 /**
172 /**
168 * Helper used to create/update plottables
173 * Helper used to create/update plottables
169 */
174 */
170 struct IPlottablesHelper {
175 struct IPlottablesHelper {
171 virtual ~IPlottablesHelper() noexcept = default;
176 virtual ~IPlottablesHelper() noexcept = default;
172 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
177 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
173 virtual void update(PlottablesMap &plottables, const SqpRange &range,
178 virtual void update(PlottablesMap &plottables, const SqpRange &range,
174 bool rescaleAxes = false) const = 0;
179 bool rescaleAxes = false) const = 0;
175 };
180 };
176
181
177 /**
182 /**
178 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
183 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
179 * @tparam T the data series' type
184 * @tparam T the data series' type
180 */
185 */
181 template <typename T>
186 template <typename T>
182 struct PlottablesHelper : public IPlottablesHelper {
187 struct PlottablesHelper : public IPlottablesHelper {
183 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
188 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
184
189
185 PlottablesMap create(QCustomPlot &plot) const override
190 PlottablesMap create(QCustomPlot &plot) const override
186 {
191 {
187 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
192 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
188 }
193 }
189
194
190 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
195 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
191 {
196 {
192 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
197 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
193 }
198 }
194
199
195 T &m_DataSeries;
200 T &m_DataSeries;
196 };
201 };
197
202
198 /// Creates IPlottablesHelper according to a data series
203 /// Creates IPlottablesHelper according to a data series
199 std::unique_ptr<IPlottablesHelper> createHelper(IDataSeries *dataSeries) noexcept
204 std::unique_ptr<IPlottablesHelper> createHelper(IDataSeries *dataSeries) noexcept
200 {
205 {
201 if (auto scalarSeries = dynamic_cast<ScalarSeries *>(dataSeries)) {
206 if (auto scalarSeries = dynamic_cast<ScalarSeries *>(dataSeries)) {
202 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
207 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
203 }
208 }
204 else if (auto vectorSeries = dynamic_cast<VectorSeries *>(dataSeries)) {
209 else if (auto vectorSeries = dynamic_cast<VectorSeries *>(dataSeries)) {
205 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
210 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
206 }
211 }
207 else {
212 else {
208 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
213 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
209 }
214 }
210 }
215 }
211
216
212 } // namespace
217 } // namespace
213
218
214 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
219 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
215 QCustomPlot &plot) noexcept
220 QCustomPlot &plot) noexcept
216 {
221 {
217 if (variable) {
222 if (variable) {
218 auto helper = createHelper(variable->dataSeries().get());
223 auto helper = createHelper(variable->dataSeries().get());
219 auto plottables = helper->create(plot);
224 auto plottables = helper->create(plot);
220 return plottables;
225 return plottables;
221 }
226 }
222 else {
227 else {
223 qCDebug(LOG_VisualizationGraphHelper())
228 qCDebug(LOG_VisualizationGraphHelper())
224 << QObject::tr("Can't create graph plottables : the variable is null");
229 << QObject::tr("Can't create graph plottables : the variable is null");
225 return PlottablesMap{};
230 return PlottablesMap{};
226 }
231 }
227 }
232 }
228
233
229 void VisualizationGraphHelper::updateData(PlottablesMap &plottables, IDataSeries *dataSeries,
234 void VisualizationGraphHelper::updateData(PlottablesMap &plottables, IDataSeries *dataSeries,
230 const SqpRange &dateTime)
235 const SqpRange &dateTime)
231 {
236 {
232 auto helper = createHelper(dataSeries);
237 auto helper = createHelper(dataSeries);
233 helper->update(plottables, dateTime);
238 helper->update(plottables, dateTime);
234 }
239 }
General Comments 0
You need to be logged in to leave comments. Login now