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

paulmthompson / WhiskerToolbox / 15909500151

26 Jun 2025 06:20PM UTC coverage: 71.617% (-0.03%) from 71.644%
15909500151

push

github

paulmthompson
scrollbar updates video time

11309 of 15791 relevant lines covered (71.62%)

1134.15 hits per line

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

80.0
/src/WhiskerToolbox/DataManager/TimeFrame.cpp
1
#include "TimeFrame.hpp"
2

3
#include <cmath>
4
#include <cstdint>// For int64_t
5

6
TimeFrame::TimeFrame(std::vector<int> const & times) {
20✔
7
    _times = times;
20✔
8
    _total_frame_count = static_cast<int>(times.size());
20✔
9
}
20✔
10

11
int TimeFrame::getTimeAtIndex(TimeFrameIndex index) const {
30,047✔
12
    if (index < TimeFrameIndex(0) || index.getValue() >= _times.size()) {
30,047✔
13
        std::cout << "Index " << index.getValue() << " out of range" << " for time frame of size " << _times.size() << std::endl;
×
14
        return 0;
×
15
    }
16
    return _times[static_cast<size_t>(index.getValue())];
30,047✔
17
}
18

19
int TimeFrame::getIndexAtTime(float time) const {
12✔
20
    // Binary search to find the index closest to the given time
21
    auto it = std::lower_bound(_times.begin(), _times.end(), time);
12✔
22

23
    // If exact match found
24
    if (it != _times.end() && static_cast<float>(*it) == time) {
12✔
25
        return static_cast<int>(std::distance(_times.begin(), it));
12✔
26
    }
27

28
    // If time is beyond the last time point
29
    if (it == _times.end()) {
6✔
30
        return static_cast<int>(_times.size() - 1);
1✔
31
    }
32

33
    // If time is before the first time point
34
    if (it == _times.begin()) {
5✔
35
        return 0;
1✔
36
    }
37

38
    // Find the closest time point
39
    auto prev = it - 1;
4✔
40
    if (std::abs(static_cast<float>(*prev) - time) <= std::abs(static_cast<float>(*it) - time)) {
4✔
41
        return static_cast<int>(std::distance(_times.begin(), prev));
6✔
42
    } else {
43
        return static_cast<int>(std::distance(_times.begin(), it));
2✔
44
    }
45
}
46

47
int TimeFrame::checkFrameInbounds(int frame_id) const {
×
48

49
    if (frame_id < 0) {
×
50
        frame_id = 0;
×
51
    } else if (frame_id >= _total_frame_count) {
×
52
        frame_id = _total_frame_count;
×
53
    }
54
    return frame_id;
×
55
}
56

57
int64_t getTimeIndexForSeries(TimeFrameIndex source_index,
×
58
                              TimeFrame const * source_time_frame,
59
                              TimeFrame const * destination_time_frame) {
60
    if (source_time_frame == destination_time_frame) {
×
61
        // Frames are the same. The time value can be used directly.
62
        return source_index.getValue();
×
63
    } else {
64
        auto destination_index = destination_time_frame->getIndexAtTime(static_cast<float>(source_index.getValue()));
×
65
        return destination_index;
×
66
    }
67
}
68

69
// ========== Filename-based TimeFrame Creation Implementation ==========
70

71
#include <algorithm>
72
#include <filesystem>
73
#include <iostream>
74
#include <numeric>
75
#include <regex>
76

