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

paulmthompson / WhiskerToolbox / 18166700002

01 Oct 2025 03:09PM UTC coverage: 71.132% (-0.04%) from 71.174%
18166700002

push

github

paulmthompson
clang format

61 of 61 new or added lines in 3 files covered. (100.0%)

20 existing lines in 3 files now uncovered.

47096 of 66209 relevant lines covered (71.13%)

1084.78 hits per line

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

42.35
/src/DataManager/Lines/Line_Data.cpp
1
#include "Line_Data.hpp"
2

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

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

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

13
LineData::LineData(std::map<TimeFrameIndex, std::vector<Line2D>> const & data) {
41✔
14
    // Convert old format to new format
15
    for (auto const & [time, lines] : data) {
96✔
16
        _data[time].reserve(lines.size());
55✔
17
        for (auto const & line : lines) {
118✔
18
            _data[time].emplace_back(line, 0); // EntityId will be 0 initially
63✔
19
        }
20
    }
21
}
41✔
22

23
// ========== Setters ==========
24

25
bool LineData::clearAtTime(TimeFrameIndex const time, bool notify) {
×
26
    auto it = _data.find(time);
×
27
    if (it != _data.end()) {
×
28
        _data.erase(it);
×
29
        if (notify) {
×
30
            notifyObservers();
×
31
        }
32
        return true;
×
33
    }
34
    return false;
×
35
}
36

37
bool LineData::clearAtTime(TimeFrameIndex const time, int const line_id, bool notify) {
×
38
    auto it = _data.find(time);
×
39
    if (it != _data.end() && static_cast<size_t>(line_id) < it->second.size()) {
×
40
        it->second.erase(it->second.begin() + static_cast<long int>(line_id));
×
41
        if (it->second.empty()) {
×
42
            _data.erase(it);
×
43
        }
44
        if (notify) {
×
45
            notifyObservers();
×
46
        }
47
        return true;
×
48
    }
49
    return false;
×
50
}
51

52
void LineData::addAtTime(TimeFrameIndex const time, std::vector<float> const & x, std::vector<float> const & y, bool notify) {
481✔
53
    Line2D new_line;
481✔
54
    for (size_t i = 0; i < std::min(x.size(), y.size()); ++i) {
6,222✔
55
        new_line.push_back(Point2D<float>{x[i], y[i]});
5,741✔
56
    }
57
    
58
    int const local_index = static_cast<int>(_data[time].size());
481✔
59
    EntityId entity_id = 0;
481✔
60
    if (_identity_registry) {
481✔
61
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index);
×
62
    }
63
    
64
    _data[time].emplace_back(std::move(new_line), entity_id);
481✔
65

66
    if (notify) {
481✔
67
        notifyObservers();
101✔
68
    }
69
}
962✔
70

71
void LineData::addAtTime(TimeFrameIndex const time, std::vector<Point2D<float>> const & line, bool notify) {
102✔
72
    addAtTime(time, Line2D(line), notify);
102✔
73
}
102✔
74

75
void LineData::addAtTime(TimeFrameIndex const time, Line2D const & line, bool notify) {
2,054✔
76
    int const local_index = static_cast<int>(_data[time].size());
2,054✔
77
    EntityId entity_id = 0;
2,054✔
78
    if (_identity_registry) {
2,054✔
79
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index);
1,600✔
80
    }
81
    
82
    _data[time].emplace_back(line, entity_id);
2,054✔
83

84
    if (notify) {
2,054✔
85
        notifyObservers();
351✔
86
    }
87
}
2,054✔
88

89
void LineData::addPointToLine(TimeFrameIndex const time, int const line_id, Point2D<float> point, bool notify) {
×
90
    if (static_cast<size_t>(line_id) < _data[time].size()) {
×
91
        _data[time][static_cast<size_t>(line_id)].line.push_back(point);
×
92
    } else {
93
        std::cerr << "LineData::addPointToLine: line_id out of range" << std::endl;
×
94
        EntityId entity_id = 0;
×
95
        if (_identity_registry) {
×
96
            int const local_index = static_cast<int>(_data[time].size());
×
97
            entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index);
×
98
        }
99
        _data[time].emplace_back(Line2D{point}, entity_id);
×
100
    }
101

102
    if (notify) {
×
103
        notifyObservers();
×
104
    }
105
}
×
106

107
void LineData::addPointToLineInterpolate(TimeFrameIndex const time, int const line_id, Point2D<float> point, bool notify) {
×
108
    if (static_cast<size_t>(line_id) >= _data[time].size()) {
×
109
        std::cerr << "LineData::addPointToLineInterpolate: line_id out of range" << std::endl;
×
110
        EntityId entity_id = 0;
×
111
        if (_identity_registry) {
×
112
            int const local_index = static_cast<int>(_data[time].size());
×
113
            entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index);
×
114
        }
115
        _data[time].emplace_back(Line2D{}, entity_id);
×
116
    }
