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

NREL / SolTrace / 20379221079

19 Dec 2025 06:37PM UTC coverage: 87.815% (-1.9%) from 89.725%
20379221079

push

github

jmaack24
Fix CI failures

0 of 2 new or added lines in 1 file covered. (0.0%)

183 existing lines in 5 files now uncovered.

6104 of 6951 relevant lines covered (87.81%)

7339992.74 hits per line

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

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

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

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

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

95
Annulus::Annulus(const nlohmann::ordered_json& jnode) 
2✔
96
    : Aperture(ApertureType::ANNULUS)
2✔
97
{
98
    this->inner_radius = jnode.at("inner_radius");
2✔
99
    this->outer_radius = jnode.at("outer_radius");
2✔
100
    this->arc_angle = jnode.at("arc_angle");
2✔
101
}
2✔
102

UNCOV
103
Aperture::Point Aperture::midpoint(const Aperture::Point& v0,
×
104
                                   const Aperture::Point& v1) const {
UNCOV
105
    return Aperture::Point((v0.x + v1.x) / 2, (v0.y + v1.y) / 2);
×
106
}
UNCOV
107
std::vector<Aperture::Triangle> Aperture::subdivide(Aperture::Triangle tri,
×
108
                                                    int n) const {
UNCOV
109
    Point                 v0  = tri.a;
×
UNCOV
110
    Point                 v1  = tri.b;
×
UNCOV
111
    Point                 v2  = tri.c;
×
UNCOV
112
    Point                 m01 = midpoint(v0, v1);
×
UNCOV
113
    Point                 m12 = midpoint(v1, v2);
×
UNCOV
114
    Point                 m20 = midpoint(v2, v0);
×
UNCOV
115
    std::vector<Triangle> result;
×
UNCOV
116
    if (n - 1 == 0) {
×
UNCOV
117
        result.push_back(Triangle(v0, m01, m20));
×
UNCOV
118
        result.push_back(Triangle(m01, v1, m12));
×
UNCOV
119
        result.push_back(Triangle(m12, m01, m20));
×
UNCOV
120
        result.push_back(Triangle(m12, m20, v2));
×
UNCOV
121
        return result;
×
122
    }
UNCOV
123
    auto t1 = subdivide(Triangle(v0, m01, m20), n - 1);
×
UNCOV
124
    auto t2 = subdivide(Triangle(v0, m01, m20), n - 1);
×
UNCOV
125
    auto t3 = subdivide(Triangle(v0, m01, m20), n - 1);
×
UNCOV
126
    auto t4 = subdivide(Triangle(v0, m01, m20), n - 1);
×
UNCOV
127
    result.insert(result.end(), t1.begin(), t1.end());
×
UNCOV
128
    result.insert(result.end(), t2.begin(), t2.end());
×
UNCOV
129
    result.insert(result.end(), t3.begin(), t3.end());
×
UNCOV
130
    result.insert(result.end(), t4.begin(), t4.end());
×
UNCOV
131
    return result;
×
UNCOV
132
}
×
UNCOV
133
int Aperture::index_of(std::vector<Aperture::Point>& v,
×
134
                       const Aperture::Point&        p) const {
UNCOV
135
    auto it = find(v.begin(), v.end(), p);
×
UNCOV
136
    if (it == v.end()) {
×
UNCOV
137
        v.push_back(p);
×
UNCOV
138
        return v.size() - 1;
×
139
    }
UNCOV
140
    return std::distance(v.begin(), it);
×
141
}
UNCOV
142
std::tuple<std::vector<double>, std::vector<int>> Aperture::indexed_triangles(
×
143
    const std::vector<Aperture::Triangle>& triangles) const {
UNCOV
144
    std::vector<int>    indices;
×
UNCOV
145
    std::vector<Point>  points;
×
UNCOV
146
    std::vector<double> flattened;
×
UNCOV
147
    for (const Triangle& tri : triangles) {
×
UNCOV
148
        indices.push_back(index_of(points, tri.a));
×
UNCOV
149
        indices.push_back(index_of(points, tri.b));
×
UNCOV
150
        indices.push_back(index_of(points, tri.c));
×
151
    }
UNCOV
152
    for (const Point& p : points) {
×
UNCOV
153
        flattened.push_back(p.x);
×
UNCOV
154
        flattened.push_back(p.y);
×
155
    }
UNCOV
156
    return std::make_pair(flattened, indices);
×
UNCOV
157
}
×
158

