1use axiolid_core::{Interval, Point2, Scalar, Vec3};
13use axiolid_curve::{Carrier, Curve3, Field2, ImplicitSection3};
14use axiolid_surface::Surface;
15use core::f64::consts::{FRAC_PI_2, PI};
16
17use crate::exact_surface_intersection::{
18 Derivation, ExactIntersectionCurve, ExactIntersectionRefusal,
19};
20use crate::field::{carrier_of, section_field};
21use crate::implicit_trace::{trace, Periodic, TraceRefusal};
22use axiolid_curve::implicit::Cell;
23
24#[must_use]
28pub fn section_field_of(carrier: &Surface, other: &Surface) -> Option<Field2> {
29 section_field(&carrier_of(carrier)?, other)
30}
31
32pub(crate) fn refusal_of(refusal: TraceRefusal) -> ExactIntersectionRefusal {
36 match refusal {
37 TraceRefusal::Touching(_) => ExactIntersectionRefusal::NotRegularCurve,
38 TraceRefusal::Singular(_) | TraceRefusal::Many(_) | TraceRefusal::Budget => {
39 ExactIntersectionRefusal::Undecided
40 }
41 }
42}
43
44pub fn implicit_surface_intersection(
61 carrier: &Surface,
62 other: &Surface,
63 window: Option<(Point2, Point2)>,
64) -> Result<Vec<ImplicitSection3>, ExactIntersectionRefusal> {
65 let form = carrier_of(carrier).ok_or(ExactIntersectionRefusal::UnsupportedPair)?;
66 let field = section_field(&form, other).ok_or(ExactIntersectionRefusal::UnsupportedPair)?;
67 let (periodic_u, periodic_v) = form.periodic();
68 let (lo, hi) = match window {
69 Some(w) => w,
70 None => default_window(&form, other).ok_or(ExactIntersectionRefusal::UnsupportedPair)?,
71 };
72 let wraps = |periodic: bool, a: Scalar, b: Scalar| {
74 periodic && ((b - a) - 2.0 * PI).abs() <= 1e-12 * (1.0 + a.abs())
75 };
76 let periodic = Periodic {
77 u: wraps(periodic_u, lo.x, hi.x),
78 v: wraps(periodic_v, lo.y, hi.y),
79 };
80 let curves = trace(&field, Cell { lo, hi }, periodic).map_err(refusal_of)?;
81 let curves: Vec<_> = match &form {
83 Carrier::Spline(b) => match b.domain() {
84 Some(((u0, u1), (v0, v1))) => curves
85 .iter()
86 .flat_map(|c| c.clipped(Point2::new(u0, v0), Point2::new(u1, v1)))
87 .collect(),
88 None => curves,
89 },
90 _ => curves,
91 };
92 if curves.is_empty() {
93 return Err(ExactIntersectionRefusal::Disjoint);
94 }
95 Ok(curves
96 .into_iter()
97 .map(|curve| ImplicitSection3 {
98 carrier: form.clone(),
99 curve,
100 })
101 .collect())
102}
103
104pub(crate) const TURN_OFFSET: Scalar = 0.123_456_789_012_345_67;
108
109fn ball(surface: &Surface) -> Option<(Vec3, Scalar)> {
111 match surface {
112 Surface::Sphere(s) => Some((s.frame.origin, s.radius)),
113 Surface::Torus(t) => Some((t.frame.origin, t.major_radius + t.minor_radius)),
114 _ => None,
115 }
116}
117
118fn default_window(carrier: &Carrier, other: &Surface) -> Option<(Point2, Point2)> {
121 let o = TURN_OFFSET;
124 match carrier {
125 Carrier::Torus(_) => Some((Point2::new(-PI + o, -PI + o), Point2::new(PI + o, PI + o))),
126 Carrier::Sphere { .. } => Some((
127 Point2::new(-PI + o, -FRAC_PI_2),
128 Point2::new(PI + o, FRAC_PI_2),
129 )),
130 Carrier::Ruled(k) => {
131 let (centre, radius) = ball(other)?;
132 let z = k.frame.z.normalize();
133 let along = (centre - k.frame.origin).dot(z);
134 let margin = 1e-6 * (1.0 + radius + along.abs());
135 let (mut v0, mut v1) = (along - radius - margin, along + radius + margin);
136 if k.slope != 0.0 {
138 let apex = -k.x_radius / k.slope;
139 let clear = 1e-9 * (1.0 + apex.abs());
140 if k.slope > 0.0 {
141 v0 = v0.max(apex + clear);
142 } else {
143 v1 = v1.min(apex - clear);
144 }
145 if v0 >= v1 {
146 return None;
147 }
148 }
149 Some((Point2::new(-PI + o, v0), Point2::new(PI + o, v1)))
150 }
151 Carrier::Spline(b) => {
152 let ((u0, u1), (v0, v1)) = b.domain()?;
155 let (pu, pv) = (1e-6 * (u1 - u0), 1e-6 * (v1 - v0));
156 Some((Point2::new(u0 - pu, v0 - pv), Point2::new(u1 + pu, v1 + pv)))
157 }
158 Carrier::Plane(f) => {
159 let (centre, radius) = ball(other)?;
160 let d = centre - f.origin;
161 let (x, y) = (d.dot(f.x.normalize()), d.dot(f.y.normalize()));
162 let r = radius * (1.0 + 1e-6);
163 Some((Point2::new(x - r, y - r), Point2::new(x + r, y + r)))
164 }
165 }
166}
167
168pub(crate) fn traced_section(
172 first: &Surface,
173 second: &Surface,
174) -> Result<Option<ExactIntersectionCurve>, ExactIntersectionRefusal> {
175 if carrier_of(first).is_none() || carrier_of(second).is_none() {
176 return Ok(None);
177 }
178 let rank = |s: &Surface| match s {
179 Surface::Torus(_) => 2,
180 Surface::Sphere(_) => 1,
181 _ => 0,
182 };
183 let (carrier, other) = if rank(first) >= rank(second) {
184 (first, second)
185 } else {
186 (second, first)
187 };
188 if rank(carrier) == 0 {
189 return Ok(None);
190 }
191 let sections = implicit_surface_intersection(carrier, other, None)?;
192 let spans = sections
193 .iter()
194 .map(|s| Some(Interval::new(0.0, s.curve.end())))
195 .collect();
196 Ok(Some(ExactIntersectionCurve {
197 branches: sections.into_iter().map(Curve3::ImplicitSection).collect(),
198 derivation: Derivation::ImplicitTrace,
199 spans,
200 }))
201}