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

paulmthompson / WhiskerToolbox / 17733471381

15 Sep 2025 12:43PM UTC coverage: 72.1% (+0.4%) from 71.744%
17733471381

push

github

paulmthompson
fix optional missing include on windows

37727 of 52326 relevant lines covered (72.1%)

1297.48 hits per line

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

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

3
#include "utils/map_timeseries.hpp"
4
#include "Entity/EntityRegistry.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) {
×
13
    for (auto const & [time, point]: data) {
×
14
        _data[time].push_back(point);
×
15
        _entity_ids_by_time[time].push_back(0); // placeholder until identity context is set
×
16
    }
17
}
×
18

19
PointData::PointData(std::map<TimeFrameIndex, std::vector<Point2D<float>>> const & data) 
1✔
20
    : _data(data) {
1✔
21
    for (auto const & [time, points] : _data) {
3✔
22
        _entity_ids_by_time[time].resize(points.size(), 0);
2✔
23
    }
24
}
1✔
25

26
// ========== Setters ==========
27

28
bool PointData::clearAtTime(TimeFrameIndex const time, bool notify) {
16✔
29
    if (clear_at_time(time, _data)) {
16✔
30
        _entity_ids_by_time.erase(time);
15✔
31
        if (notify) {
15✔
32
            notifyObservers();
2✔
33
        }
34
        return true;
15✔
35
    }
36
    return false;
1✔
37
}
38

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

58
void PointData::overwritePointAtTime(TimeFrameIndex const time, Point2D<float> const point, bool notify) {
1✔
59
    _data[time] = {point};
1✔
60
    if (_identity_registry) {
1✔
61
        _entity_ids_by_time[time] = {
×
62
            _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, 0)
×
63
        };
×
64
    } else {
65
        _entity_ids_by_time[time] = {0};
1✔
66
    }
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) {
109✔
73
    _data[time] = points;
109✔
74
    _entity_ids_by_time[time].clear();
109✔
75
    _entity_ids_by_time[time].reserve(points.size());
109✔
76
    for (int i = 0; i < static_cast<int>(points.size()); ++i) {
471✔
77
        if (_identity_registry) {
362✔
78
            _entity_ids_by_time[time].push_back(
×
79
                _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, i)
×
80
            );
81
        } else {
82
            _entity_ids_by_time[time].push_back(0);
362✔
83
        }
84
    }
85
    if (notify) {
109✔
86
        notifyObservers();
109✔
87
    }
88
}
109✔
89

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

99
    for (std::size_t i = 0; i < times.size(); i++) {
3✔
100
        _data[times[i]] = points[i];
2✔
101
        _entity_ids_by_time[times[i]].clear();
2✔
102
        _entity_ids_by_time[times[i]].reserve(points[i].size());
2✔
103
        for (int j = 0; j < static_cast<int>(points[i].size()); ++j) {
5✔
104
            if (_identity_registry) {
3✔
105
                _entity_ids_by_time[times[i]].push_back(
×
106
                    _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, times[i], j)
×
107
                );
108
            } else {
109
                _entity_ids_by_time[times[i]].push_back(0);
3✔
110
            }
111
        }
112
    }
113
    if (notify) {
1✔
114
        notifyObservers();
1✔
115
    }
116
}
117

118
void PointData::addAtTime(TimeFrameIndex const time, Point2D<float> const point, bool notify) {
145✔
119
    add_at_time(time, point, _data);
145✔
120
    int local_index = static_cast<int>(_data[time].size()) - 1;
145✔
121
    if (_identity_registry) {
145✔
122
        EntityId id = _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, local_index);
×
123
        _entity_ids_by_time[time].push_back(id);
×
124
    } else {
125
        _entity_ids_by_time[time].push_back(0);
145✔
126
    }
127

128
    if (notify) {
145✔
129
        notifyObservers();
91✔
130
    }
131
}
145✔
132

