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