159
double Annulus::aperture_area() const
6✔
160
{
161
    // TODO: input.cpp on line 219 uses the formula
162
    //    elm->ParameterC*(ACOSM1O180)*(elm->ParameterB - elm->ParameterA);
163
    //    = \theta * (r - R)
164
    // This seems to be wrong...
165
    double R = this->outer_radius;
6✔
166
    double r = this->inner_radius;
6✔
167
    // Convert to radians
168
    double arc = this->arc_angle * D2R;
6✔
169
    return 0.5 * arc * (R * R - r * r);
6✔
170
}
171

172
double Annulus::diameter_circumscribed_circle() const
25✔
173
{
174
    return 2.0 * this->outer_radius;
25✔
175
}
176

177
bool Annulus::is_in(double x, double y) const
182,749✔
178
{
179
    double r = sqrt(x * x + y * y);
182,749✔
180
    bool inside = false;
182,749✔
181
    if (this->inner_radius <= r &&
182,749✔
182
        r <= this->outer_radius)
169,750✔
183
    {
184
        double theta = atan2(y, x);
39,975✔
185
        // Arc is split across x-axis, hence the 0.5
186
        double arc = 0.5 * this->arc_angle * D2R;
39,975✔
187
        inside = (-arc <= theta && theta <= arc);
39,975✔
188
    }
189
    return inside;
182,749✔
190
}
191

192
std::tuple<std::vector<double>, std::vector<int>>
UNCOV
193
Annulus::triangulation() const {
×
UNCOV
194
    const int           resolution = 32;
×
UNCOV
195
    std::vector<double> verts;
×
UNCOV
196
    std::vector<int>    indices;
×
UNCOV
197
    for (int i = 0; i <= resolution; i++) {
×
NEW
198
        const double u = i / resolution * PI * 2;
×
UNCOV
199
        verts.push_back(inner_radius * std::cos(u));
×
UNCOV
200
        verts.push_back(inner_radius * std::sin(u));
×
UNCOV
201
        verts.push_back(outer_radius * std::cos(u));
×
UNCOV
202
        verts.push_back(outer_radius * std::sin(u));
×
203
    }
UNCOV
204
    for (int i = 0; i < resolution - 3; i += 2) {
×
UNCOV
205
        const int a = i;
×
UNCOV
206
        const int b = i + 1;
×
UNCOV
207
        const int c = i + 2;
×
UNCOV
208
        const int d = i + 3;
×
209
        // Generate two triangles for each quad in the mesh
210
        // Adjust order to be counter-clockwise
UNCOV
211
        indices.push_back(a);
×
UNCOV
212
        indices.push_back(d);
×
UNCOV
213
        indices.push_back(b);
×
UNCOV
214
        indices.push_back(b);
×
UNCOV
215
        indices.push_back(d);
×
UNCOV
216
        indices.push_back(c);
×
217
    }
UNCOV
218
    return std::make_tuple(verts, indices);
×
UNCOV
219
}
×
220

221
aperture_ptr Annulus::make_copy() const
9✔
222
{
223
    // Invokes the implicit copy constructor
224
    return make_aperture<Annulus>(*this);
9✔
225
}
226

227
void Annulus::write_json(nlohmann::ordered_json& jnode) const
1✔
228
{
229
    ApertureType type = ApertureType::ANNULUS;
1✔
230
    jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
231
    jnode["inner_radius"] = this->inner_radius;
1✔
232
    jnode["outer_radius"] = this->outer_radius;
1✔
233
    jnode["arc_angle"] = this->arc_angle;
1✔
234
}
1✔
235

236
Circle::Circle(const nlohmann::ordered_json& jnode)
2✔
237
    : Aperture(ApertureType::CIRCLE)
2✔
238
{
239
    this->diameter = jnode.at("diameter");
2✔
240
}
2✔
241

242
double Circle::aperture_area() const
4✔
243
{
244
    return 0.25 * PI * this->diameter * this->diameter;
4✔
245
}
246

247
double Circle::diameter_circumscribed_circle() const
228,613✔
248
{
249
    return this->diameter;
228,613✔
250
}
251

252
bool Circle::is_in(double x, double y) const
228,578✔
253
{
254
    double r = sqrt(x * x + y * y);
228,578✔
255
    return r <= this->radius_circumscribed_circle();
228,578✔
256
}
257

258
aperture_ptr Circle::make_copy() const
17✔
259
{
260
    // Invokes the implicit copy constructor
261
    return make_aperture<Circle>(*this);
17✔
262
}
263