133
void PointData::addPointsAtTime(TimeFrameIndex const time, std::vector<Point2D<float>> const & points, bool notify) {
179✔
134
    _data[time].insert(_data[time].end(), points.begin(), points.end());
179✔
135
    int start_index = static_cast<int>(_entity_ids_by_time[time].size());
179✔
136
    for (int i = 0; i < static_cast<int>(points.size()); ++i) {
491✔
137
        if (_identity_registry) {
312✔
138
            _entity_ids_by_time[time].push_back(
×
139
                _identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, time, start_index + i)
×
140
            );
141
        } else {
142
            _entity_ids_by_time[time].push_back(0);
312✔
143
        }
144
    }
145
    
146
    if (notify) {
179✔
147
        notifyObservers();
146✔
148
    }
149
}
179✔
150

151
// ========== Getters ==========
152

153
std::vector<Point2D<float>> const & PointData::getAtTime(TimeFrameIndex const time) const {
133✔
154
    return get_at_time(time, _data, _empty);
133✔
155
}
156

157
std::vector<Point2D<float>> const & PointData::getAtTime(TimeFrameIndex const time, 
17✔
158
                                                        TimeFrame const * source_timeframe,
159
                                                        TimeFrame const * target_timeframe) const {
160
    return get_at_time(time, _data, _empty, source_timeframe, target_timeframe);
17✔
161
}
162

163
std::size_t PointData::getMaxPoints() const {
1✔
164
    std::size_t max_points = 0;
1✔
165
    for (auto const & [time, points] : _data) {
3✔
166
        max_points = std::max(max_points, points.size());
2✔
167
    }
168
    return max_points;
1✔
169
}
170

171
// ========== Image Size ==========
172

173
void PointData::changeImageSize(ImageSize const & image_size) {
3✔
174
    if (_image_size.width == -1 || _image_size.height == -1) {
3✔
175
        std::cout << "No size set for current image. "
176
                  << " Please set a valid image size before trying to scale" << std::endl;
1✔
177
        _image_size = image_size; // Set the image size if it wasn't set before
1✔
178
        return;
1✔
179
    }
180

181
    if (_image_size.width == image_size.width && _image_size.height == image_size.height) {
2✔
182
        std::cout << "Image size is the same. No need to scale" << std::endl;
1✔
183
        return;
1✔
184
    }
185

186
    float const scale_x = static_cast<float>(image_size.width) / static_cast<float>(_image_size.width);
1✔
187
    float const scale_y = static_cast<float>(image_size.height) / static_cast<float>(_image_size.height);
1✔
188

189
    for (auto & [time, points] : _data) {
2✔
190
        for (auto & point : points) {
3✔
191
            point.x *= scale_x;
2✔
192
            point.y *= scale_y;
2✔
193
        }
194
    }
195
    _image_size = image_size;
1✔
196
}
197

198
// ========== Copy and Move ==========
199

200
std::size_t PointData::copyTo(PointData& target, TimeFrameInterval const & interval, bool notify) const {
6✔
201
    if (interval.start > interval.end) {
6✔
202
        std::cerr << "PointData::copyTo: interval start (" << interval.start.getValue() 
1✔
203
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
1✔
204
        return 0;
1✔
205
    }
206

207
    std::size_t total_points_copied = 0;
5✔
208

209
    // Iterate through all times in the source data within the interval
210
    for (auto const & [time, points] : _data) {
25✔
211
        if (time >= interval.start && time <= interval.end && !points.empty()) {
20✔
212
            target.addPointsAtTime(time, points, false); // Don't notify for each operation
8✔
213
            total_points_copied += points.size();
8✔
214
        }
215
    }
216

217
    // Notify observer only once at the end if requested
218
    if (notify && total_points_copied > 0) {
5✔
219
        target.notifyObservers();
4✔
220
    }
221

222
    return total_points_copied;
5✔
223
}
224

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

228
    // Copy points for each specified time
