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

NREL / SolTrace / 19979510856

06 Dec 2025 12:06AM UTC coverage: 87.255% (-1.9%) from 89.184%
19979510856

Pull #89

github

web-flow
Merge 5cb7cfc67 into a73a760a4
Pull Request #89: simulation_data changes to support UI-side constructions

0 of 148 new or added lines in 5 files covered. (0.0%)

1 existing line in 1 file now uncovered.

5970 of 6842 relevant lines covered (87.26%)

7824776.54 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,
15,029✔
16
                                               const std::vector<double> &args)
17
{
18
    // std::cout << "Type: " << type
19
    //           << " NArgs: " << args.size()
20
    //           << std::endl;
21
    switch (type)
15,029✔
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:
14,942✔
36
        if (args.size() < 2)
14,942✔
37
            break;
×
38
        return make_aperture<Rectangle>(args[0], args[1]); // This is assuming centered around the origin
14,942✔
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:
3✔
44
        if (args.size() < 3)
3✔
45
            break;
×
46
        return make_aperture<Rectangle>(
3✔
47
            args[1] - args[0], args[2], 
3✔
48
            -0.5 * (args[1] - args[0]), -0.5 * args[2]);
9✔
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

NEW
103
Aperture::Point Aperture::midpoint(const Aperture::Point& v0,
×
104
                                   const Aperture::Point& v1) const {
NEW
105
    return Aperture::Point((v0.x + v1.x) / 2, (v0.y + v1.y) / 2);
×
106
}
NEW
107
std::vector<Aperture::Triangle> Aperture::subdivide(Aperture::Triangle tri,
×
108
                                                    int n) const {
NEW
109
    Point                 v0  = tri.a;
×
NEW
110
    Point                 v1  = tri.b;
×
NEW
111
    Point                 v2  = tri.c;
×
NEW
112
    Point                 m01 = midpoint(v0, v1);
×
NEW
113
    Point                 m12 = midpoint(v1, v2);
×
NEW
114
    Point                 m20 = midpoint(v2, v0);
×
NEW
115
    std::vector<Triangle> result;
×
NEW
116
    if (n - 1 == 0) {
×
NEW
117
        result.push_back(Triangle(v0, m01, m20));
×
NEW
118
        result.push_back(Triangle(m01, v1, m12));
×
NEW
119
        result.push_back(Triangle(m12, m01, m20));
×
NEW
120
        result.push_back(Triangle(m12, m20, v2));
×
NEW
121
        return result;
×
122
    }
NEW
123
    auto t1 = subdivide(Triangle(v0, m01, m20), n - 1);
×
NEW
124
    auto t2 = subdivide(Triangle(v0, m01, m20), n - 1);
×
NEW
125
    auto t3 = subdivide(Triangle(v0, m01, m20), n - 1);
×
NEW
126
    auto t4 = subdivide(Triangle(v0, m01, m20), n - 1);
×
NEW
127
    result.insert(result.end(), t1.begin(), t1.end());
×
NEW
128
    result.insert(result.end(), t2.begin(), t2.end());
×
NEW
129
    result.insert(result.end(), t3.begin(), t3.end());
×
NEW
130
    result.insert(result.end(), t4.begin(), t4.end());
×
NEW
131
    return result;
×
NEW
132
}
×
NEW
133
int Aperture::index_of(std::vector<Aperture::Point>& v,
×
134
                       const Aperture::Point&        p) const {
NEW
135
    auto it = find(v.begin(), v.end(), p);
×
NEW
136
    if (it == v.end()) {
×
NEW
137
        v.push_back(p);
×
NEW
138
        return v.size() - 1;
×
139
    }
NEW
140
    return std::distance(v.begin(), it);
×
141
}
NEW
142
std::tuple<std::vector<double>, std::vector<int>> Aperture::indexed_triangles(
×
143
    const std::vector<Aperture::Triangle>& triangles) const {
NEW
144
    std::vector<int>    indices;
×
NEW
145
    std::vector<Point>  points;
×
NEW
146
    std::vector<double> flattened;
×
NEW
147
    for (const Triangle& tri : triangles) {
×
NEW
148
        indices.push_back(index_of(points, tri.a));
×
NEW
149
        indices.push_back(index_of(points, tri.b));
×
NEW
150
        indices.push_back(index_of(points, tri.c));
×
151
    }
NEW
152
    for (const Point& p : points) {
×
NEW
153
        flattened.push_back(p.x);
×
NEW
154
        flattened.push_back(p.y);
×
155
    }
NEW
156
    return std::make_pair(flattened, indices);
×
NEW
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>>
NEW
193
Annulus::triangulation() const {
×
NEW
194
    const int           resolution = 32;
×
NEW
195
    std::vector<double> verts;
×
NEW
196
    std::vector<int>    indices;
×
NEW
197
    for (int i = 0; i <= resolution; i++) {
×
NEW
198
        const double u = i / resolution * PI * 2;
×
NEW
199
        verts.push_back(inner_radius * std::cos(u));
×
NEW
200
        verts.push_back(inner_radius * std::sin(u));
×
NEW
201
        verts.push_back(outer_radius * std::cos(u));
×
NEW
202
        verts.push_back(outer_radius * std::sin(u));
×
203
    }
NEW
204
    for (int i = 0; i < resolution - 3; i += 2) {
×
NEW
205
        const int a = i;
×
NEW
206
        const int b = i + 1;
×
NEW
207
        const int c = i + 2;
×
NEW
208
        const int d = i + 3;
×
209
        // Generate two triangles for each quad in the mesh
210
        // Adjust order to be counter-clockwise
NEW
211
        indices.push_back(a);
×
NEW
212
        indices.push_back(d);
×
NEW
213
        indices.push_back(b);
×
NEW
214
        indices.push_back(b);
×
NEW
215
        indices.push_back(d);
×
NEW
216
        indices.push_back(c);
×
217
    }
NEW
218
    return std::make_tuple(verts, indices);
×
NEW
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
320,662✔
248
{
249
    return this->diameter;
320,662✔
250
}
251

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

258
aperture_ptr Circle::make_copy() const
18✔
259
{
260
    // Invokes the implicit copy constructor
261
    return make_aperture<Circle>(*this);
18✔
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>>
NEW
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
NEW
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
NEW
304
    };
×
305
    // scale from the unit cirle to our circle
NEW
306
    std::transform(
×
NEW
307
        verts.begin(), verts.end(), verts.begin(), [this](double element) {
×
NEW
308
            return element *= this->diameter / 2.0;
×
309
        });
NEW
310
    return std::make_tuple(verts, indices);
×
NEW
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

350
    return false;
×
351
}
352

353
std::tuple<std::vector<double>, std::vector<int>>
NEW
354
EqualateralTriangle::triangulation() const {
×
NEW
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)));
×
NEW
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