264
void Circle::write_json(nlohmann::ordered_json& jnode) const
1✔
265
{
266
    ApertureType type = ApertureType::CIRCLE;
1✔
267
    jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
268
    jnode["diameter"] = this->diameter;
1✔
269
}
1✔
270

271
EqualateralTriangle::EqualateralTriangle(const nlohmann::ordered_json& jnode)
2✔
272
    : Aperture(ApertureType::EQUILATERAL_TRIANGLE)
2✔
273
{
274
    this->circumscribe_diameter = jnode.at("circumscribe_diameter");
2✔
275
}
2✔
276

277
std::tuple<std::vector<double>, std::vector<int>>
UNCOV
278
Circle::triangulation() const {
×
279
    // Using a fixed Delaunay triangulation of the unit circle
280
    std::vector<double> verts = {
281
        1.00000000e+00,  0.00000000e+00,  9.51056516e-01,  3.09016994e-01,
282
        8.09016994e-01,  5.87785252e-01,  5.87785252e-01,  8.09016994e-01,
283
        3.09016994e-01,  9.51056516e-01,  6.12323400e-17,  1.00000000e+00,
284
        -3.09016994e-01, 9.51056516e-01,  -5.87785252e-01, 8.09016994e-01,
285
        -8.09016994e-01, 5.87785252e-01,  -9.51056516e-01, 3.09016994e-01,
286
        -1.00000000e+00, 1.22464680e-16,  -9.51056516e-01, -3.09016994e-01,
287
        -8.09016994e-01, -5.87785252e-01, -5.87785252e-01, -8.09016994e-01,
288
        -3.09016994e-01, -9.51056516e-01, -1.83697020e-16, -1.00000000e+00,
289
        3.09016994e-01,  -9.51056516e-01, 5.87785252e-01,  -8.09016994e-01,
290
        8.09016994e-01,  -5.87785252e-01, 9.51056516e-01,  -3.09016994e-01,
291
        0.00000000e+00,  0.00000000e+00,  -5.00000000e-01, -5.00000000e-01,
292
        -5.00000000e-01, 0.00000000e+00,  -5.00000000e-01, 5.00000000e-01,
293
        0.00000000e+00,  -5.00000000e-01, 0.00000000e+00,  5.00000000e-01,
294
        5.00000000e-01,  -5.00000000e-01, 5.00000000e-01,  0.00000000e+00,
295
        5.00000000e-01,  5.00000000e-01
UNCOV
296
    };
×
297
    std::vector<int> indices = {
298
        22, 11, 21, 11, 22, 10, 17, 18, 26, 14, 24, 21, 13, 14, 21, 24, 14, 15,
299
        25, 6,  23, 6,  25, 5,  9,  22, 23, 8,  9,  23, 22, 9,  10, 27, 1,  28,
300
        1,  27, 0,  19, 27, 26, 18, 19, 26, 27, 19, 0,  4,  25, 28, 3,  4,  28,
301
        25, 4,  5,  12, 13, 21, 11, 12, 21, 24, 16, 26, 16, 17, 26, 16, 24, 15,
302
        7,  8,  23, 6,  7,  23, 2,  3,  28, 1,  2,  28, 24, 22, 21, 22, 24, 20,
303
        24, 27, 20, 27, 24, 26, 25, 22, 20, 22, 25, 23, 27, 25, 20, 25, 27, 28
UNCOV
304
    };
×
305
    // scale from the unit cirle to our circle
306
    std::transform(
×
UNCOV
307
        verts.begin(), verts.end(), verts.begin(), [this](double element) {
×
UNCOV
308
            return element *= this->diameter / 2.0;
×
309
        });
UNCOV
310
    return std::make_tuple(verts, indices);
×
UNCOV
311
}
×
312

313
double EqualateralTriangle::aperture_area() const
3✔
314
{
315
    double r = 0.5 * this->circumscribe_diameter;
3✔
316
    return 0.75 * sqrt(3) * r * r;
3✔
317
}
318

319
double EqualateralTriangle::diameter_circumscribed_circle() const
14✔
320
{
321
    return this->circumscribe_diameter;
14✔
322
}
323

