• 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_clip.cpp
1
#include "line_clip.hpp"
2

3
#include "Lines/Line_Data.hpp"
4

5
#include <cmath>
6
#include <iostream>
7
#include <vector>
8

9
std::optional<Point2D<float>> line_segment_intersection(
×
10
    Point2D<float> const & p1, Point2D<float> const & p2,
11
    Point2D<float> const & p3, Point2D<float> const & p4) {
12
    
13
    // Calculate direction vectors
14
    float d1x = p2.x - p1.x;
×
15
    float d1y = p2.y - p1.y;
×
16
    float d2x = p4.x - p3.x;
×
17
    float d2y = p4.y - p3.y;
×
18
    
19
    // Calculate the denominator for the intersection calculation
20
    float denominator = d1x * d2y - d1y * d2x;
×
21
    
22
    // Check if lines are parallel (denominator is zero)
23
    if (std::abs(denominator) < 1e-10f) {
×
24
        return std::nullopt;
×
25
    }
26
    
27
    // Calculate parameters for intersection point
28
    float dx = p1.x - p3.x;
×
29
    float dy = p1.y - p3.y;
×
30
    
31
    float t1 = (dx * d2y - dy * d2x) / denominator;
×
32
    float t2 = (dx * d1y - dy * d1x) / denominator;
×
33
    
34
    // Check if intersection point lies within both line segments
35
    if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f) {
×
36
        // Calculate intersection point
37
        Point2D<float> intersection;
×
38
        intersection.x = p1.x + t1 * d1x;
×
39
        intersection.y = p1.y + t1 * d1y;
×
40
        return intersection;
×
41
    }
42
    
43
    return std::nullopt;
×
44
}
45

46
std::optional<std::pair<Point2D<float>, size_t>> find_line_intersection(
×
47
    Line2D const & line, Line2D const & reference_line) {
48
    
49
    if (line.size() < 2 || reference_line.size() < 2) {
×
50
        return std::nullopt;
×
51
    }
52
    
53
    // Check each segment of the main line against each segment of the reference line
54
    for (size_t i = 0; i < line.size() - 1; ++i) {
×
55
        for (size_t j = 0; j < reference_line.size() - 1; ++j) {
×
56
            auto intersection = line_segment_intersection(
×
57
                line[i], line[i + 1],
×
58
                reference_line[j], reference_line[j + 1]
×
59
            );
×
60
            
61
            if (intersection.has_value()) {
×
62
                return std::make_pair(intersection.value(), i);
×
63
            }
64
        }
65
    }
66
    
67
    return std::nullopt;
×
68
}
69

70
Line2D clip_line_at_intersection(
×
71
    Line2D const & line, 
72
    Line2D const & reference_line, 
73
    ClipSide clip_side) {
74
    
75
    if (line.size() < 2) {
×
76
        return line; // Return original line if too short
×
77
    }
78
    
79
    auto intersection_result = find_line_intersection(line, reference_line);
×
80
    
81
    if (!intersection_result.has_value()) {
×
82
        // No intersection found, return original line
83
        return line;
×
84
    }
85
    
86
    Point2D<float> intersection_point = intersection_result->first;
×
87
    size_t segment_index = intersection_result->second;
×
88
    
89
    Line2D clipped_line;
×
90
    
91
    if (clip_side == ClipSide::KeepBase) {
×
92
        // Keep from start to intersection
93
        for (size_t i = 0; i <= segment_index; ++i) {
×
94
            clipped_line.push_back(line[i]);
×
95
        }
96
        // Add intersection point if it's not the same as the last point
97
        if (clipped_line.empty() || 
×
98
            (std::abs(clipped_line.back().x - intersection_point.x) > 1e-6f ||
×
99
             std::abs(clipped_line.back().y - intersection_point.y) > 1e-6f)) {
×
100
            clipped_line.push_back(intersection_point);
×
101
        }
102
    } else { // ClipSide::KeepDistal
103
        // Keep from intersection to end
104
        clipped_line.push_back(intersection_point);
×
105
        for (size_t i = segment_index + 1; i < line.size(); ++i) {
×
106
            clipped_line.push_back(line[i]);
×
107
        }
108
    }
109
    
110
    return clipped_line;
×
111
}
×
112