229
    for (TimeFrameIndex time : times) {
20✔
230
        auto it = _data.find(time);
15✔
231
        if (it != _data.end() && !it->second.empty()) {
15✔
232
            target.addPointsAtTime(time, it->second, false); // Don't notify for each operation
10✔
233
            total_points_copied += it->second.size();
10✔
234
        }
235
    }
236

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

242
    return total_points_copied;
5✔
243
}
244

245
std::size_t PointData::moveTo(PointData& target, TimeFrameInterval const & interval, bool notify) {
4✔
246
    if (interval.start > interval.end) {
4✔
247
        std::cerr << "PointData::moveTo: interval start (" << interval.start.getValue() 
×
248
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
249
        return 0;
×
250
    }
251

252
    std::size_t total_points_moved = 0;
4✔
253
    std::vector<TimeFrameIndex> times_to_clear;
4✔
254

255
    // First, copy all points in the interval to target
256
    for (auto const & [time, points] : _data) {
20✔
257
        if (time >= interval.start && time <= interval.end && !points.empty()) {
16✔
258
            target.addPointsAtTime(time, points, false); // Don't notify for each operation
8✔
259
            total_points_moved += points.size();
8✔
260
            times_to_clear.push_back(time);
8✔
261
        }
262
    }
263

264
    // Then, clear all the times from source
265
    for (TimeFrameIndex time : times_to_clear) {
12✔
266
        (void) clearAtTime(time, false); // Don't notify for each operation
8✔
267
    }
268

269
    // Notify observers only once at the end if requested
270
    if (notify && total_points_moved > 0) {
4✔
271
        target.notifyObservers();
4✔
272
        notifyObservers();
4✔
273
    }
274

275
    return total_points_moved;
4✔
276
}
4✔
277

278
std::size_t PointData::moveTo(PointData& target, std::vector<TimeFrameIndex> const& times, bool notify) {
2✔
279
    std::size_t total_points_moved = 0;
2✔
280
    std::vector<TimeFrameIndex> times_to_clear;
2✔
281

282
    // First, copy points for each specified time to target
283
    for (TimeFrameIndex time : times) {
7✔
284
        auto it = _data.find(time);
5✔
285
        if (it != _data.end() && !it->second.empty()) {
5✔
286
            target.addPointsAtTime(time, it->second, false); // Don't notify for each operation
5✔
287
            total_points_moved += it->second.size();
5✔
288
            times_to_clear.push_back(time);
5✔
289
        }
290
    }
291

292
    // Then, clear all the times from source
293
    for (TimeFrameIndex time : times_to_clear) {
7✔
294
        (void) clearAtTime(time, false); // Don't notify for each operation
5✔
295
    }
296

297
    // Notify observers only once at the end if requested
298
    if (notify && total_points_moved > 0) {
2✔
299
        target.notifyObservers();
2✔
300
        notifyObservers();
2✔
301
    }
302

303
    return total_points_moved;
2✔
304
}
2✔
305

306
void PointData::setIdentityContext(std::string const & data_key, EntityRegistry * registry) {
1✔
307
    _identity_data_key = data_key;
1✔
308
    _identity_registry = registry;
1✔
309
}
1✔
310

311
void PointData::rebuildAllEntityIds() {
1✔
312
    if (!_identity_registry) {
1✔
313
        // Clear to placeholder zeros to maintain alignment
314
        for (auto & [t, pts] : _data) {
×
315
            _entity_ids_by_time[t].assign(pts.size(), 0);
×
316
        }
317
        return;
×
318
    }
319
    for (auto & [t, pts] : _data) {
4✔
320
        auto & ids = _entity_ids_by_time[t];
3✔
321
        ids.clear();
3✔
322
        ids.reserve(pts.size());
3✔
323
        for (int i = 0; i < static_cast<int>(pts.size()); ++i) {
12✔
324
            ids.push_back(_identity_registry->ensureId(_identity_data_key, EntityKind::PointEntity, t, i));
9✔
325
        }
326
    }
327
}
328

