• 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

70.37
/src/DataManager/Masks/Mask_Data.cpp
1
#include "Mask_Data.hpp"
2

3
#include "utils/map_timeseries.hpp"
4
#include "Entity/EntityRegistry.hpp"
5

6
#include <algorithm>
7
#include <cmath>
8
#include <iostream>
9
#include <ranges>
10

11
// ========== Constructors ==========
12

13
// ========== Setters ==========
14

15
bool MaskData::clearAtTime(TimeFrameIndex const time, bool notify) {
22✔
16
    auto it = _data.find(time);
22✔
17
    if (it != _data.end()) {
22✔
18
        _data.erase(it);
17✔
19
        if (notify) {
17✔
20
            notifyObservers();
3✔
21
        }
22
        return true;
17✔
23
    }
24
    return false;
5✔
25
}
26

27
bool MaskData::clearAtTime(TimeIndexAndFrame const & time_index_and_frame, bool notify) {
6✔
28

29
    if (time_index_and_frame.time_frame.get() == _time_frame.get()) {
6✔
30
        return clearAtTime(time_index_and_frame.index, notify);
6✔
31
    }
32

33
    if (!time_index_and_frame.time_frame || !_time_frame.get()) {
×
34
        return false;
×
35
    }
36

37
    auto time = time_index_and_frame.time_frame->getTimeAtIndex(time_index_and_frame.index);
×
38
    auto time_index = _time_frame->getIndexAtTime(static_cast<float>(time));
×
39

40

41
    if (clear_at_time(time_index, _data)) {
×
UNCOV
42
        if (notify) {
×
43
            notifyObservers();
×
44
        }
UNCOV
45
        return true;
×
46
    }
47
    return false;
×
48
}
49

UNCOV
50
bool MaskData::clearAtTime(TimeFrameIndex const time, size_t const index, bool notify) {
×
51
    auto it = _data.find(time);
×
UNCOV
52
    if (it != _data.end()) {
×
53
        if (index >= it->second.size()) {
×
UNCOV
54
            return false;
×
55
        }
UNCOV
56
        it->second.erase(it->second.begin() + static_cast<std::ptrdiff_t>(index));
×
UNCOV
57
        if (it->second.empty()) {
×
UNCOV
58
            _data.erase(it);
×
59
        }
UNCOV
60
        if (notify) {
×
UNCOV
61
            notifyObservers();
×
62
        }
UNCOV
63
        return true;
×
64
    }
UNCOV
65
    return false;
×
66
}
67

68
void MaskData::addAtTime(TimeFrameIndex const time,
151✔
69
                         std::vector<uint32_t> const & x,
70
                         std::vector<uint32_t> const & y,
71
                         bool notify) {
72
    auto new_mask = create_mask(x, y);
151✔
73
    int const local_index = static_cast<int>(_data[time].size());
151✔
74
    EntityId entity_id = 0;
151✔
75
    if (_identity_registry) {
151✔
76
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::IntervalEntity, time, local_index);
8✔
77
    }
78
    _data[time].emplace_back(std::move(new_mask), entity_id);
151✔
79

80
    if (notify) {
151✔
81
        notifyObservers();
147✔
82
    }
83
}
302✔
84

85
void MaskData::addAtTime(TimeFrameIndex const time,
246✔
86
                         std::vector<Point2D<uint32_t>> mask,
87
                         bool notify) {
88
    int const local_index = static_cast<int>(_data[time].size());
246✔
89
    EntityId entity_id = 0;
246✔
90
    if (_identity_registry) {
246✔
91
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::IntervalEntity, time, local_index);
13✔
92
    }
93
    _data[time].emplace_back(std::move(mask), entity_id);
246✔
94

95
    if (notify) {
246✔
96
        notifyObservers();
119✔
97
    }
98
}
246✔
99

100
void MaskData::addAtTime(TimeIndexAndFrame const & time_index_and_frame,
6✔
101
                         std::vector<Point2D<uint32_t>> mask,
102
                         bool notify) {
103
    if (time_index_and_frame.time_frame.get() == _time_frame.get()) {
6✔
104
        addAtTime(time_index_and_frame.index, std::move(mask), notify);
6✔
105
        return;
6✔
106
    }
107

108
    if (!time_index_and_frame.time_frame || !_time_frame.get()) {
×
UNCOV
109
        return;
×
110
    }
111

UNCOV
112
    auto time = time_index_and_frame.time_frame->getTimeAtIndex(time_index_and_frame.index);
×
113
    auto time_index = _time_frame->getIndexAtTime(static_cast<float>(time));
×
114

UNCOV
115
    int const local_index = static_cast<int>(_data[time_index].size());
×
UNCOV
116
    EntityId entity_id = 0;
×
UNCOV
117
    if (_identity_registry) {
×
UNCOV
118
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::IntervalEntity, time_index, local_index);
×
119
    }