324
bool EqualateralTriangle::is_in(double x, double y) const
7✔
325
{
326
    double r = sqrt(x * x + y * y);
7✔
327
    double ro = this->radius_circumscribed_circle();
7✔
328
    if (r > ro)
7✔
329
        return false;
2✔
330

331
    double ri = 0.5 * ro;
5✔
332
    if (r <= ri)
5✔
333
        return true;
2✔
334

335
    double y0;
336
    // double a = ro / sqrt(3.0) = 2 * ri / sqrt(3.0);
337
    if (0.0 <= x && x <= ro)
3✔
338
    {
339
        // y0 = -sqrt(3.0) * (x - a);
340
        y0 = ro - sqrt(3.0) * x;
1✔
341
        return (-ri <= y && y <= y0);
1✔
342
    }
343
    else if (-ro <= x && x < 0.0)
2✔
344
    {
345
        // y0 = sqrt(3.0) * (x + a);
346
        y0 = sqrt(3.0) * x + ro;
2✔
347
        return (-ri <= y && y <= y0);
2✔
348
    }
349

UNCOV
350
    return false;
×
351
}
352

353
std::tuple<std::vector<double>, std::vector<int>>
UNCOV
354
EqualateralTriangle::triangulation() const {
×
UNCOV
355
    double   r = circumscribe_diameter / 2.0;
×
356
    Triangle tri(Point(0, r),
357
                 Point(r * cos(-PI / 6.0), r * sin(-PI / 6.0)),
NEW
358
                 Point(r * cos(7 * PI / 6.0), r * sin(7 * PI / 6.0)));
×
UNCOV
359
    return indexed_triangles(subdivide(tri, 3));
×
360
}
361

362
aperture_ptr EqualateralTriangle::make_copy() const
1✔
363
{
364
    // Invokes the implicit copy constructor
365
    return make_aperture<EqualateralTriangle>(*this);
1✔
366
}
367

368
void EqualateralTriangle::write_json(nlohmann::ordered_json& jnode) const
1✔
369
{
370
    ApertureType type = ApertureType::EQUILATERAL_TRIANGLE;
1✔
371
    jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
372
    jnode["circumscribe_diameter"] = this->circumscribe_diameter;
1✔
373
}
1✔
374

375
Hexagon::Hexagon(const nlohmann::ordered_json& jnode)
52✔
376
    : Aperture(ApertureType::HEXAGON)
52✔
377
{
378
    this->circumscribe_diameter = jnode.at("circumscribe_diameter");
52✔
379
}
52✔
380

381
double Hexagon::aperture_area() const
3✔
382
{
383
    // TODO: input.cpp on line 210 uses the formula
384
    //    5*sqr(elm->ParameterA/2.0)*cos(30.0*(ACOSM1O180))*sin(30.0*(ACOSM1O180));
385
    //    = 5*(d/2)^2*cos(pi/6)*sin(pi/6)
386
    //    = 5*(d/2)^2*sqrt(3)/2*1/2
387
    //    = 5*sqrt(3)/4 * (d/2)^2
388
    //    = 1.25*sqrt(3) * (d/2)^2
389
    // This seems to be wrong...
390
    double r = 0.5 * this->circumscribe_diameter;
3✔
391
    return 1.5 * sqrt(3) * r * r;
3✔
392
}
393

394
double Hexagon::diameter_circumscribed_circle() const
802,401✔
395
{
396
    return circumscribe_diameter;
802,401✔
397
}
398

399
bool Hexagon::is_in(double x, double y) const
796,632✔
400
{
401
    double r = sqrt(x * x + y * y);
796,632✔
402
    double ro = this->radius_circumscribed_circle();
796,632✔
403
    if (r > ro)
796,632✔
404
        return false;
771,676✔
405

406
    double ri = 0.5 * sqrt(3.0) * ro;
24,956✔
407
    if (r <= ri)
24,956✔
408
        return true;
19,273✔
409

410
    // NOTE: Old code used
411
    //    xl = sqrt(ro^2 - ri^2)
412
    // where `ro` is the radius of the circumscribing circle and `ri` is
413
    // the radius of the inscribing circle. But this is equivalent to
414
    //    xl = 0.5 * ro
415

416
    double xl = 0.5 * this->radius_circumscribed_circle();
5,683✔
417
    double y1, y2;
418
    if (xl < x && x <= ro)
5,683✔
419
    {
420
        y1 = sqrt(3.0) * (x - ro);
2,048✔
421
        y2 = -y1;
2,048✔
422
        // if (y1 <= y && y <= y2) return true;
423
        return (y1 <= y && y <= y2);
2,048✔
424
    }
425
    else if (-xl <= x && x <= xl)
3,635✔
426
    {
427
        return (-ri <= y && y <= ri);
1,555✔
428
    }
429
    else if (-ro <= x && x < -xl)
2,080✔
430
    {
431
        y1 = sqrt(3.0) * (x + ro);
2,080✔
432
        y2 = -y1;
2,080✔
433
        return (y2 <= y && y <= y1);
2,080✔
434
    }
435

UNCOV
436
    return false;
×
437
}
438

