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

paulmthompson / WhiskerToolbox / 18477247352

13 Oct 2025 08:18PM UTC coverage: 72.391% (+0.4%) from 71.943%
18477247352

push

github

web-flow
Merge pull request #140 from paulmthompson/kdtree

Jules PR

164 of 287 new or added lines in 3 files covered. (57.14%)

350 existing lines in 9 files now uncovered.

51889 of 71679 relevant lines covered (72.39%)

63071.54 hits per line

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

63.83
/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

UNCOV
23
LineData::LineData(LineData&& other) noexcept
×
UNCOV
24
    : ObserverData(std::move(other)),
×
UNCOV
25
      _data(std::move(other._data)),
×
UNCOV
26
      _image_size(other._image_size),
×
UNCOV
27
      _time_frame(std::move(other._time_frame)),
×
UNCOV
28
      _identity_data_key(std::move(other._identity_data_key)),
×
UNCOV
29
      _identity_registry(other._identity_registry)
×
30
{
UNCOV
31
    other._identity_registry = nullptr;
×
UNCOV
32
}
×
33

34
LineData& LineData::operator=(LineData&& other) noexcept
×
35
{
UNCOV
36
    if (this != &other) {
×
37
        ObserverData::operator=(std::move(other));
×
38
        _data = std::move(other._data);
×
39
        _image_size = other._image_size;
×
40
        _time_frame = std::move(other._time_frame);
×
41
        _identity_data_key = std::move(other._identity_data_key);
×
42
        _identity_registry = other._identity_registry;
×
UNCOV
43
        other._identity_registry = nullptr;
×
44
    }
45
    return *this;
×
46
}
47

48
// ========== Setters ==========
49

50
bool LineData::clearAtTime(TimeFrameIndex const time, bool notify) {
1✔
51
    auto it = _data.find(time);
1✔
52
    if (it != _data.end()) {
1✔
53
        _data.erase(it);
1✔
54
        if (notify) {
1✔
55
            notifyObservers();
1✔
56
        }
57
        return true;
1✔
58
    }
UNCOV
59
    return false;
×
60
}
61

UNCOV
62
bool LineData::clearAtTime(TimeFrameIndex const time, int const line_id, bool notify) {
×
UNCOV
63
    auto it = _data.find(time);
×
UNCOV
64
    if (it != _data.end() && static_cast<size_t>(line_id) < it->second.size()) {
×
UNCOV
65
        it->second.erase(it->second.begin() + static_cast<long int>(line_id));
×
UNCOV
66
        if (it->second.empty()) {
×
UNCOV
67
            _data.erase(it);
×
68
        }
UNCOV
69
        if (notify) {
×
UNCOV
70
            notifyObservers();
×
71
        }
UNCOV
72
        return true;
×
73
    }
UNCOV
74
    return false;
×
75
}
76

77
void LineData::addAtTime(TimeFrameIndex const time, std::vector<float> const & x, std::vector<float> const & y, bool notify) {
624✔
78
    Line2D new_line;
624✔
79
    for (size_t i = 0; i < std::min(x.size(), y.size()); ++i) {
6,824✔
80
        new_line.push_back(Point2D<float>{x[i], y[i]});
6,200✔
81
    }
82
    
83
    int const local_index = static_cast<int>(_data[time].size());
624✔
84
    EntityId entity_id = 0;
624✔
85
    if (_identity_registry) {
624✔
86
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index);
32✔
87
    }
88
    
89
    _data[time].emplace_back(std::move(new_line), entity_id);
624✔
90

91
    if (notify) {
624✔
92
        notifyObservers();
244✔
93
    }
94
}
1,248✔
95

96
void LineData::addAtTime(TimeFrameIndex const time, std::vector<Point2D<float>> const & line, bool notify) {
102✔
97
    addAtTime(time, Line2D(line), notify);
102✔
98
}
102✔
99

100
void LineData::addAtTime(TimeFrameIndex const time, Line2D const & line, bool notify) {
2,487✔
101
    int const local_index = static_cast<int>(_data[time].size());
2,487✔
102
    EntityId entity_id = 0;
2,487✔
103
    if (_identity_registry) {
2,487✔
104
        entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index);
2,006✔
105
    }
106
    
107
    _data[time].emplace_back(line, entity_id);
