use super::*;
use super::frame::Frame;
pub(super) fn segment_endpoints_2d(curve: &crate::NurbsCurve) -> Result<([f64; 2], [f64; 2]), String> {
let domain = curve.domain()?;
let a = curve.evaluate(domain[0])?;
let b = curve.evaluate(domain[1])?;
Ok(([a.x, a.y], [b.x, b.y]))
}
pub(crate) fn circle_of_loop(loop_curves: &[crate::NurbsCurve]) -> Option<([f64; 2], f64)> {
let mut samples: Vec<[f64; 2]> = Vec::new();
for curve in loop_curves {
let domain = curve.domain().ok()?;
for step in 0..8 {
let t = domain[0] + (domain[1] - domain[0]) * (step as f64 / 8.0);
let point = curve.evaluate(t).ok()?;
samples.push([point.x, point.y]);
}
}
if samples.len() < 6 {
return None;
}
let (p1, p2, p3) = (
samples[0],
samples[samples.len() / 3],
samples[2 * samples.len() / 3],
);
let [cx, cy] = crate::circumcenter_2d(p1, p2, p3)?;
let radius = ((p1[0] - cx).powi(2) + (p1[1] - cy).powi(2)).sqrt();
if radius < 1e-9 {
return None;
}
let tolerance = radius.max(1.0) * 1e-7;
for point in &samples {
let r = ((point[0] - cx).powi(2) + (point[1] - cy).powi(2)).sqrt();
if (r - radius).abs() > tolerance {
return None;
}
}
Some(([cx, cy], radius))
}
pub(super) fn build_collar(
hole_bend: &HoleBend,
loop_curves: &[crate::NurbsCurve],
leg: f64,
frame: Frame,
thickness: f64,
fold: f64,
) -> Result<Option<BrepSolid>, String> {
let theta = hole_bend.angle_deg.to_radians() * fold;
if theta.abs() < 1e-9 {
return Ok(None);
}
let (center, rho_e) = circle_of_loop(loop_curves).ok_or_else(|| {
format!(
"sheet-metal: collar `{}` sits on a non-circular hole loop",
hole_bend.id
)
})?;
let side = if theta >= 0.0 { 1.0 } else { -1.0 };
let mag = theta.abs();
let half_t = thickness * 0.5;
let r_mid = (hole_bend.inside_radius + half_t).max(1e-6);
let r_in = (r_mid - half_t).max(1e-6);
let r_out = r_mid + half_t;
if !(rho_e - r_out > 1e-6) {
return Err(format!(
"sheet-metal: collar `{}` needs bend radius + thickness ({r_out:.4}) smaller than the hole rim radius ({rho_e:.4})",
hole_bend.id
));
}
let c = Vec3::new(rho_e, -r_mid, 0.0);
let dir = |phi: f64| [-phi.sin(), phi.cos()];
let (dm, tm) = (dir(mag), [-mag.cos(), -mag.sin()]);
let at = |r: f64, d: [f64; 2]| Vec3::new(c.x + d[0] * r, c.y + d[1] * r, 0.0);
let rim_bottom = Vec3::new(rho_e, -half_t, 0.0); let rim_top = Vec3::new(rho_e, half_t, 0.0); let bend_out_end = at(r_out, dm);
let bend_in_end = at(r_in, dm);
let wall_out_tip = Vec3::new(bend_out_end.x + tm[0] * leg, bend_out_end.y + tm[1] * leg, 0.0);
let wall_in_tip = Vec3::new(bend_in_end.x + tm[0] * leg, bend_in_end.y + tm[1] * leg, 0.0);
let ov = half_t * 0.5;
let ring_bottom = Vec3::new(rho_e + ov, -half_t, 0.0);
let ring_top = Vec3::new(rho_e + ov, half_t, 0.0);
let arc_x = Vec3::new(0.0, 1.0, 0.0);
let arc_y = Vec3::new(-1.0, 0.0, 0.0);
let profile = vec![
make_line(rim_bottom, ring_bottom)?, make_line(ring_bottom, ring_top)?, make_line(ring_top, rim_top)?, make_arc(c, arc_x, arc_y, r_out, 0.0, mag)?, make_line(bend_out_end, wall_out_tip)?, make_line(wall_out_tip, wall_in_tip)?, make_line(wall_in_tip, bend_in_end)?, make_arc(c, arc_x, arc_y, r_in, 0.0, mag)?.reversed()?, ];
let side_names = vec![
None,
Some(format!("{}:rim", hole_bend.id)),
None,
Some(format!("{}:A", hole_bend.id)),
Some(format!("{}:wall:A", hole_bend.id)),
Some(format!("{}:tip", hole_bend.id)),
Some(format!("{}:wall:B", hole_bend.id)),
Some(format!("{}:B", hole_bend.id)),
];
let collar = revolve_profile_brep_named(
&profile,
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
std::f64::consts::TAU,
&side_names,
&[None, None],
)?;
let axis_frame = Frame {
o: frame.point(center[0], center[1], 0.0),
u: frame.u,
v: frame.w.scale(side),
w: frame.v.scale(-side),
};
transform_brep(&collar, axis_frame.affine()?, false).map(Some)
}