439
std::tuple<std::vector<double>, std::vector<int>>
UNCOV
440
Hexagon::triangulation() const {
×
UNCOV
441
    double                r = circumscribe_diameter / 2.0;
×
442
    std::vector<Triangle> t0 =
443
        subdivide(Triangle(Point(0, 0),
444
                           Point(r * cos(PI / 3.0), r * sin(PI / 3.0)),
445
                           Point(r, 0)),
446
                  2);
×
447
    std::vector<Triangle> t1 = subdivide(
448
        Triangle(Point(0, 0),
449
                 Point(r * cos(2 * PI / 3.0), r * sin(2 * PI / 3.0)),
450
                 Point(r * cos(PI / 3.0), r * sin(PI / 3.0))),
UNCOV
451
        2);
×
452
    std::vector<Triangle> t2 = subdivide(
453
        Triangle(Point(0, 0),
454
                 Point(-r, 0),
455
                 Point(r * cos(2 * PI / 3.0), r * sin(2 * PI / 3.0))),
UNCOV
456
        2);
×
457
    std::vector<Triangle> t3 = subdivide(
458
        Triangle(Point(0, 0),
459
                 Point(-r, 0),
460
                 Point(r * cos(4 * PI / 3.0), r * sin(4 * PI / 3.0))),
UNCOV
461
        2);
×
462
    std::vector<Triangle> t4 = subdivide(
463
        Triangle(Point(0, 0),
464
                 Point(r * cos(5 * PI / 3.0), r * sin(5 * PI / 3.0)),
465
                 Point(r * cos(4 * PI / 3.0), r * sin(4 * PI / 3.0))),
UNCOV
466
        2);
×
467
    std::vector<Triangle> t5 = subdivide(
468
        Triangle(Point(0, 0),
469
                 Point(r, 0),
470
                 Point(r * cos(5 * PI / 3.0), r * sin(5 * PI / 3.0))),
UNCOV
471
        2);
×
UNCOV
472
    std::vector<Triangle> triangles;
×
UNCOV
473
    triangles.insert(triangles.end(), t0.begin(), t0.end());
×
UNCOV
474
    triangles.insert(triangles.end(), t1.begin(), t1.end());
×
UNCOV
475
    triangles.insert(triangles.end(), t2.begin(), t2.end());
×
UNCOV
476
    triangles.insert(triangles.end(), t3.begin(), t3.end());
×
UNCOV
477
    triangles.insert(triangles.end(), t4.begin(), t4.end());
×
UNCOV
478
    triangles.insert(triangles.end(), t5.begin(), t5.end());
×
UNCOV
479
    return indexed_triangles(triangles);
×
UNCOV
480
}
×
481

482
aperture_ptr Hexagon::make_copy() const
78✔
483
{
484
    // Invokes the implicit copy constructor
485
    return make_aperture<Hexagon>(*this);
78✔
486
}
487

488
void Hexagon::write_json(nlohmann::ordered_json& jnode) const
76✔
489
{
490
    ApertureType type = ApertureType::HEXAGON;
76✔
491
    jnode["aperture_type"] = ApertureTypeMap.at(type);
76✔
492
    jnode["circumscribe_diameter"] = this->circumscribe_diameter;
76✔
493
}
76✔
494

495
IrregularTriangle::IrregularTriangle(double x1, double y1,
3✔
496
                                     double x2, double y2,
497
                                     double x3, double y3)
3✔
498
    : Aperture(ApertureType::IRREGULAR_TRIANGLE),
499
      x1(x1), y1(y1),
3✔
500
      x2(x2), y2(y2),
3✔
501
      x3(x3), y3(y3)
3✔
502
{
503
}
3✔
504

505
IrregularTriangle::IrregularTriangle(const nlohmann::ordered_json& jnode)
2✔
506
    : Aperture(ApertureType::IRREGULAR_TRIANGLE)
2✔
507
{
508
    this->x1 = jnode.at("x1");
2✔
509
    this->y1 = jnode.at("y1");
2✔
510
    this->x2 = jnode.at("x2");
2✔
511
    this->y2 = jnode.at("y2");
2✔
512
    this->x3 = jnode.at("x3");
2✔
513
    this->y3 = jnode.at("y3");
2✔
514
}
2✔
515

516
std::tuple<std::vector<double>, std::vector<int>>
UNCOV
517
IrregularTriangle::triangulation() const {
×
UNCOV
518
    Triangle tri(Point(x1, y1), Point(x2, y2), Point(x3, y3));
×
UNCOV
519
    return indexed_triangles(subdivide(tri, 3));
×
520
}
521