2,487✔
108

109
    if (notify) {
2,487✔
110
        notifyObservers();
356✔
111
    }
112
}
2,487✔
113

UNCOV
114
void LineData::addPointToLine(TimeFrameIndex const time, int const line_id, Point2D<float> point, bool notify) {
×
115
    if (static_cast<size_t>(line_id) < _data[time].size()) {
×
UNCOV
116
        _data[time][static_cast<size_t>(line_id)].line.push_back(point);
×
117
    } else {
118
        std::cerr << "LineData::addPointToLine: line_id out of range" << std::endl;
×
119
        EntityId entity_id = 0;
×
120
        if (_identity_registry) {
×
121
            int const local_index = static_cast<int>(_data[time].size());
×
122
            entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index);
×
123
        }
124
        _data[time].emplace_back(Line2D{point}, entity_id);
×
125
    }
126

127
    if (notify) {
×
UNCOV
128
        notifyObservers();
×
129
    }
130
}
×
131

UNCOV
132
void LineData::addPointToLineInterpolate(TimeFrameIndex const time, int const line_id, Point2D<float> point, bool notify) {
×
UNCOV
133
    if (static_cast<size_t>(line_id) >= _data[time].size()) {
×
134
        std::cerr << "LineData::addPointToLineInterpolate: line_id out of range" << std::endl;
×
135
        EntityId entity_id = 0;
×
UNCOV
136
        if (_identity_registry) {
×
137
            int const local_index = static_cast<int>(_data[time].size());
×
UNCOV
138
            entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, time, local_index);
×
139
        }
UNCOV
140
        _data[time].emplace_back(Line2D{}, entity_id);
×
141
    }
142

143
    Line2D & line = _data[time][static_cast<size_t>(line_id)].line;
×
UNCOV
144
    if (!line.empty()) {
×
UNCOV
145
        Point2D<float> const last_point = line.back();
×
UNCOV
146
        float const distance = std::sqrt(std::pow(point.x - last_point.x, 2.0f) + std::pow(point.y - last_point.y, 2.0f));
×
UNCOV
147
        int const n = static_cast<int>(distance / 2.0f);
×
UNCOV
148
        for (int i = 1; i <= n; ++i) {
×
UNCOV
149
            float const t = static_cast<float>(i) / static_cast<float>(n + 1);
×
UNCOV
150
            float const interp_x = last_point.x + t * (point.x - last_point.x);
×
UNCOV
151
            float const interp_y = last_point.y + t * (point.y - last_point.y);
×
UNCOV
152
            line.push_back(Point2D<float>{interp_x, interp_y});
×
153
        }
154
    }
UNCOV
155
    line.push_back(point);
×
156
    // Note: smooth_line function needs to be implemented or included
157
    // smooth_line(line);
158

UNCOV
159
    if (notify) {
×
UNCOV
160
        notifyObservers();
×
161
    }
UNCOV
162
}
×
163

164
void LineData::addLineEntryAtTime(TimeFrameIndex const time, Line2D const & line, EntityId entity_id, bool notify) {
6✔
165
    _data[time].emplace_back(line, entity_id);
6✔
166

167
    if (notify) {
6✔
UNCOV
168
        notifyObservers();
×
169
    }
170
}
6✔
171

172
// ========== Getters ==========
173

174
std::vector<Line2D> const & LineData::getAtTime(TimeFrameIndex const time) const {
455✔
175
    // This method needs to return a reference to a vector of Line2D objects
176
    // Since we now store LineEntry objects, we need to create a temporary vector
177
    // We'll use a member variable to store the converted data to maintain reference stability
178
    
179
    auto it = _data.find(time);
455✔
180
    if (it == _data.end()) {
455✔
181
        return _empty;
42✔
182
    }
183
    
184
    // Use a mutable member variable to store the converted lines
185
    // This is not thread-safe but maintains API compatibility
186
    _temp_lines.clear();
413✔
187
    _temp_lines.reserve(it->second.size());
413✔
188
    for (auto const & entry : it->second) {
982✔
189
        _temp_lines.push_back(entry.line);
569✔
190
    }
191
    
192
    return _temp_lines;
413✔
193
}
194

