• 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

25.57
/src/DataManager/transforms/Lines/line_alignment.cpp
1
#include "line_alignment.hpp"
2

3
#include "Lines/Line_Data.hpp"
4
#include "Media/Media_Data.hpp"
5

6
#include <algorithm>
7
#include <cmath>
8
#include <iostream>
9
#include <limits>
10
#include <map>
11
#include <vector>
12

13

14

15
// Helper function to get pixel value at a given position
16
uint8_t get_pixel_value(Point2D<float> const & point, 
10,546✔
17
                        std::vector<uint8_t> const & image_data, 
18
                        ImageSize const & image_size) {
19
    int x = static_cast<int>(std::round(point.x));
10,546✔
20
    int y = static_cast<int>(std::round(point.y));
10,546✔
21
    
22
    // Check bounds
23
    if (x < 0 || x >= image_size.width || y < 0 || y >= image_size.height) {
10,546✔
24
        return 0;
1,122✔
25
    }
26
    
27
    size_t index = static_cast<size_t>(y * image_size.width + x);
9,424✔
28
    //size_t index = static_cast<size_t>(x * image_size.height + y);
29
    if (index < image_data.size()) {
9,424✔
30
        return image_data[index];
8,863✔
31
    }
32
    
33
    return 0;
561✔
34
}
35

