17void check(
const std::vector<V>& subject,
const std::vector<V>& boundary)
20 V low=subject.front(),high=low;
21 for(
const auto&
p : subject)
24 high=high.cwiseMax(
p);
26 const double tolerance=1.e-8*std::max(1.0,(high-low).norm());
27 const auto bounded=[&](
const V&
p)
29 require(
p.allFinite(),
"Clipping produced a nonfinite point");
30 require((low-
p).maxCoeff()<=tolerance && (
p-high).maxCoeff()<=tolerance,
31 "Clipping extrapolated outside the subject bounds");
32 for(
size_t k=0;
k<boundary.size();++
k)
34 const V edge=boundary[(
k+1)%boundary.size()]-boundary[
k];
35 const V relative=
p-boundary[
k];
36 require(edge.x()*relative.y()-edge.y()*relative.x()>=-tolerance*edge.norm(),
37 "Clipped point lies outside its grain patch");
40 for(
const auto&
p : clipped)
45 for(
const auto&
t : triangles)
47 for(
const auto&
p :
t)
58 const V p=low+(high-low).cwiseProduct(
V((
i+.371)/29,(
j+.613)/29));
62 for(
const auto&
t : triangles)
66 require(hits==
size_t(expected),
"Shaded coverage disagrees with polygon/patch intersection");
75 const std::vector<V> square{{0,0},{1,0},{1,1},{0,1}};
77 "Disjoint clipping should remain empty through subsequent edges");
80 check({{-.2,.2},{1.2,.2},{1.2,.8},{-.2,.8}},square);
81 check({{0,0},{1,1},{0,1},{1,0}},square);
82 std::ifstream
in(std::filesystem::path(
argv[1])/
"tests/data/polycrystalClipSpikes.txt");
85 require(cases==9,
"Missing saved spike contours");
86 for(
size_t k=0;
k<cases;++
k)
90 std::vector<V> subject(n),boundary(
m);
91 for(
auto&
p : subject)
95 for(
auto&
p : boundary)
99 require(
bool(
in),
"Incomplete saved spike contour");
100 for(
int direction=0;direction<2;++direction)
102 for(
size_t start=0;start<subject.size();++start)
104 check(subject,boundary);
105 std::rotate(subject.begin(),subject.begin()+1,subject.end());
107 std::reverse(subject.begin(),subject.end());
110 std::cout<<
"PASS: bounded clipping and spike-free loop-area coverage\n";
113 catch(
const std::exception& error)
115 std::cerr<<error.what()<<
'\n';