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

paulmthompson / WhiskerToolbox / 17465586740

04 Sep 2025 01:21PM UTC coverage: 70.828% (-0.1%) from 70.97%
17465586740

push

github

paulmthompson
feature tree widget shouldn't emit signals during rebuild

121 of 131 new or added lines in 4 files covered. (92.37%)

108 existing lines in 7 files now uncovered.

34146 of 48210 relevant lines covered (70.83%)

1299.48 hits per line

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

38.86
/src/WhiskerToolbox/DataViewer/PlottingManager/PlottingManager.cpp
1
#include "PlottingManager.hpp"
2

3
#include <algorithm>
4
#include <cmath>
5
#include <iostream>
6
#include <sstream>
7
#include <iomanip>
8

9
void PlottingManager::calculateAnalogSeriesAllocation(int series_index,
45✔
10
                                                      float & allocated_center,
11
                                                      float & allocated_height) const {
12
    if (total_analog_series <= 0) {
45✔
13
        allocated_center = 0.0f;
1✔
14
        allocated_height = 2.0f;// Full canvas height
1✔
15
        return;
1✔
16
    }
17

18
    // Calculate allocated height for this series
19
    allocated_height = (viewport_y_max - viewport_y_min) / static_cast<float>(total_analog_series);
44✔
20

21
    // Calculate center Y coordinate
22
    // Series are stacked from top to bottom starting at viewport_y_min
23
    allocated_center = viewport_y_min + allocated_height * (static_cast<float>(series_index) + 0.5f);
44✔
24
}
25

26
void PlottingManager::setVisibleDataRange(int start_index, int end_index) {
33✔
27
    visible_start_index = start_index;
33✔
28
    visible_end_index = end_index;
33✔
29
    total_data_points = end_index - start_index;
33✔
30
}
33✔
31

32
int PlottingManager::addAnalogSeries() {
22✔
33
    int series_index = total_analog_series;
22✔
34
    total_analog_series++;
22✔
35
    return series_index;
22✔
36
}
37

38
int PlottingManager::addDigitalIntervalSeries() {
8✔
39
    int series_index = total_digital_series;
8✔
40
    total_digital_series++;
8✔
41
    return series_index;
8✔
42
}
43

44
int PlottingManager::addDigitalEventSeries() {
16✔
45
    int series_index = total_event_series;
16✔
46
    total_event_series++;
16✔
47
    return series_index;
16✔
48
}
49

50
int PlottingManager::addAnalogSeries(std::string const & key,
9✔
51
                                     std::shared_ptr<AnalogTimeSeries> series,
52
                                     std::shared_ptr<TimeFrame> time_frame,
53
                                     std::string const & color) {
54
    int series_index = total_analog_series;
9✔
55
    
56
    AnalogSeriesInfo info;
9✔
57
    info.series = series;
9✔
58
    info.time_frame = time_frame;
9✔
59
    info.key = key;
9✔
60
    info.color = color.empty() ? generateDefaultColor(series_index) : color;
9✔
61
    info.visible = true;
9✔
62
    
63
    analog_series_map[key] = std::move(info);
9✔
64
    total_analog_series++;
9✔
65
    
66
    return series_index;
9✔
67
}
9✔
68

69
int PlottingManager::addDigitalEventSeries(std::string const & key,
×
70
                                           std::shared_ptr<DigitalEventSeries> series,
71
                                           std::shared_ptr<TimeFrame> time_frame,
72
                                           std::string const & color) {
73
    int series_index = total_event_series;
×
74
    
75
    DigitalEventSeriesInfo info;
×
76
    info.series = series;
×
77
    info.time_frame = time_frame;
×
78
    info.key = key;
×
79
    info.color = color.empty() ? generateDefaultColor(total_analog_series + series_index) : color;
×
80
    info.visible = true;
×
81
    
82
    digital_event_series_map[key] = std::move(info);
×
83
    total_event_series++;
×
84
    
85
    return series_index;
×
86
}
×
87

