• 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

0.0
/src/DataManager/transforms/Lines/line_subsegment.cpp
1
#include "line_subsegment.hpp"
2

3
#include "Lines/Line_Data.hpp"
4
#include "CoreGeometry/line_geometry.hpp"
5
#include "utils/polynomial/parametric_polynomial_utils.hpp"
6
#include "utils/polynomial/polynomial_fit.hpp"
7

8
#include <algorithm>
9
#include <cmath>
10
#include <iostream>
11
#include <vector>
12

13
/**
14
 * @brief Extract subsegment using direct point selection based on distance
15
 */
16
std::vector<Point2D<float>> extract_direct_subsegment(
×
17
        Line2D const & line,
18
        float start_pos,
19
        float end_pos,
20
        bool preserve_spacing) {
21
    
22
    // Use the new distance-based utility function
23
    return extract_line_subsegment_by_distance(line, start_pos, end_pos, preserve_spacing);
×
24
}
25

26
/**
27
 * @brief Extract subsegment using parametric polynomial interpolation with distance-based positioning
28
 */
29
std::vector<Point2D<float>> extract_parametric_subsegment(
×
30
        Line2D const & line,
31
        float start_pos,
32
        float end_pos,
33
        int polynomial_order,
34
        int output_points) {
35
    
36
    if (line.empty()) {
×
37
        return {};
×
38
    }
39
    
40
    if (line.size() == 1) {
×
41
        return {line[0]};
×
42
    }
43
    
44
    // Ensure valid range
45
    start_pos = std::max(0.0f, std::min(1.0f, start_pos));
×
46
    end_pos = std::max(0.0f, std::min(1.0f, end_pos));
×
47
    
48
    if (start_pos >= end_pos) {
×
49
        return {};
×
50
    }
51
    
52
    // Need enough points for polynomial fitting
53
    if (line.size() < static_cast<size_t>(polynomial_order + 1)) {
×
54
        // Fall back to direct method
55
        return extract_direct_subsegment(line, start_pos, end_pos, false);
×
56
    }
57
    
58
    // Calculate cumulative distances for distance-based parameterization
59
    std::vector<float> distances = calc_cumulative_length_vector(line);
×
60
    float total_length = distances.back();
×
61
    
62
    if (total_length < 1e-6f) {
×
63
        // Line has no length, return first point
64
        return {line[0]};
×
65
    }
66
    
67
    // Convert fractional positions to actual distances
68
    //float start_distance = start_pos * total_length;
69
    //float end_distance = end_pos * total_length;
70
    
71
    // Create normalized t-values based on cumulative distance
72
    std::vector<double> t_values;
×
73
    t_values.reserve(line.size());
×
74
    for (float distance : distances) {
×
75
        t_values.push_back(static_cast<double>(distance / total_length));
×
76
    }
77
    
78
    // Extract coordinates
79
    std::vector<double> x_coords, y_coords;
×
80
    x_coords.reserve(line.size());
×
81
    y_coords.reserve(line.size());
×
82
    
83
    for (auto const & point : line) {
×
84
        x_coords.push_back(static_cast<double>(point.x));
×
85
        y_coords.push_back(static_cast<double>(point.y));
×
86
    }
87
    
88
    // Fit parametric polynomials
89
    std::vector<double> x_coeffs = fit_single_dimension_polynomial_internal(x_coords, t_values, polynomial_order);
×
90
    std::vector<double> y_coeffs = fit_single_dimension_polynomial_internal(y_coords, t_values, polynomial_order);
×
91
    
92
    if (x_coeffs.empty() || y_coeffs.empty()) {
×
93
        // Fall back to direct method if fitting failed
94
        return extract_direct_subsegment(line, start_pos, end_pos, false);
×
95
    }
96
    
97
    // Generate subsegment points using distance-based parameterization
98
    std::vector<Point2D<float>> subsegment;
×
NEW
99
    subsegment.reserve(static_cast<size_t>(output_points));
×
100
    
101
    for (int i = 0; i < output_points; ++i) {
×
102
        // Map from output point index to t-parameter in the subsegment range
NEW
103
        float t_local = static_cast<float>(i) / static_cast<float>(output_points - 1);
×
NEW
104
        float t_global = start_pos + t_local * (end_pos - start_pos);
×
105
        
106
        // Evaluate polynomials at this t-value
NEW
107
        double x = evaluate_polynomial(x_coeffs, static_cast<double>(t_global));
×
NEW
108
        double y = evaluate_polynomial(y_coeffs, static_cast<double>(t_global));
×
109
        
110
        subsegment.push_back({static_cast<float>(x), static_cast<float>(y)});
×
111
    }
112
    
113
    return subsegment;
×
114
}
×
115

116
///////////////////////////////////////////////////////////////////////////////
117

118
std::shared_ptr<LineData> extract_line_subsegment(
×
119
        LineData const * line_data,
120
        LineSubsegmentParameters const & params) {
121
    return extract_line_subsegment(line_data, params, [](int){});
×
122
}
123

