• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 18685379784

21 Oct 2025 01:25PM UTC coverage: 72.522% (+0.1%) from 72.391%
18685379784

push

github

paulmthompson
fix failing tests

18 of 40 new or added lines in 1 file covered. (45.0%)

1765 existing lines in 32 files now uncovered.

53998 of 74457 relevant lines covered (72.52%)

46177.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.44
/src/DataManager/Masks/Mask_Data.hpp
1
#ifndef MASK_DATA_HPP
2
#define MASK_DATA_HPP
3

4
#include "CoreGeometry/ImageSize.hpp"
5
#include "CoreGeometry/masks.hpp"
6
#include "CoreGeometry/points.hpp"
7
#include "Entity/EntityTypes.hpp"
8
#include "Observer/Observer_Data.hpp"
9
#include "TimeFrame/TimeFrame.hpp"
10
#include "TimeFrame/interval_data.hpp"
11

12
#include <cstddef>
13
#include <map>
14
#include <ranges>
15
#include <unordered_set>
16
#include <vector>
17

18
class EntityRegistry;
19

20
/**
21
 * @brief Structure holding a Mask2D and its associated EntityId
22
 */
23
struct MaskEntry {
24
    Mask2D mask;
25
    EntityId entity_id;
26

27
    MaskEntry() = default;
28
    MaskEntry(Mask2D m, EntityId id)
555✔
29
        : mask(std::move(m)),
555✔
30
          entity_id(id) {}
555✔
31
};
32

33

34
/**
35
 * @brief The MaskData class
36
 *
37
 * MaskData is used for 2D data where the collection of 2D points has *no* order
38
 * Compare to LineData where the collection of 2D points has an order
39
 *
40
 */
