use brepkit_math::nurbs::projection::project_point_to_surface;
use brepkit_math::tolerance::Tolerance;
use brepkit_math::vec::{Point3, Vec3};
use brepkit_topology::edge::EdgeCurve;
use brepkit_topology::face::FaceSurface;
pub(super) fn sample_edge_point(
curve: &EdgeCurve,
p_start: Point3,
p_end: Point3,
t: f64,
) -> Point3 {
match curve {
EdgeCurve::Line => Point3::new(
p_start.x().mul_add(1.0 - t, p_end.x() * t),
p_start.y().mul_add(1.0 - t, p_end.y() * t),
p_start.z().mul_add(1.0 - t, p_end.z() * t),
),
EdgeCurve::Circle(circle) => {
let ts = circle.project(p_start);
let mut te = circle.project(p_end);
if te <= ts {
te += std::f64::consts::TAU;
}
circle.evaluate(ts + (te - ts) * t)
}
EdgeCurve::Ellipse(ellipse) => {
let ts = ellipse.project(p_start);
let mut te = ellipse.project(p_end);
if te <= ts {
te += std::f64::consts::TAU;
}
ellipse.evaluate(ts + (te - ts) * t)
}
EdgeCurve::NurbsCurve(nurbs) => {
let (u0, u1) = nurbs.domain();
nurbs.evaluate(u0 + (u1 - u0) * t)
}
}
}
pub(super) fn sample_edge_tangent(
curve: &EdgeCurve,
p_start: Point3,
p_end: Point3,
t: f64,
) -> Vec3 {
match curve {
EdgeCurve::Line => p_end - p_start,
EdgeCurve::Circle(circle) => {
let ts = circle.project(p_start);
let mut te = circle.project(p_end);
if te <= ts {
te += std::f64::consts::TAU;
}
circle.tangent(ts + (te - ts) * t)
}
EdgeCurve::Ellipse(ellipse) => {
let ts = ellipse.project(p_start);
let mut te = ellipse.project(p_end);
if te <= ts {
te += std::f64::consts::TAU;
}
ellipse.tangent(ts + (te - ts) * t)
}
EdgeCurve::NurbsCurve(nurbs) => {
let (u0, u1) = nurbs.domain();
let u = u0 + (u1 - u0) * t;
let d = nurbs.derivatives(u, 1);
d[1]
}
}
}
pub(super) fn edge_v_samples(curve: &EdgeCurve) -> usize {
match curve {
EdgeCurve::Line => 2,
EdgeCurve::Circle(_) | EdgeCurve::Ellipse(_) => 9,
EdgeCurve::NurbsCurve(_) => 7,
}
}
pub(super) struct CrossSection {
pub(super) ld1: Vec3,
pub(super) ld2: Vec3,
pub(super) bisector: Vec3,
pub(super) half_angle: f64,
}
pub(super) fn cross_section_dirs(
tan: Vec3,
n1: Vec3,
n2: Vec3,
fallback_d1: Vec3,
fallback_d2: Vec3,
) -> CrossSection {
let c1 = tan.cross(n1);
let c2 = tan.cross(n2);
let ld1 = if c1.dot(n2) < 0.0 { c1 } else { -c1 };
let ld2 = if c2.dot(n1) < 0.0 { c2 } else { -c2 };
let ld1 = ld1.normalize().unwrap_or(fallback_d1);
let ld2 = ld2.normalize().unwrap_or(fallback_d2);
let cos_half = ld1.dot(ld2).clamp(-1.0, 1.0);
let half_angle = cos_half.acos() / 2.0;
let bisector = (ld1 + ld2).normalize().unwrap_or(fallback_d1);
CrossSection {
ld1,
ld2,
bisector,
half_angle,
}
}
pub fn face_surface_normal_at(surface: &FaceSurface, point: Point3) -> Option<Vec3> {
match surface {
FaceSurface::Plane { normal, .. } => Some(*normal),
FaceSurface::Cylinder(cyl) => {
let dp = point - cyl.origin();
let along_axis = dp.dot(cyl.axis());
let on_axis = cyl.origin() + cyl.axis() * along_axis;
(point - on_axis).normalize().ok()
}
FaceSurface::Cone(cone) => {
let dp = point - cone.apex();
let along_axis = dp.dot(cone.axis());
let radial = dp - cone.axis() * along_axis;
let radial_n = radial.normalize().ok()?;
let (sin_a, cos_a) = cone.half_angle().sin_cos();
Some(radial_n * sin_a + cone.axis() * (-cos_a))
}
FaceSurface::Sphere(sph) => (point - sph.center()).normalize().ok(),
FaceSurface::Torus(tor) => {
let dp = point - tor.center();
let along_axis = dp.dot(tor.z_axis());
let in_plane = dp - tor.z_axis() * along_axis;
let ring_dir = in_plane.normalize().ok()?;
let tube_center = tor.center() + ring_dir * tor.major_radius();
(point - tube_center).normalize().ok()
}
FaceSurface::Nurbs(srf) => {
let proj_tol = Tolerance::new().linear * 1e3;
match project_point_to_surface(srf, point, proj_tol) {
Ok(proj) => srf.normal(proj.u, proj.v).ok(),
Err(_) => srf.normal(0.5, 0.5).ok(), }
}
}
}