36
// Calculate FWHM center point for a single vertex
37
Point2D<float> calculate_fwhm_center(Point2D<float> const & vertex,
19✔
38
                                     Point2D<float> const & perpendicular_dir,
39
                                     int width,
40
                                     int perpendicular_range,
41
                                     std::vector<uint8_t> const & image_data,
42
                                     ImageSize const & image_size,
43
                                     FWHMApproach /*approach*/) {
44
    if (width <= 0) {
19✔
45
        return vertex; // Return original vertex if no width
1✔
46
    }
47
    
48
    std::vector<Point2D<float>> center_points;
18✔
49
    std::vector<float> intensities;
18✔
50
    
51
    // Sample multiple points along the width of the analysis strip
52
    for (int w = -width/2; w <= width/2; ++w) {
220✔
53
        // Calculate the sampling point along the width direction
54
        Point2D<float> width_dir{-perpendicular_dir.y, perpendicular_dir.x}; // Perpendicular to perp dir
202✔
55
        
56
        Point2D<float> sample_start = {
57
            vertex.x + width_dir.x * static_cast<float>(w),
202✔
58
            vertex.y + width_dir.y * static_cast<float>(w)
202✔
59
        };
202✔
60
        
61
        // Sample intensity profile along the perpendicular direction
62
        std::vector<uint8_t> profile;
202✔
63
        std::vector<float> distances;
202✔
64
        
65
        // Sample up to perpendicular_range pixels in each direction along the perpendicular
66
        for (int d = -perpendicular_range/2; d <= perpendicular_range/2; ++d) {
10,746✔
67
            Point2D<float> sample_point = {
10,544✔
68
                sample_start.x + perpendicular_dir.x * static_cast<float>(d),
10,544✔
69
                sample_start.y + perpendicular_dir.y * static_cast<float>(d)
10,544✔
70
            };
10,544✔
71
            
72
            uint8_t intensity = get_pixel_value(sample_point, image_data, image_size);
10,544✔
73
            profile.push_back(intensity);
10,544✔
74
            distances.push_back(static_cast<float>(d));
10,544✔
75
        }
76
        
77
        // Find the maximum intensity in the profile
78
        auto max_it = std::max_element(profile.begin(), profile.end());
202✔
79
        if (max_it == profile.end()) {
202✔
80
            continue;
×
81
        }
82
        
83
        uint8_t max_intensity = *max_it;
202✔
84
        if (max_intensity == 0) {
202✔
85
            continue; // Skip if no signal
46✔
86
        }
87
        
88
        // Find all positions with the maximum intensity
89
        std::vector<int> max_indices;
156✔
90
        for (size_t i = 0; i < profile.size(); ++i) {
8,354✔
91
            if (profile[i] == max_intensity) {
8,198✔
92
                max_indices.push_back(static_cast<int>(i));
420✔
93
            }
94
        }
95
        
96
        // Calculate the average position of all maximum points
97
        float avg_max_index = 0.0f;
156✔
98
        for (int index : max_indices) {
576✔
99
            avg_max_index += static_cast<float>(index);
420✔
100
        }
101
        avg_max_index /= static_cast<float>(max_indices.size());
156✔
102
        
103
        int max_offset = static_cast<int>(std::round(avg_max_index)) - perpendicular_range/2;
156✔
104

105
        // Find minimum in profile 
106
        auto min_it = std::min_element(profile.begin(), profile.end());
156✔
107
        if (min_it == profile.end()) {
156✔
108
            continue;
×
109
        }
110
        
111
        uint8_t min_intensity = *min_it;
156✔
112
        
113
        // Find the half-maximum threshold
114
        uint8_t half_max = static_cast<uint8_t>((static_cast<int>(max_intensity) + static_cast<int>(min_intensity)) / 2);
156✔
115
        
116
        // Find the left and right boundaries at half maximum, starting from the average max position
117
        int left_bound = max_offset;
156✔
118
        int right_bound = max_offset;
156✔
119
        
120
        // Work leftward from the average max position to find the first half-maximum
121
        int avg_max_index_int = static_cast<int>(std::round(avg_max_index));
156✔
122
        for (int i = avg_max_index_int; i >= 0; --i) {
444✔
123
            if (profile[static_cast<size_t>(i)] < half_max) {
444✔
124
                left_bound = i + 1 - perpendicular_range/2; // +1 to include the last point >= half_max
156✔
125
                break;
156✔
126
            }
127
        }
128
        
129
        // Work rightward from the average max position to find the first half-maximum
130
        for (size_t i = static_cast<size_t>(avg_max_index_int); i < profile.size(); ++i) {
444✔
131
            if (profile[i] < half_max) {
433✔
132
                right_bound = static_cast<int>(i) - 1 - perpendicular_range/2; // -1 to include the last point >= half_max
145✔
133
                break;
145✔
134
            }
135
        }
136
        
137
        // Calculate the center point of the FWHM region
138
        float center_offset = (static_cast<float>(left_bound) + static_cast<float>(right_bound)) / 2.0f;
156✔
139
        Point2D<float> center_point = {
156✔
140
            sample_start.x + perpendicular_dir.x * center_offset,
156✔
141
            sample_start.y + perpendicular_dir.y * center_offset
156✔
142
        };
156✔
143
        center_points.push_back(center_point);
156✔
144
        intensities.push_back(static_cast<float>(max_intensity));
156✔
145
    }
248✔
146
    
147
    // Calculate weighted average center point based on intensity
148
    if (center_points.empty()) {
18✔
149
        return vertex; // Return original vertex if no center points found
4✔
150
    }
151
    
152
    float total_weight = 0.0f;
14✔
153
    Point2D<float> weighted_sum{0.0f, 0.0f};
14✔
154
    
155
    for (size_t i = 0; i < center_points.size(); ++i) {
170✔
156
        float weight = intensities[i];
156✔
157
        weighted_sum.x += center_points[i].x * weight;
156✔
158
        weighted_sum.y += center_points[i].y * weight;
156✔
159
        total_weight += weight;
156✔
160
    }
161
    
162
    if (total_weight > 0) {
14✔
163
        return Point2D<float>{weighted_sum.x / total_weight, weighted_sum.y / total_weight};
14✔
164
    }
165
    
166
    return vertex; // Return original vertex if no valid weights
×
167
}
18✔
168

