• 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

87.18
/src/DataManager/Points/Point_Data.cpp
1
#include "Point_Data.hpp"
2

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

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

10
// ========== Constructors ==========
11

12
PointData::PointData(std::map<TimeFrameIndex, Point2D<float>> const & data) {
40✔
13
    for (auto const & [time, point]: data) {
240✔
14
        _data[time].emplace_back(point, 0);
200✔
15
    }
16
}
40✔
17

18
PointData::PointData(std::map<TimeFrameIndex, std::vector<Point2D<float>>> const & data) {
1✔
19
    for (auto const & [time, points]: data) {
3✔
20
        auto & entries = _data[time];
2✔
21
        entries.reserve(points.size());
2✔
22
        for (auto const & p: points) {
5✔
23
            entries.emplace_back(p, 0);
3✔
24
        }
25
    }
26
}
1✔
27

28
// ========== Setters ==========
29

30
bool PointData::clearAtTime(TimeFrameIndex const time, bool notify) {
17✔
31
    auto it = _data.find(time);
17✔
32
    if (it != _data.end()) {
17✔
33
        _data.erase(it);
16✔
34
        if (notify) {
16✔
35
            notifyObservers();
2✔
36
        }
37
        return true;
16✔
38
    }
39
    return false;
1✔
40
}
41

UNCOV
42
bool PointData::clearAtTime(TimeFrameIndex const time, size_t const index, bool notify) {
×
43
    auto it = _data.find(time);
×
44
    if (it != _data.end()) {
×
45
        if (index >= it->second.size()) {
×
46
            return false;
×
47
        }
UNCOV
48
        it->second.erase(it->second.begin() + static_cast<std::ptrdiff_t>(index));
×
49
        if (it->second.empty()) {
×
50
            _data.erase(it);
×
51
        }
UNCOV
52
        if (notify) {
×
53
            notifyObservers();
×
54
        }
UNCOV
55
        return true;
×
56
    }
UNCOV
57
    return false;
×
58
}
59

60
void PointData::overwritePointAtTime(TimeFrameIndex const time, Point2D<float> const point, bool notify) {
1✔
61
    EntityId entity_id = 0;
1✔
62
    if (_identity_registry) {
1✔
UNCOV
63
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, 0);
×
64
    }
65
    _data[time].clear();
1✔
66
    _data[time].emplace_back(point, entity_id);
1✔
67
    if (notify) {
1✔
68
        notifyObservers();
1✔
69
    }
70
}
1✔
71

72
void PointData::overwritePointsAtTime(TimeFrameIndex const time, std::vector<Point2D<float>> const & points, bool notify) {
126✔
73
    auto & entries = _data[time];
126✔
74
    entries.clear();
126✔
75
    entries.reserve(points.size());
126✔
76
    for (int i = 0; i < static_cast<int>(points.size()); ++i) {
535✔
77
        EntityId id = 0;
409✔
78
        if (_identity_registry) {
409✔
UNCOV
79
            id = _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, i);
×
80
        }
81
        entries.emplace_back(points[static_cast<size_t>(i)], id);
409✔
82
    }
83
    if (notify) {
126✔
84
        notifyObservers();
126✔
85
    }
86
}
126✔
87

88
void PointData::overwritePointsAtTimes(
2✔
89
        std::vector<TimeFrameIndex> const & times,
90
        std::vector<std::vector<Point2D<float>>> const & points,
91
        bool notify) {
92
    if (times.size() != points.size()) {
2✔
93
        std::cout << "overwritePointsAtTimes: times and points must be the same size" << std::endl;
1✔
94
        return;
1✔
95
    }
96

97
    for (std::size_t i = 0; i < times.size(); i++) {
3✔
98
        auto const & pts = points[i];
2✔
99
        auto const time = times[i];
2✔
100
        auto & entries = _data[time];
2✔
101
        entries.clear();
2✔
102
        entries.reserve(pts.size());
2✔
103
        for (int j = 0; j < static_cast<int>(pts.size()); ++j) {
5✔
104
            EntityId id = 0;
3✔
105
            if (_identity_registry) {
3✔
UNCOV
106
                id = _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, j);
×
107
            }
108
            entries.emplace_back(pts[static_cast<size_t>(j)], id);
3✔
109
        }
110
    }
111
    if (notify) {
1✔
112
        notifyObservers();
1✔
113
    }
114
}
115

116
void PointData::addAtTime(TimeFrameIndex const time, Point2D<float> const point, bool notify) {
291✔
117
    int const local_index = static_cast<int>(_data[time].size());
291✔
118
    EntityId entity_id = 0;
291✔
119
    if (_identity_registry) {
291✔
120
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, local_index);
66✔
121
    }