UNCOV
120
    _data[time_index].emplace_back(std::move(mask), entity_id);
×
121
}
122

UNCOV
123
void MaskData::addAtTime(TimeFrameIndex const time,
×
124
                         std::vector<uint32_t> && x,
125
                         std::vector<uint32_t> && y,
126
                         bool notify) {
127
    // Create mask efficiently using move semantics
UNCOV
128
    auto new_mask = Mask2D{};
×
129
    auto xx = std::move(x);
×
UNCOV
130
    auto yy = std::move(y);
×
UNCOV
131
    new_mask.reserve(xx.size());
×
132

133
    for (std::size_t i = 0; i < xx.size(); i++) {
×
UNCOV
134
        new_mask.emplace_back(xx[i], yy[i]);
×
135
    }
136

UNCOV
137
    int const local_index = static_cast<int>(_data[time].size());
×
138
    EntityId entity_id = 0;
×
139
    if (_identity_registry) {
×
UNCOV
140
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::IntervalEntity, time, local_index);
×
141
    }
UNCOV
142
    _data[time].emplace_back(std::move(new_mask), entity_id);
×
143

144
    if (notify) {
×
145
        notifyObservers();
×
146
    }
UNCOV
147
}
×
148

149
void MaskData::addMaskEntryAtTime(TimeFrameIndex const time,
4✔
150
                                  Mask2D const & mask,
151
                                  EntityId const entity_id,
152
                                  bool const notify) {
153
    _data[time].emplace_back(mask, entity_id);
4✔
154
    if (notify) {
4✔
155
        notifyObservers();
×
156
    }
157
}
4✔
158

159
// ========== Getters ==========
160

161
std::vector<Mask2D> const & MaskData::getAtTime(TimeFrameIndex const time) const {
113✔
162
    auto it = _data.find(time);
113✔
163
    if (it == _data.end()) {
113✔
164
        return _empty;
18✔
165
    }
166
    _temp_masks.clear();
95✔
167
    _temp_masks.reserve(it->second.size());
95✔
168
    for (auto const & entry : it->second) {
214✔
169
        _temp_masks.push_back(entry.mask);
119✔
170
    }
171
    return _temp_masks;
95✔
172
}
173

174
std::vector<Mask2D> const & MaskData::getAtTime(TimeIndexAndFrame const & time_index_and_frame) const {
8✔
175
    auto converted = convert_time_index(time_index_and_frame.index,
16✔
176
                                        time_index_and_frame.time_frame.get(),
8✔
177
                                        _time_frame.get());
8✔
178
    return getAtTime(converted);
16✔
179
}
180

UNCOV
181
std::vector<Mask2D> const & MaskData::getAtTime(TimeFrameIndex const time,
×
182
                                                TimeFrame const * source_timeframe,
183
                                                TimeFrame const * mask_timeframe) const {
184

UNCOV
185
    TimeFrameIndex const converted = convert_time_index(time, source_timeframe, mask_timeframe);
×
UNCOV
186
    return getAtTime(converted);
×
187
}
188

189
// ========== Image Size ==========
190

UNCOV
191
void MaskData::changeImageSize(ImageSize const & image_size) {
×
UNCOV
192
    if (_image_size.width == -1 || _image_size.height == -1) {
×
193
        std::cout << "No size set for current image. "
UNCOV
194
                  << " Please set a valid image size before trying to scale" << std::endl;
×
195
    }
196

UNCOV
197
    if (_image_size.width == image_size.width && _image_size.height == image_size.height) {
×
UNCOV
198
        std::cout << "Image size is the same. No need to scale" << std::endl;
×
UNCOV
199
        return;
×
200
    }
201

UNCOV
202
    float const scale_x = static_cast<float>(image_size.width) / static_cast<float>(_image_size.width);
×
UNCOV
203
    float const scale_y = static_cast<float>(image_size.height) / static_cast<float>(_image_size.height);
×
204

UNCOV
205
    for (auto & [time, entries]: _data) {
×
206
        (void)time;
UNCOV
207
        for (auto & entry: entries) {
×
UNCOV
208
            for (auto & point: entry.mask) {
×
UNCOV
209
                point.x = static_cast<uint32_t>(std::round(static_cast<float>(point.x) * scale_x));
×
UNCOV
210
                point.y = static_cast<uint32_t>(std::round(static_cast<float>(point.y) * scale_y));
×
211
            }
212
        }
213
    }
UNCOV
214
    _image_size = image_size;
×
215
}
216