88
int PlottingManager::addDigitalIntervalSeries(std::string const & key,
×
89
                                              std::shared_ptr<DigitalIntervalSeries> series,
90
                                              std::shared_ptr<TimeFrame> time_frame,
91
                                              std::string const & color) {
92
    int series_index = total_digital_series;
×
93
    
94
    DigitalIntervalSeriesInfo info;
×
95
    info.series = series;
×
96
    info.time_frame = time_frame;
×
97
    info.key = key;
×
98
    info.color = color.empty() ? generateDefaultColor(total_analog_series + total_event_series + series_index) : color;
×
99
    info.visible = true;
×
100
    
101
    digital_interval_series_map[key] = std::move(info);
×
102
    total_digital_series++;
×
103
    
104
    return series_index;
×
105
}
×
106

UNCOV
107
bool PlottingManager::removeAnalogSeries(std::string const & key) {
×
UNCOV
108
    auto it = analog_series_map.find(key);
×
UNCOV
109
    if (it != analog_series_map.end()) {
×
110
        analog_series_map.erase(it);
×
111
        updateSeriesCounts();
×
112
        return true;
×
113
    }
UNCOV
114
    return false;
×
115
}
116

UNCOV
117
bool PlottingManager::removeDigitalEventSeries(std::string const & key) {
×
UNCOV
118
    auto it = digital_event_series_map.find(key);
×
UNCOV
119
    if (it != digital_event_series_map.end()) {
×
120
        digital_event_series_map.erase(it);
×
121
        updateSeriesCounts();
×
122
        return true;
×
123
    }
UNCOV
124
    return false;
×
125
}
126

UNCOV
127
bool PlottingManager::removeDigitalIntervalSeries(std::string const & key) {
×
UNCOV
128
    auto it = digital_interval_series_map.find(key);
×
UNCOV
129
    if (it != digital_interval_series_map.end()) {
×
130
        digital_interval_series_map.erase(it);
×
131
        updateSeriesCounts();
×
132
        return true;
×
133
    }
UNCOV
134
    return false;
×
135
}
136

137
void PlottingManager::clearAllSeries() {
×
138
    analog_series_map.clear();
×
139
    digital_event_series_map.clear();
×
140
    digital_interval_series_map.clear();
×
141
    total_analog_series = 0;
×
142
    total_event_series = 0;
×
143
    total_digital_series = 0;
×
144
}
×
145

146
PlottingManager::AnalogSeriesInfo * PlottingManager::getAnalogSeriesInfo(std::string const & key) {
×
147
    auto it = analog_series_map.find(key);
×
148
    return (it != analog_series_map.end()) ? &it->second : nullptr;
×
149
}
150

151
PlottingManager::DigitalEventSeriesInfo * PlottingManager::getDigitalEventSeriesInfo(std::string const & key) {
×
152
    auto it = digital_event_series_map.find(key);
×
153
    return (it != digital_event_series_map.end()) ? &it->second : nullptr;
×
154
}
155

156
PlottingManager::DigitalIntervalSeriesInfo * PlottingManager::getDigitalIntervalSeriesInfo(std::string const & key) {
×
157
    auto it = digital_interval_series_map.find(key);
×
158
    return (it != digital_interval_series_map.end()) ? &it->second : nullptr;
×
159
}
160

161
void PlottingManager::setSeriesVisibility(std::string const & key, bool visible) {
×
162
    // Check all series types and update visibility
163
    auto analog_it = analog_series_map.find(key);
×
164
    if (analog_it != analog_series_map.end()) {
×
165
        analog_it->second.visible = visible;
×
166
        return;
×
167
    }
168
    
169
    auto event_it = digital_event_series_map.find(key);
×
170
    if (event_it != digital_event_series_map.end()) {
×
171
        event_it->second.visible = visible;
×
172
        return;
×
173
    }
174
    
175
    auto interval_it = digital_interval_series_map.find(key);
×
176
    if (interval_it != digital_interval_series_map.end()) {
×
177
        interval_it->second.visible = visible;
×
178
        return;
×
179
    }
180
}
181