195
std::vector<Line2D> const & LineData::getAtTime(TimeFrameIndex const time, 
205✔
196
                                                TimeFrame const * source_timeframe,
197
                                                TimeFrame const * line_timeframe) const {
198
    // Convert time if needed
199
    TimeFrameIndex converted_time = time;
205✔
200
    if (source_timeframe && line_timeframe && source_timeframe != line_timeframe) {
205✔
UNCOV
201
        auto time_value = source_timeframe->getTimeAtIndex(time);
×
UNCOV
202
        converted_time = line_timeframe->getIndexAtTime(static_cast<float>(time_value));
×
203
    }
204
    
205
    return getAtTime(converted_time);
410✔
206
}
207

208
std::vector<EntityId> const & LineData::getEntityIdsAtTime(TimeFrameIndex const time) const {
1,304✔
209
    // Similar to getAtTime, we need to create a temporary vector
210
    auto it = _data.find(time);
1,304✔
211
    if (it == _data.end()) {
1,304✔
212
        return _empty_entity_ids;
10✔
213
    }
214
    
215
    _temp_entity_ids.clear();
1,294✔
216
    _temp_entity_ids.reserve(it->second.size());
1,294✔
217
    for (auto const & entry : it->second) {
3,823✔
218
        _temp_entity_ids.push_back(entry.entity_id);
2,529✔
219
    }
220
    
221
    return _temp_entity_ids;
1,294✔
222
}
223

224
std::vector<EntityId> const & LineData::getEntityIdsAtTime(TimeFrameIndex const time,
137✔
225
                                                           TimeFrame const * source_timeframe,
226
                                                           TimeFrame const * line_timeframe) const {
227
    // Convert time if needed
228
    TimeFrameIndex converted_time = time;
137✔
229
    if (source_timeframe && line_timeframe && source_timeframe != line_timeframe) {
137✔
UNCOV
230
        auto time_value = source_timeframe->getTimeAtIndex(time);
×
UNCOV
231
        converted_time = line_timeframe->getIndexAtTime(static_cast<float>(time_value));
×
232
    }
233
    
234
    return getEntityIdsAtTime(converted_time);
274✔
235
}
236

237
std::vector<EntityId> LineData::getAllEntityIds() const {
50✔
238
    std::vector<EntityId> out;
50✔
239
    for (auto const & [t, entries] : _data) {
1,543✔
240
        (void)t;
241
        for (auto const & entry : entries) {
4,448✔
242
            out.push_back(entry.entity_id);
2,955✔
243
        }
244
    }
245
    return out;
50✔
UNCOV
246
}
×
247

248
// ========== Entity Lookup Methods ==========
249

250
std::optional<Line2D> LineData::getLineByEntityId(EntityId entity_id) const {
4,042✔
251
    if (!_identity_registry) {
4,042✔
252
        return std::nullopt;
7✔
253
    }
254
    
255
    auto descriptor = _identity_registry->get(entity_id);
4,035✔
256
    if (!descriptor || descriptor->kind != EntityKind::LineEntity || descriptor->data_key != _identity_data_key) {
4,035✔
257
        return std::nullopt;
×
258
    }
259
    
260
    TimeFrameIndex const time{descriptor->time_value};
4,035✔
261
    int const local_index = descriptor->local_index;
4,035✔
262
    
263
    auto time_it = _data.find(time);
4,035✔
264
    if (time_it == _data.end()) {
4,035✔
265
        return std::nullopt;
×
266
    }
267
    
268
    if (local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
4,035✔
269
        return std::nullopt;
×
270
    }
271
    
272
    return time_it->second[static_cast<size_t>(local_index)].line;
4,035✔
273
}
4,035✔
274

UNCOV
275
std::optional<std::reference_wrapper<Line2D>> LineData::getMutableLineByEntityId(EntityId entity_id) {
×
UNCOV
276
    if (!_identity_registry) {
×
UNCOV
277
        return std::nullopt;
×
278
    }
279
    
UNCOV
280
    auto descriptor = _identity_registry->get(entity_id);
×
UNCOV
281
    if (!descriptor || descriptor->kind != EntityKind::LineEntity || descriptor->data_key != _identity_data_key) {
×
282
        return std::nullopt;
×
283
    }
284
    
UNCOV
285
    TimeFrameIndex const time{descriptor->time_value};
×
UNCOV
286
    int const local_index = descriptor->local_index;
×
287
    
UNCOV
288
    auto time_it = _data.find(time);
×
UNCOV
289
    if (time_it == _data.end()) {
×
UNCOV
290
        return std::nullopt;
×
291
    }
292
    
UNCOV
293
    if (local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
×
UNCOV
294
        return std::nullopt;
×
295
    }
296
    
UNCOV
297
    return std::ref(time_it->second[static_cast<size_t>(local_index)].line);
×
UNCOV
298
}
×
299

