• 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

37.72
/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)
39✔
14
    : _data(data)
39✔
15
{
16

17
}
39✔
18

19
// ========== Setters ==========
20

21
bool LineData::clearAtTime(TimeFrameIndex const time, bool notify) {
×
22

23
    if (clear_at_time(time, _data)) {
×
24
        _entity_ids_by_time.erase(time);
×
25
        if (notify) {
×
26
            notifyObservers();
×
27
        }
28
        return true;
×
29
    }
30
    return false;   
×
31
}
32

33
bool LineData::clearAtTime(TimeFrameIndex const time, int const line_id, bool notify) {
×
34

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

53
void LineData::addAtTime(TimeFrameIndex const time, std::vector<float> const & x, std::vector<float> const & y, bool notify) {
236✔
54

55
    auto new_line = create_line(x, y);
236✔
56
    add_at_time(time, new_line, _data);
236✔
57

58
    int const local_index = static_cast<int>(_data[time].size()) - 1;
236✔
59
    if (_identity_registry) {
236✔
60
        _entity_ids_by_time[time].push_back(
×
61
            _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index)
×
62
        );
63
    } else {
64
        _entity_ids_by_time[time].push_back(0);
236✔
65
    }
66

67
    if (notify) {
236✔
68
        notifyObservers();
101✔
69
    }
70
}
472✔
71

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

76
void LineData::addAtTime(TimeFrameIndex const time, Line2D const & line, bool notify) {
429✔
77
    add_at_time(time, line, _data);
429✔
78

79
    int const local_index = static_cast<int>(_data[time].size()) - 1;
429✔
80
    if (_identity_registry) {
429✔
81
        _entity_ids_by_time[time].push_back(
×
82
            _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index)
×
83
        );
84
    } else {
85
        _entity_ids_by_time[time].push_back(0);
429✔
86
    }
87

88
    if (notify) {
429✔
89
        notifyObservers();
329✔
90
    }
91
}
429✔
92

