• 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.3
/src/DataManager/Points/Point_Data.hpp
1
#ifndef POINT_DATA_HPP
2
#define POINT_DATA_HPP
3

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

11
#include <map>
12
#include <optional>
13
#include <ranges>
14
#include <vector>
15

16

17
class EntityRegistry;
18

19

20
/**
21
 * @brief Structure holding a Point2D<float> and its associated EntityId
22
 */
23
struct PointEntry {
24
    Point2D<float> point;
25
    EntityId entity_id;
26
    
27
    PointEntry() = default;
28
    PointEntry(Point2D<float> p, EntityId id) : point(std::move(p)), entity_id(id) {}
1,135✔
29
};
30

31

32
/**
33
 * @brief PointData
34
 *
35
 * PointData is used for storing 2D point collections at specific time frames.
36
 * Each time frame can contain multiple points, making it suitable for tracking
37
 * multiple features or keypoints over time.
38
 *
39
 * For example, keypoints for multiple body points could be a PointData object
40
 */
41
class PointData : public ObserverData {
42
public:
43

44
    // ========== Constructors ==========
45
    /**
46
     * @brief Default constructor
47
     * 
48
     * This constructor creates an empty PointData with no data
49
     */
50
    PointData() = default;
266✔
51

52
    /**
53
     * @brief Constructor for PointData from a map of TimeFrameIndex to Point2D<float>
54
     * 
55
     * @param data Map of TimeFrameIndex to Point2D<float>
56
     */
57
    explicit PointData(std::map<TimeFrameIndex, Point2D<float>> const & data);
58

59
    /**
60
     * @brief Constructor for PointData from a map of TimeFrameIndex to vector of Point2D<float>
61
     * 
62
     * @param data Map of TimeFrameIndex to vector of Point2D<float>
63
     */
64
    explicit PointData(std::map<TimeFrameIndex, std::vector<Point2D<float>>> const & data);
65
    
66
    // ========== Setters ==========
67

68
    /**
69
     * @brief Clear the points at a specific time
70
     * 
71
     * @param time The time to clear the points at
72
     * @param notify If true, the observers will be notified
73
     * @return True if the points were cleared, false if the time did not exist
74
     */
75
    [[nodiscard]] bool clearAtTime(TimeFrameIndex time, bool notify = true);
76

77
    /**
78
     * @brief Clear the points at a specific time and index
79
     * 
80
     * @param time The time to clear the points at
81
     * @param index The index of the point to clear
82
     * @param notify If true, the observers will be notified
83
     * @return True if the point was cleared, false if the time or index did not exist
84
     */
85
    [[nodiscard]] bool clearAtTime(TimeFrameIndex time, size_t index, bool notify = true);
86

87
    /**
88
     * @brief Add a point at a specific time
89
     * 
90
     * This will add a single point at a specific time.
91
     * 
92
     * If the time does not exist, it will be created.
93
     * 
94
     * If the time already exists, the point will be added to the existing points.
95
     * 
96
     * @param time The time to add the point at
97
     * @param point The point to add
98
     * @param notify If true, the observers will be notified
99
     */
100
    void addAtTime(TimeFrameIndex time, Point2D<float> point, bool notify = true);
101

102
    /**
103
     * @brief Add multiple points at a specific time
104
     * 
105
     * This will add multiple points at a specific time.
106
     * 
107
     * If the time does not exist, it will be created.
108
     * 
109
     * If the time already exists, the points will be added to the existing points.
110
     * 
111
     * @param time The time to add the points at
112
     * @param points The points to add
113
     * @param notify If true, the observers will be notified
114
     */
115
    void addPointsAtTime(TimeFrameIndex time, std::vector<Point2D<float>> const & points, bool notify = true);
116

117
    /**
118
     * @brief Add a point entry at a specific time with a specific entity ID
119
     *
120
     * This method is used internally for move operations to preserve entity IDs.
121
     *
122
     * @param time The time to add the point at
123
     * @param point The point to add
124
     * @param entity_id The entity ID to assign to the point
125
     * @param notify If true, the observers will be notified
126
     */
127
    void addPointEntryAtTime(TimeFrameIndex time, Point2D<float> const & point, EntityId entity_id, bool notify = true);
128

129
    /**
130
     * @brief Overwrite a point at a specific time
131
     * 
132
     * This will overwrite a point at a specific time.
133
     * 
134
     * @param time The time to overwrite the point at
135
     * @param point The point to overwrite
136
     * @param notify If true, the observers will be notified
137
     */
138
    void overwritePointAtTime(TimeFrameIndex time, Point2D<float> point, bool notify = true);
139

140
    /**
141
     * @brief Overwrite multiple points at a specific time
142
     * 
143
     * This will overwrite multiple points at a specific time.
144
     * 
145
     * @param time The time to overwrite the points at
146
     * @param points The points to overwrite
147
     * @param notify If true, the observers will be notified
148
     */
149
    void overwritePointsAtTime(TimeFrameIndex time, std::vector<Point2D<float>> const & points, bool notify = true);
150

151
    /**
152
     * @brief Overwrite multiple points at multiple times
153
     * 
154
     * This will overwrite multiple points at multiple times.
155
     * 
156
     * @param times The times to overwrite the points at
157
     * @param points The points to overwrite
158
     * @param notify If true, the observers will be notified
159
     */
160
    void overwritePointsAtTimes(
161
            std::vector<TimeFrameIndex> const & times,
162
            std::vector<std::vector<Point2D<float>>> const & points,
163
            bool notify = true);
164

165
    // ========== Image Size ==========
166
    /*
167
    Image size is used to store the size of the image that the points belong to.
168
    */
169

170
    /**
171
     * @brief Get the image size
172
     * 
173
     * @return The image size
174
     */
175
    [[nodiscard]] ImageSize getImageSize() const { return _image_size; }
18✔
176

177
    /**
178
     * @brief Set the image size
179
     * 
180
     * @param image_size The image size
181
     */
182
    void setImageSize(ImageSize const & image_size) { _image_size = image_size; }
93✔
183

184
    /**
185
     * @brief Change the size of the canvas the points belong to
186
     *
187
     * This will scale all points in the data structure by the ratio of the
188
     * new size to the old size.
189
     *
190
     * @param image_size
191
     */
192
    void changeImageSize(ImageSize const & image_size);
193

194
    // ========== Getters ==========
195
    /**
196
     * @brief Get all times with data
197
     * 
198
     * Returns a view over the keys of the data map for zero-copy iteration.
199
     * 
200
     * @return A view of TimeFrameIndex keys
201
     */
202
    [[nodiscard]] auto getTimesWithData() const {
23✔
203
        return _data | std::views::keys;
23✔
204
    }
205

206
    /**
207
     * @brief Get the points at a specific time
208
     * 
209
     * If the time does not exist, an empty vector will be returned.
210
     * 
211
     * @param time The time to get the points at
212
     * @return A vector of Point2D<float>
213
     */
214
    [[nodiscard]] std::vector<Point2D<float>> const & getAtTime(TimeFrameIndex time) const;
215

216
    /**
217
     * @brief Get the points at a specific time with timeframe conversion
218
     * 
219
     * Converts the time index from the source timeframe to the target timeframe (this point data's timeframe)
220
     * and returns the points at the converted time. If the timeframes are the same, no conversion is performed.
221
     * If the converted time does not exist, an empty vector will be returned.
222
     * 
223
     * @param time The time index in the source timeframe
224
     * @param source_timeframe The timeframe that the time index is expressed in
225
     * @param target_timeframe The timeframe that this point data uses
226
     * @return A vector of Point2D<float> at the converted time
227
     */
228
    [[nodiscard]] std::vector<Point2D<float>> const & getAtTime(TimeFrameIndex time, 
229
                                                                TimeFrame const * source_timeframe,
230
                                                                TimeFrame const * target_timeframe) const;
231

232
    /**
233
     * @brief Get the maximum number of points at any time
234
     * 
235
     * @return The maximum number of points
236
     */
237
    [[nodiscard]] std::size_t getMaxPoints() const;
238

239
    /**
240
    * @brief Get all points with their associated times as a range
241
    *
242
    * @return A view of time-points pairs for all times
243
    */
244
    [[nodiscard]] auto GetAllPointsAsRange() const {
104✔
245
        struct TimePointsPair {
246
            TimeFrameIndex time;
247
            std::vector<Point2D<float>> points; // Copy for backward compatibility with entry storage
248
        };
249

250
        return _data | std::views::transform([](auto const & pair) {
208✔
251
                   std::vector<Point2D<float>> points;
188✔
252
                   points.reserve(pair.second.size());
188✔
253
                   for (auto const & entry : pair.second) {
557✔
254
                       points.push_back(entry.point);
369✔
255
                   }
256
                   return TimePointsPair{pair.first, std::move(points)};
376✔
257
               });
688✔
258
    }
259

260
    /**
261
    * @brief Get all point entries with their associated times as a zero-copy range
262
    *
263
    * This method provides zero-copy access to the underlying PointEntry data structure,
264
    * which contains both Point2D<float> and EntityId information.
265
    *
266
    * @return A view of time-point entries pairs for all times
267
    */
268
    [[nodiscard]] auto GetAllPointEntriesAsRange() const {
269
        struct TimePointEntriesPair {
270
            TimeFrameIndex time;
271
            std::vector<PointEntry> const & entries;
272
        };
273

274
        return _data | std::views::transform([](auto const & pair) {
275
                   return TimePointEntriesPair{pair.first, pair.second};
276
               });
277
    }
278

279
    /**
280
    * @brief Get points with their associated times as a range within a TimeFrameInterval
281
    *
282
    * Returns a filtered view of time-points pairs for times within the specified interval [start, end] (inclusive).
283
    *
284
    * @param interval The TimeFrameInterval specifying the range [start, end] (inclusive)
285
    * @return A view of time-points pairs for times within the specified interval
286
    */
287
    [[nodiscard]] auto GetPointsInRange(TimeFrameInterval const & interval) const {
31✔
288
        struct TimePointsPair {
289
            TimeFrameIndex time;
290
            std::vector<Point2D<float>> points; // Copy for backward compatibility
291
        };
292

293
        return _data 
31✔
294
            | std::views::filter([interval](auto const & pair) {
62✔
295
                return pair.first >= interval.start && pair.first <= interval.end;
161✔
296
              })
297
            | std::views::transform([](auto const & pair) {
62✔
298
                std::vector<Point2D<float>> points;
67✔
299
                points.reserve(pair.second.size());
67✔
300
                for (auto const & entry : pair.second) {
144✔
301
                    points.push_back(entry.point);
77✔
302
                }
303
                return TimePointsPair{pair.first, std::move(points)};
134✔
304
              });
258✔
305
    }
306

307
    /**
308
    * @brief Get points with their associated times as a range within a TimeFrameInterval with timeframe conversion
309
    *
310
    * Converts the time range from the source timeframe to the target timeframe (this point data's timeframe)
311
    * and returns a filtered view of time-points pairs for times within the converted interval range.
312
    * If the timeframes are the same, no conversion is performed.
313
    *
314
    * @param interval The TimeFrameInterval in the source timeframe specifying the range [start, end] (inclusive)
315
    * @param source_timeframe The timeframe that the interval is expressed in
316
    * @param point_timeframe The timeframe that this point data uses
317
    * @return A view of time-points pairs for times within the converted interval range
318
    */
319
    [[nodiscard]] auto GetPointsInRange(TimeFrameInterval const & interval,
25✔
320
                                        TimeFrame const * source_timeframe,
321
                                        TimeFrame const * point_timeframe) const {
322
        // If the timeframes are the same object, no conversion is needed
323
        if (source_timeframe == point_timeframe) {
25✔
324
            return GetPointsInRange(interval);
21✔
325
        }
326

327
        // If either timeframe is null, fall back to original behavior
328
        if (!source_timeframe || !point_timeframe) {
4✔
UNCOV
329
            return GetPointsInRange(interval);
×
330
        }
331

332
        // Convert the time range from source timeframe to target timeframe
333
        // 1. Get the time values from the source timeframe
334
        auto start_time_value = source_timeframe->getTimeAtIndex(interval.start);
4✔
335
        auto end_time_value = source_timeframe->getTimeAtIndex(interval.end);
4✔
336

337
        // 2. Convert those time values to indices in the point timeframe
338
        auto target_start_index = point_timeframe->getIndexAtTime(static_cast<float>(start_time_value), false);
4✔
339
        auto target_end_index = point_timeframe->getIndexAtTime(static_cast<float>(end_time_value));
4✔
340

341
        // 3. Create converted interval and use the original function
342
        TimeFrameInterval target_interval{target_start_index, target_end_index};
4✔
343
        return GetPointsInRange(target_interval);
4✔
344
    }
345

346
    // ======= Move and Copy ==========
347

348
    /**
349
     * @brief Copy points from this PointData to another PointData for a time interval
350
     * 
351
     * Copies all points within the specified time interval [start, end] (inclusive)
352
     * to the target PointData. If points already exist at target times, the copied points
353
     * are added to the existing points.
354
     * 
355
     * @param target The target PointData to copy points to
356
     * @param interval The time interval to copy points from (inclusive)
357
     * @param notify If true, the target will notify its observers after the operation
358
     * @return The number of points actually copied
359
     */
360
    std::size_t copyTo(PointData& target, TimeFrameInterval const & interval, bool notify = true) const;
361

362
    /**
363
     * @brief Copy points from this PointData to another PointData for specific times
364
     * 
365
     * Copies all points at the specified times to the target PointData.
366
     * If points already exist at target times, the copied points are added to the existing points.
367
     * 
368
     * @param target The target PointData to copy points to
369
     * @param times Vector of specific times to copy (does not need to be sorted)
370
     * @param notify If true, the target will notify its observers after the operation
371
     * @return The number of points actually copied
372
     */
373
    std::size_t copyTo(PointData& target, std::vector<TimeFrameIndex> const& times, bool notify = true) const;
374

375
    /**
376
     * @brief Move points from this PointData to another PointData for a time interval
377
     * 
378
     * Moves all points within the specified time interval [start, end] (inclusive)
379
     * to the target PointData. Points are copied to target then removed from source.
380
     * If points already exist at target times, the moved points are added to the existing points.
381
     * 
382
     * @param target The target PointData to move points to
383
     * @param interval The time interval to move points from (inclusive)
384
     * @param notify If true, both source and target will notify their observers after the operation
385
     * @return The number of points actually moved
386
     */
387
    std::size_t moveTo(PointData& target, TimeFrameInterval const & interval, bool notify = true);
388

389
    /**
390
     * @brief Move points from this PointData to another PointData for specific times
391
     * 
392
     * Moves all points at the specified times to the target PointData.
393
     * Points are copied to target then removed from source.
394
     * If points already exist at target times, the moved points are added to the existing points.
395
     * 
396
     * @param target The target PointData to move points to
397
     * @param times Vector of specific times to move (does not need to be sorted)
398
     * @param notify If true, both source and target will notify their observers after the operation
399
     * @return The number of points actually moved
400
     */
401
    std::size_t moveTo(PointData& target, std::vector<TimeFrameIndex> const& times, bool notify = true);
402

403
    /**
404
     * @brief Copy points with specific EntityIds to another PointData
405
     *
406
     * Copies all points that match the given EntityIds to the target PointData.
407
     * The copied points will get new EntityIds in the target.
408
     *
409
     * @param target The target PointData to copy points to
410
     * @param entity_ids Vector of EntityIds to copy
411
     * @param notify If true, the target will notify its observers after the operation
412
     * @return The number of points actually copied
413
     */
414
    std::size_t copyPointsByEntityIds(PointData & target, std::vector<EntityId> const & entity_ids, bool notify = true);
415

416
    /**
417
     * @brief Move points with specific EntityIds to another PointData
418
     *
419
     * Moves all points that match the given EntityIds to the target PointData.
420
     * The moved points will get the same EntityIds in the target and be removed from source.
421
     *
422
     * @param target The target PointData to move points to
423
     * @param entity_ids Vector of EntityIds to move
424
     * @param notify If true, both source and target will notify their observers after the operation
425
     * @return The number of points actually moved
426
     */
427
    std::size_t movePointsByEntityIds(PointData & target, std::vector<EntityId> const & entity_ids, bool notify = true);
428

429
    // ========== Time Frame ==========
430

431
    /**
432
     * @brief Set the time frame
433
     * 
434
     * @param time_frame The time frame to set
435
     */
436
    void setTimeFrame(std::shared_ptr<TimeFrame> time_frame) { _time_frame = time_frame; }
155✔
437

438
    /**
439
     * @brief Set identity context for automatic EntityId maintenance.
440
     * @pre registry != nullptr
441
     */
442
    void setIdentityContext(std::string const & data_key, EntityRegistry * registry);
443

444
    /**
445
     * @brief Rebuild EntityIds for all points using the current identity context.
446
     * @pre Identity context has been set via setIdentityContext
447
     */
448
    void rebuildAllEntityIds();
449

450
    /**
451
     * @brief Get EntityIds aligned with points at a specific time.
452
     */
453
    [[nodiscard]] std::vector<EntityId> const & getEntityIdsAtTime(TimeFrameIndex time) const;
454

455
    /**
456
     * @brief Get flattened EntityIds for all points across all times.
457
     */
458
    [[nodiscard]] std::vector<EntityId> getAllEntityIds() const;
459

460
    // ========== Entity Lookup Methods ==========
461

462
    /**
463
     * @brief Find the point data associated with a specific EntityId.
464
     * 
465
     * This method provides reverse lookup from EntityId to the actual point data,
466
     * supporting group-based visualization workflows.
467
     * 
468
     * @param entity_id The EntityId to look up
469
     * @return Optional containing the point data if found, std::nullopt otherwise
470
     */
471
    [[nodiscard]] std::optional<Point2D<float>> getPointByEntityId(EntityId entity_id) const;
472

473
    /**
474
     * @brief Find the time frame and local index for a specific EntityId.
475
     * 
476
     * Returns the time frame and local point index (within that time frame)
477
     * associated with the given EntityId.
478
     * 
479
     * @param entity_id The EntityId to look up
480
     * @return Optional containing {time, local_index} if found, std::nullopt otherwise
481
     */
482
    [[nodiscard]] std::optional<std::pair<TimeFrameIndex, int>> getTimeAndIndexByEntityId(EntityId entity_id) const;
483

484
    /**
485
     * @brief Get all points that match the given EntityIds.
486
     * 
487
     * This method is optimized for batch lookup of multiple EntityIds,
488
     * useful for group-based operations.
489
     * 
490
     * @param entity_ids Vector of EntityIds to look up
491
     * @return Vector of pairs containing {EntityId, Point2D<float>} for found entities
492
     */
493
    [[nodiscard]] std::vector<std::pair<EntityId, Point2D<float>>> getPointsByEntityIds(std::vector<EntityId> const & entity_ids) const;
494

495
    /**
496
     * @brief Get time frame information for multiple EntityIds.
497
     * 
498
     * Returns time frame and local index information for a batch of EntityIds.
499
     * 
500
     * @param entity_ids Vector of EntityIds to look up
501
     * @return Vector of tuples containing {EntityId, time, local_index} for found entities
502
     */
503
    [[nodiscard]] std::vector<std::tuple<EntityId, TimeFrameIndex, int>> getTimeInfoByEntityIds(std::vector<EntityId> const & entity_ids) const;
504

505
protected:
506
private:
507
    std::map<TimeFrameIndex, std::vector<PointEntry>> _data;
508
    std::vector<Point2D<float>> _empty{};
509
    std::vector<EntityId> _empty_entity_ids{};
510
    mutable std::vector<Point2D<float>> _temp_points{};
511
    mutable std::vector<EntityId> _temp_entity_ids{};
512
    ImageSize _image_size;
513
    std::shared_ptr<TimeFrame> _time_frame {nullptr};
514

515
    // Identity management
516
    std::string _identity_data_key;
517
    EntityRegistry * _identity_registry {nullptr};
518
    // Entity IDs are stored within PointEntry alongside points in _data
519
};
520

521
#endif// POINT_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