122
    _data[time].emplace_back(point, entity_id);
291✔
123

124
    if (notify) {
291✔
125
        notifyObservers();
164✔
126
    }
127
}
291✔
128

129
void PointData::addPointsAtTime(TimeFrameIndex const time, std::vector<Point2D<float>> const & points, bool notify) {
148✔
130
    auto & entries = _data[time];
148✔
131
    int const start_index = static_cast<int>(entries.size());
148✔
132
    entries.reserve(entries.size() + points.size());
148✔
133
    for (int i = 0; i < static_cast<int>(points.size()); ++i) {
398✔
134
        EntityId id = 0;
250✔
135
        if (_identity_registry) {
250✔
UNCOV
136
            id = _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, start_index + i);
×
137
        }
138
        entries.emplace_back(points[static_cast<size_t>(i)], id);
250✔
139
    }
140

141
    if (notify) {
148✔
142
        notifyObservers();
146✔
143
    }
144
}
148✔
145

146
void PointData::addEntryAtTime(TimeFrameIndex const time,
163✔
147
                               Point2D<float> const & point,
148
                               EntityId const entity_id,
149
                               bool const notify) {
150
    _data[time].emplace_back(point, entity_id);
163✔
151
    if (notify) {
163✔
UNCOV
152
        notifyObservers();
×
153
    }
154
}
163✔
155

156
// ========== Getters ==========
157

158
std::vector<Point2D<float>> const & PointData::getAtTime(TimeFrameIndex const time) const {
396✔
159
    auto it = _data.find(time);
396✔
160
    if (it == _data.end()) {
396✔
161
        return _empty;
31✔
162
    }
163
    _temp_points.clear();
365✔
164
    _temp_points.reserve(it->second.size());
365✔
165
    for (auto const & entry: it->second) {
801✔
166
        _temp_points.push_back(entry.point);
436✔
167
    }
168
    return _temp_points;
365✔
169
}
170

171
std::vector<Point2D<float>> const & PointData::getAtTime(TimeFrameIndex const time,
17✔
172
                                                         TimeFrame const * source_timeframe,
173
                                                         TimeFrame const * target_timeframe) const {
174
    TimeFrameIndex const converted = convert_time_index(time, source_timeframe, target_timeframe);
17✔
175
    return getAtTime(converted);
34✔
176
}
177

178
std::size_t PointData::getMaxPoints() const {
1✔
179
    std::size_t max_points = 0;
1✔
180
    for (auto const & [time, entries]: _data) {
3✔
181
        (void) time;
182
        max_points = std::max(max_points, entries.size());
2✔
183
    }
184
    return max_points;
1✔
185
}
186

187
// ========== Image Size ==========
188

189
void PointData::changeImageSize(ImageSize const & image_size) {
3✔
190
    if (_image_size.width == -1 || _image_size.height == -1) {
3✔
191
        std::cout << "No size set for current image. "
192
                  << " Please set a valid image size before trying to scale" << std::endl;
1✔
193
        _image_size = image_size;// Set the image size if it wasn't set before
1✔
194
        return;
1✔
195
    }
196

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

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

205
    for (auto & [time, entries]: _data) {
2✔
206
        (void) time;
207
        for (auto & entry: entries) {
3✔
208
            entry.point.x *= scale_x;
2✔
209
            entry.point.y *= scale_y;
2✔
210
        }
211
    }
212
    _image_size = image_size;
1✔
213
}
214

215
// ========== Copy and Move ==========
216

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

224
    std::size_t total_points_copied = 0;
6✔
225

226
    // Iterate through all times in the source data within the interval
227
    for (auto const & [time, entries]: _data) {
28✔
228
        if (time >= interval.start && time <= interval.end && !entries.empty()) {
22✔
229
            for (auto const & entry: entries) {
29✔
230
                target.addAtTime(time, entry.point, false);
19✔
231
                total_points_copied += 1;
19✔
232
            }
233
        }
234
    }
235

236
    // Notify observer only once at the end if requested
237
    if (notify && total_points_copied > 0) {
6✔
238
        target.notifyObservers();
5✔
239
    }
240

241
    return total_points_copied;
6✔
242
}
243