117

118
    Line2D & line = _data[time][static_cast<size_t>(line_id)].line;
×
119
    if (!line.empty()) {
×
120
        Point2D<float> const last_point = line.back();
×
121
        float const distance = std::sqrt(std::pow(point.x - last_point.x, 2.0f) + std::pow(point.y - last_point.y, 2.0f));
×
122
        int const n = static_cast<int>(distance / 2.0f);
×
123
        for (int i = 1; i <= n; ++i) {
×
124
            float const t = static_cast<float>(i) / static_cast<float>(n + 1);
×
125
            float const interp_x = last_point.x + t * (point.x - last_point.x);
×
126
            float const interp_y = last_point.y + t * (point.y - last_point.y);
×
127
            line.push_back(Point2D<float>{interp_x, interp_y});
×
128
        }
129
    }
130
    line.push_back(point);
×
131
    // Note: smooth_line function needs to be implemented or included
132
    // smooth_line(line);
133

134
    if (notify) {
×
135
        notifyObservers();
×
136
    }
137
}
×
138

139
// ========== Getters ==========
140

141
std::vector<Line2D> const & LineData::getAtTime(TimeFrameIndex const time) const {
887✔
142
    // This method needs to return a reference to a vector of Line2D objects
143
    // Since we now store LineEntry objects, we need to create a temporary vector
144
    // We'll use a member variable to store the converted data to maintain reference stability
145
    
146
    auto it = _data.find(time);
887✔
147
    if (it == _data.end()) {
887✔
148
        return _empty;
25✔
149
    }
150
    
151
    // Use a mutable member variable to store the converted lines
152
    // This is not thread-safe but maintains API compatibility
153
    _temp_lines.clear();
862✔
154
    _temp_lines.reserve(it->second.size());
862✔
155
    for (auto const & entry : it->second) {
2,360✔
156
        _temp_lines.push_back(entry.line);
1,498✔
157
    }
158
    
159
    return _temp_lines;
862✔
160
}
161

162
std::vector<Line2D> const & LineData::getAtTime(TimeFrameIndex const time, 
205✔
163
                                                TimeFrame const * source_timeframe,
164
                                                TimeFrame const * line_timeframe) const {
165
    // Convert time if needed
166
    TimeFrameIndex converted_time = time;
205✔
167
    if (source_timeframe && line_timeframe && source_timeframe != line_timeframe) {
205✔
168
        auto time_value = source_timeframe->getTimeAtIndex(time);
×
169
        converted_time = line_timeframe->getIndexAtTime(static_cast<float>(time_value));
×
170
    }
171
    
172
    return getAtTime(converted_time);
410✔
173
}
174

175
std::vector<EntityId> const & LineData::getEntityIdsAtTime(TimeFrameIndex const time) const {
2,091✔
176
    // Similar to getAtTime, we need to create a temporary vector
177
    auto it = _data.find(time);
2,091✔
178
    if (it == _data.end()) {
2,091✔
179
        return _empty_entity_ids;
9✔
180
    }
181
    
182
    _temp_entity_ids.clear();
2,082✔
183
    _temp_entity_ids.reserve(it->second.size());
2,082✔
184
    for (auto const & entry : it->second) {
6,189✔
185
        _temp_entity_ids.push_back(entry.entity_id);
4,107✔
186
    }
187
    
188
    return _temp_entity_ids;
2,082✔
189
}
190

191
std::vector<EntityId> const & LineData::getEntityIdsAtTime(TimeFrameIndex const time,
137✔
192
                                                           TimeFrame const * source_timeframe,
193
                                                           TimeFrame const * line_timeframe) const {
194
    // Convert time if needed
195
    TimeFrameIndex converted_time = time;
137✔
196
    if (source_timeframe && line_timeframe && source_timeframe != line_timeframe) {
137✔
197
        auto time_value = source_timeframe->getTimeAtIndex(time);
×
198
        converted_time = line_timeframe->getIndexAtTime(static_cast<float>(time_value));
×
199
    }
200
    
201
    return getEntityIdsAtTime(converted_time);
274✔
202
}
203

204
std::vector<EntityId> LineData::getAllEntityIds() const {
39✔
205
    std::vector<EntityId> out;
39✔
206
    for (auto const & [t, entries] : _data) {
1,318✔
207
        (void)t;
208
        for (auto const & entry : entries) {
3,812✔
209
            out.push_back(entry.entity_id);
2,533✔
210
        }
211
    }
212
    return out;
39✔
213
}
×
214

215
// ========== Entity Lookup Methods ==========
216