436
    return false;
×
437
}
438

439
std::tuple<std::vector<double>, std::vector<int>>
NEW
440
Hexagon::triangulation() const {
×
NEW
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)),
NEW
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))),
NEW
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))),
NEW
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))),
NEW
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))),
NEW
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))),
NEW
471
        2);
×
NEW
472
    std::vector<Triangle> triangles;
×
NEW
473
    triangles.insert(triangles.end(), t0.begin(), t0.end());
×
NEW
474
    triangles.insert(triangles.end(), t1.begin(), t1.end());
×
NEW
475
    triangles.insert(triangles.end(), t2.begin(), t2.end());
×
NEW
476
    triangles.insert(triangles.end(), t3.begin(), t3.end());
×
NEW
477
    triangles.insert(triangles.end(), t4.begin(), t4.end());
×
NEW
478
    triangles.insert(triangles.end(), t5.begin(), t5.end());
×
NEW
479
    return indexed_triangles(triangles);
×
NEW
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>>
NEW
517
IrregularTriangle::triangulation() const {
×
NEW
518
    Triangle tri(Point(x1, y1), Point(x2, y2), Point(x3, y3));
×
NEW
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

538
double IrregularTriangle::diameter_circumscribed_circle() const
×
539
{
540
    // TODO: Not sure this is exact. Is that a problem?
541
    double xmax = std::max(std::max(x1, x2), x3);
×
542
    double ymax = std::max(std::max(y1, y2), y3);
×
543
    double xmin = std::min(std::min(x1, x2), x3);
×
544
    double ymin = std::min(std::min(y1, y2), y3);
×
545
    double dx = xmax - xmin;
×
546
    double dy = ymax - ymin;
×
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>>
NEW
622
IrregularQuadrilateral::triangulation() const {
×
623
    std::vector<Triangle> t0 =
NEW
624
        subdivide(Triangle(Point(x1, y1), Point(x3, y3), Point(x2, y2)), 2);
×
625
    std::vector<Triangle> t1 =
NEW
626
        subdivide(Triangle(Point(x1, y1), Point(x4, y4), Point(x3, y3)), 2);
×
NEW
627
    std::vector<Triangle> triangles;
×
NEW
628
    triangles.insert(triangles.end(), t0.begin(), t0.end());
×
NEW
629
    triangles.insert(triangles.end(), t1.begin(), t1.end());
×
NEW
630
    return indexed_triangles(triangles);
×
NEW
631
}
×
632

UNCOV
633
double IrregularQuadrilateral::diameter_circumscribed_circle() const
×
634
{
635
    // TODO: Not sure this is exact. Is that a problem?
636
    double xmax = std::max(std::max(x1, x2), std::max(x3, x4));
×
637
    double ymax = std::max(std::max(y1, y2), std::max(y3, y4));
×
638
    double xmin = std::min(std::min(x1, x2), std::min(x3, x4));
×
639
    double ymin = std::min(std::min(y1, y2), std::min(y3, y4));
×
640
    double dx = xmax - xmin;
×
641
    double dy = ymax - ymin;
×
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)
17,109✔
671
    : Aperture(ApertureType::RECTANGLE),
672
      x_length(xlen),
17,109✔
673
      y_length(ylen)
17,109✔
674
{
675
    // Default to rectangle centered at the origin.
676
    this->x_coord = -0.5 * this->x_length;
17,109✔
677
    this->y_coord = -0.5 * this->y_length;
17,109✔
678
    return;
17,109✔
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
41,704✔
696
{
697
    return sqrt(x_length * x_length + y_length * y_length);
41,704✔
698
}
699

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

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

715
Rectangle::Rectangle(double xlen, double ylen, double xl, double yl)
108✔
716
    : Aperture(ApertureType::RECTANGLE),
717
      x_length(xlen),
108✔
718
      y_length(ylen),
108✔
719
      x_coord(xl),
108✔
720
      y_coord(yl)
108✔
721
{
722
}
108✔
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>>
NEW
736
Rectangle::triangulation() const {
×
NEW
737
    const int           segments = 5;
×
NEW
738
    std::vector<double> verts;
×
NEW
739
    std::vector<int>    indices;
×
NEW
740
    for (int i = 0; i <= segments; ++i) {
×
NEW
741
        for (int j = 0; j <= segments; ++j) {
×
NEW
742
            const double x = i * x_length / segments + x_coord;
×
NEW
743
            const double y = j * y_length / segments + y_coord;
×
NEW
744
            verts.push_back(x);
×
NEW
745
            verts.push_back(y);
×
746
        }
747
    }
NEW
748
    for (int i = 0; i < segments; ++i) {
×
NEW
749
        for (int j = 0; j < segments; ++j) {
×
NEW
750
            const int a = (segments + 1) * i + j;
×
NEW
751
            const int c = (segments + 1) * (i + 1) + j;
×
NEW
752
            const int d = (segments + 1) * (i + 1) + j + 1;
×
NEW
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
NEW
756
            indices.push_back(a);
×
NEW
757
            indices.push_back(c);
×
NEW
758
            indices.push_back(b);
×
NEW
759
            indices.push_back(b);
×
NEW
760
            indices.push_back(c);
×
NEW
761
            indices.push_back(d);
×
762
        }
763
    }
NEW
764
    return make_pair(verts, indices);
×
NEW
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