use super::*;
pub(super) fn seams(
surface: &NurbsSurface,
loops: &[Vec<NurbsCurve>],
) -> Result<Option<[usize; 2]>, KernelRefusal> {
let (closed_u, closed_v) = surface
.closed_directions()
.or_refuse(KernelStage::Collect, "closed_directions")?;
if !closed_u || closed_v || loops.len() != 1 || loops[0].len() != 4 {
return Ok(None);
}
let [u0, u1] = surface
.domain_u()
.or_refuse(KernelStage::Collect, "domain_u")?;
let [v0, v1] = surface
.domain_v()
.or_refuse(KernelStage::Collect, "domain_v")?;
let eps_u = 1e-10 * (u1 - u0);
let eps_v = 1e-10 * (v1 - v0);
let mut points = Vec::new();
let mut ends = Vec::new();
for curve in &loops[0] {
if curve.degree != 1
|| curve.control_points.len() != 2
|| (curve.control_points[0].w - curve.control_points[1].w).abs() > 1e-12
{
return Ok(None);
}
ends.push(
curve.control_points[1]
.point()
.or_refuse(KernelStage::Collect, "point")?,
);
points.push(
curve.control_points[0]
.point()
.or_refuse(KernelStage::Collect, "point")?,
);
}
let low = points.iter().map(|p| p.y).fold(f64::INFINITY, f64::min);
let high = points.iter().map(|p| p.y).fold(f64::NEG_INFINITY, f64::max);
if low < v0 - eps_v || high > v1 + eps_v || high - low <= eps_v {
return Ok(None);
}
let corners = [
Vec3::new(u0, low, 0.0),
Vec3::new(u1, low, 0.0),
Vec3::new(u1, high, 0.0),
Vec3::new(u0, high, 0.0),
];
let matches = |p: Vec3, corner: Vec3| {
(p.x - corner.x).abs() <= eps_u
&& (p.y - corner.y).abs() <= eps_v
&& p.z.abs() <= eps_u.max(eps_v)
};
let Some(shift) = (0..4).find(|shift| {
(0..4).all(|i| {
matches(points[i], corners[(i + shift) % 4])
&& matches(ends[i], corners[(i + shift + 1) % 4])
})
}) else {
return Ok(None);
};
Ok(Some([(1 + 4 - shift) % 4, (3 + 4 - shift) % 4]))
}
pub(super) fn build(
surface: &NurbsSurface,
loops: &[Vec<NurbsCurve>],
bottom_distance: f64,
top_distance: f64,
) -> Result<Option<BrepSolid>, KernelRefusal> {
if seams(surface, loops)?.is_none() {
return Ok(None);
}
let [u0, u1] = surface
.domain_u()
.or_refuse(KernelStage::Collect, "domain_u")?;
let [v0, v1] = surface
.domain_v()
.or_refuse(KernelStage::Collect, "domain_v")?;
validate_trim_loops(
loops,
1e-7 * (u1 - u0).max(v1 - v0),
1e-10 * (u1 - u0) * (v1 - v0),
)?;
ensure_offsets_regular(surface, loops, &[bottom_distance, top_distance])?;
thicken_one_region(surface, loops, bottom_distance, top_distance).map(Some)
}