124
std::shared_ptr<LineData> extract_line_subsegment(
×
125
        LineData const * line_data,
126
        LineSubsegmentParameters const & params,
127
        ProgressCallback progressCallback) {
128
    
129
    auto result_line_data = std::make_shared<LineData>();
×
130
    
131
    if (!line_data) {
×
132
        progressCallback(100);
×
133
        return result_line_data;
×
134
    }
135
    
136
    // Copy image size
137
    result_line_data->setImageSize(line_data->getImageSize());
×
138
    
139
    // Get all times with data for progress calculation
140
    auto times_with_data = line_data->getTimesWithData();
×
141
    if (times_with_data.empty()) {
×
142
        progressCallback(100);
×
143
        return result_line_data;
×
144
    }
145
    
146
    progressCallback(0);
×
147
    
148
    size_t processed_times = 0;
×
149
    for (auto time : times_with_data) {
×
150
        auto const & lines_at_time = line_data->getAtTime(time);
×
151
        
152
        for (auto const & line : lines_at_time) {
×
153
            if (line.empty()) {
×
154
                continue;
×
155
            }
156
            
157
            std::vector<Point2D<float>> subsegment;
×
158
            
159
            if (params.method == SubsegmentExtractionMethod::Direct) {
×
160
                subsegment = extract_direct_subsegment(
×
161
                    line,
162
                    params.start_position,
×
163
                    params.end_position,
×
164
                    params.preserve_original_spacing
×
165
                );
×
166
            } else { // Parametric
167
                subsegment = extract_parametric_subsegment(
×
168
                    line,
169
                    params.start_position,
×
170
                    params.end_position,
×
171
                    params.polynomial_order,
×
172
                    params.output_points
×
173
                );
×
174
            }
175
            
176
            if (!subsegment.empty()) {
×
177
                result_line_data->addAtTime(time, subsegment, false);
×
178
            }
179
        }
×
180
        
181
        processed_times++;
×
182
        int progress = static_cast<int>(
NEW
183
            std::round(static_cast<double>(processed_times) / static_cast<double>(times_with_data.size()) * 100.0)
×
184
        );
×
185
        progressCallback(progress);
×
186
    }
187
    
188
    progressCallback(100);
×
189
    return result_line_data;
×
190
}
×
191

192
///////////////////////////////////////////////////////////////////////////////
193

194
std::string LineSubsegmentOperation::getName() const {
×
195
    return "Extract Line Subsegment";
×
196
}
197

198
std::type_index LineSubsegmentOperation::getTargetInputTypeIndex() const {
×
199
    return typeid(std::shared_ptr<LineData>);
×
200
}
201

202
bool LineSubsegmentOperation::canApply(DataTypeVariant const & dataVariant) const {
×
203
    if (!std::holds_alternative<std::shared_ptr<LineData>>(dataVariant)) {
×
204
        return false;
×
205
    }
206
    
207
    auto const * ptr_ptr = std::get_if<std::shared_ptr<LineData>>(&dataVariant);
×
208
    return ptr_ptr && *ptr_ptr;
×
209
}
210

211
std::unique_ptr<TransformParametersBase> LineSubsegmentOperation::getDefaultParameters() const {
×
212
    return std::make_unique<LineSubsegmentParameters>();
×
213
}
214

215
DataTypeVariant LineSubsegmentOperation::execute(DataTypeVariant const & dataVariant,
×
216
                                                TransformParametersBase const * transformParameters) {
217
    return execute(dataVariant, transformParameters, [](int){});
×
218
}
219

220
DataTypeVariant LineSubsegmentOperation::execute(DataTypeVariant const & dataVariant,
×
221
                                                TransformParametersBase const * transformParameters,
222
                                                ProgressCallback progressCallback) {
223
    
224
    auto const * line_data_ptr = std::get_if<std::shared_ptr<LineData>>(&dataVariant);
×
225
    
226
    if (!line_data_ptr || !(*line_data_ptr)) {
×
227
        std::cerr << "LineSubsegmentOperation::execute called with incompatible variant type or null data." << std::endl;
×
228
        return {};
×
229
    }
230
    
231
    LineData const * input_line_data = (*line_data_ptr).get();
×
232
    
233
    LineSubsegmentParameters const * params = nullptr;
×
234
    std::unique_ptr<TransformParametersBase> default_params_owner;
×
235
    
236
    if (transformParameters) {
×
237
        params = dynamic_cast<LineSubsegmentParameters const *>(transformParameters);
×
238
        if (!params) {
×
239
            std::cerr << "LineSubsegmentOperation::execute: Invalid parameter type. Using defaults." << std::endl;
×
240
            default_params_owner = getDefaultParameters();
×
241
            params = static_cast<LineSubsegmentParameters const *>(default_params_owner.get());
×
242
        }
243
    } else {
244
        default_params_owner = getDefaultParameters();
×
245
        params = static_cast<LineSubsegmentParameters const *>(default_params_owner.get());
×
246
    }
247
    
248
    if (!params) {
×
249
        std::cerr << "LineSubsegmentOperation::execute: Failed to get parameters." << std::endl;
×
250
        return {};
×
251
    }
252
    
253
    std::shared_ptr<LineData> result = extract_line_subsegment(input_line_data, *params, progressCallback);
×
254
    
255
    if (!result) {
×
256
        std::cerr << "LineSubsegmentOperation::execute: 'extract_line_subsegment' failed to produce a result." << std::endl;
×
257
        return {};
×
258
    }
259
    
260
    std::cout << "LineSubsegmentOperation executed successfully." << std::endl;
×
261
    return result;
×
262
} 
×
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