• 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/CoreGeometry/src/line_resampling.cpp
1
#include "CoreGeometry/line_resampling.hpp"
2

3
#include <cmath>// For std::sqrt, std::abs
4

5
// Function definition moved from mask_to_line.cpp
6
Line2D resample_line_points(
×
7
        Line2D const & input_points,
8
        float target_spacing) {
NEW
9
    if (input_points.empty() || target_spacing <= 1e-6f) {
×
10
        return input_points;// Return original if no points or invalid spacing
×
11
    }
12
    if (input_points.size() == 1) {
×
13
        return input_points;// Return single point as is
×
14
    }
15

16
    Line2D resampled_points;
×
17
    resampled_points.push_back(input_points.front());// Always include the first point
×
18

NEW
19
    auto accumulated_distance_since_last_sample = 0.0f;
×
NEW
20
    auto current_segment_traversed_distance = 0.0f;
×
21

22
    for (size_t i = 0; i < input_points.size() - 1; ++i) {
×
23
        Point2D<float> const & p1 = input_points[i];
×
24
        Point2D<float> const & p2 = input_points[i + 1];
×
25

NEW
26
        auto segment_dx = p2.x - p1.x;
×
NEW
27
        auto segment_dy = p2.y - p1.y;
×
NEW
28
        auto segment_length = std::sqrt(segment_dx * segment_dx + segment_dy * segment_dy);
×
29

NEW
30
        if (segment_length < 1e-6f) continue;// Skip zero-length segments
×
31

32
        // Position along the current segment where the next sample point should be placed
NEW
33
        auto dist_to_next_in_segment = target_spacing - accumulated_distance_since_last_sample;
×
34

35
        while (dist_to_next_in_segment <= segment_length - current_segment_traversed_distance) {
×
NEW
36
            auto interpolation_factor = (current_segment_traversed_distance + dist_to_next_in_segment) / segment_length;
×
37

NEW
38
            float new_x = p1.x + segment_dx * interpolation_factor;
×
NEW
39
            float new_y = p1.y + segment_dy * interpolation_factor;
×
40
            // Avoid duplicate points if possible by checking distance to last added point
41
            if (resampled_points.empty() ||
×
NEW
42
                (std::abs(new_x - resampled_points.back().x) > 1e-3f || std::abs(new_y - resampled_points.back().y) > 1e-3f)) {
×
43
                resampled_points.push_back({new_x, new_y});
×
44
            }
45

46
            current_segment_traversed_distance += dist_to_next_in_segment;// Advance position in current segment
×
NEW
47
            accumulated_distance_since_last_sample = 0.0f;                 // Reset for next sample distance calculation
×
48
            dist_to_next_in_segment = target_spacing;                     // Next sample is a full target_spacing away
×
49
        }
50
        // Add the remaining part of the segment to the accumulated distance for the next segment
51
        accumulated_distance_since_last_sample += (segment_length - current_segment_traversed_distance);
×
NEW
52
        current_segment_traversed_distance = 0.0f;// Reset for next segment
×
53
    }
54

55
    // Ensure the last point is always included if it wasn't the last one sampled
56
    if (!input_points.empty()) {
×
57
        auto const & last_original = input_points.back();
×
58
        bool add_last = true;
×
59
        if (!resampled_points.empty()) {
×
60
            auto const & last_sampled = resampled_points.back();
×
NEW
61
            if (std::abs(last_original.x - last_sampled.x) < 1e-3f && std::abs(last_original.y - last_sampled.y) < 1e-3f) {
×
62
                add_last = false;// Already added or very close
×
63
            }
64
        }
65
        if (add_last) {
×
66
            resampled_points.push_back(last_original);
×
67
        }
68
    }
69
    // If, after all, only one point was added (the first one), and there are more original points, add the last one too.
70
    // This handles cases with very short lines or large target_spacing.
71
    if (resampled_points.size() == 1 && input_points.size() > 1) {
×
72
        resampled_points.push_back(input_points.back());
×
73
    }
74

75
    return resampled_points;
×
76
}
×
77