93
void LineData::addPointToLine(TimeFrameIndex const time, int const line_id, Point2D<float> point, bool notify) {
×
94

95
    if (static_cast<size_t>(line_id) < _data[time].size()) {
×
96
        _data[time][static_cast<size_t>(line_id)].push_back(point);
×
97
    } else {
98
        std::cerr << "LineData::addPointToLine: line_id out of range" << std::endl;
×
99
        _data[time].emplace_back();
×
100
        _data[time].back().push_back(point);
×
101
    }
102

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

108
void LineData::addPointToLineInterpolate(TimeFrameIndex const time, int const line_id, Point2D<float> point, bool notify) {
×
109

110
    if (static_cast<size_t>(line_id) >= _data[time].size()) {
×
111
        std::cerr << "LineData::addPointToLineInterpolate: line_id out of range" << std::endl;
×
112
        _data[time].emplace_back();
×
113
    }
114

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

130
    if (notify) {
×
131
        notifyObservers();
×
132
    }
133
}
×
134

135
// ========== Getters ==========
136

137
std::vector<Line2D> const & LineData::getAtTime(TimeFrameIndex const time) const {
176✔
138
    return get_at_time(time, _data, _empty);
176✔
139
}
140

141
std::vector<Line2D> const & LineData::getAtTime(TimeFrameIndex const time, 
102✔
142
                                                TimeFrame const * source_timeframe,
143
                                                TimeFrame const * line_timeframe) const {
144

145
    return get_at_time(time, _data, _empty, source_timeframe, line_timeframe);
102✔
146
}
147

148
std::vector<EntityId> const & LineData::getEntityIdsAtTime(TimeFrameIndex const time) const {
54✔
149
    auto it = _entity_ids_by_time.find(time);
54✔
150
    if (it == _entity_ids_by_time.end()) {
54✔
151
        static const std::vector<EntityId> kEmpty;
×
152
        return kEmpty;
×
153
    }
154
    return it->second;
54✔
155
}
156

157
std::vector<EntityId> LineData::getAllEntityIds() const {
25✔
158
    std::vector<EntityId> out;
25✔
159
    for (auto const & [t, ids] : _entity_ids_by_time) {
100✔
160
        (void)t;
161
        out.insert(out.end(), ids.begin(), ids.end());
75✔
162
    }
163
    return out;
25✔
164
}
×
165

166
// ========== Entity Lookup Methods ==========
167

168
std::optional<Line2D> LineData::getLineByEntityId(EntityId entity_id) const {
7✔
169
    if (!_identity_registry) {
7✔
170
        return std::nullopt;
×
171
    }
172
    
173
    auto descriptor = _identity_registry->get(entity_id);
7✔
174
    if (!descriptor || descriptor->kind != EntityKind::LineEntity || descriptor->data_key != _identity_data_key) {
7✔
175
        return std::nullopt;
×
176
    }
177
    
178
    TimeFrameIndex time{descriptor->time_value};
7✔
179
    int local_index = descriptor->local_index;
7✔
180
    
181
    auto time_it = _data.find(time);
7✔
182
    if (time_it == _data.end()) {
7✔
183
        return std::nullopt;
×
184
    }
185
    
186
    if (local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
7✔
187
        return std::nullopt;
×
188
    }
189
    
190
    return time_it->second[static_cast<size_t>(local_index)];
7✔
191
}
7✔
192

193
std::optional<std::pair<TimeFrameIndex, int>> LineData::getTimeAndIndexByEntityId(EntityId entity_id) const {
4✔
194
    if (!_identity_registry) {
4✔
195
        return std::nullopt;
×
196
    }
197
    
198
    auto descriptor = _identity_registry->get(entity_id);
4✔
199
    if (!descriptor || descriptor->kind != EntityKind::LineEntity || descriptor->data_key != _identity_data_key) {
4✔
200
        return std::nullopt;
×
201
    }
202
    
203
    TimeFrameIndex time{descriptor->time_value};
4✔
204
    int local_index = descriptor->local_index;
4✔
205
    
206
    // Verify the time and index are valid
207
    auto time_it = _data.find(time);
4✔
208
    if (time_it == _data.end() || local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
4✔
209
        return std::nullopt;
×
210
    }
211
    
212
    return std::make_pair(time, local_index);
4✔
213
}
4✔
214

215
std::vector<std::pair<EntityId, Line2D>> LineData::getLinesByEntityIds(std::vector<EntityId> const & entity_ids) const {
3✔
216
    std::vector<std::pair<EntityId, Line2D>> results;
3✔
217
    results.reserve(entity_ids.size());
3✔
218
    
219
    for (EntityId entity_id : entity_ids) {
10✔
220
        auto line = getLineByEntityId(entity_id);
7✔
221
        if (line.has_value()) {
7✔
222
            results.emplace_back(entity_id, std::move(line.value()));
7✔
223
        }
224
    }
7✔
225
    
226
    return results;
3✔
227
}
×
228

229
std::vector<std::tuple<EntityId, TimeFrameIndex, int>> LineData::getTimeInfoByEntityIds(std::vector<EntityId> const & entity_ids) const {
2✔
230
    std::vector<std::tuple<EntityId, TimeFrameIndex, int>> results;
2✔
231
    results.reserve(entity_ids.size());
2✔
232
    
233
    for (EntityId entity_id : entity_ids) {
6✔
234
        auto time_info = getTimeAndIndexByEntityId(entity_id);
4✔
235
        if (time_info.has_value()) {
4✔
236
            results.emplace_back(entity_id, time_info->first, time_info->second);
4✔
237
        }
238
    }
239
    
240
    return results;
2✔
241
}
×
242

243
// ========== Image Size ==========
244

245
void LineData::changeImageSize(ImageSize const & image_size)
×
246
{
247
    if (_image_size.width == -1 || _image_size.height == -1) {
×
248
        std::cout << "No size set for current image. "
249
                  << " Please set a valid image size before trying to scale" << std::endl;
×
250
    }
251

252
    if (_image_size.width == image_size.width && _image_size.height == image_size.height) {
×
253
        std::cout << "Image size is the same. No need to scale" << std::endl;
×
254
        return;
×
255
    }
256

257
    float const scale_x = static_cast<float>(image_size.width) / static_cast<float>(_image_size.width);
×
258
    float const scale_y = static_cast<float>(image_size.height) / static_cast<float>(_image_size.height);
×
259

260
    for (auto & [time, lines] : _data) {
×
261
        for (auto & line : lines) {
×
262
            for (auto & point : line) {
×
263
                point.x *= scale_x;
×
264
                point.y *= scale_y;
×
265
            }
266
        }
267
    }
268
    _image_size = image_size;
×
269

270
}
271

272
void LineData::setIdentityContext(std::string const & data_key, EntityRegistry * registry) {
14✔
273
    _identity_data_key = data_key;
14✔
274
    _identity_registry = registry;
14✔
275
}
14✔
276

277
void LineData::rebuildAllEntityIds() {
14✔
278
    if (!_identity_registry) {
14✔
279
        for (auto & [t, lines] : _data) {
×
280
            _entity_ids_by_time[t].assign(lines.size(), 0);
×
281
        }
282
        return;
×
283
    }
284
    for (auto & [t, lines] : _data) {
51✔
285
        auto & ids = _entity_ids_by_time[t];
37✔
286
        ids.clear();
37✔
287
        ids.reserve(lines.size());
37✔
288
        for (int i = 0; i < static_cast<int>(lines.size()); ++i) {
98✔
289
            ids.push_back(_identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, t, i));
61✔
290
        }
291
    }
292
}
293

