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

paulmthompson / WhiskerToolbox / 17270491352

27 Aug 2025 02:57PM UTC coverage: 65.333%. Remained the same
17270491352

push

github

paulmthompson
Merge branch 'main' of https://github.com/paulmthompson/WhiskerToolbox

352 of 628 new or added lines in 92 files covered. (56.05%)

357 existing lines in 24 files now uncovered.

26429 of 40453 relevant lines covered (65.33%)

1119.34 hits per line

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

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

17
}
6✔
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()) {
×
NEW
38
            if (static_cast<size_t>(line_id) < it->second.size()) {
×
NEW
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) {
163✔
54

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

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

67
    if (notify) {
163✔
68
        notifyObservers();
34✔
69
    }
70
}
326✔
71

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

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

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

88
    if (notify) {
302✔
89
        notifyObservers();
247✔
90
    }
91
}
302✔
92

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

NEW
95
    if (static_cast<size_t>(line_id) < _data[time].size()) {
×
NEW
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

NEW
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

NEW
115
    Line2D & line = _data[time][static_cast<size_t>(line_id)];
×
116
    if (!line.empty()) {
×
117
        Point2D<float> const last_point = line.back();
×
NEW
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 {
43✔
138
    return get_at_time(time, _data, _empty);
43✔
139
}
140

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

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

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

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

166
// ========== Image Size ==========
167

168
void LineData::changeImageSize(ImageSize const & image_size)
×
169
{
170
    if (_image_size.width == -1 || _image_size.height == -1) {
×
171
        std::cout << "No size set for current image. "
172
                  << " Please set a valid image size before trying to scale" << std::endl;
×
173
    }
174

175
    if (_image_size.width == image_size.width && _image_size.height == image_size.height) {
×
176
        std::cout << "Image size is the same. No need to scale" << std::endl;
×
177
        return;
×
178
    }
179

180
    float const scale_x = static_cast<float>(image_size.width) / static_cast<float>(_image_size.width);
×
181
    float const scale_y = static_cast<float>(image_size.height) / static_cast<float>(_image_size.height);
×
182

183
    for (auto & [time, lines] : _data) {
×
184
        for (auto & line : lines) {
×
185
            for (auto & point : line) {
×
186
                point.x *= scale_x;
×
187
                point.y *= scale_y;
×
188
            }
189
        }
190
    }
191
    _image_size = image_size;
×
192

193
}
194

195
void LineData::setIdentityContext(std::string const & data_key, EntityRegistry * registry) {
3✔
196
    _identity_data_key = data_key;
3✔
197
    _identity_registry = registry;
3✔
198
}
3✔
199

200
void LineData::rebuildAllEntityIds() {
3✔
201
    if (!_identity_registry) {
3✔
202
        for (auto & [t, lines] : _data) {
×
203
            _entity_ids_by_time[t].assign(lines.size(), 0);
×
204
        }
205
        return;
×
206
    }
207
    for (auto & [t, lines] : _data) {
7✔
208
        auto & ids = _entity_ids_by_time[t];
4✔
209
        ids.clear();
4✔
210
        ids.reserve(lines.size());
4✔
211
        for (int i = 0; i < static_cast<int>(lines.size()); ++i) {
10✔
212
            ids.push_back(_identity_registry->ensureId(_identity_data_key, EntityKind::LineEntity, t, i));
6✔
213
        }
214
    }
215
}
216

217
// ========== Copy and Move ==========
218

219
std::size_t LineData::copyTo(LineData& target, TimeFrameInterval const & interval, bool notify) const {
×
220
    if (interval.start > interval.end) {
×
221
        std::cerr << "LineData::copyTo: interval start (" << interval.start.getValue() 
×
222
                  << ") must be <= interval end (" << interval.end.getValue() << ")" << std::endl;
×
223
        return 0;
×
224
    }
225

226
    std::size_t total_lines_copied = 0;
×
227

228
    // Iterate through all times in the source data within the interval
229
    for (auto const & [time, lines] : _data) {
×
230
        if (time >= interval.start && time <= interval.end && !lines.empty()) {
×
231
            for (auto const& line : lines) {
×
232
                target.addAtTime(time, line, false); // Don't notify for each operation
×
233
                total_lines_copied++;
×
234
            }
235
        }
236
    }
237

238
    // Notify observer only once at the end if requested
239
    if (notify && total_lines_copied > 0) {
×
240
        target.notifyObservers();
×
241
    }
242

243
    return total_lines_copied;
×
244
}
245

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

249
    // Copy lines for each specified time
250
    for (TimeFrameIndex time : times) {
×
251
        auto it = _data.find(time);
×
252
        if (it != _data.end() && !it->second.empty()) {
×
253
            for (auto const& line : it->second) {
×
254
                target.addAtTime(time, line, false); // Don't notify for each operation
×
255
                total_lines_copied++;
×
256
            }
257
        }
258
    }
259

260
    // Notify observer only once at the end if requested
261
    if (notify && total_lines_copied > 0) {
×
262
        target.notifyObservers();
×
263
    }
264

265
    return total_lines_copied;
×
266
}
267

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

275
    std::size_t total_lines_moved = 0;
×
276
    std::vector<TimeFrameIndex> times_to_clear;
×
277

278
    // First, copy all lines in the interval to target
279
    for (auto const & [time, lines] : _data) {
×
280
        if (time >= interval.start && time <= interval.end && !lines.empty()) {
×
281
            for (auto const& line : lines) {
×
282
                target.addAtTime(time, line, false); // Don't notify for each operation
×
283
                total_lines_moved++;
×
284
            }
285
            times_to_clear.push_back(time);
×
286
        }
287
    }
288

289
    // Then, clear all the times from source
290
    for (TimeFrameIndex time : times_to_clear) {
×
NEW
291
        (void)clearAtTime(time, false); // Don't notify for each operation
×
292
    }
293

294
    // Notify observers only once at the end if requested
295
    if (notify && total_lines_moved > 0) {
×
296
        target.notifyObservers();
×
297
        notifyObservers();
×
298
    }
299

300
    return total_lines_moved;
×
301
}
×
302

303
std::size_t LineData::moveTo(LineData& target, std::vector<TimeFrameIndex> const & times, bool notify) {
×
304
    std::size_t total_lines_moved = 0;
×
305
    std::vector<TimeFrameIndex> times_to_clear;
×
306

307
    // First, copy lines for each specified time to target
308
    for (TimeFrameIndex time : times) {
×
309
        auto it = _data.find(time);
×
310
        if (it != _data.end() && !it->second.empty()) {
×
311
            for (auto const& line : it->second) {
×
312
                target.addAtTime(time, line, false); // Don't notify for each operation
×
313
                total_lines_moved++;
×
314
            }
315
            times_to_clear.push_back(time);
×
316
        }
317
    }
318

319
    // Then, clear all the times from source
320
    for (TimeFrameIndex time : times_to_clear) {
×
NEW
321
        (void)clearAtTime(time, false); // Don't notify for each operation
×
322
    }
323

324
    // Notify observers only once at the end if requested
325
    if (notify && total_lines_moved > 0) {
×
326
        target.notifyObservers();
×
327
        notifyObservers();
×
328
    }
329

330
    return total_lines_moved;
×
331
}
×
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