182
std::vector<std::string> PlottingManager::getVisibleAnalogSeriesKeys() const {
52✔
183
    std::vector<std::string> visible_keys;
52✔
184
    for (auto const & [key, info] : analog_series_map) {
144✔
185
        if (info.visible) {
92✔
186
            visible_keys.push_back(key);
92✔
187
        }
188
    }
189
    return visible_keys;
52✔
190
}
×
191

192
std::vector<std::string> PlottingManager::getVisibleDigitalEventSeriesKeys() const {
52✔
193
    std::vector<std::string> visible_keys;
52✔
194
    for (auto const & [key, info] : digital_event_series_map) {
52✔
195
        if (info.visible) {
×
196
            visible_keys.push_back(key);
×
197
        }
198
    }
199
    return visible_keys;
52✔
200
}
×
201

202
std::vector<std::string> PlottingManager::getVisibleDigitalIntervalSeriesKeys() const {
52✔
203
    std::vector<std::string> visible_keys;
52✔
204
    for (auto const & [key, info] : digital_interval_series_map) {
52✔
205
        if (info.visible) {
×
206
            visible_keys.push_back(key);
×
207
        }
208
    }
209
    return visible_keys;
52✔
210
}
×
211

212
void PlottingManager::setGlobalZoom(float zoom) {
10✔
213
    global_zoom = std::max(0.01f, zoom); // Prevent negative or zero zoom
10✔
214
}
10✔
215

216
float PlottingManager::getGlobalZoom() const {
×
217
    return global_zoom;
×
218
}
219

220
void PlottingManager::setGlobalVerticalScale(float scale) {
9✔
221
    global_vertical_scale = std::max(0.01f, scale); // Prevent negative or zero scale
9✔
222
}
9✔
223

224
float PlottingManager::getGlobalVerticalScale() const {
×
225
    return global_vertical_scale;
×
226
}
227

228
void PlottingManager::calculateDigitalIntervalSeriesAllocation(int series_index,
8✔
229
                                                               float & allocated_center,
230
                                                               float & allocated_height) const {
231

232
    static_cast<void>(series_index);
233

234
    // Digital intervals use the full canvas height by default
235
    allocated_center = (viewport_y_min + viewport_y_max) * 0.5f;// Center of viewport
8✔
236
    allocated_height = viewport_y_max - viewport_y_min;         // Full viewport height
8✔
237
}
8✔
238

239
void PlottingManager::calculateDigitalEventSeriesAllocation(int series_index,
12✔
240
                                                            float & allocated_center,
241
                                                            float & allocated_height) const {
242
    // For now, assume stacked mode allocation (like analog series)
243
    // This can be extended to support different plotting modes
244
    if (total_event_series <= 0) {
12✔
245
        allocated_center = 0.0f;
×
246
        allocated_height = 2.0f;// Full canvas height
×
247
        return;
×
248
    }
249

250
    // Calculate allocated height for this series
251
    allocated_height = (viewport_y_max - viewport_y_min) / static_cast<float>(total_event_series);
12✔
252

253
    // Calculate center Y coordinate
254
    // Series are stacked from top to bottom starting at viewport_y_min
255
    allocated_center = viewport_y_min + allocated_height * (static_cast<float>(series_index) + 0.5f);
12✔
256
}
257