78
/**
79
 * @brief Calculates the perpendicular distance from a point to a line segment.
80
 *
81
 * @param point The point to calculate distance from.
82
 * @param line_start The start point of the line segment.
83
 * @param line_end The end point of the line segment.
84
 * @return The perpendicular distance from the point to the line segment.
85
 */
86
static float perpendicular_distance(
×
87
        Point2D<float> const & point,
88
        Point2D<float> const & line_start,
89
        Point2D<float> const & line_end) {
90

NEW
91
    if (std::abs(line_end.x - line_start.x) < 1e-6f && std::abs(line_end.y - line_start.y) < 1e-6f) {
×
92
        // Line segment is actually a point, calculate distance to that point
93
        float dx = point.x - line_start.x;
×
94
        float dy = point.y - line_start.y;
×
95
        return std::sqrt(dx * dx + dy * dy);
×
96
    }
97

98
    // Calculate the perpendicular distance from point to line segment
99
    float A = line_end.y - line_start.y;
×
100
    float B = line_start.x - line_end.x;
×
101
    float C = line_end.x * line_start.y - line_start.x * line_end.y;
×
102

103
    float numerator = std::abs(A * point.x + B * point.y + C);
×
104
    float denominator = std::sqrt(A * A + B * B);
×
105

106
    return numerator / denominator;
×
107
}
108

109
/**
110
 * @brief Recursive helper function for Douglas-Peucker algorithm.
111
 *
112
 * @param points The input points.
113
 * @param start_idx Starting index of the current segment.
114
 * @param end_idx Ending index of the current segment.
115
 * @param epsilon The maximum perpendicular distance tolerance.
116
 * @param result_indices Vector to store indices of points to keep.
117
 */
118
static void douglas_peucker_recursive(
×
119
        Line2D const & points,
120
        size_t start_idx,
121
        size_t end_idx,
122
        float epsilon,
123
        std::vector<bool> & keep_point) {
124

125
    if (end_idx <= start_idx + 1) {
×
126
        return;// No intermediate points to check
×
127
    }
128

129
    Point2D<float> const & start_point = points[start_idx];
×
130
    Point2D<float> const & end_point = points[end_idx];
×
131

132
    float max_distance = 0.0f;
×
133
    size_t max_distance_idx = start_idx;
×
134

135
    // Find the point with maximum perpendicular distance
136
    for (size_t i = start_idx + 1; i < end_idx; ++i) {
×
137
        float distance = perpendicular_distance(points[i], start_point, end_point);
×
138
        if (distance > max_distance) {
×
139
            max_distance = distance;
×
140
            max_distance_idx = i;
×
141
        }
142
    }
143

144
    // If the maximum distance is greater than epsilon, recursively simplify
145
    if (max_distance > epsilon) {
×
146
        keep_point[max_distance_idx] = true;
×
147

148
        // Recursively simplify the two sub-segments
149
        douglas_peucker_recursive(points, start_idx, max_distance_idx, epsilon, keep_point);
×
150
        douglas_peucker_recursive(points, max_distance_idx, end_idx, epsilon, keep_point);
×
151
    }
152
}
153

154
Line2D douglas_peucker_simplify(
×
155
        Line2D const & input_points,
156
        float epsilon) {
157

158
    if (input_points.size() <= 2) {
×
159
        return input_points;// No simplification possible for 2 or fewer points
×
160
    }
161

162
    if (epsilon <= 0.0f) {
×
163
        return input_points;// No simplification if epsilon is non-positive
×
164
    }
165

166
    // Vector to track which points to keep
167
    std::vector<bool> keep_point(input_points.size(), false);
×
168

169
    // Always keep the first and last points
170
    keep_point[0] = true;
×
171
    keep_point[input_points.size() - 1] = true;
×
172

173
    // Recursively simplify the line
174
    douglas_peucker_recursive(input_points, 0, input_points.size() - 1, epsilon, keep_point);
×
175

176
    // Build the result from kept points
177
    Line2D simplified_line;
×
178
    for (size_t i = 0; i < input_points.size(); ++i) {
×
179
        if (keep_point[i]) {
×
180
            simplified_line.push_back(input_points[i]);
×
181
        }
182
    }
183

184
    return simplified_line;
×
185
}
×
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