• 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

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

3
#include "Entity/EntityRegistry.hpp"
4
#include "utils/map_timeseries.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)) {
×
42
        if (notify) {
×
43
            notifyObservers();
×
44
        }
45
        return true;
×
46
    }
47
    return false;
×
48
}
49

50
bool MaskData::clearAtTime(TimeFrameIndex const time, size_t const index, bool notify) {
×
51
    auto it = _data.find(time);
×
52
    if (it != _data.end()) {
×
53
        if (index >= it->second.size()) {
×
54
            return false;
×
55
        }
56
        it->second.erase(it->second.begin() + static_cast<std::ptrdiff_t>(index));
×
57
        if (it->second.empty()) {
×
58
            _data.erase(it);
×
59
        }
60
        if (notify) {
×
61
            notifyObservers();
×
62
        }
63
        return true;
×
64
    }
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,
400✔
86
                         std::vector<Point2D<uint32_t>> mask,
87
                         bool notify) {
88
    int const local_index = static_cast<int>(_data[time].size());
400✔
89
    EntityId entity_id = 0;
400✔
90
    if (_identity_registry) {
400✔
91
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::IntervalEntity, time, local_index);
156✔
92
    }
93
    _data[time].emplace_back(std::move(mask), entity_id);
400✔
94

95
    if (notify) {
400✔
96
        notifyObservers();
273✔
97
    }
98
}
400✔
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()) {
×
109
        return;
×
110
    }
111

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

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

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
128
    auto new_mask = Mask2D{};
×
129
    auto xx = std::move(x);
×
130
    auto yy = std::move(y);
×
131
    new_mask.reserve(xx.size());
×
132

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

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

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

149
void MaskData::addEntryAtTime(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 {
266✔
162
    auto it = _data.find(time);
266✔
163
    if (it == _data.end()) {
266✔
164
        return _empty;
23✔
165
    }
166
    _temp_masks.clear();
243✔
167
    _temp_masks.reserve(it->second.size());
243✔
168
    for (auto const & entry: it->second) {
510✔
169
        _temp_masks.push_back(entry.mask);
267✔
170
    }
171
    return _temp_masks;
243✔
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

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

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

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

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

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

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

205
    for (auto & [time, entries]: _data) {
×
206
        (void) time;
207
        for (auto & entry: entries) {
×
208
            for (auto & point: entry.mask) {
×
209
                point.x = static_cast<uint32_t>(std::round(static_cast<float>(point.x) * scale_x));
×
210
                point.y = static_cast<uint32_t>(std::round(static_cast<float>(point.y) * scale_y));
×
211
            }
212
        }
213
    }
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 (TimeFrameIndex const 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✔
276
        std::cerr << "MaskData::moveTo: interval start (" << interval.start.getValue()
×
277
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
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 (TimeFrameIndex const 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 (TimeFrameIndex const 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 (TimeFrameIndex const 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::copyByEntityIds(MaskData & target, std::unordered_set<EntityId> const & entity_ids, bool const notify) {
3✔
340
    return copy_by_entity_ids(_data, target, entity_ids, notify,
3✔
341
                              [](MaskEntry const & entry) -> Mask2D const & { return entry.mask; });
7✔
342
}
343

344
std::size_t MaskData::moveByEntityIds(MaskData & target, std::unordered_set<EntityId> const & entity_ids, bool notify) {
3✔
345
    auto result = move_by_entity_ids(_data, target, entity_ids, notify,
3✔
346
                                     [](MaskEntry const & entry) -> Mask2D const & { return entry.mask; });
4✔
347
    
348
    if (notify && result > 0) {
3✔
349
        notifyObservers();
2✔
350
    }
351
    
352
    return result;
3✔
353
}
354

355
void MaskData::setIdentityContext(std::string const & data_key, EntityRegistry * registry) {
68✔
356
    _identity_data_key = data_key;
68✔
357
    _identity_registry = registry;
68✔
358
}
68✔
359

360
void MaskData::rebuildAllEntityIds() {
68✔
361
    if (!_identity_registry) {
68✔
UNCOV
362
        for (auto & [t, entries]: _data) {
×
363
            (void) t;
UNCOV
364
            for (auto & entry: entries) {
×
UNCOV
365
                entry.entity_id = 0;
×
366
            }
367
        }
UNCOV
368
        return;
×
369
    }
370
    for (auto & [t, entries]: _data) {
123✔
371
        for (int i = 0; i < static_cast<int>(entries.size()); ++i) {
128✔
372
            entries[static_cast<size_t>(i)].entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::IntervalEntity, t, i);
73✔
373
        }
374
    }
375
}
376

377
std::vector<EntityId> const & MaskData::getEntityIdsAtTime(TimeFrameIndex time) const {
6✔
378
    auto it = _data.find(time);
6✔
379
    if (it == _data.end()) {
6✔
UNCOV
380
        return _empty_entity_ids;
×
381
    }
382
    _temp_entity_ids.clear();
6✔
383
    _temp_entity_ids.reserve(it->second.size());
6✔
384
    for (auto const & entry: it->second) {
14✔
385
        _temp_entity_ids.push_back(entry.entity_id);
8✔
386
    }
387
    return _temp_entity_ids;
6✔
388
}
389

390
std::vector<EntityId> MaskData::getAllEntityIds() const {
2✔
391
    std::vector<EntityId> out;
2✔
392
    for (auto const & [t, entries]: _data) {
4✔
393
        (void) t;
394
        for (auto const & entry: entries) {
6✔
395
            out.push_back(entry.entity_id);
4✔
396
        }
397
    }
398
    return out;
2✔
399
}
×
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