522
double IrregularTriangle::aperture_area() const
3✔
523
{
524
    double v11 = this->x1 - this->x2;
3✔
525
    double v12 = this->y1 - this->y2;
3✔
526
    double v21 = this->x3 - this->x2;
3✔
527
    double v22 = this->y3 - this->y2;
3✔
528

529
    double v1m = sqrt(v11 * v11 + v12 * v12);
3✔
530
    double v2m = sqrt(v21 * v21 + v22 * v22);
3✔
531

532
    double theta = acos((v11 * v21 + v12 * v22) / (v1m * v2m));
3✔
533
    double area = 0.5 * v1m * v2m * sin(theta);
3✔
534

535
    return area;
3✔
536
}
537

UNCOV
538
double IrregularTriangle::diameter_circumscribed_circle() const
×
539
{
540
    // TODO: Not sure this is exact. Is that a problem?
UNCOV
541
    double xmax = std::max(std::max(x1, x2), x3);
×
UNCOV
542
    double ymax = std::max(std::max(y1, y2), y3);
×
UNCOV
543
    double xmin = std::min(std::min(x1, x2), x3);
×
UNCOV
544
    double ymin = std::min(std::min(y1, y2), y3);
×
UNCOV
545
    double dx = xmax - xmin;
×
UNCOV
546
    double dy = ymax - ymin;
×
UNCOV
547
    return sqrt(dx * dx + dy * dy);
×
548
}
549

550
bool IrregularTriangle::is_in(double x, double y) const
4✔
551
{
552
    return intri(x1, y1, x2, y2, x3, y3, x, y);
4✔
553
}
554

555
aperture_ptr IrregularTriangle::make_copy() const
1✔
556
{
557
    // Invokes the implicit copy constructor
558
    return make_aperture<IrregularTriangle>(*this);
1✔
559
}
560

561
void IrregularTriangle::write_json(nlohmann::ordered_json& jnode) const
1✔
562
{
563
    ApertureType type = ApertureType::IRREGULAR_TRIANGLE;
1✔
564
    jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
565
    jnode["x1"] = this->x1;
1✔
566
    jnode["y1"] = this->y1;
1✔
567
    jnode["x2"] = this->x2;
1✔
568
    jnode["y2"] = this->y2;
1✔
569
    jnode["x3"] = this->x3;
1✔
570
    jnode["y3"] = this->y3;
1✔
571
}
1✔
572

573
IrregularQuadrilateral::IrregularQuadrilateral(double x1, double y1,
3✔
574
                                               double x2, double y2,
575
                                               double x3, double y3,
576
                                               double x4, double y4)
3✔
577
    : Aperture(ApertureType::IRREGULAR_QUADRILATERAL),
578
      x1(x1), y1(y1),
3✔
579
      x2(x2), y2(y2),
3✔
580
      x3(x3), y3(y3),
3✔
581
      x4(x4), y4(y4)
3✔
582
{
583
}
3✔
584

585
IrregularQuadrilateral::IrregularQuadrilateral(const nlohmann::ordered_json& jnode)
2✔
586
    : Aperture(ApertureType::IRREGULAR_QUADRILATERAL)
2✔
587
{
588
    this->x1 = jnode.at("x1");
2✔
589
    this->y1 = jnode.at("y1");
2✔
590
    this->x2 = jnode.at("x2");
2✔
591
    this->y2 = jnode.at("y2");
2✔
592
    this->x3 = jnode.at("x3");
2✔
593
    this->y3 = jnode.at("y3");
2✔
594
    this->x4 = jnode.at("x4");
2✔
595
    this->y4 = jnode.at("y4");
2✔
596
}
2✔
597

598
double IrregularQuadrilateral::aperture_area() const
2✔
599
{
600
    double v11 = this->x1 - this->x2;
2✔
601
    double v12 = this->y1 - this->y2;
2✔
602
    double v21 = this->x3 - this->x2;
2✔
603
    double v22 = this->y3 - this->y2;
2✔
604
    double v31 = this->x3 - this->x4;
2✔
605
    double v32 = this->y3 - this->y4;
2✔
606
    double v41 = this->x1 - this->x4;
2✔
607
    double v42 = this->y1 - this->y4;
2✔
608

609
    double v1m = sqrt(v11 * v11 + v12 * v12);
2✔
610
    double v2m = sqrt(v21 * v21 + v22 * v22);
2✔
611
    double v3m = sqrt(v31 * v31 + v32 * v32);
2✔
612
    double v4m = sqrt(v41 * v41 + v42 * v42);
2✔
613

614
    double theta1 = acos((v11 * v21 + v12 * v22) / (v1m * v2m));
2✔
615
    double theta2 = acos((v31 * v41 + v32 * v42) / (v3m * v4m));
2✔
616

617
    double area = 0.5 * (v1m * v2m * sin(theta1) + v3m * v4m * sin(theta2));
2✔
618
    return area;
2✔
619
}
620