300
std::optional<std::pair<TimeFrameIndex, int>> LineData::getTimeAndIndexByEntityId(EntityId entity_id) const {
338✔
301
    if (!_identity_registry) {
338✔
302
        return std::nullopt;
7✔
303
    }
304
    
305
    auto descriptor = _identity_registry->get(entity_id);
331✔
306
    if (!descriptor || descriptor->kind != EntityKind::LineEntity || descriptor->data_key != _identity_data_key) {
331✔
UNCOV
307
        return std::nullopt;
×
308
    }
309
    
310
    TimeFrameIndex const time{descriptor->time_value};
331✔
311
    int const local_index = descriptor->local_index;
331✔
312
    
313
    // Verify the time and index are valid
314
    auto time_it = _data.find(time);
331✔
315
    if (time_it == _data.end() || local_index < 0 || static_cast<size_t>(local_index) >= time_it->second.size()) {
331✔
UNCOV
316
        return std::nullopt;
×
317
    }
318
    
319
    return std::make_pair(time, local_index);
331✔
320
}
331✔
321

322
std::vector<std::pair<EntityId, Line2D>> LineData::getLinesByEntityIds(std::vector<EntityId> const & entity_ids) const {
5✔
323
    std::vector<std::pair<EntityId, Line2D>> results;
5✔
324
    results.reserve(entity_ids.size());
5✔
325
    
326
    for (EntityId const entity_id : entity_ids) {
17✔
327
        auto line = getLineByEntityId(entity_id);
12✔
328
        if (line.has_value()) {
12✔
329
            results.emplace_back(entity_id, std::move(line.value()));
7✔
330
        }
331
    }
12✔
332
    
333
    return results;
5✔
334
}
×
335

336
std::vector<std::tuple<EntityId, TimeFrameIndex, int>> LineData::getTimeInfoByEntityIds(std::vector<EntityId> const & entity_ids) const {
4✔
337
    std::vector<std::tuple<EntityId, TimeFrameIndex, int>> results;
4✔
338
    results.reserve(entity_ids.size());
4✔
339
    
340
    for (EntityId const entity_id : entity_ids) {
13✔
341
        auto time_info = getTimeAndIndexByEntityId(entity_id);
9✔
342
        if (time_info.has_value()) {
9✔
343
            results.emplace_back(entity_id, time_info->first, time_info->second);
4✔
344
        }
345
    }
346
    
347
    return results;
4✔
UNCOV
348
}
×
349

350
// ========== Image Size ==========
351

UNCOV
352
void LineData::changeImageSize(ImageSize const & image_size)
×
353
{
UNCOV
354
    if (_image_size.width == -1 || _image_size.height == -1) {
×
355
        std::cout << "No size set for current image. "
UNCOV
356
                  << " Please set a valid image size before trying to scale" << std::endl;
×
357
    }
358

UNCOV
359
    if (_image_size.width == image_size.width && _image_size.height == image_size.height) {
×
360
        std::cout << "Image size is the same. No need to scale" << std::endl;
×
361
        return;
×
362
    }
363

UNCOV
364
    float const scale_x = static_cast<float>(image_size.width) / static_cast<float>(_image_size.width);
×
365
    float const scale_y = static_cast<float>(image_size.height) / static_cast<float>(_image_size.height);
×
366

UNCOV
367
    for (auto & [time, entries] : _data) {
×
UNCOV
368
        for (auto & entry : entries) {
×
UNCOV
369
            for (auto & point : entry.line) {
×
UNCOV
370
                point.x *= scale_x;
×
UNCOV
371
                point.y *= scale_y;
×
372
            }
373
        }
374
    }
UNCOV
375
    _image_size = image_size;
×
376
}
377

