use super::*;
fn enabled() -> bool {
switch!("BREP_OFFSET_TORUS_APPLE", true)
}
pub(super) fn torus_apple(source: &BrepSolid, distance: f64) -> Option<OffsetShellResultRecord> {
if !enabled() || distance >= 0.0 {
return None;
}
match build(source, -distance) {
Ok(result) => result,
Err(reason) => {
os_debug!("torus apple lane declined: {reason}");
None
}
}
}
fn build(source: &BrepSolid, grow: f64) -> Result<Option<OffsetShellResultRecord>, String> {
let faces = source.shells.iter().flat_map(|shell| &shell.faces).collect::<Vec<_>>();
if source.shells.len() != 1 || faces.len() != 1 {
return Ok(None);
}
let face = faces[0];
let Some(crate::AnalyticSurface::Torus { frame, major_radius, minor_radius }) = face.surface.analytic() else {
return Ok(None);
};
for coedge in face.loops.iter().flat_map(|loop_record| &loop_record.coedges) {
let Some(edge) = source.edges.iter().find(|edge| edge.id == coedge.edge_id) else {
return Ok(None);
};
let uses = face
.loops
.iter()
.flat_map(|loop_record| &loop_record.coedges)
.filter(|other| other.edge_id == edge.id)
.count();
if !edge.degenerate && uses != 2 {
return Ok(None);
}
}
let (big, s) = (*major_radius, *minor_radius + grow);
if !(s > big * (1.0 + 1e-9)) || !(big > 0.0) {
return Ok(None);
}
let c = (s * s - big * big).sqrt();
let phi = (big / s).acos();
let centre = frame.origin.add(frame.x_axis.scale(big));
let arc = crate::make_arc(centre, frame.x_axis, frame.axis, s, -(std::f64::consts::PI - phi), std::f64::consts::PI - phi)?;
let top = frame.origin.add(frame.axis.scale(c));
let bottom = frame.origin.sub(frame.axis.scale(c));
let axis_segment = crate::make_line(top, bottom)?;
let solid = crate::revolve_profile_brep(&[arc, axis_segment], frame.origin, frame.axis, std::f64::consts::TAU)?;
let pi = std::f64::consts::PI;
let expected = 2.0 * pi * (pi * big * s * s + 2.0 * c.powi(3) / 3.0 - big * (s * s * phi - big * c));
let volume = crate::solid_mass_properties(&solid)?.volume;
if !((volume - expected).abs() <= 1e-9 * expected) || !solid.validate().is_empty() {
return Err(format!("the revolved apple reads volume {volume} against {expected}"));
}
os_debug!("torus apple lane built: s = {s}, dimples at ±{c}");
let face_images = solid
.shells
.iter()
.flat_map(|shell| &shell.faces)
.map(|_| OffsetShellFaceImageRecord { role: OffsetFaceRole::Offset, source_face_id: face.id })
.collect();
Ok(Some(OffsetShellResultRecord { solid, face_images }))
}