621
std::tuple<std::vector<double>, std::vector<int>>
UNCOV
622
IrregularQuadrilateral::triangulation() const {
×
623
    std::vector<Triangle> t0 =
UNCOV
624
        subdivide(Triangle(Point(x1, y1), Point(x3, y3), Point(x2, y2)), 2);
×
625
    std::vector<Triangle> t1 =
UNCOV
626
        subdivide(Triangle(Point(x1, y1), Point(x4, y4), Point(x3, y3)), 2);
×
UNCOV
627
    std::vector<Triangle> triangles;
×
UNCOV
628
    triangles.insert(triangles.end(), t0.begin(), t0.end());
×
UNCOV
629
    triangles.insert(triangles.end(), t1.begin(), t1.end());
×
UNCOV
630
    return indexed_triangles(triangles);
×
UNCOV
631
}
×
632

UNCOV
633
double IrregularQuadrilateral::diameter_circumscribed_circle() const
×
634
{
635
    // TODO: Not sure this is exact. Is that a problem?
UNCOV
636
    double xmax = std::max(std::max(x1, x2), std::max(x3, x4));
×
UNCOV
637
    double ymax = std::max(std::max(y1, y2), std::max(y3, y4));
×
UNCOV
638
    double xmin = std::min(std::min(x1, x2), std::min(x3, x4));
×
UNCOV
639
    double ymin = std::min(std::min(y1, y2), std::min(y3, y4));
×
UNCOV
640
    double dx = xmax - xmin;
×
UNCOV
641
    double dy = ymax - ymin;
×
UNCOV
642
    return sqrt(dx * dx + dy * dy);
×
643
}
644

645
bool IrregularQuadrilateral::is_in(double x, double y) const
6✔
646
{
647
    return inquad(x1, y1, x2, y2, x3, y3, x4, y4, x, y);
6✔
648
}
649

650
aperture_ptr IrregularQuadrilateral::make_copy() const
1✔
651
{
652
    // Invokes the implicit copy constructor
653
    return make_aperture<IrregularQuadrilateral>(*this);
1✔
654
}
655

656
void IrregularQuadrilateral::write_json(nlohmann::ordered_json& jnode) const
1✔
657
{
658
    ApertureType type = ApertureType::IRREGULAR_QUADRILATERAL;
1✔
659
    jnode["aperture_type"] = ApertureTypeMap.at(type);
1✔
660
    jnode["x1"] = this->x1;
1✔
661
    jnode["y1"] = this->y1;
1✔
662
    jnode["x2"] = this->x2;
1✔
663
    jnode["y2"] = this->y2;
1✔
664
    jnode["x3"] = this->x3;
1✔
665
    jnode["y3"] = this->y3;
1✔
666
    jnode["x4"] = this->x4;
1✔
667
    jnode["y4"] = this->y4;
1✔
668
}
1✔
669

670
Rectangle::Rectangle(double xlen, double ylen)
21,471✔
671
    : Aperture(ApertureType::RECTANGLE),
672
      x_length(xlen),
21,471✔
673
      y_length(ylen)
21,471✔
674
{
675
    // Default to rectangle centered at the origin.
676
    this->x_coord = -0.5 * this->x_length;
21,471✔
677
    this->y_coord = -0.5 * this->y_length;
21,471✔
678
    return;
21,471✔
679
}
680

681
Rectangle::Rectangle(const nlohmann::ordered_json& jnode)
2,377✔
682
    : Aperture(ApertureType::RECTANGLE)
2,377✔
683
{
684
    this->x_length = jnode.at("x_length");
2,377✔
685
    this->y_length = jnode.at("y_length");
2,377✔
686
    this->x_coord = jnode.at("x_coord");
2,377✔
687
    this->y_coord = jnode.at("y_coord");
2,377✔
688
}
2,377✔
689

690
double Rectangle::aperture_area() const
7✔
691
{
692
    return this->x_length * this->y_length;
7✔
693
}
694