329
std::vector<EntityId> const & PointData::getEntityIdsAtTime(TimeFrameIndex time) const {
4✔
330
    auto it = _entity_ids_by_time.find(time);
4✔
331
    if (it == _entity_ids_by_time.end()) {
4✔
332
        static const std::vector<EntityId> kEmpty;
×
333
        return kEmpty;
×
334
    }
335
    return it->second;
4✔
336
}
337

338
std::vector<EntityId> PointData::getAllEntityIds() const {
1✔
339
    std::vector<EntityId> out;
1✔
340
    for (auto const & [t, ids] : _entity_ids_by_time) {
4✔
341
        out.insert(out.end(), ids.begin(), ids.end());
3✔
342
    }
343
    return out;
1✔
344
}
×
345

346
// ========== Entity Lookup Methods ==========
347

348
std::optional<Point2D<float>> PointData::getPointByEntityId(EntityId entity_id) const {
8✔
349
    if (!_identity_registry) {
8✔
350
        return std::nullopt;
×
351
    }
352
    
353
    auto descriptor = _identity_registry->get(entity_id);
8✔
354
    if (!descriptor || descriptor->kind != EntityKind::PointEntity || descriptor->data_key != _identity_data_key) {
8✔
355
        return std::nullopt;
×
356
    }
357
    
358
    TimeFrameIndex time{descriptor->time_value};
8✔
359
    int local_index = descriptor->local_index;
8✔
360
    
361
    auto time_it = _data.find(time);
8✔
362
    if (time_it == _data.end()) {
8✔
363
        return std::nullopt;
×
364
    }
365
    
366
    if (local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
8✔
367
        return std::nullopt;
×
368
    }
369
    
370
    return time_it->second[static_cast<size_t>(local_index)];
8✔
371
}
8✔
372

373
std::optional<std::pair<TimeFrameIndex, int>> PointData::getTimeAndIndexByEntityId(EntityId entity_id) const {
4✔
374
    if (!_identity_registry) {
4✔
375
        return std::nullopt;
×
376
    }
377
    
378
    auto descriptor = _identity_registry->get(entity_id);
4✔
379
    if (!descriptor || descriptor->kind != EntityKind::PointEntity || descriptor->data_key != _identity_data_key) {
4✔
380
        return std::nullopt;
×
381
    }
382
    
383
    TimeFrameIndex time{descriptor->time_value};
4✔
384
    int local_index = descriptor->local_index;
4✔
385
    
386
    // Verify the time and index are valid
387
    auto time_it = _data.find(time);
4✔
388
    if (time_it == _data.end() || local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
4✔
389
        return std::nullopt;
×
390
    }
391
    
392
    return std::make_pair(time, local_index);
4✔
393
}
4✔
394

395
std::vector<std::pair<EntityId, Point2D<float>>> PointData::getPointsByEntityIds(std::vector<EntityId> const & entity_ids) const {
1✔
396
    std::vector<std::pair<EntityId, Point2D<float>>> result;
1✔
397
    result.reserve(entity_ids.size());
1✔
398
    
399
    for (EntityId entity_id : entity_ids) {
4✔
400
        auto point = getPointByEntityId(entity_id);
3✔
401
        if (point) {
3✔
402
            result.emplace_back(entity_id, *point);
3✔
403
        }
404
    }
405
    
406
    return result;
1✔
407
}
×
408

409
std::vector<std::tuple<EntityId, TimeFrameIndex, int>> PointData::getTimeInfoByEntityIds(std::vector<EntityId> const & entity_ids) const {
1✔
410
    std::vector<std::tuple<EntityId, TimeFrameIndex, int>> result;
1✔
411
    result.reserve(entity_ids.size());
1✔
412
    
413
    for (EntityId entity_id : entity_ids) {
4✔
414
        auto time_info = getTimeAndIndexByEntityId(entity_id);
3✔
415
        if (time_info) {
3✔
416
            result.emplace_back(entity_id, time_info->first, time_info->second);
3✔
417
        }
418
    }
419
    
420
    return result;
1✔
421
}
×
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