use super::*;
use super::frame::{rotate_about, Frame};
use super::plate::build_flat_plate;
pub(super) fn build_bend(
bend: &Bend,
a: [f64; 2],
b: [f64; 2],
parent: Frame,
thickness: f64,
fold: f64,
) -> Result<(Option<BrepSolid>, Frame), String> {
let theta = bend.angle_deg.to_radians() * fold;
let half_t = thickness * 0.5;
let r_mid = bend.mid_radius(thickness);
let r_in = (r_mid - half_t).max(1e-6);
let r_out = r_mid + half_t;
let ea = parent.point(a[0], a[1], 0.0);
let eb = parent.point(b[0], b[1], 0.0);
let edge_len = eb.sub(ea).length();
if !(edge_len > 1e-9) {
return Err(format!("sheet-metal: bend `{}` on a degenerate edge", bend.id));
}
let e_hat = eb.sub(ea).normalized()?; let n_hat = parent.w;
if theta.abs() < 1e-9 {
let out0 = e_hat.cross(n_hat); let allowance = bend.allowance(thickness);
let child_frame = Frame {
o: ea.add(out0.scale(allowance)),
u: out0,
v: e_hat,
w: n_hat,
};
if allowance < 1e-9 {
return Ok((None, child_frame)); }
let strip_frame = Frame {
o: ea,
u: out0,
v: e_hat,
w: n_hat,
};
let strip = build_flat_plate(&strip_flat(bend, allowance, edge_len), thickness, strip_frame)?;
return Ok((Some(strip), child_frame));
}
let side = if theta >= 0.0 { 1.0 } else { -1.0 };
let mag = theta.abs();
let n_eff = n_hat.scale(side);
let axis_a = ea.sub(n_eff.scale(r_mid));
let wedge_frame = if side >= 0.0 {
Frame {
o: axis_a,
u: n_eff,
v: e_hat,
w: n_eff.cross(e_hat),
}
} else {
let axis_b = eb.sub(n_eff.scale(r_mid));
let v = e_hat.scale(-1.0);
Frame {
o: axis_b,
u: n_eff,
v,
w: n_eff.cross(v),
}
};
let wedge = match build_bend_wedge(bend, r_in, r_out, edge_len, mag)? {
Some(wedge) => {
let wedge = super::relief::cut_bend_reliefs(wedge, bend, thickness, edge_len, side < 0.0)?;
Some(transform_brep(&wedge, wedge_frame.affine()?, false)?)
}
None => None,
};
let n_child = rotate_about(n_eff, e_hat, side * mag);
let out0 = e_hat.cross(n_hat);
let out_child = rotate_about(out0, e_hat, side * mag);
let child_edge_a = axis_a.add(n_child.scale(r_mid));
let child_frame = Frame {
o: child_edge_a,
u: out_child,
v: e_hat,
w: n_child.scale(side),
};
Ok((wedge, child_frame))
}
fn strip_flat(bend: &Bend, allowance: f64, length: f64) -> Flat {
let direction = if bend.angle_deg >= 0.0 { "DOWN" } else { "UP" };
Flat {
id: format!("{}:BAND:{direction}:{}", bend.id, bend.angle_deg.abs()),
outline: vec![[0.0, 0.0], [allowance, 0.0], [allowance, length], [0.0, length]],
edges: (0..4)
.map(|i| Edge {
id: format!("s{i}"),
bend: None,
})
.collect(),
holes: bend.reliefs.clone(),
hole_bends: Vec::new(),
outline_curves: Default::default(),
end_cuts: Vec::new(),
}
}
fn build_bend_wedge(
bend: &Bend,
r_in: f64,
r_out: f64,
length: f64,
theta: f64,
) -> Result<Option<BrepSolid>, String> {
if theta.abs() < 1e-9 {
return Ok(None);
}
let p = |radial: f64, axial: f64| Vec3::new(radial, axial, 0.0);
let profile = vec![
make_line(p(r_in, 0.0), p(r_out, 0.0))?, make_line(p(r_out, 0.0), p(r_out, length))?, make_line(p(r_out, length), p(r_in, length))?, make_line(p(r_in, length), p(r_in, 0.0))?, ];
let side_names = vec![
Some(format!("{}:END:0", bend.id)),
Some(format!("{}:A", bend.id)), Some(format!("{}:END:L", bend.id)),
Some(format!("{}:B", bend.id)), ];
revolve_profile_brep_named(
&profile,
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
theta,
&side_names,
&[None, None],
)
.map(Some)
}