258
void PlottingManager::calculateGlobalStackedAllocation(int analog_series_index,
4✔
259
                                                       int event_series_index,
260
                                                       int total_stackable_series,
261
                                                       float & allocated_center,
262
                                                       float & allocated_height) const {
263
    if (total_stackable_series <= 0) {
4✔
264
        allocated_center = 0.0f;
×
265
        allocated_height = 2.0f;// Full canvas height
×
266
        return;
×
267
    }
268

269
    // Calculate allocated height for each series (equal division)
270
    allocated_height = (viewport_y_max - viewport_y_min) / static_cast<float>(total_stackable_series);
4✔
271

272
    // Determine the global index for this series
273
    int global_series_index;
274
    if (analog_series_index >= 0) {
4✔
275
        // This is an analog series
276
        global_series_index = analog_series_index;
2✔
277
    } else {
278
        // This is a digital event series, comes after all analog series
279
        global_series_index = total_analog_series + event_series_index;
2✔
280
    }
281

282
    // Calculate center Y coordinate based on global stacking order
283
    allocated_center = viewport_y_min + allocated_height * (static_cast<float>(global_series_index) + 0.5f);
4✔
284
}
285

286
void PlottingManager::setPanOffset(float pan_offset) {
43✔
287
    vertical_pan_offset = pan_offset;
43✔
288
}
43✔
289

290
void PlottingManager::applyPanDelta(float pan_delta) {
4✔
291
    vertical_pan_offset += pan_delta;
4✔
292
}
4✔
293

294
float PlottingManager::getPanOffset() const {
13✔
295
    return vertical_pan_offset;
13✔
296
}
297

298
void PlottingManager::resetPan() {
8✔
299
    vertical_pan_offset = 0.0f;
8✔
300
}
8✔
301

302
void PlottingManager::updateSeriesCounts() {
×
303
    // Count only visible series
304
    total_analog_series = static_cast<int>(std::count_if(
×
305
        analog_series_map.begin(), analog_series_map.end(),
306
        [](auto const & pair) { return pair.second.visible; }));
×
307
    
308
    total_event_series = static_cast<int>(std::count_if(
×
309
        digital_event_series_map.begin(), digital_event_series_map.end(),
310
        [](auto const & pair) { return pair.second.visible; }));
×
311
    
312
    total_digital_series = static_cast<int>(std::count_if(
×
313
        digital_interval_series_map.begin(), digital_interval_series_map.end(),
314
        [](auto const & pair) { return pair.second.visible; }));
×
315
}
×
316

317
std::string PlottingManager::generateDefaultColor(int series_index) const {
×
318
    // Generate a nice color palette for series
319
    // Use HSV color space with fixed saturation and value, varying hue
320
    constexpr int num_colors = 12;
×
321
    float hue = (static_cast<float>(series_index % num_colors) / static_cast<float>(num_colors)) * 360.0f;
×
322
    
323
    // Convert HSV to RGB
324
    constexpr float saturation = 0.8f;
×
325
    constexpr float value = 0.9f;
×
326
    
327
    float c = value * saturation;
×
328
    float x = c * (1.0f - std::abs(std::fmod(hue / 60.0f, 2.0f) - 1.0f));
×
329
    float m = value - c;
×
330
    
331
    float r, g, b;
332
    if (hue >= 0 && hue < 60) {
×
333
        r = c; g = x; b = 0;
×
334
    } else if (hue >= 60 && hue < 120) {
×
335
        r = x; g = c; b = 0;
×
336
    } else if (hue >= 120 && hue < 180) {
×
337
        r = 0; g = c; b = x;
×
338
    } else if (hue >= 180 && hue < 240) {
×
339
        r = 0; g = x; b = c;
×
340
    } else if (hue >= 240 && hue < 300) {
×
341
        r = x; g = 0; b = c;
×
342
    } else {
343
        r = c; g = 0; b = x;
×
344
    }
345
    
346
    // Convert to 0-255 range and create hex string
347
    int red = static_cast<int>((r + m) * 255);
×
348
    int green = static_cast<int>((g + m) * 255);
×
349
    int blue = static_cast<int>((b + m) * 255);
×
350
    
351
    std::ostringstream ss;
×
352
    ss << "#" << std::hex << std::setfill('0') 
×
353
       << std::setw(2) << red 
×
354
       << std::setw(2) << green 
×
355
       << std::setw(2) << blue;
×
356
    
357
    return ss.str();
×
358
}
×
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