41
class MaskData : public ObserverData {
42
public:
43
    // ========== Constructors ==========
44
    MaskData() = default;
242✔
45

46
    // ========== Setters ==========
47

48
    /**
49
    * @brief Removes all masks at the specified time
50
    *
51
    * @param time The timestamp at which to clear masks
52
    * @param notify If true, observers will be notified of the change
53
    * @return True if masks existed at the specified time and were cleared, false otherwise
54
    */
55
    [[nodiscard]] bool clearAtTime(TimeFrameIndex time, bool notify = true);
56

57
    [[nodiscard]] bool clearAtTime(TimeIndexAndFrame const & time_index_and_frame, bool notify = true);
58

59
    /**
60
     * @brief Removes a mask at the specified time and index
61
     * 
62
     * @param time The timestamp at which to clear the mask
63
     * @param index The index of the mask to clear
64
     * @param notify If true, observers will be notified of the change
65
     */
66
    [[nodiscard]] bool clearAtTime(TimeFrameIndex time, size_t index, bool notify = true);
67

68
    /**
69
     * @brief Adds a new mask at the specified time using separate x and y coordinate arrays
70
    *
71
    * If masks already exist at the specified time, the new mask is added to the collection.
72
    * If no masks exist at that time, a new entry is created.
73
    *
74
    * @param time The timestamp at which to add the mask
75
    * @param x Vector of x-coordinates defining the mask points
76
    * @param y Vector of y-coordinates defining the mask points
77
    * @param notify If true, observers will be notified of the change (default: true)
78
    *
79
    * @note x and y vectors must be the same length, representing coordinate pairs
80
    */
81
    void addAtTime(TimeFrameIndex time,
82
                   std::vector<uint32_t> const & x,
83
                   std::vector<uint32_t> const & y,
84
                   bool notify = true);
85

86
    /**
87
    * @brief Adds a new mask at the specified time using a vector of 2D points
88
    *
89
    * If masks already exist at the specified time, the new mask is added to the collection.
90
    * If no masks exist at that time, a new entry is created.
91
    *
92
    * @param time The timestamp at which to add the mask
93
    * @param mask Vector of 2D points defining the mask
94
    * @param notify If true, observers will be notified of the change (default: true)
95
    */
96
    void addAtTime(TimeFrameIndex time,
97
                   std::vector<Point2D<uint32_t>> mask,
98
                   bool notify = true);
99

100
    void addAtTime(TimeIndexAndFrame const & time_index_and_frame,
101
                   std::vector<Point2D<uint32_t>> mask,
102
                   bool notify = true);
103

104
    /**
105
     * @brief Adds a new mask at the specified time using separate x and y coordinate arrays (move version)
106
    *
107
    * This overload avoids copying the input vectors by moving them.
108
    * If masks already exist at the specified time, the new mask is added to the collection.
109
    * If no masks exist at that time, a new entry is created.
110
    *
111
    * @param time The timestamp at which to add the mask
112
    * @param x Vector of x-coordinates defining the mask points (will be moved)
113
    * @param y Vector of y-coordinates defining the mask points (will be moved)
114
    * @param notify If true, observers will be notified of the change (default: true)
115
    *
116
    * @note x and y vectors must be the same length, representing coordinate pairs
117
    */
118
    void addAtTime(TimeFrameIndex time,
119
                   std::vector<uint32_t> && x,
120
                   std::vector<uint32_t> && y,
121
                   bool notify = true);
122

123
    /**
124
     * @brief Add a mask entry at a specific time with a specific entity ID
125
     *
126
     * This method is used internally for move operations to preserve entity IDs.
127
     *
128
     * @param time The time to add the mask at
129
     * @param mask The mask to add
130
     * @param entity_id The entity ID to assign to the mask
131
     * @param notify If true, the observers will be notified
132
     */
133
    void addEntryAtTime(TimeFrameIndex time, Mask2D const & mask, EntityId entity_id, bool notify = true);
134

135

136
    // ========== Getters ==========
137

138
    /**
139
     * @brief Get all times with data
140
     * 
141
     * Returns a view over the keys of the data map for zero-copy iteration.
142
     * 
143
     * @return A view of TimeFrameIndex keys
144
     */
145
    [[nodiscard]] auto getTimesWithData() const {
31✔
146
        return _data | std::views::keys;
31✔
147
    }
148

149
    /**
150
     * @brief Get the masks at a specific time
151
     * 
152
     * @param time The time to get the masks at
153
     * @return A vector of Mask2D
154
     */
155
    [[nodiscard]] std::vector<Mask2D> const & getAtTime(TimeFrameIndex time) const;
156

157
    [[nodiscard]] std::vector<Mask2D> const & getAtTime(TimeIndexAndFrame const & time_index_and_frame) const;
158

159
    /**
160
     * @brief Get the masks at a specific time with timeframe conversion
161
     * 
162
     * @param time The time to get the masks at
163
     * @param source_timeframe The timeframe that the time index is expressed in
164
     * @param mask_timeframe The timeframe that this mask data uses
165
     * @return A vector of Mask2D at the converted time
166
     */
167
    [[nodiscard]] std::vector<Mask2D> const & getAtTime(TimeFrameIndex time,
168
                                                        TimeFrame const * source_timeframe,
169
                                                        TimeFrame const * mask_timeframe) const;
170

171
    /**
172
     * @brief Get all masks with their associated times as a range
173
     *
174
     * @return A view of time-mask pairs for all times
175
     */
176
    [[nodiscard]] auto getAllAsRange() const {
163✔
177
        struct TimeMaskPair {
178
            TimeFrameIndex time;
179
            std::vector<Mask2D> masks;// Copy for backward compatibility with entry storage
180
        };
181

182
        return _data | std::views::transform([](auto const & pair) {
326✔
183
                   std::vector<Mask2D> masks;
188✔
184
                   masks.reserve(pair.second.size());
188✔
185
                   for (auto const & entry: pair.second) {
434✔
186
                       masks.push_back(entry.mask);
246✔
187
                   }
188
                   return TimeMaskPair{pair.first, std::move(masks)};
376✔
189
               });
865✔
190
    };
191

192
    /**
193
    * @brief Get masks with their associated times as a range within a TimeFrameInterval
194
    *
195
    * Returns a filtered view of time-mask pairs for times within the specified interval [start, end] (inclusive).
196
    *
197
    * @param interval The TimeFrameInterval specifying the range [start, end] (inclusive)
198
    * @return A view of time-mask pairs for times within the specified interval
199
    */
200
    [[nodiscard]] auto GetMasksInRange(TimeFrameInterval const & interval) const {
7✔
201
        struct TimeMaskPair {
202
            TimeFrameIndex time;
203
            std::vector<Mask2D> masks;// Copy out of entries
204
        };
205

206
        return _data | std::views::filter([interval](auto const & pair) {
14✔
207
                   return pair.first >= interval.start && pair.first <= interval.end;
33✔
208
               }) |
209
               std::views::transform([](auto const & pair) {
14✔
210
                   std::vector<Mask2D> masks;
15✔
211
                   masks.reserve(pair.second.size());
15✔
212
                   for (auto const & entry: pair.second) {
33✔
213
                       masks.push_back(entry.mask);
18✔
214
                   }
215
                   return TimeMaskPair{pair.first, std::move(masks)};
30✔
216
               });
58✔
217
    }
218

219
    /**
220
    * @brief Get masks with their associated times as a range within a TimeFrameInterval with timeframe conversion
221
    *
222
    * Converts the time range from the source timeframe to the target timeframe (this mask data's timeframe)
223
    * and returns a filtered view of time-mask pairs for times within the converted interval range.
224
    * If the timeframes are the same, no conversion is performed.
225
    *
226
    * @param interval The TimeFrameInterval in the source timeframe specifying the range [start, end] (inclusive)
227
    * @param source_timeframe The timeframe that the interval is expressed in
228
    * @param target_timeframe The timeframe that this mask data uses
229
    * @return A view of time-mask pairs for times within the converted interval range
230
    */
231
    [[nodiscard]] auto GetMasksInRange(TimeFrameInterval const & interval,
2✔
232
                                       std::shared_ptr<TimeFrame> source_timeframe,
233
                                       std::shared_ptr<TimeFrame> target_timeframe) const {
234
        // If the timeframes are the same object, no conversion is needed
235
        if (source_timeframe.get() == target_timeframe.get()) {
2✔
236
            return GetMasksInRange(interval);
1✔
237
        }
238

239
        // If either timeframe is null, fall back to original behavior
240
        if (!source_timeframe || !target_timeframe) {
1✔
UNCOV
241
            return GetMasksInRange(interval);
×
242
        }
243

244
        // Convert the time range from source timeframe to target timeframe
245
        // 1. Get the time values from the source timeframe
246
        auto start_time_value = source_timeframe->getTimeAtIndex(interval.start);
1✔
247
        auto end_time_value = source_timeframe->getTimeAtIndex(interval.end);
1✔
248

249
        // 2. Convert those time values to indices in the target timeframe
250
        auto target_start_index = target_timeframe->getIndexAtTime(static_cast<float>(start_time_value));
1✔
251
        auto target_end_index = target_timeframe->getIndexAtTime(static_cast<float>(end_time_value));
1✔
252

253
        // 3. Create converted interval and use the original function
254
        TimeFrameInterval target_interval{target_start_index, target_end_index};
1✔
255
        return GetMasksInRange(target_interval);
1✔
256
    }
257

258
    [[nodiscard]] size_t size() const { return _data.size(); };
16✔
259

260
    // ========== Image Size ==========
261
    /**
262
     * @brief Change the size of the canvas the mask belongs to
263
     *
264
     * This will scale all mask in the data structure by the ratio of the
265
     * new size to the old size.
266
     *
267
     * @param image_size
268
     */
269
    void changeImageSize(ImageSize const & image_size);
270

271
    /**
272
     * @brief Get the image size
273
     * 
274
     * @return The image size
275
     */
276
    [[nodiscard]] ImageSize getImageSize() const { return _image_size; }
169✔
277

278
    /**
279
     * @brief Set the image size
280
     */
281
    void setImageSize(ImageSize const & image_size) { _image_size = image_size; }
110✔
282

283
    // ========== Copy and Move ==========
284

285
    /**
286
     * @brief Copy masks from this MaskData to another MaskData for a time interval
287
     * 
288
     * Copies all masks within the specified time interval [start, end] (inclusive)
289
     * to the target MaskData. If masks already exist at target times, the copied masks
290
     * are added to the existing masks.
291
     * 
292
     * @param target The target MaskData to copy masks to
293
     * @param interval The time interval to copy masks from (inclusive)
294
     * @param notify If true, the target will notify its observers after the operation
295
     * @return The number of masks actually copied
296
     */
297
    std::size_t copyTo(MaskData & target, TimeFrameInterval const & interval, bool notify = true) const;
298

299
    /**
300
     * @brief Copy masks from this MaskData to another MaskData for specific times
301
     * 
302
     * Copies all masks at the specified times to the target MaskData.
303
     * If masks already exist at target times, the copied masks are added to the existing masks.
304
     * 
305
     * @param target The target MaskData to copy masks to
306
     * @param times Vector of specific times to copy (does not need to be sorted)
307
     * @param notify If true, the target will notify its observers after the operation
308
     * @return The number of masks actually copied
309
     */
310
    std::size_t copyTo(MaskData & target, std::vector<TimeFrameIndex> const & times, bool notify = true) const;
311

312
    /**
313
     * @brief Move masks from this MaskData to another MaskData for a time interval
314
     * 
315
     * Moves all masks within the specified time interval [start, end] (inclusive)
316
     * to the target MaskData. Masks are copied to target then removed from source.
317
     * If masks already exist at target times, the moved masks are added to the existing masks.
318
     * 
319
     * @param target The target MaskData to move masks to
320
     * @param interval The time interval to move masks from (inclusive)
321
     * @param notify If true, both source and target will notify their observers after the operation
322
     * @return The number of masks actually moved
323
     */
324
    std::size_t moveTo(MaskData & target, TimeFrameInterval const & interval, bool notify = true);
325

326
    /**
327
     * @brief Move masks from this MaskData to another MaskData for specific times
328
     * 
329
     * Moves all masks at the specified times to the target MaskData.
330
     * Masks are copied to target then removed from source.
331
     * If masks already exist at target times, the moved masks are added to the existing masks.
332
     * 
333
     * @param target The target MaskData to move masks to
334
     * @param times Vector of specific times to move (does not need to be sorted)
335
     * @param notify If true, both source and target will notify their observers after the operation
336
     * @return The number of masks actually moved
337
     */
338
    std::size_t moveTo(MaskData & target, std::vector<TimeFrameIndex> const & times, bool notify = true);
339

340
    /**
341
     * @brief Copy masks with specific EntityIds to another MaskData
342
     *
343
     * Copies all masks that match the given EntityIds to the target MaskData.
344
     * The copied masks will get new EntityIds in the target.
345
     *
346
     * @param target The target MaskData to copy masks to
347
     * @param entity_ids Vector of EntityIds to copy
348
     * @param notify If true, the target will notify its observers after the operation
349
     * @return The number of masks actually copied
350
     */
351
    std::size_t copyByEntityIds(MaskData & target, std::unordered_set<EntityId> const & entity_ids, bool notify = true);
352

353
    /**
354
     * @brief Move masks with specific EntityIds to another MaskData
355
     *
356
     * Moves all masks that match the given EntityIds to the target MaskData.
357
     * The moved masks will keep the same EntityIds in the target and be removed from source.
358
     *
359
     * @param target The target MaskData to move masks to
360
     * @param entity_ids Vector of EntityIds to move
361
     * @param notify If true, both source and target will notify their observers after the operation
362
     * @return The number of masks actually moved
363
     */
364
    std::size_t moveByEntityIds(MaskData & target, std::unordered_set<EntityId> const & entity_ids, bool notify = true);
365

366

367
    // ========== Time Frame ==========
368
    /**
369
     * @brief Set the time frame
370
     * 
371
     * @param time_frame The time frame to set
372
     */
373
    void setTimeFrame(std::shared_ptr<TimeFrame> time_frame) { _time_frame = time_frame; }
80✔
374

375
    /**
376
     * @brief Set identity context for automatic EntityId maintenance.
377
     */
378
    void setIdentityContext(std::string const & data_key, EntityRegistry * registry);
379

380
    /**
381
     * @brief Rebuild EntityIds for all masks using the current identity context.
382
     */
383
    void rebuildAllEntityIds();
384

385
    /**
386
     * @brief Get EntityIds aligned with masks at a specific time.
387
     */
388
    [[nodiscard]] std::vector<EntityId> const & getEntityIdsAtTime(TimeFrameIndex time) const;
389

390
    /**
391
     * @brief Get flattened EntityIds for all masks across all times.
392
     */
393
    [[nodiscard]] std::vector<EntityId> getAllEntityIds() const;
394

395
protected:
396
private:
397
    std::map<TimeFrameIndex, std::vector<MaskEntry>> _data;
398
    std::vector<Mask2D> _empty{};
399
    std::vector<EntityId> _empty_entity_ids{};
400
    mutable std::vector<Mask2D> _temp_masks{};
401
    mutable std::vector<EntityId> _temp_entity_ids{};
402
    ImageSize _image_size;
403
    std::shared_ptr<TimeFrame> _time_frame{nullptr};
404

405
    // Identity management
406
    std::string _identity_data_key;
407
    EntityRegistry * _identity_registry{nullptr};
408
};
409

410

411
#endif// MASK_DATA_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc