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

paulmthompson / WhiskerToolbox / 18389801194

09 Oct 2025 09:35PM UTC coverage: 71.943% (+0.1%) from 71.826%
18389801194

push

github

paulmthompson
add correlation matrix to filtering interface

207 of 337 new or added lines in 5 files covered. (61.42%)

867 existing lines in 31 files now uncovered.

49964 of 69449 relevant lines covered (71.94%)

1103.53 hits per line

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

97.37
/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 "Observer/Observer_Data.hpp"
8
#include "TimeFrame/TimeFrame.hpp"
9
#include "TimeFrame/interval_data.hpp"
10
#include "Entity/EntityTypes.hpp"
11

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

17
class EntityRegistry;
18

19
/**
20
 * @brief Structure holding a Mask2D and its associated EntityId
21
 */
22
struct MaskEntry {
23
    Mask2D mask;
24
    EntityId entity_id;
25
    
26
    MaskEntry() = default;
27
    MaskEntry(Mask2D m, EntityId id) : mask(std::move(m)), entity_id(id) {}
401✔
28
};
29

30

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

43
    // ========== Setters ==========
44

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

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

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

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

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

97
    void addAtTime(TimeIndexAndFrame const & time_index_and_frame,
98
                       std::vector<Point2D<uint32_t>> mask,
99
                       bool notify = true);
100

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

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

132

133
    // ========== Getters ==========
134

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

281
    // ========== Copy and Move ==========
282

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

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

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

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

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

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

364

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

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

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

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

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

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

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

408

409
#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