77
std::shared_ptr<TimeFrame> createTimeFrameFromFilenames(FilenameTimeFrameOptions const & options) {
12✔
78
    try {
79
        // Check if directory exists - be more permissive for WSL/Windows mounted drives
80
        std::cout << "Checking directory: " << options.folder_path << std::endl;
12✔
81
        
82
        bool directory_accessible = false;
12✔
83
        try {
84
            // First try the standard check
85
            if (std::filesystem::exists(options.folder_path) && std::filesystem::is_directory(options.folder_path)) {
12✔
86
                directory_accessible = true;
11✔
87
                std::cout << "Directory verified via filesystem::exists" << std::endl;
11✔
88
            } else {
89
                // If that fails, try to iterate the directory as a fallback
90
                // This can work even when exists() fails on WSL mounted drives
91
                std::filesystem::directory_iterator dir_iter(options.folder_path);
2✔
92
                directory_accessible = true; // If we get here, directory is accessible
×
93
                std::cout << "Directory verified via directory_iterator (filesystem::exists failed)" << std::endl;
×
94
            }
×
95
        } catch (std::filesystem::filesystem_error const & e) {
1✔
96
            std::cerr << "Error accessing directory: " << options.folder_path << " - " << e.what() << std::endl;
1✔
97
            return nullptr;
1✔
98
        }
1✔
99
        
100
        if (!directory_accessible) {
11✔
101
            std::cerr << "Error: Directory is not accessible: " << options.folder_path << std::endl;
×
102
            return nullptr;
×
103
        }
104

105
        std::vector<int64_t> extracted_values;
11✔
106
        std::regex const pattern(options.regex_pattern);
11✔
107
        std::smatch match;
10✔
108

109
        // Iterate through files in directory
110
        for (auto const & entry: std::filesystem::directory_iterator(options.folder_path)) {
45✔
111
            if (!entry.is_regular_file()) {
35✔
112
                continue;
×
113
            }
114

115
            std::string const filename = entry.path().filename().string();
35✔
116

117
                        // Check file extension
118
            if (!options.file_extension.empty() && 
70✔
119
                !filename.ends_with(options.file_extension)) {
35✔
120
                continue;
2✔
121
            }
122

123
            // Extract numerical value using regex
124
            if (std::regex_search(filename, match, pattern)) {
33✔
125
                if (match.size() >= 2) {// Must have at least one capture group
31✔
126
                    try {
127
                        int64_t const value = std::stoll(match[1].str());
33✔
128
                        extracted_values.push_back(value);
29✔
129
                    } catch (std::exception const & e) {
2✔
130
                        std::cout << "Warning: Could not parse number from filename " << filename
131
                                  << " (matched: '" << match[1].str() << "'): " << e.what() << std::endl;
2✔
132
                    }
2✔
133
                } else {
134
                    std::cout << "Warning: Regex pattern matched but no capture group found in filename: "
135
                              << filename << std::endl;
×
136
                }
137
            } else {
138
                std::cout << "Warning: No match found for pattern in filename: " << filename << std::endl;
2✔
139
            }
140
        }
45✔
141

142
        if (extracted_values.empty()) {
10✔
143
            std::cerr << "Error: No valid numerical values extracted from filenames" << std::endl;
2✔
144
            return nullptr;
2✔
145
        }
146

147
        // Sort values if requested
148
        if (options.sort_ascending) {
8✔
149
            std::sort(extracted_values.begin(), extracted_values.end());
7✔
150
        }
151

152
        // Create TimeFrame based on mode
153
        std::vector<int> time_values;
8✔
154

155
        switch (options.mode) {
8✔
156
            case FilenameTimeFrameMode::FOUND_VALUES: {
6✔
157
                // Use only the extracted values
158
                time_values.reserve(extracted_values.size());
6✔
159
                for (int64_t val: extracted_values) {
27✔
160
                    time_values.push_back(static_cast<int>(val));
21✔
161
                }
162
                break;
163
            }
164
            case FilenameTimeFrameMode::ZERO_TO_MAX: {
1✔
165
                // Create range from 0 to maximum value
166
                auto max_val = *std::max_element(extracted_values.begin(), extracted_values.end());
1✔
167
                time_values.resize(static_cast<size_t>(max_val + 1));
1✔
168
                std::iota(time_values.begin(), time_values.end(), 0);
1✔
169
                break;
1✔
170
            }
171
            case FilenameTimeFrameMode::MIN_TO_MAX: {
1✔
172
                // Create range from minimum to maximum value
173
                auto [min_it, max_it] = std::minmax_element(extracted_values.begin(), extracted_values.end());
1✔
174
                int64_t const min_val = *min_it;
1✔
175
                int64_t const max_val = *max_it;
1✔
176
                auto const range_size = static_cast<size_t>(max_val - min_val + 1);
1✔
177
                time_values.resize(range_size);
1✔
178
                std::iota(time_values.begin(), time_values.end(), static_cast<int>(min_val));
1✔
179
                break;
1✔
180
            }
181
        }
182

183
        std::cout << "Created TimeFrame from " << extracted_values.size()
8✔
184
                  << " filenames with " << time_values.size() << " time points" << std::endl;
8✔
185

186
        return std::make_shared<TimeFrame>(time_values);
8✔
187

188
    } catch (std::exception const & e) {
12✔
189
        std::cerr << "Error creating TimeFrame from filenames: " << e.what() << std::endl;
1✔
190
        return nullptr;
1✔
191
    }
1✔
192
}
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