217
std::optional<Line2D> LineData::getLineByEntityId(EntityId entity_id) const {
3,216✔
218
    if (!_identity_registry) {
3,216✔
219
        return std::nullopt;
×
220
    }
221
    
222
    auto descriptor = _identity_registry->get(entity_id);
3,216✔
223
    if (!descriptor || descriptor->kind != EntityKind::LineEntity || descriptor->data_key != _identity_data_key) {
3,216✔
224
        return std::nullopt;
×
225
    }
226
    
227
    TimeFrameIndex time{descriptor->time_value};
3,216✔
228
    int local_index = descriptor->local_index;
3,216✔
229
    
230
    auto time_it = _data.find(time);
3,216✔
231
    if (time_it == _data.end()) {
3,216✔
232
        return std::nullopt;
×
233
    }
234
    
235
    if (local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
3,216✔
236
        return std::nullopt;
×
237
    }
238
    
239
    return time_it->second[static_cast<size_t>(local_index)].line;
3,216✔
240
}
3,216✔
241

242
std::optional<std::pair<TimeFrameIndex, int>> LineData::getTimeAndIndexByEntityId(EntityId entity_id) const {
89✔
243
    if (!_identity_registry) {
89✔
UNCOV
244
        return std::nullopt;
×
245
    }
246
    
247
    auto descriptor = _identity_registry->get(entity_id);
89✔
248
    if (!descriptor || descriptor->kind != EntityKind::LineEntity || descriptor->data_key != _identity_data_key) {
89✔
249
        return std::nullopt;
×
250
    }
251
    
252
    TimeFrameIndex time{descriptor->time_value};
89✔
253
    int local_index = descriptor->local_index;
89✔
254
    
255
    // Verify the time and index are valid
256
    auto time_it = _data.find(time);
89✔
257
    if (time_it == _data.end() || local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
89✔
258
        return std::nullopt;
×
259
    }
260
    
261
    return std::make_pair(time, local_index);
89✔
262
}
89✔
263

264
std::vector<std::pair<EntityId, Line2D>> LineData::getLinesByEntityIds(std::vector<EntityId> const & entity_ids) const {
3✔
265
    std::vector<std::pair<EntityId, Line2D>> results;
3✔
266
    results.reserve(entity_ids.size());
3✔
267
    
268
    for (EntityId entity_id : entity_ids) {
10✔
269
        auto line = getLineByEntityId(entity_id);
7✔
270
        if (line.has_value()) {
7✔
271
            results.emplace_back(entity_id, std::move(line.value()));
7✔
272
        }
273
    }
7✔
274
    
275
    return results;
3✔
276
}
×
277

278
std::vector<std::tuple<EntityId, TimeFrameIndex, int>> LineData::getTimeInfoByEntityIds(std::vector<EntityId> const & entity_ids) const {
2✔
279
    std::vector<std::tuple<EntityId, TimeFrameIndex, int>> results;
2✔
280
    results.reserve(entity_ids.size());
2✔
281
    
282
    for (EntityId entity_id : entity_ids) {
6✔
283
        auto time_info = getTimeAndIndexByEntityId(entity_id);
4✔
284
        if (time_info.has_value()) {
4✔
285
            results.emplace_back(entity_id, time_info->first, time_info->second);
4✔
286
        }
287
    }
288
    
289
    return results;
2✔
290
}
×
291

292
// ========== Image Size ==========
293

294
void LineData::changeImageSize(ImageSize const & image_size)
×
295
{
296
    if (_image_size.width == -1 || _image_size.height == -1) {
×
297
        std::cout << "No size set for current image. "
298
                  << " Please set a valid image size before trying to scale" << std::endl;
×
299
    }
300

301
    if (_image_size.width == image_size.width && _image_size.height == image_size.height) {
×
302
        std::cout << "Image size is the same. No need to scale" << std::endl;
×
303
        return;
×
304
    }
305

306
    float const scale_x = static_cast<float>(image_size.width) / static_cast<float>(_image_size.width);
×
307
    float const scale_y = static_cast<float>(image_size.height) / static_cast<float>(_image_size.height);
×
308

309
    for (auto & [time, entries] : _data) {
×
310
        for (auto & entry : entries) {
×
311
            for (auto & point : entry.line) {
×
312
                point.x *= scale_x;
×
313
                point.y *= scale_y;
×
314
            }
315
        }
316
    }
317
    _image_size = image_size;
×
318
}
319

320
void LineData::setIdentityContext(std::string const & data_key, EntityRegistry * registry) {
313✔
321
    _identity_data_key = data_key;
313✔
322
    _identity_registry = registry;
313✔
323
}
313✔
324

