File indexing completed on 2025-09-17 09:07:47
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include <algorithm>
0010
0011 #include "corecel/Assert.hh"
0012 #include "corecel/Types.hh"
0013 #include "corecel/cont/Array.hh"
0014 #include "corecel/cont/Range.hh"
0015 #include "orange/OrangeTypes.hh"
0016 #include "orange/univ/detail/Utils.hh"
0017
0018 #include "PolygonUtils.hh"
0019
0020 namespace celeritas
0021 {
0022 namespace orangeinp
0023 {
0024 namespace detail
0025 {
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039 template<class T>
0040 class ConvexHullFinder
0041 {
0042 public:
0043
0044
0045 using real_type = T;
0046 using Real2 = celeritas::Array<real_type, 2>;
0047 using VecReal2 = std::vector<Real2>;
0048 using VecVecReal2 = std::vector<VecReal2>;
0049
0050
0051 public:
0052
0053 explicit ConvexHullFinder(VecReal2 const& points, Tolerance<> const& tol);
0054
0055
0056 VecReal2 make_convex_hull() const;
0057
0058
0059 VecVecReal2 calc_concave_regions() const;
0060
0061 private:
0062
0063 using ConvexMask = std::vector<bool>;
0064
0065
0066 VecReal2 const& points_;
0067 Tolerance<> const& tol_;
0068 SoftOrientation<real_type> soft_ori_;
0069 ConvexMask convex_mask_;
0070 size_type start_index_;
0071
0072
0073
0074
0075 SoftOrientation<real_type> make_soft_ori() const;
0076
0077
0078 ConvexMask calc_convex_mask() const;
0079
0080
0081 size_type min_element_idx() const;
0082
0083
0084 bool is_clockwise(size_type i_prev, size_type i, size_type i_next) const;
0085
0086
0087 size_type calc_next(size_type i) const;
0088
0089
0090 size_type calc_previous(size_type i) const;
0091 };
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104 template<class T>
0105 ConvexHullFinder<T>::ConvexHullFinder(ConvexHullFinder::VecReal2 const& points,
0106 Tolerance<> const& tol)
0107 : points_{points}, tol_{tol}
0108 {
0109 CELER_EXPECT(points_.size() > 2);
0110
0111 soft_ori_ = this->make_soft_ori();
0112 start_index_ = this->min_element_idx();
0113 convex_mask_ = this->calc_convex_mask();
0114 }
0115
0116
0117
0118
0119
0120 template<class T>
0121 auto ConvexHullFinder<T>::make_soft_ori() const -> SoftOrientation<real_type>
0122 {
0123 auto const x_cmp
0124 = [](Real2 const& a, Real2 const& b) { return a[0] < b[0]; };
0125 auto const y_cmp
0126 = [](Real2 const& a, Real2 const& b) { return a[1] < b[1]; };
0127
0128 auto const [x_min, x_max]
0129 = std::minmax_element(points_.begin(), points_.end(), x_cmp);
0130 auto const [y_min, y_max]
0131 = std::minmax_element(points_.begin(), points_.end(), y_cmp);
0132
0133
0134 using extent_type = Real3::value_type;
0135 Real3 const extents{static_cast<extent_type>((*x_max)[0] - (*x_min)[0]),
0136 static_cast<extent_type>((*y_max)[1] - (*y_min)[1]),
0137 0};
0138
0139 return SoftOrientation<real_type>(
0140 ::celeritas::detail::BumpCalculator(tol_)(extents));
0141 }
0142
0143
0144
0145
0146
0147 template<class T>
0148 auto ConvexHullFinder<T>::make_convex_hull() const -> VecReal2
0149 {
0150 CELER_EXPECT(convex_mask_.size() > 2);
0151
0152 VecReal2 convex_hull;
0153 for (auto i : range(convex_mask_.size()))
0154 {
0155 if (convex_mask_[i])
0156 {
0157 convex_hull.push_back(points_[i]);
0158 }
0159 }
0160 return convex_hull;
0161 }
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180 template<class T>
0181 auto ConvexHullFinder<T>::calc_concave_regions() const -> VecVecReal2
0182 {
0183 CELER_EXPECT(convex_mask_.size() > 2);
0184 VecVecReal2 concave_regions;
0185
0186
0187
0188
0189 size_type i = this->calc_previous(start_index_);
0190 while (i != start_index_)
0191 {
0192 if (convex_mask_[i])
0193 {
0194 i = this->calc_previous(i);
0195 }
0196 else
0197 {
0198 VecReal2 concave_region;
0199 concave_region.push_back(points_[calc_next(i)]);
0200 do
0201 {
0202 concave_region.push_back(points_[i]);
0203 i = this->calc_previous(i);
0204 } while (!convex_mask_[i]);
0205
0206 concave_region.push_back(points_[i]);
0207 concave_regions.push_back(std::move(concave_region));
0208 }
0209 }
0210 return concave_regions;
0211 }
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221 template<class T>
0222 auto ConvexHullFinder<T>::calc_convex_mask() const -> ConvexMask
0223 {
0224
0225
0226 std::vector<size_type> hull;
0227 auto i = start_index_;
0228 hull.push_back(i);
0229 i = this->calc_next(i);
0230
0231 for (size_type j = 0, n = points_.size(); j != n; ++j)
0232 {
0233 size_type i_next = this->calc_next(i);
0234
0235 if (this->is_clockwise(hull.back(), i, i_next))
0236 {
0237
0238 hull.push_back(i);
0239 }
0240 else
0241 {
0242
0243
0244 while (hull.size() >= 2
0245 && !this->is_clockwise(
0246 hull[hull.size() - 2], hull.back(), i_next))
0247 {
0248 hull.pop_back();
0249 }
0250 }
0251
0252 i = i_next;
0253 }
0254
0255
0256 ConvexMask convex_mask(points_.size(), false);
0257 for (auto h : hull)
0258 {
0259 convex_mask[h] = true;
0260 }
0261
0262 return convex_mask;
0263 }
0264
0265
0266
0267
0268
0269 template<class T>
0270 size_type ConvexHullFinder<T>::min_element_idx() const
0271 {
0272 auto starting_it = std::min_element(
0273 points_.begin(), points_.end(), [](Real2 const& a, Real2 const& b) {
0274 return a[1] < b[1];
0275 });
0276 return std::distance(points_.begin(), starting_it);
0277 };
0278
0279
0280
0281
0282
0283
0284
0285 template<class T>
0286 auto ConvexHullFinder<T>::is_clockwise(size_type i_prev,
0287 size_type i,
0288 size_type i_next) const -> bool
0289 {
0290 auto const& a = points_[i_prev];
0291 auto const& b = points_[i];
0292 auto const& c = points_[i_next];
0293 return soft_ori_(a, b, c) != detail::Orientation::counterclockwise;
0294 }
0295
0296
0297
0298
0299
0300 template<class T>
0301 size_type ConvexHullFinder<T>::calc_next(size_type i) const
0302 {
0303 return (i + 1 < points_.size() ? i + 1 : 0);
0304 }
0305
0306
0307
0308
0309
0310 template<class T>
0311 size_type ConvexHullFinder<T>::calc_previous(size_type i) const
0312 {
0313 return (i > 0 ? i : points_.size()) - 1;
0314 }
0315
0316
0317 }
0318 }
0319 }