169
// Calculate FWHM profile extents for a single vertex
170
Line2D calculate_fwhm_profile_extents(Point2D<float> const & vertex,
×
171
                                      Point2D<float> const & perpendicular_dir,
172
                                      int width,
173
                                      int perpendicular_range,
174
                                      std::vector<uint8_t> const & image_data,
175
                                      ImageSize const & image_size,
176
                                      FWHMApproach /*approach*/) {
177
    if (width <= 0) {
×
178
        // Return a line with just the original vertex repeated
179
        Line2D debug_line;
×
180
        debug_line.push_back(vertex);
×
181
        debug_line.push_back(vertex);
×
182
        debug_line.push_back(vertex);
×
183
        return debug_line;
×
184
    }
×
185
    
186
    std::vector<Point2D<float>> left_extents;
×
187
    std::vector<Point2D<float>> right_extents;
×
188
    std::vector<Point2D<float>> max_points;
×
189
    std::vector<float> intensities;
×
190
    
191
    // Sample multiple points along the width of the analysis strip
192
    for (int w = -width/2; w <= width/2; ++w) {
×
193
        // Calculate the sampling point along the width direction
194
        Point2D<float> width_dir{-perpendicular_dir.y, perpendicular_dir.x}; // Perpendicular to perp dir
×
195
        
196
        Point2D<float> sample_start = {
NEW
197
            vertex.x + width_dir.x * static_cast<float>(w),
×
NEW
198
            vertex.y + width_dir.y * static_cast<float>(w)
×
UNCOV
199
        };
×
200
        
201
        // Sample intensity profile along the perpendicular direction
202
        std::vector<uint8_t> profile;
×
203
        std::vector<float> distances;
×
204
        
205
        // Sample up to perpendicular_range pixels in each direction along the perpendicular
206
        for (int d = -perpendicular_range/2; d <= perpendicular_range/2; ++d) {
×
207
            Point2D<float> sample_point = {
×
NEW
208
                sample_start.x + perpendicular_dir.x * static_cast<float>(d),
×
NEW
209
                sample_start.y + perpendicular_dir.y * static_cast<float>(d)
×
UNCOV
210
            };
×
211
            
212
            uint8_t intensity = get_pixel_value(sample_point, image_data, image_size);
×
213
            profile.push_back(intensity);
×
214
            distances.push_back(static_cast<float>(d));
×
215
        }
216
        
217
        // Find the maximum intensity in the profile
218
        auto max_it = std::max_element(profile.begin(), profile.end());
×
219
        if (max_it == profile.end()) {
×
220
            continue;
×
221
        }
222
        
223
        uint8_t max_intensity = *max_it;
×
224
        if (max_intensity == 0) {
×
225
            continue; // Skip if no signal
×
226
        }
227
        
228
        // Find all positions with the maximum intensity
229
        std::vector<int> max_indices;
×
NEW
230
        for (size_t i = 0; i < profile.size(); ++i) {
×
231
            if (profile[i] == max_intensity) {
×
NEW
232
                max_indices.push_back(static_cast<int>(i));
×
233
            }
234
        }
235
        
236
        // Calculate the average position of all maximum points
237
        float avg_max_index = 0.0f;
×
238
        for (int index : max_indices) {
×
239
            avg_max_index += static_cast<float>(index);
×
240
        }
241
        avg_max_index /= static_cast<float>(max_indices.size());
×
242
        
243
        int max_offset = static_cast<int>(std::round(avg_max_index)) - perpendicular_range/2;
×
244
        
245
        // Calculate the maximum point location
246
        Point2D<float> max_point = {
×
NEW
247
            sample_start.x + perpendicular_dir.x * static_cast<float>(max_offset),
×
NEW
248
            sample_start.y + perpendicular_dir.y * static_cast<float>(max_offset)
×
UNCOV
249
        };
×
250
        
251
        // Find minimum in profile 
252
        auto min_it = std::min_element(profile.begin(), profile.end());
×
253
        if (min_it == profile.end()) {
×
254
            continue;
×
255
        }
256
        
257
        uint8_t min_intensity = *min_it;
×
258
        
259
        // Find the half-maximum threshold
NEW
260
        uint8_t half_max = static_cast<uint8_t>((static_cast<int>(max_intensity) + static_cast<int>(min_intensity)) / 2);
×
261
        
262
        // Find the left and right boundaries at half maximum, starting from the average max position
263
        int left_bound = max_offset;
×
264
        int right_bound = max_offset;
×
265
        
266
        // Work leftward from the average max position to find the first half-maximum
267
        int avg_max_index_int = static_cast<int>(std::round(avg_max_index));
×
268
        for (int i = avg_max_index_int; i >= 0; --i) {
×
NEW
269
            if (profile[static_cast<size_t>(i)] < half_max) {
×
270
                left_bound = i + 1 - perpendicular_range/2; // +1 to include the last point >= half_max
×
271
                break;
×
272
            }
273
        }
274
        
275
        // Work rightward from the average max position to find the first half-maximum
NEW
276
        for (size_t i = static_cast<size_t>(avg_max_index_int); i < profile.size(); ++i) {
×
277
            if (profile[i] < half_max) {
×
NEW
278
                right_bound = static_cast<int>(i) - 1 - perpendicular_range/2; // -1 to include the last point >= half_max
×
279
                break;
×
280
            }
281
        }
282
        
283
        // Calculate the left and right extent points
284
        Point2D<float> left_extent = {
×
NEW
285
            sample_start.x + perpendicular_dir.x * static_cast<float>(left_bound),
×
NEW
286
            sample_start.y + perpendicular_dir.y * static_cast<float>(left_bound)
×
UNCOV
287
        };
×
288
        
289
        Point2D<float> right_extent = {
×
NEW
290
            sample_start.x + perpendicular_dir.x * static_cast<float>(right_bound),
×
NEW
291
            sample_start.y + perpendicular_dir.y * static_cast<float>(right_bound)
×
UNCOV
292
        };
×
293
        
294
        left_extents.push_back(left_extent);
×
295
        right_extents.push_back(right_extent);
×
296
        max_points.push_back(max_point);
×
297
        intensities.push_back(static_cast<float>(max_intensity));
×
298
    }
×
299
    
300
    // Calculate weighted average extents based on intensity
301
    if (left_extents.empty()) {
×
302
        // Return a line with just the original vertex repeated
303
        Line2D debug_line;
×
304
        debug_line.push_back(vertex);
×
305
        debug_line.push_back(vertex);
×
306
        debug_line.push_back(vertex);
×
307
        return debug_line;
×
308
    }
×
309
    
310
    float total_weight = 0.0f;
×
311
    Point2D<float> weighted_left_sum{0.0f, 0.0f};
×
312
    Point2D<float> weighted_right_sum{0.0f, 0.0f};
×
313
    Point2D<float> weighted_max_sum{0.0f, 0.0f};
×
314
    
315
    for (size_t i = 0; i < left_extents.size(); ++i) {
×
316
        float weight = intensities[i];
×
317
        weighted_left_sum.x += left_extents[i].x * weight;
×
318
        weighted_left_sum.y += left_extents[i].y * weight;
×
319
        weighted_right_sum.x += right_extents[i].x * weight;
×
320
        weighted_right_sum.y += right_extents[i].y * weight;
×
321
        weighted_max_sum.x += max_points[i].x * weight;
×
322
        weighted_max_sum.y += max_points[i].y * weight;
×
323
        total_weight += weight;
×
324
    }
325
    
326
    if (total_weight > 0) {
×
327
        Point2D<float> avg_left = {weighted_left_sum.x / total_weight, weighted_left_sum.y / total_weight};
×
328
        Point2D<float> avg_right = {weighted_right_sum.x / total_weight, weighted_right_sum.y / total_weight};
×
329
        Point2D<float> avg_max = {weighted_max_sum.x / total_weight, weighted_max_sum.y / total_weight};
×
330
        
331
        Line2D debug_line;
×
332
        debug_line.push_back(avg_left);
×
333
        debug_line.push_back(avg_max);
×
334
        debug_line.push_back(avg_right);
×
335
        return debug_line;
×
336
    }
×
337
    
338
    // Return a line with just the original vertex repeated
339
    Line2D debug_line;
×
340
    debug_line.push_back(vertex);
×
341
    debug_line.push_back(vertex);
×
342
    debug_line.push_back(vertex);
×
343
    return debug_line;
×
344
}
×
345