294
// ========== Copy and Move ==========
295

296
std::size_t LineData::copyTo(LineData& target, TimeFrameInterval const & interval, bool notify) const {
×
297
    if (interval.start > interval.end) {
×
298
        std::cerr << "LineData::copyTo: interval start (" << interval.start.getValue() 
×
299
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
300
        return 0;
×
301
    }
302

303
    std::size_t total_lines_copied = 0;
×
304

305
    // Iterate through all times in the source data within the interval
306
    for (auto const & [time, lines] : _data) {
×
307
        if (time >= interval.start && time <= interval.end && !lines.empty()) {
×
308
            for (auto const& line : lines) {
×
309
                target.addAtTime(time, line, false); // Don't notify for each operation
×
310
                total_lines_copied++;
×
311
            }
312
        }
313
    }
314

315
    // Notify observer only once at the end if requested
316
    if (notify && total_lines_copied > 0) {
×
317
        target.notifyObservers();
×
318
    }
319

320
    return total_lines_copied;
×
321
}
322

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

326
    // Copy lines for each specified time
327
    for (TimeFrameIndex time : times) {
×
328
        auto it = _data.find(time);
×
329
        if (it != _data.end() && !it->second.empty()) {
×
330
            for (auto const& line : it->second) {
×
331
                target.addAtTime(time, line, false); // Don't notify for each operation
×
332
                total_lines_copied++;
×
333
            }
334
        }
335
    }
336

337
    // Notify observer only once at the end if requested
338
    if (notify && total_lines_copied > 0) {
×
339
        target.notifyObservers();
×
340
    }
341

342
    return total_lines_copied;
×
343
}
344

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

352
    std::size_t total_lines_moved = 0;
×
353
    std::vector<TimeFrameIndex> times_to_clear;
×
354

355
    // First, copy all lines in the interval to target
356
    for (auto const & [time, lines] : _data) {
×
357
        if (time >= interval.start && time <= interval.end && !lines.empty()) {
×
358
            for (auto const& line : lines) {
×
359
                target.addAtTime(time, line, false); // Don't notify for each operation
×
360
                total_lines_moved++;
×
361
            }
362
            times_to_clear.push_back(time);
×
363
        }
364
    }
365

366
    // Then, clear all the times from source
367
    for (TimeFrameIndex time : times_to_clear) {
×
368
        (void)clearAtTime(time, false); // Don't notify for each operation
×
369
    }
370

371
    // Notify observers only once at the end if requested
372
    if (notify && total_lines_moved > 0) {
×
373
        target.notifyObservers();
×
374
        notifyObservers();
×
375
    }
376

377
    return total_lines_moved;
×
378
}
×
379

380
std::size_t LineData::moveTo(LineData& target, std::vector<TimeFrameIndex> const & times, bool notify) {
×
381
    std::size_t total_lines_moved = 0;
×
382
    std::vector<TimeFrameIndex> times_to_clear;
×
383

384
    // First, copy lines for each specified time to target
385
    for (TimeFrameIndex time : times) {
×
386
        auto it = _data.find(time);
×
387
        if (it != _data.end() && !it->second.empty()) {
×
388
            for (auto const& line : it->second) {
×
389
                target.addAtTime(time, line, false); // Don't notify for each operation
×
390
                total_lines_moved++;
×
391
            }
392
            times_to_clear.push_back(time);
×
393
        }
394
    }
395

396
    // Then, clear all the times from source
397
    for (TimeFrameIndex time : times_to_clear) {
×
398
        (void)clearAtTime(time, false); // Don't notify for each operation
×
399
    }
400

401
    // Notify observers only once at the end if requested
402
    if (notify && total_lines_moved > 0) {
×
403
        target.notifyObservers();
×
404
        notifyObservers();
×
405
    }
406

407
    return total_lines_moved;
×
408
}
×
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