244
std::size_t PointData::copyTo(PointData & target, std::vector<TimeFrameIndex> const & times, bool notify) const {
5✔
245
    std::size_t total_points_copied = 0;
5✔
246

247
    // Copy points for each specified time
248
    for (TimeFrameIndex time: times) {
20✔
249
        auto it = _data.find(time);
15✔
250
        if (it != _data.end() && !it->second.empty()) {
15✔
251
            for (auto const & entry: it->second) {
31✔
252
                target.addAtTime(time, entry.point, false);
21✔
253
                total_points_copied += 1;
21✔
254
            }
255
        }
256
    }
257

258
    // Notify observer only once at the end if requested
259
    if (notify && total_points_copied > 0) {
5✔
260
        target.notifyObservers();
4✔
261
    }
262

263
    return total_points_copied;
5✔
264
}
265

266
std::size_t PointData::moveTo(PointData & target, TimeFrameInterval const & interval, bool notify) {
5✔
267
    if (interval.start > interval.end) {
5✔
UNCOV
268
        std::cerr << "PointData::moveTo: interval start (" << interval.start.getValue()
×
269
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
270
        return 0;
×
271
    }
272

273
    std::size_t total_points_moved = 0;
5✔
274
    std::vector<TimeFrameIndex> times_to_clear;
5✔
275

276
    // First, copy all points in the interval to target
277
    for (auto const & [time, entries]: _data) {
23✔
278
        if (time >= interval.start && time <= interval.end && !entries.empty()) {
18✔
279
            for (auto const & entry: entries) {
27✔
280
                target.addAtTime(time, entry.point, false);
18✔
281
                total_points_moved += 1;
18✔
282
            }
283
            times_to_clear.push_back(time);
9✔
284
        }
285
    }
286

287
    // Then, clear all the times from source
288
    for (TimeFrameIndex time: times_to_clear) {
14✔
289
        (void) clearAtTime(time, false);// Don't notify for each operation
9✔
290
    }
291

292
    // Notify observers only once at the end if requested
293
    if (notify && total_points_moved > 0) {
5✔
294
        target.notifyObservers();
5✔
295
        notifyObservers();
5✔
296
    }
297

298
    return total_points_moved;
5✔
299
}
5✔
300

301
std::size_t PointData::moveTo(PointData & target, std::vector<TimeFrameIndex> const & times, bool notify) {
2✔
302
    std::size_t total_points_moved = 0;
2✔
303
    std::vector<TimeFrameIndex> times_to_clear;
2✔
304

305
    // First, copy points for each specified time to target
306
    for (TimeFrameIndex time: times) {
7✔
307
        auto it = _data.find(time);
5✔
308
        if (it != _data.end() && !it->second.empty()) {
5✔
309
            for (auto const & entry: it->second) {
14✔
310
                target.addAtTime(time, entry.point, false);
9✔
311
                total_points_moved += 1;
9✔
312
            }
313
            times_to_clear.push_back(time);
5✔
314
        }
315
    }
316

317
    // Then, clear all the times from source
318
    for (TimeFrameIndex time: times_to_clear) {
7✔
319
        (void) clearAtTime(time, false);// Don't notify for each operation
5✔
320
    }
321

322
    // Notify observers only once at the end if requested
323
    if (notify && total_points_moved > 0) {
2✔
324
        target.notifyObservers();
2✔
325
        notifyObservers();
2✔
326
    }
327

328
    return total_points_moved;
2✔
329
}
2✔
330

331
void PointData::setIdentityContext(std::string const & data_key, EntityRegistry * registry) {
184✔
332
    _identity_data_key = data_key;
184✔
333
    _identity_registry = registry;
184✔
334
}
184✔
335

336
void PointData::rebuildAllEntityIds() {
184✔
337
    if (!_identity_registry) {
184✔
UNCOV
338
        for (auto & [t, entries]: _data) {
×
339
            (void) t;
UNCOV
340
            for (auto & entry: entries) {
×
341
                entry.entity_id = 0;
×
342
            }
343
        }
UNCOV
344
        return;
×
345
    }
346
    for (auto & [t, entries]: _data) {
534✔
347
        for (int i = 0; i < static_cast<int>(entries.size()); ++i) {
939✔
348
            entries[static_cast<size_t>(i)].entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, t, i);
589✔
349
        }
350
    }
351
}
352

353
std::vector<EntityId> const & PointData::getEntityIdsAtTime(TimeFrameIndex time) const {
90✔
354
    auto it = _data.find(time);
90✔
355
    if (it == _data.end()) {
90✔
UNCOV
356
        return _empty_entity_ids;
×
357
    }
358
    _temp_entity_ids.clear();
90✔
359
    _temp_entity_ids.reserve(it->second.size());
90✔
360
    for (auto const & entry: it->second) {
283✔
361
        _temp_entity_ids.push_back(entry.entity_id);
193✔
362
    }
363
    return _temp_entity_ids;
90✔
364
}
365