346
///////////////////////////////////////////////////////////////////////////////
347

348
std::string LineAlignmentOperation::getName() const {
×
349
    return "Line Alignment to Bright Features";
×
350
}
351

352
std::type_index LineAlignmentOperation::getTargetInputTypeIndex() const {
×
353
    return typeid(std::shared_ptr<LineData>);
×
354
}
355

356
bool LineAlignmentOperation::canApply(DataTypeVariant const & dataVariant) const {
×
357
    if (!std::holds_alternative<std::shared_ptr<LineData>>(dataVariant)) {
×
358
        return false;
×
359
    }
360

361
    auto const * ptr_ptr = std::get_if<std::shared_ptr<LineData>>(&dataVariant);
×
362

363
    return ptr_ptr && *ptr_ptr;
×
364
}
365

366
std::unique_ptr<TransformParametersBase> LineAlignmentOperation::getDefaultParameters() const {
×
367
    return std::make_unique<LineAlignmentParameters>();
×
368
}
369

370
DataTypeVariant LineAlignmentOperation::execute(DataTypeVariant const & dataVariant,
×
371
                                               TransformParametersBase const * transformParameters) {
372
    return execute(dataVariant, transformParameters, [](int) {});
×
373
}
374

375
DataTypeVariant LineAlignmentOperation::execute(DataTypeVariant const & dataVariant,
×
376
                                               TransformParametersBase const * transformParameters,
377
                                               ProgressCallback progressCallback) {
378
    auto const * ptr_ptr = std::get_if<std::shared_ptr<LineData>>(&dataVariant);
×
379
    if (!ptr_ptr || !(*ptr_ptr)) {
×
380
        std::cerr << "LineAlignmentOperation::execute: Incompatible variant type or null data." << std::endl;
×
381
        if (progressCallback) progressCallback(100);
×
382
        return {};
×
383
    }
384

385
    auto line_data = *ptr_ptr;
×
386

387
    auto const * typed_params =
388
            transformParameters ? dynamic_cast<LineAlignmentParameters const *>(transformParameters) : nullptr;
×
389

390
    // Auto-find media data if not provided in parameters
391
    std::shared_ptr<MediaData> media_data;
×
392
    if (typed_params && typed_params->media_data) {
×
393
        media_data = typed_params->media_data;
×
394
    } else {
395
        std::cerr << "LineAlignmentOperation::execute: No media data provided. Operation requires media data to align lines to bright features." << std::endl;
×
396
        if (progressCallback) progressCallback(100);
×
397
        return {};
×
398
    }
399

400
    if (progressCallback) progressCallback(0);
×
401

402
    // Use default parameters if none provided
403
    int width = typed_params ? typed_params->width : 20;
×
404
    int perpendicular_range = typed_params ? typed_params->perpendicular_range : 50;
×
405
    bool use_processed_data = typed_params ? typed_params->use_processed_data : true;
×
406
    FWHMApproach approach = typed_params ? typed_params->approach : FWHMApproach::PEAK_WIDTH_HALF_MAX;
×
407
    LineAlignmentOutputMode output_mode = typed_params ? typed_params->output_mode : LineAlignmentOutputMode::ALIGNED_VERTICES;
×
408

409
    std::shared_ptr<LineData> result = line_alignment(
×
410
            line_data.get(),
×
411
            media_data.get(),
412
            width,
413
            perpendicular_range,
414
            use_processed_data,
415
            approach,
416
            output_mode,
417
            progressCallback
418
    );
×
419

420
    if (!result) {
×
421
        std::cerr << "LineAlignmentOperation::execute: Alignment failed." << std::endl;
×
422
        return {};
×
423
    }
424

425
    std::cout << "LineAlignmentOperation executed successfully." << std::endl;
×
426
    return result;
×
427
}
×
428