695
double Rectangle::diameter_circumscribed_circle() const
56,710✔
696
{
697
    return sqrt(x_length * x_length + y_length * y_length);
56,710✔
698
}
699

700
aperture_ptr Rectangle::make_copy() const
18,966✔
701
{
702
    // Invokes the implicit copy constructor
703
    return make_aperture<Rectangle>(*this);
18,966✔
704
}
705

706
bool Rectangle::is_in(double x, double y) const
437,912,429✔
707
{
708
    double xl = this->x_coord;
437,912,429✔
709
    double yl = this->y_coord;
437,912,429✔
710
    double xu = xl + this->x_length;
437,912,429✔
711
    double yu = yl + this->y_length;
437,912,429✔
712
    return (xl <= x && x <= xu && yl <= y && y <= yu);
437,912,429✔
713
}
714

715
Rectangle::Rectangle(double xlen, double ylen, double xl, double yl)
109✔
716
    : Aperture(ApertureType::RECTANGLE),
717
      x_length(xlen),
109✔
718
      y_length(ylen),
109✔
719
      x_coord(xl),
109✔
720
      y_coord(yl)
109✔
721
{
722
}
109✔
723

724
void Rectangle::write_json(nlohmann::ordered_json& jnode) const
2,378✔
725
{
726
    ApertureType type = ApertureType::RECTANGLE;
2,378✔
727
    jnode["aperture_type"] = ApertureTypeMap.at(type);
2,378✔
728
    jnode["x_length"] = this->x_length;
2,378✔
729
    jnode["y_length"] = this->y_length;
2,378✔
730
    jnode["x_coord"] = this->x_coord;
2,378✔
731
    jnode["y_coord"] = this->y_coord;
2,378✔
732
}
2,378✔
733

734

735
std::tuple<std::vector<double>, std::vector<int>>
UNCOV
736
Rectangle::triangulation() const {
×
UNCOV
737
    const int           segments = 5;
×
UNCOV
738
    std::vector<double> verts;
×
UNCOV
739
    std::vector<int>    indices;
×
UNCOV
740
    for (int i = 0; i <= segments; ++i) {
×
UNCOV
741
        for (int j = 0; j <= segments; ++j) {
×
UNCOV
742
            const double x = i * x_length / segments + x_coord;
×
UNCOV
743
            const double y = j * y_length / segments + y_coord;
×
UNCOV
744
            verts.push_back(x);
×
UNCOV
745
            verts.push_back(y);
×
746
        }
747
    }
UNCOV
748
    for (int i = 0; i < segments; ++i) {
×
UNCOV
749
        for (int j = 0; j < segments; ++j) {
×
UNCOV
750
            const int a = (segments + 1) * i + j;
×
UNCOV
751
            const int c = (segments + 1) * (i + 1) + j;
×
UNCOV
752
            const int d = (segments + 1) * (i + 1) + j + 1;
×
UNCOV
753
            const int b = (segments + 1) * i + j + 1;
×
754
            // Generate two triangles for each quad in the mesh
755
            // Adjust order to be counter-clockwise
UNCOV
756
            indices.push_back(a);
×
UNCOV
757
            indices.push_back(c);
×
UNCOV
758
            indices.push_back(b);
×
UNCOV
759
            indices.push_back(b);
×
UNCOV
760
            indices.push_back(c);
×
UNCOV
761
            indices.push_back(d);
×
762
        }
763
    }
UNCOV
764
    return make_pair(verts, indices);
×
UNCOV
765
}
×
766

767
bool intri(double x1, double y1,
14✔
768
           double x2, double y2,
769
           double x3, double y3,
770
           double xt, double yt)
771
{
772
    double a = (x1 - xt) * (y2 - yt) - (x2 - xt) * (y1 - yt);
14✔
773
    double b = (x2 - xt) * (y3 - yt) - (x3 - xt) * (y2 - yt);
14✔
774
    double c = (x3 - xt) * (y1 - yt) - (x1 - xt) * (y3 - yt);
14✔
775
    return (std::signbit(a) == std::signbit(b) &&
22✔
776
            std::signbit(b) == std::signbit(c));
22✔
777
    // return (sign(a) == sign(b) && sign(b) == sign(c));
778
}
779

780
bool inquad(double x1, double y1,
6✔
781
            double x2, double y2,
782
            double x3, double y3,
783
            double x4, double y4,
784
            double xt, double yt)
785
{
786
    return (intri(x1, y1, x2, y2, x3, y3, xt, yt) ||
10✔
787
            intri(x1, y1, x3, y3, x4, y4, xt, yt));
10✔
788
}
789

790
} // 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