113
///////////////////////////////////////////////////////////////////////////////
114

115
std::shared_ptr<LineData> clip_lines(
×
116
    LineData const * line_data,
117
    LineClipParameters const * params) {
118
    return clip_lines(line_data, params, [](int){});
×
119
}
120

121
std::shared_ptr<LineData> clip_lines(
×
122
    LineData const * line_data,
123
    LineClipParameters const * params,
124
    ProgressCallback progressCallback) {
125
    
126
    auto result_line_data = std::make_shared<LineData>();
×
127
    
128
    if (!line_data || !params || !params->reference_line_data) {
×
129
        std::cerr << "LineClip: Invalid input parameters." << std::endl;
×
130
        progressCallback(100);
×
131
        return result_line_data;
×
132
    }
133
    
134
    // Copy image size from input
135
    result_line_data->setImageSize(line_data->getImageSize());
×
136
    
137
    // Get reference line from the specified frame
138
    auto const & reference_lines = params->reference_line_data->getAtTime(TimeFrameIndex(params->reference_frame));
×
139
    if (reference_lines.empty()) {
×
140
        std::cerr << "LineClip: No reference line found at frame " << params->reference_frame << std::endl;
×
141
        progressCallback(100);
×
142
        return result_line_data;
×
143
    }
144
    
145
    // Use the first line from the reference frame
146
    Line2D const & reference_line = reference_lines[0];
×
147
    
148
    // Get all times with data for progress calculation
149
    auto times_with_data = line_data->getTimesWithData();
×
150
    if (times_with_data.empty()) {
×
151
        progressCallback(100);
×
152
        return result_line_data;
×
153
    }
154
    
155
    progressCallback(0);
×
156
    
157
    size_t processed_times = 0;
×
158
    
159
    for (auto time : times_with_data) {
×
160
        auto const & lines_at_time = line_data->getAtTime(time);
×
161
        
162
        for (auto const & line : lines_at_time) {
×
163
            if (line.size() < 2) {
×
164
                continue; // Skip lines that are too short
×
165
            }
166
            
167
            // Clip the line using the reference line
168
            Line2D clipped_line = clip_line_at_intersection(line, reference_line, params->clip_side);
×
169
            
170
            // Add clipped line to result (only if it has at least 2 points)
171
            if (clipped_line.size() >= 2) {
×
172
                result_line_data->addAtTime(time, clipped_line, false);
×
173
            }
174
        }
×
175
        
176
        processed_times++;
×
177
        int progress = static_cast<int>(
NEW
178
            std::round(static_cast<double>(processed_times) / static_cast<double>(times_with_data.size()) * 100.0)
×
179
        );
×
180
        progressCallback(progress);
×
181
    }
182
    
183
    progressCallback(100);
×
184
    return result_line_data;
×
185
}
×
186

187
///////////////////////////////////////////////////////////////////////////////
188

189
std::string LineClipOperation::getName() const {
×
190
    return "Clip Line by Reference Line";
×
191
}
192

193
std::type_index LineClipOperation::getTargetInputTypeIndex() const {
×
194
    return typeid(std::shared_ptr<LineData>);
×
195
}
196

197
bool LineClipOperation::canApply(DataTypeVariant const & dataVariant) const {
×
198
    if (!std::holds_alternative<std::shared_ptr<LineData>>(dataVariant)) {
×
199
        return false;
×
200
    }
201
    
202
    auto const * ptr_ptr = std::get_if<std::shared_ptr<LineData>>(&dataVariant);
×
203
    return ptr_ptr && *ptr_ptr;
×
204
}
205

206
std::unique_ptr<TransformParametersBase> LineClipOperation::getDefaultParameters() const {
×
207
    return std::make_unique<LineClipParameters>();
×
208
}
209

210
DataTypeVariant LineClipOperation::execute(DataTypeVariant const & dataVariant,
×
211
                                          TransformParametersBase const * transformParameters) {
212
    return execute(dataVariant, transformParameters, [](int){});
×
213
}
214

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