##// END OF EJS Templates
Updates ArrayData to hold a single QVector
Alexandre Leroux -
r599:cec120c895dd
parent child
Show More
@@ -1,325 +1,301
1 1 #ifndef SCIQLOP_ARRAYDATA_H
2 2 #define SCIQLOP_ARRAYDATA_H
3 3
4 4 #include "Data/ArrayDataIterator.h"
5 5 #include <Common/SortUtils.h>
6 6
7 7 #include <QReadLocker>
8 8 #include <QReadWriteLock>
9 9 #include <QVector>
10 10
11 11 #include <memory>
12 12
13 13 template <int Dim>
14 14 class ArrayData;
15 15
16 using DataContainer = QVector<QVector<double> >;
16 using DataContainer = QVector<double>;
17 17
18 18 namespace arraydata_detail {
19 19
20 20 /// Struct used to sort ArrayData
21 21 template <int Dim>
22 22 struct Sort {
23 23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
24 24 const std::vector<int> &sortPermutation)
25 25 {
26 26 auto nbComponents = data.size();
27 27 auto sortedData = DataContainer(nbComponents);
28 28
29 29 for (auto i = 0; i < nbComponents; ++i) {
30 30 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
31 31 }
32 32
33 33 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
34 34 }
35 35 };
36 36
37 37 /// Specialization for uni-dimensional ArrayData
38 38 template <>
39 39 struct Sort<1> {
40 40 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
41 41 const std::vector<int> &sortPermutation)
42 42 {
43 43 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
44 44 }
45 45 };
46 46
47 47 template <int Dim>
48 48 class IteratorValue : public ArrayDataIteratorValue::Impl {
49 49 public:
50 50 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
51 51 {
52 52 for (auto i = 0; i < container.size(); ++i) {
53 53 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
54 54 }
55 55 }
56 56
57 57 IteratorValue(const IteratorValue &other) = default;
58 58
59 59 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
60 60 {
61 61 return std::make_unique<IteratorValue<Dim> >(*this);
62 62 }
63 63
64 64 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
65 65 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
66 66 return m_Its == otherImpl.m_Its;
67 67 }
68 68 catch (const std::bad_cast &) {
69 69 return false;
70 70 }
71 71
72 72 void next() override
73 73 {
74 74 for (auto &it : m_Its) {
75 75 ++it;
76 76 }
77 77 }
78 78
79 79 void prev() override
80 80 {
81 81 for (auto &it : m_Its) {
82 82 --it;
83 83 }
84 84 }
85 85
86 86 double at(int componentIndex) const override { return *m_Its.at(componentIndex); }
87 87 double first() const override { return *m_Its.front(); }
88 88 double min() const override
89 89 {
90 90 auto end = m_Its.cend();
91 91 auto it = std::min_element(m_Its.cbegin(), end, [](const auto &it1, const auto &it2) {
92 92 return SortUtils::minCompareWithNaN(*it1, *it2);
93 93 });
94 94 return it != end ? **it : std::numeric_limits<double>::quiet_NaN();
95 95 }
96 96 double max() const override
97 97 {
98 98 auto end = m_Its.cend();
99 99 auto it = std::max_element(m_Its.cbegin(), end, [](const auto &it1, const auto &it2) {
100 100 return SortUtils::maxCompareWithNaN(*it1, *it2);
101 101 });
102 102 return it != end ? **it : std::numeric_limits<double>::quiet_NaN();
103 103 }
104 104
105 105 private:
106 106 std::vector<DataContainer::value_type::const_iterator> m_Its;
107 107 };
108 108
109 109 } // namespace arraydata_detail
110 110
111 111 /**
112 112 * @brief The ArrayData class represents a dataset for a data series.
113 113 *
114 114 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
115 115 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
116 116 * number of values
117 117 *
118 118 * @tparam Dim the dimension of the ArrayData (one or two)
119 119 * @sa IDataSeries
120 120 */
121 121 template <int Dim>
122 122 class ArrayData {
123 123 public:
124 124 // ///// //
125 125 // Ctors //
126 126 // ///// //
127 127
128 128 /**
129 129 * Ctor for a unidimensional ArrayData
130 130 * @param data the data the ArrayData will hold
131 131 */
132 132 template <int D = Dim, typename = std::enable_if_t<D == 1> >
133 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
133 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
134 134 {
135 m_Data[0] = std::move(data);
136 135 }
137 136
138 137 /**
139 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
140 * greater than 2 and each component must have the same number of values
138 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
139 * greater than 2 and must be a divisor of the total number of data in the vector
141 140 * @param data the data the ArrayData will hold
142 * @throws std::invalid_argument if the number of components is less than 2
143 * @remarks if the number of values is not the same for each component, no value is set
141 * @param nbComponents the number of components
142 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
143 * of the size of the data
144 144 */
145 145 template <int D = Dim, typename = std::enable_if_t<D == 2> >
146 explicit ArrayData(DataContainer data)
146 explicit ArrayData(DataContainer data, int nbComponents)
147 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
147 148 {
148 auto nbComponents = data.size();
149 149 if (nbComponents < 2) {
150 150 throw std::invalid_argument{
151 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
152 .arg(data.size())
151 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
152 .arg(nbComponents)
153 153 .toStdString()};
154 154 }
155 155
156 auto nbValues = data.front().size();
157 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
158 return component.size() == nbValues;
159 })) {
160 m_Data = std::move(data);
161 }
162 else {
163 m_Data = DataContainer{nbComponents, QVector<double>{}};
156 if (m_Data.size() % m_NbComponents != 0) {
157 throw std::invalid_argument{QString{
158 "The number of components (%1) is inconsistent with the total number of data (%2)"}
159 .arg(m_Data.size(), nbComponents)
160 .toStdString()};
164 161 }
165 162 }
166 163
167 164 /// Copy ctor
168 165 explicit ArrayData(const ArrayData &other)
169 166 {
170 167 QReadLocker otherLocker{&other.m_Lock};
171 168 m_Data = other.m_Data;
169 m_NbComponents = other.m_NbComponents;
172 170 }
173 171
174 172 // /////////////// //
175 173 // General methods //
176 174 // /////////////// //
177 175
178 176 /**
179 177 * Merges into the array data an other array data. The two array datas must have the same number
180 178 * of components so the merge can be done
181 179 * @param other the array data to merge with
182 180 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
183 181 * inserted at the end
184 182 */
185 183 void add(const ArrayData<Dim> &other, bool prepend = false)
186 184 {
187 185 QWriteLocker locker{&m_Lock};
188 186 QReadLocker otherLocker{&other.m_Lock};
189 187
190 auto nbComponents = m_Data.size();
191 if (nbComponents != other.m_Data.size()) {
188 if (m_NbComponents != other.componentCount()) {
192 189 return;
193 190 }
194 191
195 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
196 if (prepend) {
197 const auto &otherData = other.data(componentIndex);
198 const auto otherDataSize = otherData.size();
199
200 auto &data = m_Data[componentIndex];
201 data.insert(data.begin(), otherDataSize, 0.);
202
203 for (auto i = 0; i < otherDataSize; ++i) {
204 data.replace(i, otherData.at(i));
205 }
206 }
207 else {
208 m_Data[componentIndex] += other.data(componentIndex);
192 if (prepend) {
193 auto otherDataSize = other.m_Data.size();
194 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
195 for (auto i = 0; i < otherDataSize; ++i) {
196 m_Data.replace(i, other.m_Data.at(i));
209 197 }
210 198 }
199 else {
200 m_Data.append(other.m_Data);
201 }
211 202 }
212 203
213 204 void clear()
214 205 {
215 206 QWriteLocker locker{&m_Lock};
216
217 auto nbComponents = m_Data.size();
218 for (auto i = 0; i < nbComponents; ++i) {
219 m_Data[i].clear();
220 }
207 m_Data.clear();
221 208 }
222 209
223 int componentCount() const noexcept { return m_Data.size(); }
224
225 /**
226 * @return the data of a component
227 * @param componentIndex the index of the component to retrieve the data
228 * @return the component's data, empty vector if the index is invalid
229 */
230 QVector<double> data(int componentIndex) const noexcept
231 {
232 QReadLocker locker{&m_Lock};
233
234 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
235 : QVector<double>{};
236 }
210 int componentCount() const noexcept { return m_NbComponents; }
237 211
238 212 /// @return the size (i.e. number of values) of a single component
239 213 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
240 214 int size() const
241 215 {
242 216 QReadLocker locker{&m_Lock};
243 return m_Data[0].size();
217 return m_Data.size() / m_NbComponents;
244 218 }
245 219
246 220 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
247 221 {
248 222 QReadLocker locker{&m_Lock};
249 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
223 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
250 224 }
251 225
252 226 // ///////// //
253 227 // Iterators //
254 228 // ///////// //
255 229
256 230 ArrayDataIterator cbegin() const
257 231 {
258 232 return ArrayDataIterator{ArrayDataIteratorValue{
259 233 std::make_unique<arraydata_detail::IteratorValue<Dim> >(m_Data, true)}};
260 234 }
261 235 ArrayDataIterator cend() const
262 236 {
263 237 return ArrayDataIterator{ArrayDataIteratorValue{
264 238 std::make_unique<arraydata_detail::IteratorValue<Dim> >(m_Data, false)}};
265 239 }
266 240
267 241 // ///////////// //
268 242 // 1-dim methods //
269 243 // ///////////// //
270 244
271 245 /**
272 246 * @return the data at a specified index
273 247 * @remarks index must be a valid position
274 248 * @remarks this method is only available for a unidimensional ArrayData
275 249 */
276 250 template <int D = Dim, typename = std::enable_if_t<D == 1> >
277 251 double at(int index) const noexcept
278 252 {
279 253 QReadLocker locker{&m_Lock};
280 return m_Data[0].at(index);
254 return m_Data.at(index);
281 255 }
282 256
283 257 /**
284 258 * @return the data as a vector, as a const reference
285 259 * @remarks this method is only available for a unidimensional ArrayData
286 260 */
287 261 template <int D = Dim, typename = std::enable_if_t<D == 1> >
288 262 const QVector<double> &cdata() const noexcept
289 263 {
290 264 QReadLocker locker{&m_Lock};
291 265 return m_Data.at(0);
292 266 }
293 267
294 268 /**
295 269 * @return the data as a vector
296 270 * @remarks this method is only available for a unidimensional ArrayData
297 271 */
298 272 template <int D = Dim, typename = std::enable_if_t<D == 1> >
299 273 QVector<double> data() const noexcept
300 274 {
301 275 QReadLocker locker{&m_Lock};
302 276 return m_Data[0];
303 277 }
304 278
305 279 // ///////////// //
306 280 // 2-dim methods //
307 281 // ///////////// //
308 282
309 283 /**
310 284 * @return the data
311 285 * @remarks this method is only available for a two-dimensional ArrayData
312 286 */
313 287 template <int D = Dim, typename = std::enable_if_t<D == 2> >
314 288 DataContainer data() const noexcept
315 289 {
316 290 QReadLocker locker{&m_Lock};
317 291 return m_Data;
318 292 }
319 293
320 294 private:
321 295 DataContainer m_Data;
296 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
297 int m_NbComponents;
322 298 mutable QReadWriteLock m_Lock;
323 299 };
324 300
325 301 #endif // SCIQLOP_ARRAYDATA_H
General Comments 0
You need to be logged in to leave comments. Login now