378
void LineData::setIdentityContext(std::string const & data_key, EntityRegistry * registry) {
333✔
379
    _identity_data_key = data_key;
333✔
380
    _identity_registry = registry;
333✔
381
}
333✔
382

383
void LineData::rebuildAllEntityIds() {
333✔
384
    if (!_identity_registry) {
333✔
UNCOV
385
        for (auto & [t, entries] : _data) {
×
UNCOV
386
            for (auto & entry : entries) {
×
UNCOV
387
                entry.entity_id = 0;
×
388
            }
389
        }
UNCOV
390
        return;
×
391
    }
392
    
393
    for (auto & [t, entries] : _data) {
1,263✔
394
        for (int i = 0; i < static_cast<int>(entries.size()); ++i) {
2,149✔
395
            entries[static_cast<size_t>(i)].entity_id = _identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, t, i);
1,219✔
396
        }
397
    }
398
}
399

400
// ========== Copy and Move ==========
401

402
std::size_t LineData::copyTo(LineData& target, TimeFrameInterval const & interval, bool notify) const {
8✔
403
    if (interval.start > interval.end) {
8✔
404
        std::cerr << "LineData::copyTo: interval start (" << interval.start.getValue() 
1✔
405
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
1✔
406
        return 0;
1✔
407
    }
408

409
    std::size_t total_lines_copied = 0;
7✔
410

411
    // Iterate through all times in the source data within the interval
412
    for (auto const & [time, entries] : _data) {
27✔
413
        if (time >= interval.start && time <= interval.end && !entries.empty()) {
20✔
414
            for (auto const& entry : entries) {
21✔
415
                target.addAtTime(time, entry.line, false); // Don't notify for each operation
13✔
416
                total_lines_copied++;
13✔
417
            }
418
        }
419
    }
420

421
    // Notify observer only once at the end if requested
422
    if (notify && total_lines_copied > 0) {
7✔
423
        target.notifyObservers();
5✔
424
    }
425

426
    return total_lines_copied;
7✔
427
}
428

429
std::size_t LineData::copyTo(LineData& target, std::vector<TimeFrameIndex> const& times, bool notify) const {
1✔
430
    std::size_t total_lines_copied = 0;
1✔
431

432
    // Copy lines for each specified time
433
    for (TimeFrameIndex const time : times) {
3✔
434
        auto it = _data.find(time);
2✔
435
        if (it != _data.end() && !it->second.empty()) {
2✔
436
            for (auto const& entry : it->second) {
5✔
437
                target.addAtTime(time, entry.line, false); // Don't notify for each operation
3✔
438
                total_lines_copied++;
3✔
439
            }
440
        }
441
    }
442

443
    // Notify observer only once at the end if requested
444
    if (notify && total_lines_copied > 0) {
1✔
445
        target.notifyObservers();
1✔
446
    }
447

448
    return total_lines_copied;
1✔
449
}
450

451
std::size_t LineData::moveTo(LineData& target, TimeFrameInterval const & interval, bool notify) {
4✔
452
    if (interval.start > interval.end) {
4✔
UNCOV
453
        std::cerr << "LineData::moveTo: interval start (" << interval.start.getValue()
×
UNCOV
454
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
UNCOV
455
        return 0;
×
456
    }
457

458
    std::size_t total_lines_moved = 0;
4✔
459

460
    auto start_it = _data.lower_bound(interval.start);
4✔
461
    auto end_it = _data.upper_bound(interval.end);
4✔
462

463
    for (auto it = start_it; it != end_it; ) {
9✔
464
        auto node = _data.extract(it++);
5✔
465
        total_lines_moved += node.mapped().size();
5✔
466
        auto [target_it, inserted, node_handle] = target._data.insert(std::move(node));
5✔
467
        if (!inserted) {
5✔
468
            // If the key already exists in the target, merge the vectors
UNCOV
469
            target_it->second.insert(target_it->second.end(),
×
UNCOV
470
                                     std::make_move_iterator(node_handle.mapped().begin()),
×
UNCOV
471
                                     std::make_move_iterator(node_handle.mapped().end()));
×
472
        }
473
    }
5✔
474

475
    if (notify && total_lines_moved > 0) {
4✔
476
        target.notifyObservers();
3✔
477
        notifyObservers();
3✔
478
    }
479

480
    return total_lines_moved;
4✔
481
}
482

