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

paulmthompson / WhiskerToolbox / 17338960954

30 Aug 2025 03:43AM UTC coverage: 67.086% (+0.6%) from 66.478%
17338960954

push

github

paulmthompson
added json testing for line alignment

99 of 106 new or added lines in 2 files covered. (93.4%)

19 existing lines in 2 files now uncovered.

27442 of 40906 relevant lines covered (67.09%)

1106.41 hits per line

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

98.21
/src/DataManager/transforms/Lines/Line_Alignment/line_alignment.test.cpp
1
#include "transforms/Lines/Line_Alignment/line_alignment.hpp"
2
#include "CoreGeometry/points.hpp"
3
#include "CoreGeometry/ImageSize.hpp"
4
#include "CoreGeometry/lines.hpp"
5
#include "CoreGeometry/Image.hpp"
6
#include "Media/Media_Data.hpp"
7

8
#include <catch2/catch_test_macros.hpp>
9
#include <catch2/matchers/catch_matchers_floating_point.hpp>
10

11
#include <cmath>
12
#include <vector>
13

14
// Mock MediaData subclass for testing
15
class MockMediaData : public MediaData {
16
public:
17
    MockMediaData() = default;
1✔
18
    
NEW
19
    MediaType getMediaType() const override { 
×
NEW
20
        return MediaType::Images; 
×
21
    }
22
    
23
    /**
24
     * @brief Add an image to the mock media data
25
     * 
26
     * @param image The image to add
27
     */
28
    void addImage(Image const& image) {
1✔
29
        _images.push_back(image);
1✔
30
        setTotalFrameCount(static_cast<int>(_images.size()));
1✔
31
        
32
        // Update dimensions based on the first image
33
        if (_images.size() == 1) {
1✔
34
            updateWidth(image.size.width);
1✔
35
            updateHeight(image.size.height);
1✔
36
        }
37
    }
1✔
38
    
39
    /**
40
     * @brief Add image data directly
41
     * 
42
     * @param image_data The image data as uint8_t vector
43
     * @param image_size The image dimensions
44
     */
45
    void addImage(std::vector<uint8_t> const& image_data, ImageSize const& image_size) {
1✔
46
        Image image(image_data, image_size);
1✔
47
        addImage(image);
1✔
48
    }
2✔
49
    
50
    /**
51
     * @brief Get the image at the specified frame
52
     * 
53
     * @param frame_id The frame index
54
     * @return The image at the specified frame
55
     */
56
    Image getImage(int frame_id) const {
57
        if (frame_id >= 0 && static_cast<size_t>(frame_id) < _images.size()) {
58
            return _images[static_cast<size_t>(frame_id)];
59
        }
60
        return Image(); // Return empty image if frame not found
61
    }
62
    
63
    /**
64
     * @brief Get raw data for a frame (compatible with MediaData interface)
65
     * 
66
     * @param frame_number The frame number
67
     * @return Raw data as uint8_t vector
68
     */
69
    std::vector<uint8_t> getRawDataForFrame(int frame_number) const {
70
        if (frame_number >= 0 && static_cast<size_t>(frame_number) < _images.size()) {
71
            return _images[static_cast<size_t>(frame_number)].data;
72
        }
73
        return std::vector<uint8_t>();
74
    }
75
    
76
protected:
NEW
77
    void doLoadMedia(std::string const& name) override {
×
78
        // No-op for mock data
79
        static_cast<void>(name);
NEW
80
    }
×
81
    
82
    void doLoadFrame(int frame_id) override {
1✔
83
        // Load the frame data into the base class's raw data
84
        if (frame_id >= 0 && static_cast<size_t>(frame_id) < _images.size()) {
1✔
85
            auto const& image = _images[static_cast<size_t>(frame_id)];
1✔
86
            setRawData(image.data);
1✔
87
        }
88
    }
1✔
89
    
90
private:
91
    std::vector<Image> _images;
92
};
93

