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

paulmthompson / WhiskerToolbox / 15566405961

10 Jun 2025 05:39PM UTC coverage: 62.158% (+6.9%) from 55.215%
15566405961

push

github

paulmthompson
add interface for interval grouping transformation

6851 of 11022 relevant lines covered (62.16%)

605.76 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 "DigitalIntervalSeriesDisplayOptions.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
30
        Model = glm::scale(Model, glm::vec3(1.0f, global_scale, 1.0f));
×
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<Interval> 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<Interval> 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✔
90
            end_time = max_time;
×
91
            start_time = std::max(0.0f, end_time - duration);
×
92
        }
93

94
        // Convert to int64_t for Interval struct
95
        intervals.emplace_back(Interval{static_cast<int64_t>(start_time), static_cast<int64_t>(end_time)});
180✔
96
    }
97

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

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

112
    return intervals;
4✔
113
}
×
114

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

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

125
    // Calculate some basic statistics for potential future use
126
    int64_t total_duration = 0;
3✔
127
    int64_t min_duration_val = std::numeric_limits<int64_t>::max();
3✔
128
    int64_t max_duration_val = 0;
3✔
129

130
    for (auto const & interval: intervals) {
133✔
131
        int64_t const duration = interval.end - interval.start;
130✔
132
        total_duration += duration;
130✔
133
        min_duration_val = std::min(min_duration_val, duration);
130✔
134
        max_duration_val = std::max(max_duration_val, duration);
130✔
135
    }
136

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

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

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

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