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

paulmthompson / WhiskerToolbox / 15545304457

09 Jun 2025 09:44PM UTC coverage: 55.215% (+4.0%) from 51.194%
15545304457

push

github

paulmthompson
Refactor to new MVP display options system

- Replace old display options with NewAnalog/DigitalEvent/DigitalIntervalSeriesDisplayOptions
- Migrate all MVP matrix functions to new_ prefixed versions
- Add compatibility members and function overloads for smooth transition
- Fix standard deviation recalculation and canvas resize errors
- Remove legacy MVP functions and old display option structures
- Apply consistent formatting throughout codebase

All tests pass with improved coordinate handling and panning behavior.

0 of 7 new or added lines in 1 file covered. (0.0%)

13 existing lines in 5 files now uncovered.

5225 of 9463 relevant lines covered (55.22%)

770.5 hits per line

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

87.72
/src/WhiskerToolbox/DataViewer/DigitalInterval/MVP_DigitalInterval.cpp
1
#include "MVP_DigitalInterval.hpp"
2

3
#include "DisplayOptions/TimeSeriesDisplayOptions.hpp"
4
#include "PlottingManager/PlottingManager.hpp"
5

6
#include <algorithm>
7
#include <cmath>
8
#include <iostream>
9
#include <random>
10

11

12
glm::mat4 new_getIntervalModelMat(NewDigitalIntervalSeriesDisplayOptions const & display_options,
8✔
13
                                  PlottingManager const & plotting_manager) {
14
    auto Model = glm::mat4(1.0f);
8✔
15

16
    // Apply global zoom scaling
17
    float const global_scale = display_options.global_zoom * display_options.global_vertical_scale;
8✔
18

19
    // For digital intervals extending full canvas, we want them to span the entire allocated height
20
    // The allocated_height represents the total height available for this series
21
    if (display_options.extend_full_canvas) {
8✔
22
        // Scale to use the full allocated height with margin factor
23
        float const height_scale = display_options.allocated_height * display_options.margin_factor * 0.5f;
8✔
24
        Model = glm::scale(Model, glm::vec3(1.0f, height_scale * global_scale, 1.0f));
8✔
25

26
        // Translate to allocated center position
27
        Model = glm::translate(Model, glm::vec3(0.0f, display_options.allocated_y_center, 0.0f));
8✔
28
    } else {
29
        // Apply standard scaling and positioning
UNCOV
30
        Model = glm::scale(Model, glm::vec3(1.0f, global_scale, 1.0f));
×
UNCOV
31
        Model = glm::translate(Model, glm::vec3(0.0f, display_options.allocated_y_center, 0.0f));
×
32
    }
33

34
    return Model;
8✔
35
}
36

37
glm::mat4 new_getIntervalViewMat(PlottingManager const & plotting_manager) {
8✔
38
    auto View = glm::mat4(1.0f);
8✔
39

40
    // Digital intervals should NOT move with vertical panning
41
    // They always extend from the top to bottom of the current view
42
    // Unlike analog series, panning should not affect their position
43
    // (they remain "pinned" to the current viewport)
44

45
    return View;
8✔
46
}
47

48
glm::mat4 new_getIntervalProjectionMat(int start_data_index,
8✔
49
                                       int end_data_index,
50
                                       float y_min,
51
                                       float y_max,
52
                                       PlottingManager const & plotting_manager) {
53
    // Convert data indices to normalized coordinate range
54
    // Map [start_data_index, end_data_index] to [-1, 1] in X
55
    // Map [y_min, y_max] to viewport range in Y
56

57
    auto const left = static_cast<float>(start_data_index);
8✔
58
    auto const right = static_cast<float>(end_data_index);
8✔
59

60
    // Use viewport Y range from plotting manager with any additional pan offset
61
    float bottom = plotting_manager.viewport_y_min;
8✔
62
    float top = plotting_manager.viewport_y_max;
8✔
63

64
    // Create orthographic projection matrix
65
    auto Projection = glm::ortho(left, right, bottom, top);
8✔
66

67
    return Projection;
8✔
68
}
69

70
std::vector<IntervalData> generateTestIntervalData(size_t num_intervals,
4✔
71
                                                   float max_time,
72
                                                   float min_duration,
73
                                                   float max_duration,
74
                                                   unsigned int seed) {
75
    std::mt19937 gen(seed);
4✔
76
    std::uniform_real_distribution<float> time_dist(0.0f, max_time * 0.8f);// Leave some space for intervals
4✔
77
    std::uniform_real_distribution<float> duration_dist(min_duration, max_duration);
4✔
78

79
    std::vector<IntervalData> intervals;
4✔
80
    intervals.reserve(num_intervals);
4✔
81

82
    // Generate random intervals
83
    for (size_t i = 0; i < num_intervals; ++i) {
184✔
84
        float start_time = time_dist(gen);
180✔
85
        float duration = duration_dist(gen);
180✔
86
        float end_time = start_time + duration;
180✔
87

88
        // Ensure we don't exceed max_time
89
        if (end_time > max_time) {
180✔
UNCOV
90
            end_time = max_time;
×
UNCOV
91
            start_time = std::max(0.0f, end_time - duration);
×
92
        }
93

94
        intervals.emplace_back(start_time, end_time);
180✔
95
    }
96

97
    // Sort intervals by start time to ensure ordering requirement
98
    std::sort(intervals.begin(), intervals.end(),
4✔
99
              [](IntervalData const & a, IntervalData const & b) {
1,281✔
100
                  return a.start_time < b.start_time;
1,281✔
101
              });
102

103
    // Validate all intervals (end > start)
104
    for (auto & interval: intervals) {
184✔
105
        if (!interval.isValid()) {
180✔
106
            // This shouldn't happen with our generation method, but just in case
UNCOV
107
            interval.end_time = interval.start_time + min_duration;
×
108
        }
109
    }
110

111
    return intervals;
4✔
UNCOV
112
}
×
113

114
void setIntervalIntrinsicProperties(std::vector<IntervalData> const & intervals,
3✔
115
                                    NewDigitalIntervalSeriesDisplayOptions & display_options) {
116
    if (intervals.empty()) {
3✔
UNCOV
117
        return;
×
118
    }
119

120
    // For digital intervals, intrinsic properties are minimal since they're meant to be
121
    // visual indicators rather than quantitative data. The main consideration is
122
    // ensuring good visibility across the canvas.
123

124
    // Calculate some basic statistics for potential future use
125
    float total_duration = 0.0f;
3✔
126
    float min_duration = std::numeric_limits<float>::max();
3✔
127
    float max_duration = 0.0f;
3✔
128

129
    for (auto const & interval: intervals) {
133✔
130
        float const duration = interval.getDuration();
130✔
131
        total_duration += duration;
130✔
132
        min_duration = std::min(min_duration, duration);
130✔
133
        max_duration = std::max(max_duration, duration);
130✔
134
    }
135

136
    float const avg_duration = total_duration / static_cast<float>(intervals.size());
3✔
137

138
    // For now, we keep the default display options
139
    // Future enhancements could adjust alpha based on interval density,
140
    // or adjust positioning based on overlap analysis
141

142
    // Ensure alpha is appropriate for overlapping intervals
143
    if (intervals.size() > 50) {
3✔
144
        // With many intervals, reduce alpha to prevent over-saturation
145
        display_options.alpha = std::max(0.1f, 0.3f * std::sqrt(50.0f / static_cast<float>(intervals.size())));
1✔
146
    }
147

148
    // Set full canvas extension for maximum visibility
149
    display_options.extend_full_canvas = true;
3✔
150
}
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