325
void LineData::rebuildAllEntityIds() {
313✔
326
    if (!_identity_registry) {
313✔
327
        for (auto & [t, entries] : _data) {
×
328
            for (auto & entry : entries) {
×
329
                entry.entity_id = 0;
×
330
            }
331
        }
332
        return;
×
333
    }
334
    
335
    for (auto & [t, entries] : _data) {
1,243✔
336
        for (int i = 0; i < static_cast<int>(entries.size()); ++i) {
2,149✔
337
            entries[static_cast<size_t>(i)].entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, t, i);
1,219✔
338
        }
339
    }
340
}
341

342
// ========== Copy and Move ==========
343

344
std::size_t LineData::copyTo(LineData& target, TimeFrameInterval const & interval, bool notify) const {
×
345
    if (interval.start > interval.end) {
×
346
        std::cerr << "LineData::copyTo: interval start (" << interval.start.getValue() 
×
347
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
348
        return 0;
×
349
    }
350

351
    std::size_t total_lines_copied = 0;
×
352

353
    // Iterate through all times in the source data within the interval
354
    for (auto const & [time, entries] : _data) {
×
355
        if (time >= interval.start && time <= interval.end && !entries.empty()) {
×
356
            for (auto const& entry : entries) {
×
357
                target.addAtTime(time, entry.line, false); // Don't notify for each operation
×
358
                total_lines_copied++;
×
359
            }
360
        }
361
    }
362

363
    // Notify observer only once at the end if requested
364
    if (notify && total_lines_copied > 0) {
×
365
        target.notifyObservers();
×
366
    }
367

368
    return total_lines_copied;
×
369
}
370

371
std::size_t LineData::copyTo(LineData& target, std::vector<TimeFrameIndex> const& times, bool notify) const {
×
372
    std::size_t total_lines_copied = 0;
×
373

374
    // Copy lines for each specified time
375
    for (TimeFrameIndex time : times) {
×
376
        auto it = _data.find(time);
×
377
        if (it != _data.end() && !it->second.empty()) {
×
378
            for (auto const& entry : it->second) {
×
379
                target.addAtTime(time, entry.line, false); // Don't notify for each operation
×
380
                total_lines_copied++;
×
381
            }
382
        }
383
    }
384

385
    // Notify observer only once at the end if requested
386
    if (notify && total_lines_copied > 0) {
×
387
        target.notifyObservers();
×
388
    }
389

390
    return total_lines_copied;
×
391
}
392

393
std::size_t LineData::moveTo(LineData& target, TimeFrameInterval const & interval, bool notify) {
×
394
    if (interval.start > interval.end) {
×
395
        std::cerr << "LineData::moveTo: interval start (" << interval.start.getValue() 
×
396
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
397
        return 0;
×
398
    }
399

400
    std::size_t total_lines_moved = 0;
×
401
    std::vector<TimeFrameIndex> times_to_clear;
×
402

403
    // First, copy all lines in the interval to target
404
    for (auto const & [time, entries] : _data) {
×
405
        if (time >= interval.start && time <= interval.end && !entries.empty()) {
×
406
            for (auto const& entry : entries) {
×
407
                target.addAtTime(time, entry.line, false); // Don't notify for each operation
×
408
                total_lines_moved++;
×
409
            }
410
            times_to_clear.push_back(time);
×
411
        }
412
    }
413

414
    // Then, clear all the times from source
415
    for (TimeFrameIndex time : times_to_clear) {
×
416
        (void)clearAtTime(time, false); // Don't notify for each operation
×
417
    }
418

419
    // Notify observers only once at the end if requested
420
    if (notify && total_lines_moved > 0) {
×
421
        target.notifyObservers();
×
422
        notifyObservers();
×
423
    }
424

425
    return total_lines_moved;
×
426
}
×
427

428
std::size_t LineData::moveTo(LineData& target, std::vector<TimeFrameIndex> const & times, bool notify) {
×
429
    std::size_t total_lines_moved = 0;
×
430
    std::vector<TimeFrameIndex> times_to_clear;
×
431

432
    // First, copy lines for each specified time to target
433
    for (TimeFrameIndex time : times) {
×
434
        auto it = _data.find(time);
×
435
        if (it != _data.end() && !it->second.empty()) {
×
436
            for (auto const& entry : it->second) {
×
437
                target.addAtTime(time, entry.line, false); // Don't notify for each operation
×
438
                total_lines_moved++;
×
439
            }
440
            times_to_clear.push_back(time);
×
441
        }
442
    }
443

444
    // Then, clear all the times from source
445
    for (TimeFrameIndex time : times_to_clear) {
×
446
        (void)clearAtTime(time, false); // Don't notify for each operation
×
447
    }
448

449
    // Notify observers only once at the end if requested
450
    if (notify && total_lines_moved > 0) {
×
451
        target.notifyObservers();
×
452
        notifyObservers();
×
453
    }
454

455
    return total_lines_moved;
×
456
}
×
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