94
TEST_CASE("FWHM displacement calculation - Core functionality", "[line][alignment][fwhm][transform]") {
14✔
95
    
96
    SECTION("Simple bright line detection") {
14✔
97
        // Create a simple test image with a bright horizontal line
98
        ImageSize image_size{100, 100};
1✔
99
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
100
        
101
        // Create a bright horizontal line at y=50
102
        for (int x = 0; x < 100; ++x) {
101✔
103
            image_data[50 * 100 + x] = 255; // Bright white line
100✔
104
        }
105
        
106
        // Test vertex at (50, 50) with perpendicular direction pointing up
107
        Point2D<float> vertex{50.0f, 50.0f};
1✔
108
        Point2D<float> perp_dir{0.0f, -1.0f}; // Pointing up
1✔
109
        
110
        Point2D<float> center_point = calculate_fwhm_center(
1✔
111
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
112
        
113
        // Should find the bright line and return center point at (50, 50) (already on the line)
114
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
115
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
116
    }
15✔
117
    
118
    SECTION("Coordinate system verification") {
14✔
119
        // Create a test image to verify coordinate system
120
        ImageSize image_size{10, 10};
1✔
121
        std::vector<uint8_t> image_data(100, 0); // 10x10 = 100 pixels
3✔
122
        
123
        // Set pixel at (5, 3) to white
124
        image_data[3 * 10 + 5] = 255; // y=3, x=5
1✔
125
        
126
        // Test that get_pixel_value returns correct value
127
        Point2D<float> test_point{5.0f, 3.0f};
1✔
128
        uint8_t pixel_value = get_pixel_value(test_point, image_data, image_size);
1✔
129
        REQUIRE(pixel_value == 255);
1✔
130
        
131
        // Test that get_pixel_value returns 0 for other positions
132
        Point2D<float> test_point2{4.0f, 3.0f};
1✔
133
        uint8_t pixel_value2 = get_pixel_value(test_point2, image_data, image_size);
1✔
134
        REQUIRE(pixel_value2 == 0);
1✔
135
    }
15✔
136
    
137
    SECTION("Bright line detection with offset") {
14✔
138
        // Create a test image with a bright horizontal line
139
        ImageSize image_size{100, 100};
1✔
140
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
141
        
142
        // Create a bright horizontal line at y=60
143
        for (int x = 0; x < 100; ++x) {
101✔
144
            image_data[60 * 100 + x] = 255; // Bright white line
100✔
145
        }
146
        
147
        // Test vertex at (50, 50) with perpendicular direction pointing up
148
        Point2D<float> vertex{50.0f, 50.0f};
1✔
149
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
1✔
150
        
151
        Point2D<float> center_point = calculate_fwhm_center(
1✔
152
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
153
        
154
        // Should find the bright line at y=60 and return center point at (50, 60)
155
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
156
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
157
    }
15✔
158
    
159
    SECTION("Bright line detection with offset and thickness") {
14✔
160
        // Create a test image with a bright horizontal line with thickness
161
        ImageSize image_size{100, 100};
1✔
162
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
163
        
164
        // Create a bright horizontal line at y=60 with thickness of 3 pixels
165
        for (int x = 0; x < 100; ++x) {
101✔
166
            // Line spans from y=59 to y=61 (3 pixels thick)
167
            for (int y = 59; y <= 61; ++y) {
400✔
168
                if (y >= 0 && y < 100) {
300✔
169
                    image_data[y * 100 + x] = 255; // Bright white line
300✔
170
                }
171
            }
172
        }
173
        
174
        // Test vertex at (50, 50) with perpendicular direction pointing down
175
        Point2D<float> vertex{50.0f, 50.0f};
1✔
176
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
1✔
177
        
178
        Point2D<float> center_point = calculate_fwhm_center(
1✔
179
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
180
        
181
        // Should find the center of the thick bright line at y=60 and return center point at (50, 60)
182
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
183
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
184
    }
15✔
185
    
186
    SECTION("Bright line detection with varying thickness") {
14✔
187
        // Create a test image with a bright horizontal line with varying thickness
188
        ImageSize image_size{100, 100};
1✔
189
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
190
        
191
        // Create a bright horizontal line at y=60 with thickness of 5 pixels
192
        for (int x = 0; x < 100; ++x) {
101✔
193
            // Line spans from y=58 to y=62 (5 pixels thick)
194
            for (int y = 58; y <= 62; ++y) {
600✔
195
                if (y >= 0 && y < 100) {
500✔
196
                    image_data[y * 100 + x] = 255; // Bright white line
500✔
197
                }
198
            }
199
        }
200
        
201
        // Test vertex at (50, 50) with perpendicular direction pointing down
202
        Point2D<float> vertex{50.0f, 50.0f};
1✔
203
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
1✔
204
        
205
        Point2D<float> center_point = calculate_fwhm_center(
1✔
206
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
207
        
208
        // Should find the center of the thick bright line at y=60 and return center point at (50, 60)
209
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
210
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
211
    }
15✔
212
    
213
    SECTION("Bright line detection with very thick line") {
14✔
214
        // Create a test image with a very thick bright horizontal line
215
        ImageSize image_size{100, 100};
1✔
216
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
217
        
218
        // Create a bright horizontal line at y=60 with thickness of 9 pixels
219
        for (int x = 0; x < 100; ++x) {
101✔
220
            // Line spans from y=56 to y=64 (9 pixels thick)
221
            for (int y = 56; y <= 64; ++y) {
1,000✔
222
                if (y >= 0 && y < 100) {
900✔
223
                    image_data[y * 100 + x] = 255; // Bright white line
900✔
224
                }
225
            }
226
        }
227
        
228
        // Test vertex at (50, 50) with perpendicular direction pointing down
229
        Point2D<float> vertex{50.0f, 50.0f};
1✔
230
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
1✔
231
        
232
        Point2D<float> center_point = calculate_fwhm_center(
1✔
233
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
234
        
235
        // Should find the center of the thick bright line at y=60 and return center point at (50, 60)
236
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
237
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
238
    }
15✔
239
    
240
    SECTION("Bright line detection with diagonal perpendicular") {
14✔
241
        // Create a test image with a bright diagonal line
242
        ImageSize image_size{100, 100};
1✔
243
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
244
        
245
        // Create a bright diagonal line
246
        for (int i = 0; i < 100; ++i) {
101✔
247
            int x = i;
100✔
248
            int y = i + 10; // Offset by 10 pixels
100✔
249
            if (x < 100 && y < 100) {
100✔
250
                image_data[y * 100 + x] = 255; // Bright white line
90✔
251
            }
252
        }
253
        
254
        // Test vertex at (50, 50) with perpendicular direction
255
        Point2D<float> vertex{50.0f, 50.0f};
1✔
256
        Point2D<float> perp_dir{-0.707f, 0.707f}; // Diagonal perpendicular
1✔
257
        
258
        Point2D<float> center_point = calculate_fwhm_center(
1✔
259
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
260
        
261
        // Should find the bright line and return appropriate center point
262
        // This will be the nearest position of the diagonal line stretching from
263
        // (40, 50) to (50, 60) . This is (45, 55)
264
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(45.0f, 2.0f));
1✔
265
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(55.0f, 2.0f));
1✔
266
    }
15✔
267
    
268
    SECTION("Bright diagonal line detection with thickness") {
14✔
269
        // Create a test image with a bright diagonal line with thickness
270
        ImageSize image_size{100, 100};
1✔
271
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
272
        
273
        // Create a bright diagonal line from (10,10) to (90,90) with thickness
274
        for (int i = 10; i <= 90; ++i) {
82✔
275
            // Line spans from (i,i) with thickness of 3 pixels perpendicular to the line
276
            for (int offset = -1; offset <= 1; ++offset) {
324✔
277
                int x = i + offset;
243✔
278
                int y = i + offset;
243✔
279
                if (x >= 0 && x < 100 && y >= 0 && y < 100) {
243✔
280
                    image_data[y * 100 + x] = 255; // Bright white line
243✔
281
                }
282
            }
283
        }
284
        
285
        // Test vertex at (50, 50) with perpendicular direction
286
        Point2D<float> vertex{50.0f, 50.0f};
1✔
287
        Point2D<float> perp_dir{-0.707f, 0.707f}; // Perpendicular to diagonal
1✔
288
        
289
        Point2D<float> center_point = calculate_fwhm_center(
1✔
290
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
291
        
292
        // Should find the diagonal line and return appropriate center point
293
        // The exact value depends on the FWHM calculation, but should be reasonable
294
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 5.0f));
1✔
295
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(50.0f, 5.0f));
1✔
296
    }
15✔
297
    
298
    SECTION("No bright features - should return zero") {
14✔
299
        // Create a completely black image
300
        ImageSize image_size{100, 100};
1✔
301
        std::vector<uint8_t> image_data(100 * 100, 0); // All black
3✔
302
        
303
        Point2D<float> vertex{50.0f, 50.0f};
1✔
304
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
1✔
305
        
306
        Point2D<float> center_point = calculate_fwhm_center(
1✔
307
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
308
        
309
        // Should return original vertex when no bright features are found
310
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 0.001f));
1✔
311
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(50.0f, 0.001f));
1✔
312
    }
15✔
313
    
314
    SECTION("Bright spot detection") {
14✔
315
        // Create a test image with a bright spot
316
        ImageSize image_size{100, 100};
1✔
317
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
318
        
319
        // Create a bright spot at (60, 50)
320
        for (int y = 45; y <= 55; ++y) {
12✔
321
            for (int x = 55; x <= 65; ++x) {
132✔
322
                if (x >= 0 && x < 100 && y >= 0 && y < 100) {
121✔
323
                    image_data[y * 100 + x] = 255; // Bright white spot
121✔
324
                }
325
            }
326
        }
327
        
328
        // Test vertex at (50, 50) with perpendicular direction pointing right
329
        Point2D<float> vertex{50.0f, 50.0f};
1✔
330
        Point2D<float> perp_dir{1.0f, 0.0f}; // Pointing right
1✔
331
        
332
        Point2D<float> center_point = calculate_fwhm_center(
1✔
333
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
334
        
335
        // Should find the bright spot and return center point at (60, 50)
336
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
337
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
338
    }
15✔
339
    
340
    SECTION("Multiple bright lines - should find the closest") {
14✔
341
        // Create a test image with multiple bright horizontal lines
342
        ImageSize image_size{100, 100};
1✔
343
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
344
        
345
        // Create bright horizontal lines at y=30 and y=70
346
        for (int x = 0; x < 100; ++x) {
101✔
347
            image_data[30 * 100 + x] = 255; // Bright white line
100✔
348
            image_data[80 * 100 + x] = 255; // Bright white line
100✔
349
        }
350
        
351
        // Test vertex at (50, 50) with perpendicular direction pointing up
352
        Point2D<float> vertex{50.0f, 50.0f};
1✔
353
        Point2D<float> perp_dir{0.0f, -1.0f}; // Pointing up
1✔
354
        
355
        Point2D<float> center_point = calculate_fwhm_center(
1✔
356
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
357
        
358
        // Should find the closer line at y=30 and return center point at (50, 30)
359
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
360
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(30.0f, 1.0f));
1✔
361
    }
15✔
362
    
363
    SECTION("Edge case - vertex at image boundary") {
14✔
364
        // Create a test image with a bright line
365
        ImageSize image_size{100, 100};
1✔
366
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
367
        
368
        // Create a bright horizontal line at y=50
369
        for (int x = 0; x < 100; ++x) {
101✔
370
            image_data[50 * 100 + x] = 255; // Bright white line
100✔
371
        }
372
        
373
        // Test vertex at (50, 0) with perpendicular direction pointing right
374
        Point2D<float> vertex{50.0f, 0.0f};
1✔
375
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
1✔
376

377
        Point2D<float> center_point = calculate_fwhm_center(
1✔
378
            vertex, perp_dir, 10, 102, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
379
        
380
        // Should handle boundary case gracefully
381
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
382
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
383
    }
15✔
384
    
385
    SECTION("Different perpendicular range values") {
14✔
386
        // Create a test image with a bright horizontal line
387
        ImageSize image_size{100, 100};
1✔
388
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
389
        
390
        // Create a bright horizontal line at y=60
391
        for (int x = 0; x < 100; ++x) {
101✔
392
            image_data[60 * 100 + x] = 255; // Bright white line
100✔
393
        }
394
        
395
        Point2D<float> vertex{50.0f, 50.0f};
1✔
396
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
1✔
397
        
398
        // Test with different perpendicular ranges
399
        Point2D<float> center_point1 = calculate_fwhm_center(
1✔
400
            vertex, perp_dir, 10, 20, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
401
        
402
        Point2D<float> center_point2 = calculate_fwhm_center(
1✔
403
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
404
        
405
        // Both should find the same line and return similar center points
406
        REQUIRE_THAT(center_point1.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
407
        REQUIRE_THAT(center_point1.y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
408
        REQUIRE_THAT(center_point2.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
409
        REQUIRE_THAT(center_point2.y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
410
    }
15✔
411
    
412
    SECTION("Different width values") {
14✔
413
        // Create a test image with a bright horizontal line
414
        ImageSize image_size{100, 100};
1✔
415
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
416
        
417
        // Create a bright horizontal line at y=60
418
        for (int x = 0; x < 100; ++x) {
101✔
419
            image_data[60 * 100 + x] = 255; // Bright white line
100✔
420
        }
421
        
422
        Point2D<float> vertex{50.0f, 50.0f};
1✔
423
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
1✔
424
        
425
        // Test with different width values
426
        Point2D<float> center_point1 = calculate_fwhm_center(
1✔
427
            vertex, perp_dir, 5, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
428
        
429
        Point2D<float> center_point2 = calculate_fwhm_center(
1✔
430
            vertex, perp_dir, 20, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
431
        
432
        // Both should find the same line and return similar center points
433
        REQUIRE_THAT(center_point1.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
434
        REQUIRE_THAT(center_point1.y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
435
        REQUIRE_THAT(center_point2.x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
436
        REQUIRE_THAT(center_point2.y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
437
    }
15✔
438
}
14✔
439

440
TEST_CASE("Perpendicular direction calculation - Core functionality", "[line][alignment][perpendicular][transform]") {
7✔
441
    
442
    SECTION("Horizontal line - perpendicular should be vertical") {
7✔
443
        // Create a horizontal line from (0,0) to (10,0)
444
        Line2D horizontal_line;
1✔
445
        horizontal_line.push_back(Point2D<float>{0.0f, 0.0f});
1✔
446
        horizontal_line.push_back(Point2D<float>{10.0f, 0.0f});
1✔
447
        
448
        // Test first vertex (should use direction to next vertex)
449
        Point2D<float> perp_dir_first = calculate_perpendicular_direction(horizontal_line, 0);
1✔
450
        REQUIRE_THAT(perp_dir_first.x, Catch::Matchers::WithinAbs(0.0f, 0.001f));
1✔
451
        REQUIRE_THAT(perp_dir_first.y, Catch::Matchers::WithinAbs(1.0f, 0.001f)); // Pointing down
1✔
452
        
453
        // Test last vertex (should use direction from previous vertex)
454
        Point2D<float> perp_dir_last = calculate_perpendicular_direction(horizontal_line, 1);
1✔
455
        REQUIRE_THAT(perp_dir_last.x, Catch::Matchers::WithinAbs(0.0f, 0.001f));
1✔
456
        REQUIRE_THAT(perp_dir_last.y, Catch::Matchers::WithinAbs(1.0f, 0.001f)); // Pointing down
1✔
457
    }
8✔
458
    
459
    SECTION("Vertical line - perpendicular should be horizontal") {
7✔
460
        // Create a vertical line from (0,0) to (0,10)
461
        Line2D vertical_line;
1✔
462
        vertical_line.push_back(Point2D<float>{0.0f, 0.0f});
1✔
463
        vertical_line.push_back(Point2D<float>{0.0f, 10.0f});
1✔
464
        
465
        // Test first vertex
466
        Point2D<float> perp_dir_first = calculate_perpendicular_direction(vertical_line, 0);
1✔
467
        REQUIRE_THAT(perp_dir_first.x, Catch::Matchers::WithinAbs(-1.0f, 0.001f)); // Pointing left
1✔
468
        REQUIRE_THAT(perp_dir_first.y, Catch::Matchers::WithinAbs(0.0f, 0.001f));
1✔
469
        
470
        // Test last vertex
471
        Point2D<float> perp_dir_last = calculate_perpendicular_direction(vertical_line, 1);
1✔
472
        REQUIRE_THAT(perp_dir_last.x, Catch::Matchers::WithinAbs(-1.0f, 0.001f)); // Pointing left
1✔
473
        REQUIRE_THAT(perp_dir_last.y, Catch::Matchers::WithinAbs(0.0f, 0.001f));
1✔
474
    }
8✔
475
    
476
    SECTION("Diagonal line - perpendicular should be perpendicular") {
7✔
477
        // Create a diagonal line from (0,0) to (10,10)
478
        Line2D diagonal_line;
1✔
479
        diagonal_line.push_back(Point2D<float>{0.0f, 0.0f});
1✔
480
        diagonal_line.push_back(Point2D<float>{10.0f, 10.0f});
1✔
481
        
482
        // Test first vertex
483
        Point2D<float> perp_dir_first = calculate_perpendicular_direction(diagonal_line, 0);
1✔
484
        // Perpendicular to (10,10) - (0,0) = (10,10) should be (-10,10) normalized
485
        REQUIRE_THAT(perp_dir_first.x, Catch::Matchers::WithinAbs(-0.707f, 0.001f)); // -1/sqrt(2)
1✔
486
        REQUIRE_THAT(perp_dir_first.y, Catch::Matchers::WithinAbs(0.707f, 0.001f));  // 1/sqrt(2)
1✔
487
        
488
        // Test last vertex
489
        Point2D<float> perp_dir_last = calculate_perpendicular_direction(diagonal_line, 1);
1✔
490
        REQUIRE_THAT(perp_dir_last.x, Catch::Matchers::WithinAbs(-0.707f, 0.001f));
1✔
491
        REQUIRE_THAT(perp_dir_last.y, Catch::Matchers::WithinAbs(0.707f, 0.001f));
1✔
492
    }
8✔
493
    
494
    SECTION("Multi-segment line - middle vertices average perpendiculars") {
7✔
495
        // Create a line with three segments: horizontal, diagonal, vertical
496
        Line2D multi_line;
1✔
497
        multi_line.push_back(Point2D<float>{0.0f, 0.0f});   // Start
1✔
498
        multi_line.push_back(Point2D<float>{10.0f, 0.0f});  // Horizontal segment
1✔
499
        multi_line.push_back(Point2D<float>{20.0f, 10.0f}); // Diagonal segment
1✔
500
        multi_line.push_back(Point2D<float>{20.0f, 20.0f}); // Vertical segment
1✔
501
        
502
        // Test middle vertex (index 1) - should average perpendiculars from adjacent segments
503
        Point2D<float> perp_dir_middle = calculate_perpendicular_direction(multi_line, 1);
1✔
504
        
505
        // First segment: (10,0) - (0,0) = (10,0), perpendicular = (0,1)
506
        // Second segment: (20,10) - (10,0) = (10,10), perpendicular = (-10,10)/sqrt(200) = (-0.707, 0.707)
507
        // Average: (0 + (-0.707))/2 = -0.3535, (1 + 0.707)/2 = 0.8535
508
        // Normalized: approximately (-0.383, 0.924)
509
        REQUIRE_THAT(perp_dir_middle.x, Catch::Matchers::WithinAbs(-0.383f, 0.1f));
1✔
510
        REQUIRE_THAT(perp_dir_middle.y, Catch::Matchers::WithinAbs(0.924f, 0.1f));
1✔
511
    }
8✔
512
    
513
    SECTION("Line with fewer than 2 points - should return zero vector") {
7✔
514
        // Create a line with only 1 point
515
        Line2D short_line;
1✔
516
        short_line.push_back(Point2D<float>{0.0f, 0.0f});
1✔
517
        
518
        // Test with 1 point (invalid for perpendicular calculation)
519
        Point2D<float> perp_dir = calculate_perpendicular_direction(short_line, 0);
1✔
520
        REQUIRE_THAT(perp_dir.x, Catch::Matchers::WithinAbs(0.0f, 0.001f));
1✔
521
        REQUIRE_THAT(perp_dir.y, Catch::Matchers::WithinAbs(0.0f, 0.001f));
1✔
522
    }
8✔
523
    
524
    SECTION("Line with zero-length segments - should return zero vector") {
7✔
525
        // Create a line with zero-length segments
526
        Line2D zero_line;
1✔
527
        zero_line.push_back(Point2D<float>{5.0f, 5.0f});
1✔
528
        zero_line.push_back(Point2D<float>{5.0f, 5.0f}); // Same point
1✔
529
        zero_line.push_back(Point2D<float>{5.0f, 5.0f}); // Same point
1✔
530
        
531
        // Test middle vertex
532
        Point2D<float> perp_dir = calculate_perpendicular_direction(zero_line, 1);
1✔
533
        REQUIRE_THAT(perp_dir.x, Catch::Matchers::WithinAbs(0.0f, 0.001f));
1✔
534
        REQUIRE_THAT(perp_dir.y, Catch::Matchers::WithinAbs(0.0f, 0.001f));
1✔
535
    }
8✔
536
    
537
    SECTION("Normalized perpendicular vectors") {
7✔
538
        // Test that all perpendicular vectors are normalized (unit length)
539
        Line2D test_line;
1✔
540
        test_line.push_back(Point2D<float>{0.0f, 0.0f});
1✔
541
        test_line.push_back(Point2D<float>{3.0f, 4.0f}); // 3-4-5 triangle
1✔
542
        test_line.push_back(Point2D<float>{6.0f, 8.0f});
1✔
543
        
544
        // Test first vertex
545
        Point2D<float> perp_dir = calculate_perpendicular_direction(test_line, 0);
1✔
546
        float length = std::sqrt(perp_dir.x * perp_dir.x + perp_dir.y * perp_dir.y);
1✔
547
        REQUIRE_THAT(length, Catch::Matchers::WithinAbs(1.0f, 0.001f));
1✔
548
        
549
        // Test middle vertex
550
        Point2D<float> perp_dir_middle = calculate_perpendicular_direction(test_line, 1);
1✔
551
        float length_middle = std::sqrt(perp_dir_middle.x * perp_dir_middle.x + perp_dir_middle.y * perp_dir_middle.y);
1✔
552
        REQUIRE_THAT(length_middle, Catch::Matchers::WithinAbs(1.0f, 0.001f));
1✔
553
        
554
        // Test last vertex
555
        Point2D<float> perp_dir_last = calculate_perpendicular_direction(test_line, 2);
1✔
556
        float length_last = std::sqrt(perp_dir_last.x * perp_dir_last.x + perp_dir_last.y * perp_dir_last.y);
1✔
557
        REQUIRE_THAT(length_last, Catch::Matchers::WithinAbs(1.0f, 0.001f));
1✔
558
    }
8✔
559
}
7✔
560

561
TEST_CASE("FWHM center calculation - Edge cases and error handling", "[line][alignment][fwhm][edge]") {
4✔
562
    
563
    SECTION("Zero width parameter") {
4✔
564
        ImageSize image_size{100, 100};
1✔
565
        std::vector<uint8_t> image_data(100 * 100, 0);
3✔
566
        
567
        Point2D<float> vertex{50.0f, 50.0f};
1✔
568
        Point2D<float> perp_dir{0.0f, 1.0f};
1✔
569
        
570
        Point2D<float> center_point = calculate_fwhm_center(
1✔
571
            vertex, perp_dir, 0, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
572
        
573
        // Should return original vertex for invalid width
574
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 0.001f));
1✔
575
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(50.0f, 0.001f));
1✔
576
    }
5✔
577
    
578
    SECTION("Zero perpendicular direction") {
4✔
579
        ImageSize image_size{100, 100};
1✔
580
        std::vector<uint8_t> image_data(100 * 100, 0);
3✔
581
        
582
        Point2D<float> vertex{50.0f, 50.0f};
1✔
583
        Point2D<float> perp_dir{0.0f, 0.0f}; // Zero direction
1✔
584
        
585
        Point2D<float> center_point = calculate_fwhm_center(
1✔
586
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
587
        
588
        // Should handle zero direction gracefully
589
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 0.001f));
1✔
590
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(50.0f, 0.001f));
1✔
591
    }
5✔
592
    
593
    SECTION("Empty image data") {
4✔
594
        ImageSize image_size{100, 100};
1✔
595
        std::vector<uint8_t> image_data; // Empty
1✔
596
        
597
        Point2D<float> vertex{50.0f, 50.0f};
1✔
598
        Point2D<float> perp_dir{0.0f, 1.0f};
1✔
599
        
600
        Point2D<float> center_point = calculate_fwhm_center(
1✔
601
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
602
        
603
        // Should handle empty data gracefully
604
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(50.0f, 0.001f));
1✔
605
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(50.0f, 0.001f));
1✔
606
    }
5✔
607
    
608
    SECTION("Vertex outside image bounds") {
4✔
609
        ImageSize image_size{100, 100};
1✔
610
        std::vector<uint8_t> image_data(100 * 100, 0);
3✔
611
        
612
        Point2D<float> vertex{150.0f, 150.0f}; // Outside bounds
1✔
613
        Point2D<float> perp_dir{0.0f, 1.0f};
1✔
614
        
615
        Point2D<float> center_point = calculate_fwhm_center(
1✔
616
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
617
        
618
        // Should handle out-of-bounds vertex gracefully
619
        REQUIRE_THAT(center_point.x, Catch::Matchers::WithinAbs(150.0f, 0.001f));
1✔
620
        REQUIRE_THAT(center_point.y, Catch::Matchers::WithinAbs(150.0f, 0.001f));
1✔
621
    }
5✔
622
}
4✔
623

624
#if LINE_ALIGNMENT_DEBUG_MODE
625
TEST_CASE("FWHM profile extents calculation - Debug mode", "[line][alignment][fwhm][debug]") {
626
    
627
    SECTION("Debug mode profile extents for bright line") {
628
        // Create a test image with a bright horizontal line
629
        ImageSize image_size{100, 100};
630
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
631
        
632
        // Create a bright horizontal line at y=60
633
        for (int x = 0; x < 100; ++x) {
634
            image_data[60 * 100 + x] = 255; // Bright white line
635
        }
636
        
637
        // Test vertex at (50, 50) with perpendicular direction pointing down
638
        Point2D<float> vertex{50.0f, 50.0f};
639
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
640
        
641
        Line2D debug_line = calculate_fwhm_profile_extents(
642
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
643
        
644
        // Should return a line with 3 points: [left_extent, max_point, right_extent]
645
        REQUIRE(debug_line.size() == 3);
646
        
647
        // First point should be left extent (around y=55-65)
648
        REQUIRE_THAT(debug_line[0].x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
649
        REQUIRE_THAT(debug_line[0].y, Catch::Matchers::WithinAbs(60.0f, 5.0f));
650
        
651
        // Second point should be max point (around y=60)
652
        REQUIRE_THAT(debug_line[1].x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
653
        REQUIRE_THAT(debug_line[1].y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
654
        
655
        // Third point should be right extent (around y=55-65)
656
        REQUIRE_THAT(debug_line[2].x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
657
        REQUIRE_THAT(debug_line[2].y, Catch::Matchers::WithinAbs(60.0f, 5.0f));
658
    }
659
    
660
    SECTION("Debug mode with multiple maximum values") {
661
        // Create a test image with a bright horizontal line that has multiple maximum values
662
        ImageSize image_size{100, 100};
663
        std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
664
        
665
        // Create a bright horizontal line at y=60 with some thickness
666
        for (int x = 0; x < 100; ++x) {
667
            // Create a line with multiple maximum values at different positions
668
            for (int y = 58; y <= 62; ++y) {
669
                if (y >= 0 && y < 100) {
670
                    image_data[y * 100 + x] = 255; // Bright white line
671
                }
672
            }
673
        }
674
        
675
        // Test vertex at (50, 50) with perpendicular direction pointing down
676
        Point2D<float> vertex{50.0f, 50.0f};
677
        Point2D<float> perp_dir{0.0f, 1.0f}; // Pointing down
678
        
679
        Line2D debug_line = calculate_fwhm_profile_extents(
680
            vertex, perp_dir, 10, 50, image_data, image_size, FWHMApproach::PEAK_WIDTH_HALF_MAX);
681
        
682
        // Should return a line with 3 points: [left_extent, max_point, right_extent]
683
        REQUIRE(debug_line.size() == 3);
684
        
685
        // First point should be left extent
686
        REQUIRE_THAT(debug_line[0].x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
687
        REQUIRE_THAT(debug_line[0].y, Catch::Matchers::WithinAbs(60.0f, 5.0f));
688
        
689
        // Second point should be max point (average of multiple max positions)
690
        REQUIRE_THAT(debug_line[1].x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
691
        REQUIRE_THAT(debug_line[1].y, Catch::Matchers::WithinAbs(60.0f, 2.0f)); // Should be around center of thick line
692
        
693
        // Third point should be right extent
694
        REQUIRE_THAT(debug_line[2].x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
695
        REQUIRE_THAT(debug_line[2].y, Catch::Matchers::WithinAbs(60.0f, 5.0f));
696
    }
697
}
698
#endif
699

700
#include "DataManager.hpp"
701
#include "IO/LoaderRegistry.hpp"
702
#include "transforms/TransformPipeline.hpp"
703
#include "transforms/TransformRegistry.hpp"
704
#include "Lines/Line_Data.hpp"
705
#include "Media/Media_Data.hpp"
706

707
#include <filesystem>
708
#include <fstream>
709
#include <iostream>
710

711
TEST_CASE("Data Transform: Line Alignment - JSON pipeline", "[transforms][line_alignment][json]") {
1✔
712
    // Create DataManager and populate it with LineData and MediaData in code
713
    DataManager dm;
1✔
714

715
    // Create a TimeFrame for our data
716
    auto time_frame = std::make_shared<TimeFrame>();
1✔
717
    dm.setTime(TimeKey("default"), time_frame);
1✔
718
    
719
    // Create test line data - a simple horizontal line
720
    Line2D test_line;
1✔
721
    test_line.push_back(Point2D<float>{10.0f, 50.0f});
1✔
722
    test_line.push_back(Point2D<float>{50.0f, 50.0f});
1✔
723
    test_line.push_back(Point2D<float>{90.0f, 50.0f});
1✔
724
    
725
    auto test_line_data = std::make_shared<LineData>();
1✔
726
    test_line_data->setTimeFrame(time_frame);
1✔
727
    test_line_data->addAtTime(TimeFrameIndex(0), test_line);
1✔
728
    
729
    // Store the line data in DataManager with a known key
730
    dm.setData("test_line", test_line_data, TimeKey("default"));
3✔
731
    
732
    // Create test media data with a bright horizontal line at y=60
733
    auto media_data = std::make_shared<MockMediaData>();
1✔
734
    media_data->setTimeFrame(time_frame);
1✔
735
    
736
    // Create a test image with a bright horizontal line at y=60
737
    ImageSize image_size{100, 100};
1✔
738
    std::vector<uint8_t> image_data(100 * 100, 0); // All black initially
3✔
739
    
740
    // Create a bright horizontal line at y=60
741
    for (int x = 0; x < 100; ++x) {
101✔
742
        image_data[60 * 100 + x] = 255; // Bright white line
100✔
743
    }
744
    
745
    // Add the image to media data
746
    media_data->addImage(image_data, image_size);
1✔
747
    
748
    // Store the media data in DataManager with a known key
749
    dm.setData("test_media", media_data, TimeKey("default"));
3✔
750
    
751
    // Create JSON configuration for transformation pipeline using unified format
752
    const char* json_config = 
1✔
753
        "[\n"
754
        "{\n"
755
        "    \"transformations\": {\n"
756
        "        \"metadata\": {\n"
757
        "            \"name\": \"Line Alignment Pipeline\",\n"
758
        "            \"description\": \"Test line alignment to bright features in media\",\n"
759
        "            \"version\": \"1.0\"\n"
760
        "        },\n"
761
        "        \"steps\": [\n"
762
        "            {\n"
763
        "                \"step_id\": \"1\",\n"
764
        "                \"transform_name\": \"Line Alignment to Bright Features\",\n"
765
        "                \"phase\": \"analysis\",\n"
766
        "                \"input_key\": \"test_line\",\n"
767
        "                \"output_key\": \"aligned_line\",\n"
768
        "                \"parameters\": {\n"
769
        "                    \"media_data\": \"test_media\",\n"
770
        "                    \"width\": 20,\n"
771
        "                    \"perpendicular_range\": 50,\n"
772
        "                    \"use_processed_data\": true,\n"
773
        "                    \"approach\": \"PEAK_WIDTH_HALF_MAX\",\n"
774
        "                    \"output_mode\": \"ALIGNED_VERTICES\"\n"
775
        "                }\n"
776
        "            }\n"
777
        "        ]\n"
778
        "    }\n"
779
        "}\n"
780
        "]";
781
    
782
    // Create temporary directory and write JSON config to file
783
    std::filesystem::path test_dir = std::filesystem::temp_directory_path() / "line_alignment_pipeline_test";
1✔
784
    std::filesystem::create_directories(test_dir);
1✔
785
    
786
    std::filesystem::path json_filepath = test_dir / "pipeline_config.json";
1✔
787
    {
788
        std::ofstream json_file(json_filepath);
1✔
789
        REQUIRE(json_file.is_open());
1✔
790
        json_file << json_config;
1✔
791
        json_file.close();
1✔
792
    }
1✔
793
    
794
    // Execute the transformation pipeline using load_data_from_json_config
795
    auto data_info_list = load_data_from_json_config(&dm, json_filepath.string());
1✔
796
    
797
    // Verify the transformation was executed and results are available
798
    auto result_line = dm.getData<LineData>("aligned_line");
3✔
799
    REQUIRE(result_line != nullptr);
1✔
800
    
801
    // Verify the line alignment results
802
    // The original line was at y=50, but there's a bright line at y=60
803
    // The aligned line should have vertices moved to y=60
804
    auto aligned_lines = result_line->getAtTime(TimeFrameIndex(0));
1✔
805
    REQUIRE(aligned_lines.size() == 1);
1✔
806
    auto aligned_vertices = aligned_lines[0];
1✔
807
    REQUIRE(aligned_vertices.size() == 3);
1✔
808
    
809
    // Check that vertices were aligned to the bright line at y=60
810
    REQUIRE_THAT(aligned_vertices[0].x, Catch::Matchers::WithinAbs(10.0f, 1.0f));
1✔
811
    REQUIRE_THAT(aligned_vertices[0].y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
812
    REQUIRE_THAT(aligned_vertices[1].x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
813
    REQUIRE_THAT(aligned_vertices[1].y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
814
    REQUIRE_THAT(aligned_vertices[2].x, Catch::Matchers::WithinAbs(90.0f, 1.0f));
1✔
815
    REQUIRE_THAT(aligned_vertices[2].y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
816
    
817
    // Test another pipeline with different parameters (different width and range)
818
    const char* json_config_different_params = 
1✔
819
        "[\n"
820
        "{\n"
821
        "    \"transformations\": {\n"
822
        "        \"metadata\": {\n"
823
        "            \"name\": \"Line Alignment with Different Parameters\",\n"
824
        "            \"description\": \"Test line alignment with different width and range\",\n"
825
        "            \"version\": \"1.0\"\n"
826
        "        },\n"
827
        "        \"steps\": [\n"
828
        "            {\n"
829
        "                \"step_id\": \"1\",\n"
830
        "                \"transform_name\": \"Line Alignment to Bright Features\",\n"
831
        "                \"phase\": \"analysis\",\n"
832
        "                \"input_key\": \"test_line\",\n"
833
        "                \"output_key\": \"aligned_line_different_params\",\n"
834
        "                \"parameters\": {\n"
835
        "                    \"media_data\": \"test_media\",\n"
836
        "                    \"width\": 10,\n"
837
        "                    \"perpendicular_range\": 30,\n"
838
        "                    \"use_processed_data\": true,\n"
839
        "                    \"approach\": \"PEAK_WIDTH_HALF_MAX\",\n"
840
        "                    \"output_mode\": \"ALIGNED_VERTICES\"\n"
841
        "                }\n"
842
        "            }\n"
843
        "        ]\n"
844
        "    }\n"
845
        "}\n"
846
        "]";
847
    
848
    std::filesystem::path json_filepath_different_params = test_dir / "pipeline_config_different_params.json";
1✔
849
    {
850
        std::ofstream json_file(json_filepath_different_params);
1✔
851
        REQUIRE(json_file.is_open());
1✔
852
        json_file << json_config_different_params;
1✔
853
        json_file.close();
1✔
854
    }
1✔
855
    
856
    // Execute the different parameters pipeline
857
    auto data_info_list_different_params = load_data_from_json_config(&dm, json_filepath_different_params.string());
1✔
858
    
859
    // Verify the different parameters results
860
    auto result_line_different_params = dm.getData<LineData>("aligned_line_different_params");
3✔
861
    REQUIRE(result_line_different_params != nullptr);
1✔
862
    
863
    auto aligned_lines_different_params = result_line_different_params->getAtTime(TimeFrameIndex(0));
1✔
864
    REQUIRE(aligned_lines_different_params.size() == 1);
1✔
865
    auto aligned_vertices_different_params = aligned_lines_different_params[0];
1✔
866
    REQUIRE(aligned_vertices_different_params.size() == 3);
1✔
867
    
868
    // Should still align to the bright line at y=60
869
    REQUIRE_THAT(aligned_vertices_different_params[0].x, Catch::Matchers::WithinAbs(10.0f, 1.0f));
1✔
870
    REQUIRE_THAT(aligned_vertices_different_params[0].y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
871
    REQUIRE_THAT(aligned_vertices_different_params[1].x, Catch::Matchers::WithinAbs(50.0f, 1.0f));
1✔
872
    REQUIRE_THAT(aligned_vertices_different_params[1].y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
873
    REQUIRE_THAT(aligned_vertices_different_params[2].x, Catch::Matchers::WithinAbs(90.0f, 1.0f));
1✔
874
    REQUIRE_THAT(aligned_vertices_different_params[2].y, Catch::Matchers::WithinAbs(60.0f, 1.0f));
1✔
875
    
876
    // Test FWHM profile extents output mode
877
    const char* json_config_fwhm_extents = 
1✔
878
        "[\n"
879
        "{\n"
880
        "    \"transformations\": {\n"
881
        "        \"metadata\": {\n"
882
        "            \"name\": \"Line Alignment FWHM Extents\",\n"
883
        "            \"description\": \"Test line alignment with FWHM profile extents output\",\n"
884
        "            \"version\": \"1.0\"\n"
885
        "        },\n"
886
        "        \"steps\": [\n"
887
        "            {\n"
888
        "                \"step_id\": \"1\",\n"
889
        "                \"transform_name\": \"Line Alignment to Bright Features\",\n"
890
        "                \"phase\": \"analysis\",\n"
891
        "                \"input_key\": \"test_line\",\n"
892
        "                \"output_key\": \"fwhm_extents_line\",\n"
893
        "                \"parameters\": {\n"
894
        "                    \"media_data\": \"test_media\",\n"
895
        "                    \"width\": 20,\n"
896
        "                    \"perpendicular_range\": 50,\n"
897
        "                    \"use_processed_data\": true,\n"
898
        "                    \"approach\": \"PEAK_WIDTH_HALF_MAX\",\n"
899
        "                    \"output_mode\": \"FWHM_PROFILE_EXTENTS\"\n"
900
        "                }\n"
901
        "            }\n"
902
        "        ]\n"
903
        "    }\n"
904
        "}\n"
905
        "]";
906
    
907
    std::filesystem::path json_filepath_fwhm_extents = test_dir / "pipeline_config_fwhm_extents.json";
1✔
908
    {
909
        std::ofstream json_file(json_filepath_fwhm_extents);
1✔
910
        REQUIRE(json_file.is_open());
1✔
911
        json_file << json_config_fwhm_extents;
1✔
912
        json_file.close();
1✔
913
    }
1✔
914
    
915
    // Execute the FWHM extents pipeline
916
    auto data_info_list_fwhm_extents = load_data_from_json_config(&dm, json_filepath_fwhm_extents.string());
1✔
917
    
918
    // Verify the FWHM extents results
919
    auto result_line_fwhm_extents = dm.getData<LineData>("fwhm_extents_line");
3✔
920
    REQUIRE(result_line_fwhm_extents != nullptr);
1✔
921
    
922
    auto fwhm_extents_lines = result_line_fwhm_extents->getAtTime(TimeFrameIndex(0));
1✔
923
    REQUIRE(fwhm_extents_lines.size() == 3); // One line per vertex
1✔
924
    auto fwhm_extents_vertices = fwhm_extents_lines[0];
1✔
925
    // Should have 3 points per vertex: [left_extent, max_point, right_extent]
926
    // For 3 vertices, this should be 9 points total
927
    REQUIRE(fwhm_extents_vertices.size() == 3);
1✔
928
    
929
    // Cleanup
930
    try {
931
        std::filesystem::remove_all(test_dir);
1✔
NEW
932
    } catch (const std::exception& e) {
×
NEW
933
        std::cerr << "Warning: Cleanup failed: " << e.what() << std::endl;
×
NEW
934
    }
×
935
} 
2✔
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