217
// ========== Copy and Move ==========
218

219
std::size_t MaskData::copyTo(MaskData & target, TimeFrameInterval const & interval, bool notify) const {
9✔
220
    if (interval.start > interval.end) {
9✔
221
        std::cerr << "MaskData::copyTo: interval start (" << interval.start.getValue()
1✔
222
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
1✔
223
        return 0;
1✔
224
    }
225

226
    // Ensure target is not the same as source
227
    if (this == &target) {
8✔
228
        std::cerr << "MaskData::copyTo: Cannot copy to self" << std::endl;
1✔
229
        return 0;
1✔
230
    }
231

232
    std::size_t total_masks_copied = 0;
7✔
233

234
    // Iterate through all times in the source data within the interval
235
    for (auto const & [time, entries]: _data) {
21✔
236
        if (time >= interval.start && time <= interval.end && !entries.empty()) {
14✔
237
            for (auto const & entry: entries) {
16✔
238
                target.addAtTime(time, entry.mask, false);// Don't notify for each operation
9✔
239
                total_masks_copied++;
9✔
240
            }
241
        }
242
    }
243

244
    // Notify observer only once at the end if requested
245
    if (notify && total_masks_copied > 0) {
7✔
246
        target.notifyObservers();
4✔
247
    }
248

249
    return total_masks_copied;
7✔
250
}
251

252
std::size_t MaskData::copyTo(MaskData & target, std::vector<TimeFrameIndex> const & times, bool notify) const {
2✔
253
    std::size_t total_masks_copied = 0;
2✔
254

255
    // Copy masks for each specified time
256
    for (const TimeFrameIndex time: times) {
8✔
257
        auto it = _data.find(time);
6✔
258
        if (it != _data.end() && !it->second.empty()) {
6✔
259
            for (auto const & entry: it->second) {
10✔
260
                target.addAtTime(time, entry.mask, false);// Don't notify for each operation
6✔
261
                total_masks_copied++;
6✔
262
            }
263
        }
264
    }
265

266
    // Notify observer only once at the end if requested
267
    if (notify && total_masks_copied > 0) {
2✔
268
        target.notifyObservers();
2✔
269
    }
270

271
    return total_masks_copied;
2✔
272
}
273

274
std::size_t MaskData::moveTo(MaskData & target, TimeFrameInterval const & interval, bool notify) {
5✔
275
    if (interval.start > interval.end) {
5✔
UNCOV
276
        std::cerr << "MaskData::moveTo: interval start (" << interval.start.getValue()
×
UNCOV
277
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
UNCOV
278
        return 0;
×
279
    }
280

281
    std::size_t total_masks_moved = 0;
5✔
282
    std::vector<TimeFrameIndex> times_to_clear;
5✔
283

284
    // First, copy all masks in the interval to target
285
    for (auto const & [time, entries]: _data) {
17✔
286
        if (time >= interval.start && time <= interval.end && !entries.empty()) {
12✔
287
            for (auto const & entry: entries) {
14✔
288
                target.addAtTime(time, entry.mask, false);// Don't notify for each operation
8✔
289
                total_masks_moved++;
8✔
290
            }
291
            times_to_clear.push_back(time);
6✔
292
        }
293
    }
294

295
    // Then, clear all the times from source
296
    for (const TimeFrameIndex time: times_to_clear) {
11✔
297
        (void)clearAtTime(time, false);// Don't notify for each operation
6✔
298
    }
299

300
    // Notify observers only once at the end if requested
301
    if (notify && total_masks_moved > 0) {
5✔
302
        target.notifyObservers();
3✔
303
        notifyObservers();
3✔
304
    }
305

306
    return total_masks_moved;
5✔
307
}
5✔
308

309
std::size_t MaskData::moveTo(MaskData & target, std::vector<TimeFrameIndex> const & times, bool notify) {
2✔
310
    std::size_t total_masks_moved = 0;
2✔
311
    std::vector<TimeFrameIndex> times_to_clear;
2✔
312

313
    // First, copy masks for each specified time to target
314
    for (const TimeFrameIndex time: times) {
8✔
315
        auto it = _data.find(time);
6✔
316
        if (it != _data.end() && !it->second.empty()) {
6✔
317
            for (auto const & entry: it->second) {
10✔
318
                target.addAtTime(time, entry.mask, false);// Don't notify for each operation
6✔
319
                total_masks_moved++;
6✔
320
            }
321
            times_to_clear.push_back(time);
4✔
322
        }
323
    }
324

325
    // Then, clear all the times from source
326
    for (const TimeFrameIndex time: times_to_clear) {
6✔
327
        (void)clearAtTime(time, false);// Don't notify for each operation
4✔
328
    }
329

330
    // Notify observers only once at the end if requested
331
    if (notify && total_masks_moved > 0) {
2✔
332
        target.notifyObservers();
2✔
333
        notifyObservers();
2✔
334
    }
335

336
    return total_masks_moved;
2✔
337
}
2✔
338

