use crate::feature_pipeline::features::common;
use crate::feature_pipeline::{FaceRef, ProfileLoop, SketchProfile};
pub fn face_profile(face: FaceRef) -> Result<SketchProfile, String> {
let frame = common::face_frame(face).map_err(|error| format!("face-profile: {error}"))?;
let loops = crate::with_registered_solid_str(face.handle, |solid| {
let record = solid
.shells
.iter()
.flat_map(|shell| &shell.faces)
.find(|candidate| candidate.id == face.face_id)
.ok_or_else(|| format!("face-profile: face {} not found on solid", face.face_id))?;
let mut loops: Vec<ProfileLoop> = Vec::with_capacity(record.loops.len());
for loop_record in &record.loops {
let mut curves = Vec::with_capacity(loop_record.coedges.len());
let mut edge_names = Vec::with_capacity(loop_record.coedges.len());
for coedge in &loop_record.coedges {
let edge = solid
.edges
.iter()
.find(|candidate| candidate.id == coedge.edge_id)
.ok_or_else(|| {
format!("face-profile: edge {} not found on solid", coedge.edge_id)
})?;
if edge.degenerate {
continue; }
let trimmed = common::trimmed_curve(&edge.curve, edge.t0, edge.t1).map_err(|error| {
format!("face-profile: edge {} trim failed: {error}", edge.id)
})?;
let oriented = if coedge.forward {
trimmed
} else {
trimmed.reversed()?
};
curves.push(oriented);
edge_names.push(edge.name.clone());
}
if curves.is_empty() {
continue; }
if curves.len() == 1 {
let only = curves.pop().ok_or("face-profile: loop vanished")?;
let [start, end] = only.domain()?;
let (first, second) = only.split((start + end) * 0.5)?;
let name = edge_names.pop().flatten();
curves.push(first);
curves.push(second);
edge_names.push(name.clone());
edge_names.push(name);
}
loops.push(ProfileLoop {
curves,
edge_names,
loop_id: None,
});
}
if loops.is_empty() {
return Err(format!(
"face-profile: face {} has no usable boundary loop",
face.face_id
));
}
Ok(loops)
})?;
Ok(SketchProfile {
origin: frame.origin,
x_axis: frame.x_axis,
y_axis: frame.y_axis,
z_axis: frame.z_axis,
regions: vec![loops],
})
}