366
std::vector<EntityId> PointData::getAllEntityIds() const {
9✔
367
    std::vector<EntityId> out;
9✔
368
    for (auto const & [t, entries]: _data) {
24✔
369
        (void) t;
370
        for (auto const & entry: entries) {
43✔
371
            out.push_back(entry.entity_id);
28✔
372
        }
373
    }
374
    return out;
9✔
UNCOV
375
}
×
376

377
// ========== Entity Lookup Methods ==========
378

379
std::optional<Point2D<float>> PointData::getPointByEntityId(EntityId entity_id) const {
8✔
380
    if (!_identity_registry) {
8✔
UNCOV
381
        return std::nullopt;
×
382
    }
383

384
    auto descriptor = _identity_registry->get(entity_id);
8✔
385
    if (!descriptor || descriptor->kind != EntityKind::PointEntity || descriptor->data_key != _identity_data_key) {
8✔
UNCOV
386
        return std::nullopt;
×
387
    }
388

389
    TimeFrameIndex time{descriptor->time_value};
8✔
390
    int local_index = descriptor->local_index;
8✔
391

392
    auto time_it = _data.find(time);
8✔
393
    if (time_it == _data.end()) {
8✔
UNCOV
394
        return std::nullopt;
×
395
    }
396

397
    if (local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
8✔
UNCOV
398
        return std::nullopt;
×
399
    }
400

401
    return time_it->second[static_cast<size_t>(local_index)].point;
8✔
402
}
8✔
403

404
std::optional<std::pair<TimeFrameIndex, int>> PointData::getTimeAndIndexByEntityId(EntityId entity_id) const {
5✔
405
    if (!_identity_registry) {
5✔
UNCOV
406
        return std::nullopt;
×
407
    }
408

409
    auto descriptor = _identity_registry->get(entity_id);
5✔
410
    if (!descriptor || descriptor->kind != EntityKind::PointEntity || descriptor->data_key != _identity_data_key) {
5✔
UNCOV
411
        return std::nullopt;
×
412
    }
413

414
    TimeFrameIndex time{descriptor->time_value};
5✔
415
    int local_index = descriptor->local_index;
5✔
416

417
    // Verify the time and index are valid
418
    auto time_it = _data.find(time);
5✔
419
    if (time_it == _data.end() || local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
5✔
UNCOV
420
        return std::nullopt;
×
421
    }
422

423
    return std::make_pair(time, local_index);
5✔
424
}
5✔
425

426
std::vector<std::pair<EntityId, Point2D<float>>> PointData::getPointsByEntityIds(std::vector<EntityId> const & entity_ids) const {
1✔
427
    std::vector<std::pair<EntityId, Point2D<float>>> result;
1✔
428
    result.reserve(entity_ids.size());
1✔
429

430
    for (EntityId entity_id: entity_ids) {
4✔
431
        auto point = getPointByEntityId(entity_id);
3✔
432
        if (point) {
3✔
433
            result.emplace_back(entity_id, *point);
3✔
434
        }
435
    }
436

437
    return result;
1✔
UNCOV
438
}
×
439

440
std::vector<std::tuple<EntityId, TimeFrameIndex, int>> PointData::getTimeInfoByEntityIds(std::vector<EntityId> const & entity_ids) const {
1✔
441
    std::vector<std::tuple<EntityId, TimeFrameIndex, int>> result;
1✔
442
    result.reserve(entity_ids.size());
1✔
443

444
    for (EntityId entity_id: entity_ids) {
4✔
445
        auto time_info = getTimeAndIndexByEntityId(entity_id);
3✔
446
        if (time_info) {
3✔
447
            result.emplace_back(entity_id, time_info->first, time_info->second);
3✔
448
        }
449
    }
450

451
    return result;
1✔
UNCOV
452
}
×
453

454
// ======== Copy/Move by EntityIds =========
455

456
std::size_t PointData::copyByEntityIds(PointData & target, std::unordered_set<EntityId> const & entity_ids, bool const notify) {
5✔
457
    return copy_by_entity_ids(_data, target, entity_ids, notify,
5✔
458
                              [](PointEntry const & entry) -> Point2D<float> const & { return entry.point; });
11✔
459
}
460

461
std::size_t PointData::moveByEntityIds(PointData & target, std::unordered_set<EntityId> const & entity_ids, bool notify) {
4✔
462
    auto result = move_by_entity_ids(_data, target, entity_ids, notify, 
4✔
463
                                     [](PointEntry const & entry) -> Point2D<float> const & { return entry.point; });
6✔
464
    
465
    if (notify && result > 0) {
4✔
466
        notifyObservers();
3✔
467
    }
468
    
469
    return result;
4✔
470
}
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