483
std::size_t LineData::moveTo(LineData& target, std::vector<TimeFrameIndex> const & times, bool notify) {
2✔
484
    std::size_t total_lines_moved = 0;
2✔
485

486
    for (TimeFrameIndex const time : times) {
6✔
487
        if (auto it = _data.find(time); it != _data.end()) {
4✔
488
            auto node = _data.extract(it);
2✔
489
            total_lines_moved += node.mapped().size();
2✔
490
            auto [target_it, inserted, node_handle] = target._data.insert(std::move(node));
2✔
491
            if (!inserted) {
2✔
492
                // If the key already exists in the target, merge the vectors
UNCOV
493
                target_it->second.insert(target_it->second.end(),
×
UNCOV
494
                                         std::make_move_iterator(node_handle.mapped().begin()),
×
UNCOV
495
                                         std::make_move_iterator(node_handle.mapped().end()));
×
496
            }
497
        }
2✔
498
    }
499

500
    if (notify && total_lines_moved > 0) {
2✔
501
        target.notifyObservers();
1✔
502
        notifyObservers();
1✔
503
    }
504

505
    return total_lines_moved;
2✔
506
}
507

508
std::size_t LineData::copyLinesByEntityIds(LineData & target, std::vector<EntityId> const & entity_ids, bool notify) {
5✔
509
    std::size_t total_lines_copied = 0;
5✔
510
    
511
    // Iterate through all data to find matching EntityIds
512
    for (auto const & [time, entries] : _data) {
17✔
513
        for (auto const & entry : entries) {
28✔
514
            // Check if this entry's EntityId is in the list to copy
515
            if (std::ranges::find(entity_ids, entry.entity_id) != entity_ids.end()) {
16✔
516
                target.addAtTime(time, entry.line, false); // Don't notify for each operation
6✔
517
                total_lines_copied++;
6✔
518
            }
519
        }
520
    }
521
    
522
    // Notify observer only once at the end if requested
523
    if (notify && total_lines_copied > 0) {
5✔
524
        target.notifyObservers();
3✔
525
    }
526
    
527
    return total_lines_copied;
5✔
528
}
529

530
std::size_t LineData::moveLinesByEntityIds(LineData & target, std::vector<EntityId> const & entity_ids, bool notify) {
4✔
531
    std::size_t total_lines_moved = 0;
4✔
532
    std::vector<std::pair<TimeFrameIndex, size_t>> entries_to_remove;
4✔
533
    
534
    // First, copy all matching lines to target and collect removal information
535
    for (auto const & [time, entries] : _data) {
16✔
536
        for (size_t i = 0; i < entries.size(); ++i) {
28✔
537
            auto const & entry = entries[i];
16✔
538
            // Check if this entry's EntityId is in the list to move
539
            if (std::ranges::find(entity_ids, entry.entity_id) != entity_ids.end()) {
16✔
540
                // For move operations, preserve the original entity ID
541
                target.addLineEntryAtTime(time, entry.line, entry.entity_id, false);
6✔
542
                entries_to_remove.emplace_back(time, i);
6✔
543
                total_lines_moved++;
6✔
544
            }
545
        }
546
    }
547
    
548
    // Then, remove the moved entries from source (in reverse order to maintain indices)
549
    std::ranges::sort(entries_to_remove, 
4✔
550
                      [](auto const & a, auto const & b) {
3✔
551
                          if (a.first != b.first) return a.first > b.first; // Sort by time descending
3✔
552
                          return a.second > b.second; // Then by index descending
2✔
553
                      });
554
    
555
    for (auto const & [time, index] : entries_to_remove) {
10✔
556
        auto it = _data.find(time);
6✔
557
        if (it != _data.end() && index < it->second.size()) {
6✔
558
            it->second.erase(it->second.begin() + static_cast<long>(index));
6✔
559
            if (it->second.empty()) {
6✔
560
                _data.erase(it);
3✔
561
            }
562
        }
563
    }
564
    
565
    // Notify observers only once at the end if requested
566
    if (notify && total_lines_moved > 0) {
4✔
567
        target.notifyObservers();
3✔
568
        notifyObservers();
3✔
569
    }
570
    
571
    return total_lines_moved;
4✔
572
}
4✔
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

© 2025 Coveralls, Inc