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

NREL / SolTrace / 21046664913

15 Jan 2026 09:17PM UTC coverage: 87.999% (+0.2%) from 87.815%
21046664913

Pull #96

github

web-flow
Merge a5971e7da into e78d2bfb0
Pull Request #96: 95 implement embree runner

605 of 797 new or added lines in 17 files covered. (75.91%)

14 existing lines in 7 files now uncovered.

6255 of 7108 relevant lines covered (88.0%)

7221404.67 hits per line

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

69.32
/coretrace/simulation_data/aperture.cpp
1

2
#include "aperture.hpp"
3
#include "simdata_io.hpp"
4

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

9
// #include <iostream>
10

11
#include "constants.hpp"
12

13
namespace SolTrace::Data
14
{
15

16
    aperture_ptr Aperture::make_aperture_from_type(ApertureType type,
21,312✔
17
                                                   const std::vector<double> &args)
18
    {
19
        // std::cout << "Type: " << type
20
        //           << " NArgs: " << args.size()
21
        //           << std::endl;
22
        switch (type)
21,312✔
23
        {
24
        case ApertureType::ANNULUS:
2✔
25
            if (args.size() < 3)
2✔
26
                break;
1✔
27
            return make_aperture<Annulus>(args[0], args[1], args[2]);
1✔
28
        case ApertureType::CIRCLE:
2✔
29
            if (args.size() < 1)
2✔
30
                break;
1✔
31
            return make_aperture<Circle>(args[0]);
1✔
32
        case ApertureType::HEXAGON:
77✔
33
            if (args.size() < 1)
77✔
NEW
34
                break;
×
35
            return make_aperture<Hexagon>(args[0]);
77✔
36
        case ApertureType::RECTANGLE:
21,224✔
37
            if (args.size() < 2)
21,224✔
NEW
38
                break;
×
39
            return make_aperture<Rectangle>(args[0], args[1]); // This is assuming centered around the origin
21,224✔
40
        case ApertureType::EQUILATERAL_TRIANGLE:
1✔
41
            if (args.size() < 1)
1✔
NEW
42
                break;
×
43
            return make_aperture<EqualateralTriangle>(args[0]);
1✔
44
        case ApertureType::SINGLE_AXIS_CURVATURE_SECTION:
4✔
45
            if (args.size() < 3)
4✔
NEW
46
                break;
×
47
            return make_aperture<Rectangle>(
4✔
48
                args[1] - args[0], args[2],
4✔
49
                -0.5 * (args[1] - args[0]), -0.5 * args[2]);
12✔
50
            // return make_aperture<Rectangle>(args[1] - args[0], args[2]);
51
        case ApertureType::IRREGULAR_TRIANGLE:
1✔
52
            if (args.size() < 6)
1✔
NEW
53
                break;
×
54
            return make_aperture<IrregularTriangle>(
2✔
55
                args[0], args[1], args[2], args[3], args[4], args[5]);
2✔
56
        case ApertureType::IRREGULAR_QUADRILATERAL:
1✔
57
            if (args.size() < 8)
1✔
NEW
58
                break;
×
59
            return make_aperture<IrregularQuadrilateral>(
2✔
60
                args[0], args[1], args[2], args[3],
1✔
61
                args[4], args[5], args[6], args[7]);
2✔
NEW
62
        default:
×
63
            // TODO handle error
64
            // Unsupported case
NEW
65
            return nullptr;
×
66
            // break;
67
        }
68

69
        // TODO handle error
70
        // Wrong number of arguments
71

72
        return nullptr;
2✔
73
        // return aperture_ptr();
74
    }
75

76
    aperture_ptr Aperture::make_aperture_from_json(const nlohmann::ordered_json &jnode)
2,441✔
77
    {
78
        if (!jnode.contains("aperture_type"))
2,441✔
79
            throw std::invalid_argument("Missing aperture_type");
1✔
80
        std::string type_str = jnode.at("aperture_type");
2,440✔
81
        ApertureType aperture_type = get_enum_from_string(type_str, ApertureTypeMap, ApertureType::APERTURE_UNKNOWN);
2,440✔
82
        switch (aperture_type)
2,440✔
83
        {
84
        case ApertureType::ANNULUS:
2✔
85
            return make_aperture<Annulus>(jnode);
2✔
86
        case ApertureType::CIRCLE:
2✔
87
            return make_aperture<Circle>(jnode);
2✔
88
        case ApertureType::HEXAGON:
52✔
89
            return make_aperture<Hexagon>(jnode);
52✔
90
        case ApertureType::RECTANGLE:
2,377✔
91
            return make_aperture<Rectangle>(jnode);
2,377✔
92
        case ApertureType::EQUILATERAL_TRIANGLE:
2✔
93
            return make_aperture<EqualateralTriangle>(jnode);
2✔
94
        case ApertureType::IRREGULAR_TRIANGLE:
2✔
95
            return make_aperture<IrregularTriangle>(jnode);
2✔
96
        case ApertureType::IRREGULAR_QUADRILATERAL:
2✔
97
            return make_aperture<IrregularQuadrilateral>(jnode);
2✔
98
        default:
1✔
99
            throw std::invalid_argument("Unsupported aperture_type: " + type_str);
1✔
100
        }
101
    }
2,440✔
102

NEW
103
    Aperture::Point Aperture::midpoint(const Aperture::Point &v0,
×
104
                                       const Aperture::Point &v1) const
105
    {
NEW
106
        return Aperture::Point((v0.x + v1.x) / 2, (v0.y + v1.y) / 2);
×
107
    }
108

NEW
109
    std::vector<Aperture::Triangle> Aperture::subdivide(Aperture::Triangle tri,
×
110
                                                        int n) const
111
    {
NEW
112
        Point v0 = tri.a;
×
NEW
113
        Point v1 = tri.b;
×
NEW
114
        Point v2 = tri.c;
×
NEW
115
        Point m01 = midpoint(v0, v1);
×
NEW
116
        Point m12 = midpoint(v1, v2);
×
NEW
117
        Point m20 = midpoint(v2, v0);
×
NEW
118
        std::vector<Triangle> result;
×
NEW
119
        if (n - 1 == 0)
×
120
        {
NEW
121
            result.push_back(Triangle(v0, m01, m20));
×
NEW
122
            result.push_back(Triangle(m01, v1, m12));
×
NEW
123
            result.push_back(Triangle(m12, m01, m20));
×
NEW
124
            result.push_back(Triangle(m12, m20, v2));
×
NEW
125
            return result;
×
126
        }
NEW
127
        auto t1 = subdivide(Triangle(v0, m01, m20), n - 1);
×
NEW
128
        auto t2 = subdivide(Triangle(v0, m01, m20), n - 1);
×
NEW
129
        auto t3 = subdivide(Triangle(v0, m01, m20), n - 1);
×
NEW
130
        auto t4 = subdivide(Triangle(v0, m01, m20), n - 1);
×
NEW
131
        result.insert(result.end(), t1.begin(), t1.end());
×
NEW
132
        result.insert(result.end(), t2.begin(), t2.end());
×
NEW
133
        result.insert(result.end(), t3.begin(), t3.end());
×
NEW
134
        result.insert(result.end(), t4.begin(), t4.end());
×
UNCOV
135
        return result;
×
UNCOV
136
    }
×
137

NEW
138
    int Aperture::index_of(std::vector<Aperture::Point> &v,
×
139
                           const Aperture::Point &p) const
140
    {
NEW
141
        auto it = find(v.begin(), v.end(), p);
×
NEW
142
        if (it == v.end())
×
143
        {
NEW
144
            v.push_back(p);
×
NEW
145
            return v.size() - 1;
×
146
        }
NEW
147
        return std::distance(v.begin(), it);
×
148
    }
149

NEW
150
    std::tuple<std::vector<double>, std::vector<int>> Aperture::indexed_triangles(
×
151
        const std::vector<Aperture::Triangle> &triangles) const
152
    {
NEW
153
        std::vector<int> indices;
×
NEW
154
        std::vector<Point> points;
×
NEW
155
        std::vector<double> flattened;
×
NEW
156
        for (const Triangle &tri : triangles)
×
157
        {
NEW
158
            indices.push_back(index_of(points, tri.a));
×
NEW
159
            indices.push_back(index_of(points, tri.b));
×
NEW
160
            indices.push_back(index_of(points, tri.c));
×
161
        }
NEW
162
        for (const Point &p : points)
×
163
        {
NEW
164
            flattened.push_back(p.x);
×
NEW
165
            flattened.push_back(p.y);
×
166
        }
NEW
167
        return std::make_pair(flattened, indices);
×
NEW
168
    }
×
169

170
    Annulus::Annulus(const nlohmann::ordered_json &jnode)
2✔
171
        : Aperture(ApertureType::ANNULUS)
2✔
172
    {
173
        this->inner_radius = jnode.at("inner_radius");
2✔
174
        this->outer_radius = jnode.at("outer_radius");
2✔
175
        this->arc_angle = jnode.at("arc_angle");
2✔
176
    }
2✔
177

178
    double Annulus::aperture_area() const
7✔
179
    {
180
        // TODO: input.cpp on line 219 uses the formula
181
        //    elm->ParameterC*(ACOSM1O180)*(elm->ParameterB - elm->ParameterA);
182
        //    = \theta * (r - R)
183
        // This seems to be wrong...
184
        double R = this->outer_radius;
7✔
185
        double r = this->inner_radius;
7✔
186
        // Convert to radians
187
        double arc = this->arc_angle * D2R;
7✔
188
        return 0.5 * arc * (R * R - r * r);
7✔
189
    }
190

191
    double Annulus::diameter_circumscribed_circle() const
25✔
192
    {
193
        return 2.0 * this->outer_radius;
25✔
194
    }
195

196
    void Annulus::bounding_box(double &xmin,
1✔
197
                               double &xmax,
198
                               double &ymin,
199
                               double &ymax) const
200
    {
201
        xmin = -this->outer_radius;
1✔
202
        xmax = this->outer_radius;
1✔
203
        ymin = -this->outer_radius;
1✔
204
        ymax = this->outer_radius;
1✔
205
        return;
1✔
206
    }
207

208
    bool Annulus::is_in(double x, double y) const
1,184,750✔
209
    {
210
        double r = sqrt(x * x + y * y);
1,184,750✔
211
        bool inside = false;
1,184,750✔
212
        if (this->inner_radius <= r &&
1,184,750✔
213
            r <= this->outer_radius)
1,149,930✔
214
        {
215
            double theta = atan2(y, x);
563,595✔
216
            // Arc is split across x-axis, hence the 0.5
217
            double arc = 0.5 * this->arc_angle * D2R;
563,595✔
218
            inside = (-arc <= theta && theta <= arc);
563,595✔
219
        }
220
        return inside;
1,184,750✔
221
    }
222

223
    std::tuple<std::vector<double>, std::vector<int>>
NEW
224
    Annulus::triangulation() const
×
225
    {
NEW
226
        const int resolution = 32;
×
NEW
227
        std::vector<double> verts;
×
NEW
228
        std::vector<int> indices;
×
NEW
229
        for (int i = 0; i <= resolution; i++)
×
230
        {
NEW
231
            const double u = i / resolution * PI * 2;
×
NEW
232
            verts.push_back(inner_radius * std::cos(u));
×
NEW
233
            verts.push_back(inner_radius * std::sin(u));
×
NEW
234
            verts.push_back(outer_radius * std::cos(u));
×
NEW
235
            verts.push_back(outer_radius * std::sin(u));
×
236
        }
NEW
237
        for (int i = 0; i < resolution - 3; i += 2)
×
238
        {
NEW
239
            const int a = i;
×
NEW
240
            const int b = i + 1;
×
NEW
241
            const int c = i + 2;
×
NEW
242
            const int d = i + 3;
×
243
            // Generate two triangles for each quad in the mesh
244
            // Adjust order to be counter-clockwise
NEW
245
            indices.push_back(a);
×
NEW
246
            indices.push_back(d);
×
NEW
247
            indices.push_back(b);
×
NEW
248
            indices.push_back(b);
×
NEW
249
            indices.push_back(d);
×
NEW
250
            indices.push_back(c);
×
251
        }
NEW
252
        return std::make_tuple(verts, indices);
×
NEW
253
    }
×
254

255
    aperture_ptr Annulus::make_copy() const
9✔
256
    {
257
        // Invokes the implicit copy constructor
258
        return make_aperture<Annulus>(*this);
9✔
259
    }
260

261
    void Annulus::write_json(nlohmann::ordered_json &jnode) const
1✔
262
    {
263
        ApertureType type = ApertureType::ANNULUS;
1✔
264
        jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
265
        jnode["inner_radius"] = this->inner_radius;
1✔
266
        jnode["outer_radius"] = this->outer_radius;
1✔
267
        jnode["arc_angle"] = this->arc_angle;
1✔
268
    }
1✔
269

270
    Circle::Circle(const nlohmann::ordered_json &jnode)
2✔
271
        : Aperture(ApertureType::CIRCLE)
2✔
272
    {
273
        this->diameter = jnode.at("diameter");
2✔
274
    }
2✔
275

276
    double Circle::aperture_area() const
5✔
277
    {
278
        return 0.25 * PI * this->diameter * this->diameter;
5✔
279
    }
280

281
    double Circle::diameter_circumscribed_circle() const
1,230,615✔
282
    {
283
        return this->diameter;
1,230,615✔
284
    }
285

286
    void Circle::bounding_box(double &xmin,
1✔
287
                              double &xmax,
288
                              double &ymin,
289
                              double &ymax) const
290
    {
291
        double r = this->radius_circumscribed_circle();
1✔
292
        xmin = -r;
1✔
293
        xmax = r;
1✔
294
        ymin = -r;
1✔
295
        ymax = r;
1✔
296
        return;
1✔
297
    }
298

299
    bool Circle::is_in(double x, double y) const
1,230,579✔
300
    {
301
        double r = sqrt(x * x + y * y);
1,230,579✔
302
        return r <= this->radius_circumscribed_circle();
1,230,579✔
303
    }
304

305
    aperture_ptr Circle::make_copy() const
17✔
306
    {
307
        // Invokes the implicit copy constructor
308
        return make_aperture<Circle>(*this);
17✔
309
    }
310

311
    void Circle::write_json(nlohmann::ordered_json &jnode) const
1✔
312
    {
313
        ApertureType type = ApertureType::CIRCLE;
1✔
314
        jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
315
        jnode["diameter"] = this->diameter;
1✔
316
    }
1✔
317

318
    std::tuple<std::vector<double>, std::vector<int>>
NEW
319
    Circle::triangulation() const
×
320
    {
321
        // Using a fixed Delaunay triangulation of the unit circle
322
        std::vector<double> verts = {
323
            1.00000000e+00, 0.00000000e+00, 9.51056516e-01, 3.09016994e-01,
324
            8.09016994e-01, 5.87785252e-01, 5.87785252e-01, 8.09016994e-01,
325
            3.09016994e-01, 9.51056516e-01, 6.12323400e-17, 1.00000000e+00,
326
            -3.09016994e-01, 9.51056516e-01, -5.87785252e-01, 8.09016994e-01,
327
            -8.09016994e-01, 5.87785252e-01, -9.51056516e-01, 3.09016994e-01,
328
            -1.00000000e+00, 1.22464680e-16, -9.51056516e-01, -3.09016994e-01,
329
            -8.09016994e-01, -5.87785252e-01, -5.87785252e-01, -8.09016994e-01,
330
            -3.09016994e-01, -9.51056516e-01, -1.83697020e-16, -1.00000000e+00,
331
            3.09016994e-01, -9.51056516e-01, 5.87785252e-01, -8.09016994e-01,
332
            8.09016994e-01, -5.87785252e-01, 9.51056516e-01, -3.09016994e-01,
333
            0.00000000e+00, 0.00000000e+00, -5.00000000e-01, -5.00000000e-01,
334
            -5.00000000e-01, 0.00000000e+00, -5.00000000e-01, 5.00000000e-01,
335
            0.00000000e+00, -5.00000000e-01, 0.00000000e+00, 5.00000000e-01,
336
            5.00000000e-01, -5.00000000e-01, 5.00000000e-01, 0.00000000e+00,
NEW
337
            5.00000000e-01, 5.00000000e-01};
×
338
        std::vector<int> indices = {
339
            22, 11, 21, 11, 22, 10, 17, 18, 26, 14, 24, 21, 13, 14, 21, 24, 14, 15,
340
            25, 6, 23, 6, 25, 5, 9, 22, 23, 8, 9, 23, 22, 9, 10, 27, 1, 28,
341
            1, 27, 0, 19, 27, 26, 18, 19, 26, 27, 19, 0, 4, 25, 28, 3, 4, 28,
342
            25, 4, 5, 12, 13, 21, 11, 12, 21, 24, 16, 26, 16, 17, 26, 16, 24, 15,
343
            7, 8, 23, 6, 7, 23, 2, 3, 28, 1, 2, 28, 24, 22, 21, 22, 24, 20,
NEW
344
            24, 27, 20, 27, 24, 26, 25, 22, 20, 22, 25, 23, 27, 25, 20, 25, 27, 28};
×
345
        // scale from the unit cirle to our circle
NEW
346
        std::transform(
×
NEW
347
            verts.begin(), verts.end(), verts.begin(), [this](double element)
×
NEW
348
            { return element *= this->diameter / 2.0; });
×
NEW
349
        return std::make_tuple(verts, indices);
×
NEW
350
    }
×
351

352
    EqualateralTriangle::EqualateralTriangle(const nlohmann::ordered_json &jnode)
2✔
353
        : Aperture(ApertureType::EQUILATERAL_TRIANGLE)
2✔
354
    {
355
        this->circumscribe_diameter = jnode.at("circumscribe_diameter");
2✔
356
    }
2✔
357

358
    double EqualateralTriangle::aperture_area() const
4✔
359
    {
360
        double r = 0.5 * this->circumscribe_diameter;
4✔
361
        return 0.75 * sqrt(3) * r * r;
4✔
362
    }
363

364
    double EqualateralTriangle::diameter_circumscribed_circle() const
1,002,016✔
365
    {
366
        return this->circumscribe_diameter;
1,002,016✔
367
    }
368

369
    void EqualateralTriangle::bounding_box(double &xmin,
1✔
370
                                           double &xmax,
371
                                           double &ymin,
372
                                           double &ymax) const
373
    {
374
        double r = this->radius_circumscribed_circle();
1✔
375
        double side = sqrt(3.0) * r;
1✔
376
        double h = 1.5 * r;
1✔
377
        xmin = -0.5 * side;
1✔
378
        xmax = 0.5 * side;
1✔
379
        ymin = r - h;
1✔
380
        ymax = r;
1✔
381
        return;
1✔
382
    }
383

384
    bool EqualateralTriangle::is_in(double x, double y) const
1,002,008✔
385
    {
386
        double r = sqrt(x * x + y * y);
1,002,008✔
387
        double ro = this->radius_circumscribed_circle();
1,002,008✔
388
        if (r > ro)
1,002,008✔
389
            return false;
805,684✔
390

391
        double ri = 0.5 * ro;
196,324✔
392
        if (r <= ri)
196,324✔
393
            return true;
49,075✔
394

395
        double y0;
396
        // double a = ro / sqrt(3.0) = 2 * ri / sqrt(3.0);
397
        if (0.0 <= x && x <= ro)
147,249✔
398
        {
399
            // y0 = -sqrt(3.0) * (x - a);
400
            y0 = ro - sqrt(3.0) * x;
73,749✔
401
            return (-ri <= y && y <= y0);
73,749✔
402
        }
403
        else if (-ro <= x && x < 0.0)
73,500✔
404
        {
405
            // y0 = sqrt(3.0) * (x + a);
406
            y0 = sqrt(3.0) * x + ro;
73,500✔
407
            return (-ri <= y && y <= y0);
73,500✔
408
        }
409

UNCOV
410
        return false;
×
411
    }
412

413
    std::tuple<std::vector<double>, std::vector<int>>
NEW
414
    EqualateralTriangle::triangulation() const
×
415
    {
NEW
416
        double r = circumscribe_diameter / 2.0;
×
417
        Triangle tri(Point(0, r),
418
                     Point(r * cos(-PI / 6.0), r * sin(-PI / 6.0)),
NEW
419
                     Point(r * cos(7 * PI / 6.0), r * sin(7 * PI / 6.0)));
×
NEW
420
        return indexed_triangles(subdivide(tri, 3));
×
421
    }
422

423
    aperture_ptr EqualateralTriangle::make_copy() const
1✔
424
    {
425
        // Invokes the implicit copy constructor
426
        return make_aperture<EqualateralTriangle>(*this);
1✔
427
    }
428

429
    void EqualateralTriangle::write_json(nlohmann::ordered_json &jnode) const
1✔
430
    {
431
        ApertureType type = ApertureType::EQUILATERAL_TRIANGLE;
1✔
432
        jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
433
        jnode["circumscribe_diameter"] = this->circumscribe_diameter;
1✔
434
    }
1✔
435

436
    Hexagon::Hexagon(const nlohmann::ordered_json &jnode)
52✔
437
        : Aperture(ApertureType::HEXAGON)
52✔
438
    {
439
        this->circumscribe_diameter = jnode.at("circumscribe_diameter");
52✔
440
    }
52✔
441

442
    double Hexagon::aperture_area() const
4✔
443
    {
444
        // TODO: input.cpp on line 210 uses the formula
445
        //    5*sqr(elm->ParameterA/2.0)*cos(30.0*(ACOSM1O180))*sin(30.0*(ACOSM1O180));
446
        //    = 5*(d/2)^2*cos(pi/6)*sin(pi/6)
447
        //    = 5*(d/2)^2*sqrt(3)/2*1/2
448
        //    = 5*sqrt(3)/4 * (d/2)^2
449
        //    = 1.25*sqrt(3) * (d/2)^2
450
        // This seems to be wrong...
451
        double r = 0.5 * this->circumscribe_diameter;
4✔
452
        return 1.5 * sqrt(3) * r * r;
4✔
453
    }
454

455
    double Hexagon::diameter_circumscribed_circle() const
1,853,453✔
456
    {
457
        return circumscribe_diameter;
1,853,453✔
458
    }
459

460
    void Hexagon::bounding_box(double &xmin,
1✔
461
                               double &xmax,
462
                               double &ymin,
463
                               double &ymax) const
464
    {
465
        double r = this->radius_circumscribed_circle();
1✔
466
        double apothem = 0.5 * r * sqrt(3.0);
1✔
467
        xmin = -r;
1✔
468
        xmax = r;
1✔
469
        ymin = -apothem;
1✔
470
        ymax = apothem;
1✔
471
        return;
1✔
472
    }
473

474
    bool Hexagon::is_in(double x, double y) const
1,798,633✔
475
    {
476
        double r = sqrt(x * x + y * y);
1,798,633✔
477
        double ro = this->radius_circumscribed_circle();
1,798,633✔
478
        if (r > ro)
1,798,633✔
479
            return false;
1,577,358✔
480

481
        double ri = 0.5 * sqrt(3.0) * ro;
221,275✔
482
        if (r <= ri)
221,275✔
483
            return true;
166,542✔
484

485
        // NOTE: Old code used
486
        //    xl = sqrt(ro^2 - ri^2)
487
        // where `ro` is the radius of the circumscribing circle and `ri` is
488
        // the radius of the inscribing circle. But this is equivalent to
489
        //    xl = 0.5 * ro
490

491
        double xl = 0.5 * this->radius_circumscribed_circle();
54,733✔
492
        double y1, y2;
493
        if (xl < x && x <= ro)
54,733✔
494
        {
495
            y1 = sqrt(3.0) * (x - ro);
17,700✔
496
            y2 = -y1;
17,700✔
497
            // if (y1 <= y && y <= y2) return true;
498
            return (y1 <= y && y <= y2);
17,700✔
499
        }
500
        else if (-xl <= x && x <= xl)
37,033✔
501
        {
502
            return (-ri <= y && y <= ri);
19,299✔
503
        }
504
        else if (-ro <= x && x < -xl)
17,734✔
505
        {
506
            y1 = sqrt(3.0) * (x + ro);
17,734✔
507
            y2 = -y1;
17,734✔
508
            return (y2 <= y && y <= y1);
17,734✔
509
        }
510

NEW
511
        return false;
×
512
    }
513

514
    std::tuple<std::vector<double>, std::vector<int>>
NEW
515
    Hexagon::triangulation() const
×
516
    {
NEW
517
        double r = circumscribe_diameter / 2.0;
×
518
        std::vector<Triangle> t0 =
519
            subdivide(Triangle(Point(0, 0),
520
                               Point(r * cos(PI / 3.0), r * sin(PI / 3.0)),
521
                               Point(r, 0)),
NEW
522
                      2);
×
523
        std::vector<Triangle> t1 = subdivide(
524
            Triangle(Point(0, 0),
525
                     Point(r * cos(2 * PI / 3.0), r * sin(2 * PI / 3.0)),
526
                     Point(r * cos(PI / 3.0), r * sin(PI / 3.0))),
NEW
527
            2);
×
528
        std::vector<Triangle> t2 = subdivide(
529
            Triangle(Point(0, 0),
530
                     Point(-r, 0),
531
                     Point(r * cos(2 * PI / 3.0), r * sin(2 * PI / 3.0))),
NEW
532
            2);
×
533
        std::vector<Triangle> t3 = subdivide(
534
            Triangle(Point(0, 0),
535
                     Point(-r, 0),
536
                     Point(r * cos(4 * PI / 3.0), r * sin(4 * PI / 3.0))),
NEW
537
            2);
×
538
        std::vector<Triangle> t4 = subdivide(
539
            Triangle(Point(0, 0),
540
                     Point(r * cos(5 * PI / 3.0), r * sin(5 * PI / 3.0)),
541
                     Point(r * cos(4 * PI / 3.0), r * sin(4 * PI / 3.0))),
NEW
542
            2);
×
543
        std::vector<Triangle> t5 = subdivide(
544
            Triangle(Point(0, 0),
545
                     Point(r, 0),
546
                     Point(r * cos(5 * PI / 3.0), r * sin(5 * PI / 3.0))),
NEW
547
            2);
×
NEW
548
        std::vector<Triangle> triangles;
×
NEW
549
        triangles.insert(triangles.end(), t0.begin(), t0.end());
×
NEW
550
        triangles.insert(triangles.end(), t1.begin(), t1.end());
×
NEW
551
        triangles.insert(triangles.end(), t2.begin(), t2.end());
×
NEW
552
        triangles.insert(triangles.end(), t3.begin(), t3.end());
×
NEW
553
        triangles.insert(triangles.end(), t4.begin(), t4.end());
×
NEW
554
        triangles.insert(triangles.end(), t5.begin(), t5.end());
×
NEW
555
        return indexed_triangles(triangles);
×
NEW
556
    }
×
557

558
    aperture_ptr Hexagon::make_copy() const
78✔
559
    {
560
        // Invokes the implicit copy constructor
561
        return make_aperture<Hexagon>(*this);
78✔
562
    }
563

564
    void Hexagon::write_json(nlohmann::ordered_json &jnode) const
76✔
565
    {
566
        ApertureType type = ApertureType::HEXAGON;
76✔
567
        jnode["aperture_type"] = ApertureTypeMap.at(type);
76✔
568
        jnode["circumscribe_diameter"] = this->circumscribe_diameter;
76✔
569
    }
76✔
570

571
    IrregularTriangle::IrregularTriangle(double x1, double y1,
3✔
572
                                         double x2, double y2,
573
                                         double x3, double y3)
3✔
574
        : Aperture(ApertureType::IRREGULAR_TRIANGLE),
575
          x1(x1), y1(y1),
3✔
576
          x2(x2), y2(y2),
3✔
577
          x3(x3), y3(y3)
3✔
578
    {
579
    }
3✔
580

581
    IrregularTriangle::IrregularTriangle(const nlohmann::ordered_json &jnode)
2✔
582
        : Aperture(ApertureType::IRREGULAR_TRIANGLE)
2✔
583
    {
584
        this->x1 = jnode.at("x1");
2✔
585
        this->y1 = jnode.at("y1");
2✔
586
        this->x2 = jnode.at("x2");
2✔
587
        this->y2 = jnode.at("y2");
2✔
588
        this->x3 = jnode.at("x3");
2✔
589
        this->y3 = jnode.at("y3");
2✔
590
    }
2✔
591

592
    std::tuple<std::vector<double>, std::vector<int>>
NEW
593
    IrregularTriangle::triangulation() const
×
594
    {
NEW
595
        Triangle tri(Point(x1, y1), Point(x2, y2), Point(x3, y3));
×
NEW
596
        return indexed_triangles(subdivide(tri, 3));
×
597
    }
598

599
    double IrregularTriangle::aperture_area() const
4✔
600
    {
601
        double v11 = this->x1 - this->x2;
4✔
602
        double v12 = this->y1 - this->y2;
4✔
603
        double v21 = this->x3 - this->x2;
4✔
604
        double v22 = this->y3 - this->y2;
4✔
605

606
        double v1m = sqrt(v11 * v11 + v12 * v12);
4✔
607
        double v2m = sqrt(v21 * v21 + v22 * v22);
4✔
608

609
        double theta = acos((v11 * v21 + v12 * v22) / (v1m * v2m));
4✔
610
        double area = 0.5 * v1m * v2m * sin(theta);
4✔
611

612
        return area;
4✔
613
    }
614

NEW
615
    double IrregularTriangle::diameter_circumscribed_circle() const
×
616
    {
617
        // TODO: Not sure this is exact. Is that a problem?
NEW
618
        double xmax = std::max(std::max(x1, x2), x3);
×
NEW
619
        double ymax = std::max(std::max(y1, y2), y3);
×
NEW
620
        double xmin = std::min(std::min(x1, x2), x3);
×
NEW
621
        double ymin = std::min(std::min(y1, y2), y3);
×
NEW
622
        double dx = xmax - xmin;
×
NEW
623
        double dy = ymax - ymin;
×
NEW
624
        return sqrt(dx * dx + dy * dy);
×
625
    }
626

627
    void IrregularTriangle::bounding_box(double &xmin,
1✔
628
                                         double &xmax,
629
                                         double &ymin,
630
                                         double &ymax) const
631
    {
632
        xmin = std::min(x1, std::min(x2, x3));
1✔
633
        xmax = std::max(x1, std::max(x2, x3));
1✔
634
        ymin = std::min(y1, std::min(y2, y3));
1✔
635
        ymax = std::max(y1, std::max(y2, y3));
1✔
636
        return;
1✔
637
    }
638

639
    bool IrregularTriangle::is_in(double x, double y) const
1,002,005✔
640
    {
641
        return intri(x1, y1, x2, y2, x3, y3, x, y);
1,002,005✔
642
    }
643

644
    aperture_ptr IrregularTriangle::make_copy() const
1✔
645
    {
646
        // Invokes the implicit copy constructor
647
        return make_aperture<IrregularTriangle>(*this);
1✔
648
    }
649

650
    void IrregularTriangle::write_json(nlohmann::ordered_json &jnode) const
1✔
651
    {
652
        ApertureType type = ApertureType::IRREGULAR_TRIANGLE;
1✔
653
        jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
654
        jnode["x1"] = this->x1;
1✔
655
        jnode["y1"] = this->y1;
1✔
656
        jnode["x2"] = this->x2;
1✔
657
        jnode["y2"] = this->y2;
1✔
658
        jnode["x3"] = this->x3;
1✔
659
        jnode["y3"] = this->y3;
1✔
660
    }
1✔
661

662
    IrregularQuadrilateral::IrregularQuadrilateral(double x1, double y1,
3✔
663
                                                   double x2, double y2,
664
                                                   double x3, double y3,
665
                                                   double x4, double y4)
3✔
666
        : Aperture(ApertureType::IRREGULAR_QUADRILATERAL),
667
          x1(x1), y1(y1),
3✔
668
          x2(x2), y2(y2),
3✔
669
          x3(x3), y3(y3),
3✔
670
          x4(x4), y4(y4)
3✔
671
    {
672
    }
3✔
673

674
    IrregularQuadrilateral::IrregularQuadrilateral(const nlohmann::ordered_json &jnode)
2✔
675
        : Aperture(ApertureType::IRREGULAR_QUADRILATERAL)
2✔
676
    {
677
        this->x1 = jnode.at("x1");
2✔
678
        this->y1 = jnode.at("y1");
2✔
679
        this->x2 = jnode.at("x2");
2✔
680
        this->y2 = jnode.at("y2");
2✔
681
        this->x3 = jnode.at("x3");
2✔
682
        this->y3 = jnode.at("y3");
2✔
683
        this->x4 = jnode.at("x4");
2✔
684
        this->y4 = jnode.at("y4");
2✔
685
    }
2✔
686

687
    double IrregularQuadrilateral::aperture_area() const
3✔
688
    {
689
        double v11 = this->x1 - this->x2;
3✔
690
        double v12 = this->y1 - this->y2;
3✔
691
        double v21 = this->x3 - this->x2;
3✔
692
        double v22 = this->y3 - this->y2;
3✔
693
        double v31 = this->x3 - this->x4;
3✔
694
        double v32 = this->y3 - this->y4;
3✔
695
        double v41 = this->x1 - this->x4;
3✔
696
        double v42 = this->y1 - this->y4;
3✔
697

698
        double v1m = sqrt(v11 * v11 + v12 * v12);
3✔
699
        double v2m = sqrt(v21 * v21 + v22 * v22);
3✔
700
        double v3m = sqrt(v31 * v31 + v32 * v32);
3✔
701
        double v4m = sqrt(v41 * v41 + v42 * v42);
3✔
702

703
        double theta1 = acos((v11 * v21 + v12 * v22) / (v1m * v2m));
3✔
704
        double theta2 = acos((v31 * v41 + v32 * v42) / (v3m * v4m));
3✔
705

706
        double area = 0.5 * (v1m * v2m * sin(theta1) + v3m * v4m * sin(theta2));
3✔
707
        return area;
3✔
708
    }
709

710
    std::tuple<std::vector<double>, std::vector<int>>
NEW
711
    IrregularQuadrilateral::triangulation() const
×
712
    {
713
        std::vector<Triangle> t0 =
NEW
714
            subdivide(Triangle(Point(x1, y1), Point(x3, y3), Point(x2, y2)), 2);
×
715
        std::vector<Triangle> t1 =
NEW
716
            subdivide(Triangle(Point(x1, y1), Point(x4, y4), Point(x3, y3)), 2);
×
NEW
717
        std::vector<Triangle> triangles;
×
NEW
718
        triangles.insert(triangles.end(), t0.begin(), t0.end());
×
NEW
719
        triangles.insert(triangles.end(), t1.begin(), t1.end());
×
NEW
720
        return indexed_triangles(triangles);
×
NEW
721
    }
×
722

NEW
723
    double IrregularQuadrilateral::diameter_circumscribed_circle() const
×
724
    {
725
        // TODO: Not sure this is exact. Is that a problem?
NEW
726
        double xmax = std::max(std::max(x1, x2), std::max(x3, x4));
×
NEW
727
        double ymax = std::max(std::max(y1, y2), std::max(y3, y4));
×
NEW
728
        double xmin = std::min(std::min(x1, x2), std::min(x3, x4));
×
NEW
729
        double ymin = std::min(std::min(y1, y2), std::min(y3, y4));
×
NEW
730
        double dx = xmax - xmin;
×
NEW
731
        double dy = ymax - ymin;
×
NEW
732
        return sqrt(dx * dx + dy * dy);
×
733
    }
734

735
    void IrregularQuadrilateral::bounding_box(double &xmin,
1✔
736
                                              double &xmax,
737
                                              double &ymin,
738
                                              double &ymax) const
739
    {
740
        xmin = std::min(std::min(x1, x2), std::min(x3, x4));
1✔
741
        xmax = std::max(std::max(x1, x2), std::max(x3, x4));
1✔
742
        ymin = std::min(std::min(y1, y2), std::min(y3, y4));
1✔
743
        ymax = std::max(std::max(y1, y2), std::max(y3, y4));
1✔
744
        return;
1✔
745
    }
746

747
    bool IrregularQuadrilateral::is_in(double x, double y) const
1,002,007✔
748
    {
749
        return inquad(x1, y1, x2, y2, x3, y3, x4, y4, x, y);
1,002,007✔
750
    }
751

752
    aperture_ptr IrregularQuadrilateral::make_copy() const
1✔
753
    {
754
        // Invokes the implicit copy constructor
755
        return make_aperture<IrregularQuadrilateral>(*this);
1✔
756
    }
757

758
    void IrregularQuadrilateral::write_json(nlohmann::ordered_json &jnode) const
1✔
759
    {
760
        ApertureType type = ApertureType::IRREGULAR_QUADRILATERAL;
1✔
761
        jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
762
        jnode["x1"] = this->x1;
1✔
763
        jnode["y1"] = this->y1;
1✔
764
        jnode["x2"] = this->x2;
1✔
765
        jnode["y2"] = this->y2;
1✔
766
        jnode["x3"] = this->x3;
1✔
767
        jnode["y3"] = this->y3;
1✔
768
        jnode["x4"] = this->x4;
1✔
769
        jnode["y4"] = this->y4;
1✔
770
    }
1✔
771

772
    Rectangle::Rectangle(double xlen, double ylen)
21,471✔
773
        : Aperture(ApertureType::RECTANGLE),
774
          x_length(xlen),
21,471✔
775
          y_length(ylen)
21,471✔
776
    {
777
        // Default to rectangle centered at the origin.
778
        this->x_coord = -0.5 * this->x_length;
21,471✔
779
        this->y_coord = -0.5 * this->y_length;
21,471✔
780
        return;
21,471✔
781
    }
782

783
    Rectangle::Rectangle(double xlen, double ylen, double xl, double yl)
109✔
784
        : Aperture(ApertureType::RECTANGLE),
785
          x_length(xlen),
109✔
786
          y_length(ylen),
109✔
787
          x_coord(xl),
109✔
788
          y_coord(yl)
109✔
789
    {
790
    }
109✔
791

792
    Rectangle::Rectangle(const nlohmann::ordered_json &jnode)
2,377✔
793
        : Aperture(ApertureType::RECTANGLE)
2,377✔
794
    {
795
        this->x_length = jnode.at("x_length");
2,377✔
796
        this->y_length = jnode.at("y_length");
2,377✔
797
        this->x_coord = jnode.at("x_coord");
2,377✔
798
        this->y_coord = jnode.at("y_coord");
2,377✔
799
    }
2,377✔
800

801
    double Rectangle::aperture_area() const
8✔
802
    {
803
        return this->x_length * this->y_length;
8✔
804
    }
805

806
    double Rectangle::diameter_circumscribed_circle() const
56,710✔
807
    {
808
        return sqrt(x_length * x_length + y_length * y_length);
56,710✔
809
    }
810

811
    void Rectangle::bounding_box(double &xmin,
1✔
812
                                 double &xmax,
813
                                 double &ymin,
814
                                 double &ymax) const
815
    {
816
        xmin = this->x_coord;
1✔
817
        xmax = this->x_coord + this->x_length;
1✔
818
        ymin = this->y_coord;
1✔
819
        ymax = this->y_coord + this->y_length;
1✔
820
        return;
1✔
821
    }
822

823
    bool Rectangle::is_in(double x, double y) const
441,260,793✔
824
    {
825
        double xl = this->x_coord;
441,260,793✔
826
        double yl = this->y_coord;
441,260,793✔
827
        double xu = xl + this->x_length;
441,260,793✔
828
        double yu = yl + this->y_length;
441,260,793✔
829
        return (xl <= x && x <= xu && yl <= y && y <= yu);
441,260,793✔
830
    }
831

832
    aperture_ptr Rectangle::make_copy() const
18,966✔
833
    {
834
        // Invokes the implicit copy constructor
835
        return make_aperture<Rectangle>(*this);
18,966✔
836
    }
837

838
    void Rectangle::write_json(nlohmann::ordered_json &jnode) const
2,378✔
839
    {
840
        ApertureType type = ApertureType::RECTANGLE;
2,378✔
841
        jnode["aperture_type"] = ApertureTypeMap.at(type);
2,378✔
842
        jnode["x_length"] = this->x_length;
2,378✔
843
        jnode["y_length"] = this->y_length;
2,378✔
844
        jnode["x_coord"] = this->x_coord;
2,378✔
845
        jnode["y_coord"] = this->y_coord;
2,378✔
846
    }
2,378✔
847

848
    std::tuple<std::vector<double>, std::vector<int>>
NEW
849
    Rectangle::triangulation() const
×
850
    {
NEW
851
        const int segments = 5;
×
NEW
852
        std::vector<double> verts;
×
NEW
853
        std::vector<int> indices;
×
NEW
854
        for (int i = 0; i <= segments; ++i)
×
855
        {
NEW
856
            for (int j = 0; j <= segments; ++j)
×
857
            {
NEW
858
                const double x = i * x_length / segments + x_coord;
×
NEW
859
                const double y = j * y_length / segments + y_coord;
×
NEW
860
                verts.push_back(x);
×
NEW
861
                verts.push_back(y);
×
862
            }
863
        }
NEW
864
        for (int i = 0; i < segments; ++i)
×
865
        {
NEW
866
            for (int j = 0; j < segments; ++j)
×
867
            {
NEW
868
                const int a = (segments + 1) * i + j;
×
NEW
869
                const int c = (segments + 1) * (i + 1) + j;
×
NEW
870
                const int d = (segments + 1) * (i + 1) + j + 1;
×
NEW
871
                const int b = (segments + 1) * i + j + 1;
×
872
                // Generate two triangles for each quad in the mesh
873
                // Adjust order to be counter-clockwise
NEW
874
                indices.push_back(a);
×
NEW
875
                indices.push_back(c);
×
NEW
876
                indices.push_back(b);
×
NEW
877
                indices.push_back(b);
×
NEW
878
                indices.push_back(c);
×
NEW
879
                indices.push_back(d);
×
880
            }
881
        }
NEW
882
        return make_pair(verts, indices);
×
883
    }
×
884

885
    bool intri(double x1, double y1,
2,856,282✔
886
               double x2, double y2,
887
               double x3, double y3,
888
               double xt, double yt)
889
    {
890
        double a = (x1 - xt) * (y2 - yt) - (x2 - xt) * (y1 - yt);
2,856,282✔
891
        double b = (x2 - xt) * (y3 - yt) - (x3 - xt) * (y2 - yt);
2,856,282✔
892
        double c = (x3 - xt) * (y1 - yt) - (x1 - xt) * (y3 - yt);
2,856,282✔
893
        return (std::signbit(a) == std::signbit(b) &&
4,245,910✔
894
                std::signbit(b) == std::signbit(c));
4,245,910✔
895
        // return (sign(a) == sign(b) && sign(b) == sign(c));
896
    }
897

898
    bool inquad(double x1, double y1,
1,002,007✔
899
                double x2, double y2,
900
                double x3, double y3,
901
                double x4, double y4,
902
                double xt, double yt)
903
    {
904
        return (intri(x1, y1, x2, y2, x3, y3, xt, yt) ||
1,854,277✔
905
                intri(x1, y1, x3, y3, x4, y4, xt, yt));
1,854,277✔
906
    }
907

908
} // namespace SolTrace::Data
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