429
    std::shared_ptr<LineData> line_alignment(LineData const * line_data,
×
430
                                             MediaData * media_data,
431
                                             int width,
432
                                             int perpendicular_range,
433
                                             bool use_processed_data,
434
                                             FWHMApproach approach,
435
                                             LineAlignmentOutputMode output_mode) {
436
    return line_alignment(line_data, media_data, width, perpendicular_range, use_processed_data, approach, output_mode, [](int) {});
×
437
}
438

439
std::shared_ptr<LineData> line_alignment(LineData const * line_data,
×
440
                                         MediaData * media_data,
441
                                         int width,
442
                                         int perpendicular_range,
443
                                         bool use_processed_data,
444
                                         FWHMApproach approach,
445
                                         LineAlignmentOutputMode output_mode,
446
                                         ProgressCallback progressCallback) {
447
    if (!line_data || !media_data) {
×
448
        std::cerr << "LineAlignment: Null LineData or MediaData provided." << std::endl;
×
449
        if (progressCallback) progressCallback(100);
×
450
        return std::make_shared<LineData>();
×
451
    }
452

453
    // Check if line data has at least 3 vertices
454
    auto line_times = line_data->getTimesWithData();
×
455
    if (line_times.empty()) {
×
456
        if (progressCallback) progressCallback(100);
×
457
        return std::make_shared<LineData>();
×
458
    }
459

460
    // Create new LineData for the aligned lines
461
    auto aligned_line_data = std::make_shared<LineData>();
×
462
    aligned_line_data->setImageSize(line_data->getImageSize());
×
463

464
    size_t total_time_points = line_times.size();
×
465
    size_t processed_time_points = 0;
×
466
    if (progressCallback) progressCallback(0);
×
467

468
    // Process each time that has line data
469
    for (auto time : line_times) {
×
470
        // Get lines at this time
471
        auto const & lines = line_data->getAtTime(time);
×
472
        if (lines.empty()) {
×
473
            continue;
×
474
        }
475

476
        // Get media data for this time
477
        std::vector<uint8_t> image_data;
×
478
        if (use_processed_data) {
×
NEW
479
            image_data = media_data->getProcessedData(static_cast<int>(time.getValue()));
×
480
        } else {
NEW
481
            image_data = media_data->getRawData(static_cast<int>(time.getValue()));
×
482
        }
483

484
        if (image_data.empty()) {
×
485
            continue;
×
486
        }
487

488
        ImageSize image_size = media_data->getImageSize();
×
489

490
        // Process each line
491
        std::vector<Line2D> aligned_lines;
×
492
        for (auto const & line : lines) {
×
493
            if (line.size() < 3) {
×
494
                // Skip lines with fewer than 3 vertices
495
                aligned_lines.push_back(line);
×
496
                continue;
×
497
            }
498

499
                if (output_mode == LineAlignmentOutputMode::FWHM_PROFILE_EXTENTS) {
×
500
            // Debug mode: create a debug line for each vertex
501
            for (size_t i = 0; i < line.size(); ++i) {
×
502
                Point2D<float> vertex = line[i];
×
503
                
504
                // Calculate perpendicular direction
505
                Point2D<float> perp_dir = calculate_perpendicular_direction(line, i);
×
506
                
507
                if (perp_dir.x == 0.0f && perp_dir.y == 0.0f) {
×
508
                    // If we can't calculate a perpendicular direction, create a debug line with just the vertex repeated
509
                    Line2D debug_line;
×
510
                    debug_line.push_back(vertex);
×
511
                    debug_line.push_back(vertex);
×
512
                    debug_line.push_back(vertex);
×
513
                    aligned_lines.push_back(debug_line);
×
514
                    continue;
×
515
                }
×
516
                
517
                // Debug mode: calculate FWHM profile extents
518
                Line2D debug_line = calculate_fwhm_profile_extents(
×
519
                    vertex, perp_dir, width, perpendicular_range, image_data, image_size, approach);
×
520
                aligned_lines.push_back(debug_line);
×
521
            }
×
522
                } else {
523
            // Normal mode: create a single aligned line
524
            Line2D aligned_line;
×
525
            
526
            // Process each vertex in the line
527
            for (size_t i = 0; i < line.size(); ++i) {
×
528
                Point2D<float> vertex = line[i];
×
529
                
530
                // Calculate perpendicular direction
531
                Point2D<float> perp_dir = calculate_perpendicular_direction(line, i);
×
532
                
533
                if (perp_dir.x == 0.0f && perp_dir.y == 0.0f) {
×
534
                    // If we can't calculate a perpendicular direction, keep the original vertex
535
                    aligned_line.push_back(vertex);
×
536
                    continue;
×
537
                }
538
                
539
                // Normal mode: calculate FWHM center point
540
                Point2D<float> aligned_vertex = calculate_fwhm_center(
×
541
                    vertex, perp_dir, width, perpendicular_range, image_data, image_size, approach);
542
                
543
                // Ensure the aligned vertex stays within image bounds
544
                aligned_vertex.x = std::max(0.0f, std::min(static_cast<float>(image_size.width - 1), aligned_vertex.x));
×
545
                aligned_vertex.y = std::max(0.0f, std::min(static_cast<float>(image_size.height - 1), aligned_vertex.y));
×
546
                
547
                aligned_line.push_back(aligned_vertex);
×
548
            }
549
            
550
            aligned_lines.push_back(aligned_line);
×
551
                }
×
552
        }
553

554
        // Add the aligned lines to the new LineData
555
        for (auto const & aligned_line : aligned_lines) {
×
556
            aligned_line_data->addAtTime(time, aligned_line, false);
×
557
        }
558

559
        processed_time_points++;
×
560
        if (progressCallback) {
×
NEW
561
            int current_progress = static_cast<int>(std::round(static_cast<double>(processed_time_points) / static_cast<double>(total_time_points) * 100.0));
×
562
            progressCallback(current_progress);
×
563
        }
564
    }
×
565

566
    if (progressCallback) progressCallback(100);
×
567
    return aligned_line_data;
×
568
}
×
569

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