1use ogeom_core::{OgeomResult, Tolerances, ogeom_bail};
14use ogeom_math::Point2;
15use ogeom_topo::{Model, Shape};
16
17use crate::discretize::Deflection;
18use crate::triangulate::face_boundary;
19
20pub fn hatch_face(
34 model: &Model,
35 face: &Shape,
36 angle: f64,
37 spacing: f64,
38 deflection: Deflection,
39 tol: Tolerances,
40) -> OgeomResult<Vec<[Point2; 2]>> {
41 if !spacing.is_finite() || spacing <= tol.confusion() {
42 ogeom_bail!(Construction, "a hatch spacing of {spacing} draws nothing");
43 }
44 if !angle.is_finite() {
45 ogeom_bail!(Construction, "a hatch angle of {angle} is not a direction");
46 }
47 let rings = face_boundary(model, face, deflection, tol)?;
48
49 let (sin, cos) = angle.sin_cos();
51 let turn = |p: Point2| Point2::new(p.x.mul_add(cos, p.y * sin), p.y.mul_add(cos, -(p.x * sin)));
52 let back = |p: Point2| Point2::new(p.x.mul_add(cos, -(p.y * sin)), p.y.mul_add(cos, p.x * sin));
53 let turned: Vec<Vec<Point2>> = rings
54 .iter()
55 .map(|ring| ring.iter().map(|p| turn(*p)).collect())
56 .collect();
57
58 let (mut lo, mut hi) = (f64::INFINITY, f64::NEG_INFINITY);
59 for ring in &turned {
60 for p in ring {
61 lo = lo.min(p.y);
62 hi = hi.max(p.y);
63 }
64 }
65 if !lo.is_finite() || hi <= lo {
66 return Ok(Vec::new());
67 }
68
69 let mut out = Vec::new();
70 let first = (lo / spacing).floor();
71 let mut k = first;
72 loop {
73 let level = (k + 0.5) * spacing;
74 k += 1.0;
75 if level >= hi {
76 break;
77 }
78 if level <= lo {
79 continue;
80 }
81 let mut crossings: Vec<f64> = Vec::new();
84 for ring in &turned {
85 for i in 0..ring.len() {
86 let (a, b) = (ring[i], ring[(i + 1) % ring.len()]);
87 if (a.y > level) != (b.y > level) {
88 crossings.push((b.x - a.x).mul_add((level - a.y) / (b.y - a.y), a.x));
89 }
90 }
91 }
92 crossings.sort_by(|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal));
93 for pair in crossings.as_chunks::<2>().0 {
94 if pair[1] - pair[0] > tol.parametric() {
95 out.push([
96 back(Point2::new(pair[0], level)),
97 back(Point2::new(pair[1], level)),
98 ]);
99 }
100 }
101 }
102 Ok(out)
103}