File indexing completed on 2026-11-07 10:25:34
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009
0010 #include <algorithm>
0011 #include <iostream>
0012 #include <vector>
0013
0014 #include "corecel/cont/Array.hh"
0015 #include "corecel/cont/Range.hh"
0016 #include "corecel/cont/Span.hh"
0017 #include "corecel/math/ArrayOperators.hh"
0018 #include "corecel/math/ArrayUtils.hh"
0019 #include "corecel/math/SoftEqual.hh"
0020 #include "orange/OrangeTypes.hh"
0021
0022 namespace celeritas
0023 {
0024 namespace orangeinp
0025 {
0026 namespace detail
0027 {
0028
0029
0030
0031
0032 enum class Orientation
0033 {
0034 clockwise = -1,
0035 collinear = 0,
0036 counterclockwise = 1,
0037 };
0038
0039
0040
0041
0042
0043
0044
0045 template<class T>
0046 inline Orientation calc_orientation(celeritas::Array<T, 2> const& a,
0047 celeritas::Array<T, 2> const& b,
0048 celeritas::Array<T, 2> const& c)
0049 {
0050 auto crossp = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0]);
0051 return crossp < 0 ? Orientation::clockwise
0052 : crossp > 0 ? Orientation::counterclockwise
0053 : Orientation::collinear;
0054 }
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 inline Orientation calc_orientation(Span<Real2 const> corners)
0068 {
0069 CELER_EXPECT(corners.size() >= 3);
0070
0071 auto const& origin = corners.front();
0072 real_type twice_area = 0;
0073 for (auto i : range<size_type>(1, corners.size() - 1))
0074 {
0075 auto const a = corners[i] - origin;
0076 auto const b = corners[i + 1] - origin;
0077 twice_area += a[0] * b[1] - b[0] * a[1];
0078 }
0079 return twice_area < 0 ? Orientation::clockwise
0080 : twice_area > 0 ? Orientation::counterclockwise
0081 : Orientation::collinear;
0082 }
0083
0084
0085
0086
0087
0088
0089
0090 inline bool has_orientation(Span<Real2 const> corners, Orientation o)
0091 {
0092 CELER_EXPECT(corners.size() > 2);
0093 for (auto i : range(corners.size()))
0094 {
0095 auto j = (i + 1) % corners.size();
0096 auto k = (i + 2) % corners.size();
0097 if (calc_orientation<Real2::value_type>(
0098 corners[i], corners[j], corners[k])
0099 != o)
0100 return false;
0101 }
0102 return true;
0103 }
0104
0105
0106
0107
0108
0109 inline bool is_same_orientation(
0110 Orientation a, Orientation b, bool degen_ok = false)
0111 {
0112 if (a == Orientation::collinear || b == Orientation::collinear)
0113 {
0114 return degen_ok;
0115 }
0116 return (a == b);
0117 }
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164 template<class T>
0165 class SoftOrientation
0166 {
0167 public:
0168 using real_type = T;
0169 using Real2 = Array<real_type, 2>;
0170
0171
0172 CELER_CONSTEXPR_FUNCTION SoftOrientation() : soft_zero_{} {}
0173
0174
0175 explicit CELER_FUNCTION SoftOrientation(real_type abs_tol)
0176 : soft_zero_{abs_tol}
0177 {
0178 }
0179
0180
0181 CELER_FUNCTION Orientation operator()(
0182 Real2 const& a, Real2 const& b, Real2 const& c) const
0183 {
0184 Real2 u{b[0] - a[0], b[1] - a[1]};
0185 Real2 v{c[0] - a[0], c[1] - a[1]};
0186
0187 auto cross_product = (u[0] * v[1] - v[0] * u[1]);
0188 auto magnitude = std::hypot(v[0], v[1]);
0189
0190 if (magnitude == 0 || soft_zero_(cross_product / magnitude))
0191 {
0192 return Orientation::collinear;
0193 }
0194 else
0195 {
0196 return calc_orientation<real_type>(a, b, c);
0197 }
0198 }
0199
0200 private:
0201 SoftZero<real_type> soft_zero_;
0202 };
0203
0204
0205
0206
0207
0208
0209
0210
0211 inline bool is_convex(Span<Real2 const> corners, bool degen_ok = false)
0212 {
0213 CELER_EXPECT(corners.size() > 2);
0214 auto ref = Orientation::collinear;
0215 for (auto i : range<size_type>(corners.size()))
0216 {
0217 auto j = (i + 1) % corners.size();
0218 auto k = (i + 2) % corners.size();
0219 auto cur = calc_orientation<Real2::value_type>(
0220 corners[i], corners[j], corners[k]);
0221 if (ref == Orientation::collinear)
0222 {
0223
0224 ref = cur;
0225 }
0226 if (!is_same_orientation(cur, ref, degen_ok))
0227 {
0228
0229
0230 return false;
0231 }
0232 }
0233 return true;
0234 }
0235
0236
0237
0238
0239
0240
0241
0242
0243 inline std::vector<Real2> filter_collinear_points(
0244 std::vector<Real2> const& corners, double abs_tol)
0245 {
0246 CELER_EXPECT(corners.size() >= 3);
0247
0248 std::vector<Real2> result;
0249 result.reserve(corners.size());
0250
0251 SoftOrientation<Real2::value_type> soft_ori(abs_tol);
0252
0253
0254
0255
0256 result.push_back(corners[0]);
0257
0258 for (auto i : range<size_type>(1, corners.size()))
0259 {
0260 auto j = i + 1 < corners.size() ? i + 1 : 0;
0261
0262 if (soft_ori(result.back(), corners[i], corners[j])
0263 != Orientation::collinear)
0264 {
0265 result.push_back(corners[i]);
0266 }
0267 }
0268
0269
0270 CELER_ASSERT(result.size() >= 3);
0271
0272
0273 if (soft_ori(result.back(), result[0], result[1]) == Orientation::collinear)
0274 {
0275 result.erase(result.begin());
0276 }
0277
0278
0279
0280 CELER_ENSURE(result.size() >= 3);
0281
0282 return result;
0283 }
0284
0285
0286
0287
0288
0289 template<class T>
0290 inline std::pair<T, T> find_extrema(Span<Array<T, 2> const> polygon,
0291 size_type dim)
0292 {
0293 CELER_EXPECT(polygon.size() >= 3);
0294 CELER_EXPECT(dim < 2);
0295
0296 auto [poly_min_it, poly_max_it] = std::minmax_element(
0297 polygon.begin(), polygon.end(), [dim](auto const& a, auto const& b) {
0298 return a[dim] < b[dim];
0299 });
0300 return {(*poly_min_it)[dim], (*poly_max_it)[dim]};
0301 }
0302
0303 template<class T>
0304 inline auto find_extrema(T const& polygon, size_type dim)
0305 {
0306 return find_extrema(make_span(polygon), dim);
0307 }
0308
0309
0310 template<class T>
0311 inline auto find_extrema(T const& polygon, Axis ax)
0312 {
0313 CELER_EXPECT(ax < Axis::z);
0314 return find_extrema(polygon, to_int(ax));
0315 }
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342 inline Real3 normal_from_triangle(
0343 Real3 const& p0, Real3 const& p1, Real3 const& p2)
0344 {
0345 return make_unit_vector(cross_product(p1 - p0, p2 - p0));
0346 }
0347
0348
0349 }
0350 }
0351 }