339
std::size_t MaskData::copyMasksByEntityIds(MaskData & target, std::vector<EntityId> const & entity_ids, bool const notify) {
3✔
340
    std::size_t total_copied = 0;
3✔
341
    for (auto const & [time, entries] : _data) {
8✔
342
        for (auto const & entry : entries) {
11✔
343
            if (std::ranges::find(entity_ids, entry.entity_id) != entity_ids.end()) {
6✔
344
                target.addAtTime(time, entry.mask, false);
4✔
345
                total_copied++;
4✔
346
            }
347
        }
348
    }
349
    if (notify && total_copied > 0) {
3✔
350
        target.notifyObservers();
2✔
351
    }
352
    return total_copied;
3✔
353
}
354

355
std::size_t MaskData::moveMasksByEntityIds(MaskData & target, std::vector<EntityId> const & entity_ids, bool const notify) {
3✔
356
    std::size_t total_moved = 0;
3✔
357
    std::vector<std::pair<TimeFrameIndex, size_t>> to_remove;
3✔
358

359
    for (auto const & [time, entries] : _data) {
7✔
360
        for (size_t i = 0; i < entries.size(); ++i) {
9✔
361
            auto const & entry = entries[i];
5✔
362
            if (std::ranges::find(entity_ids, entry.entity_id) != entity_ids.end()) {
5✔
363
                target.addMaskEntryAtTime(time, entry.mask, entry.entity_id, false);
4✔
364
                to_remove.emplace_back(time, i);
4✔
365
                total_moved++;
4✔
366
            }
367
        }
368
    }
369

370
    std::ranges::sort(to_remove, [](auto const & a, auto const & b) {
3✔
371
        if (a.first != b.first) return a.first > b.first;
2✔
372
        return a.second > b.second;
1✔
373
    });
374

375
    for (auto const & [time, idx] : to_remove) {
7✔
376
        auto it = _data.find(time);
4✔
377
        if (it != _data.end() && idx < it->second.size()) {
4✔
378
            it->second.erase(it->second.begin() + static_cast<long>(idx));
4✔
379
            if (it->second.empty()) {
4✔
380
                _data.erase(it);
3✔
381
            }
382
        }
383
    }
384

385
    if (notify && total_moved > 0) {
3✔
386
        target.notifyObservers();
2✔
387
        notifyObservers();
2✔
388
    }
389
    return total_moved;
3✔
390
}
3✔
391

392
void MaskData::setIdentityContext(std::string const & data_key, EntityRegistry * registry) {
55✔
393
    _identity_data_key = data_key;
55✔
394
    _identity_registry = registry;
55✔
395
}
55✔
396

397
void MaskData::rebuildAllEntityIds() {
55✔
398
    if (!_identity_registry) {
55✔
UNCOV
399
        for (auto & [t, entries] : _data) {
×
400
            (void)t;
UNCOV
401
            for (auto & entry : entries) {
×
UNCOV
402
                entry.entity_id = 0;
×
403
            }
404
        }
UNCOV
405
        return;
×
406
    }
407
    for (auto & [t, entries] : _data) {
110✔
408
        for (int i = 0; i < static_cast<int>(entries.size()); ++i) {
128✔
409
            entries[static_cast<size_t>(i)].entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::IntervalEntity, t, i);
73✔
410
        }
411
    }
412
}
413

414
std::vector<EntityId> const & MaskData::getEntityIdsAtTime(TimeFrameIndex time) const {
6✔
415
    auto it = _data.find(time);
6✔
416
    if (it == _data.end()) {
6✔
UNCOV
417
        return _empty_entity_ids;
×
418
    }
419
    _temp_entity_ids.clear();
6✔
420
    _temp_entity_ids.reserve(it->second.size());
6✔
421
    for (auto const & entry : it->second) {
14✔
422
        _temp_entity_ids.push_back(entry.entity_id);
8✔
423
    }
424
    return _temp_entity_ids;
6✔
425
}
426

427
std::vector<EntityId> MaskData::getAllEntityIds() const {
2✔
428
    std::vector<EntityId> out;
2✔
429
    for (auto const & [t, entries] : _data) {
4✔
430
        (void)t;
431
        for (auto const & entry : entries) {
6✔
432
            out.push_back(entry.entity_id);
4✔
433
        }
434
    }
435
    return out;
2✔
UNCOV
436
}
×
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