use brepkit_math::nurbs::curve::NurbsCurve;
use brepkit_math::traits::ParametricSurface;
use brepkit_math::vec::{Point3, Vec3};
use brepkit_topology::Topology;
use brepkit_topology::edge::{Edge, EdgeCurve};
use brepkit_topology::face::{Face, FaceId, FaceSurface};
use brepkit_topology::vertex::{Vertex, VertexId};
use brepkit_topology::wire::{OrientedEdge, Wire};
use crate::BlendError;
use crate::stripe::Stripe;
#[must_use]
pub fn sample_nurbs_endpoints(curve: &NurbsCurve) -> Vec<Point3> {
let (t0, t1) = curve.domain();
vec![curve.evaluate(t0), curve.evaluate(t1)]
}
pub fn create_blend_face_with_contacts(
topo: &mut Topology,
stripe: &Stripe,
contact1_edge: Option<brepkit_topology::edge::EdgeId>,
contact2_edge: Option<brepkit_topology::edge::EdgeId>,
) -> Result<BlendFaceInfo, BlendError> {
const WELD: f64 = 1e-5;
let (t0_1, t1_1) = stripe.contact1.domain();
let (t0_2, t1_2) = stripe.contact2.domain();
let p1_start = stripe.contact1.evaluate(t0_1);
let p1_end = stripe.contact1.evaluate(t1_1);
let p2_start = stripe.contact2.evaluate(t0_2);
let p2_end = stripe.contact2.evaluate(t1_2);
let adopt = |topo: &Topology,
eid: Option<brepkit_topology::edge::EdgeId>,
want_s: Point3,
want_e: Point3|
-> Option<(brepkit_topology::edge::EdgeId, bool, VertexId, VertexId)> {
let eid = eid?;
let e = topo.edge(eid).ok()?;
let (sv, ev) = (e.start(), e.end());
let sp = topo.vertex(sv).ok()?.point();
let ep = topo.vertex(ev).ok()?.point();
if (sp - want_s).length() <= WELD && (ep - want_e).length() <= WELD {
Some((eid, true, sv, ev))
} else if (sp - want_e).length() <= WELD && (ep - want_s).length() <= WELD {
Some((eid, false, ev, sv))
} else {
None
}
};
let adopt1 = adopt(topo, contact1_edge, p1_start, p1_end);
let adopt2 = adopt(topo, contact2_edge, p2_end, p2_start);
let (v1s, v1e) = adopt1.map_or_else(
|| {
(
topo.add_vertex(Vertex::new(p1_start, 1e-7)),
topo.add_vertex(Vertex::new(p1_end, 1e-7)),
)
},
|(_, _, s, e)| (s, e),
);
let (v2e, v2s) = adopt2.map_or_else(
|| {
(
topo.add_vertex(Vertex::new(p2_end, 1e-7)),
topo.add_vertex(Vertex::new(p2_start, 1e-7)),
)
},
|(_, _, s, e)| (s, e),
);
let (e0, e0_fwd) = adopt1.map_or_else(
|| {
(
topo.add_edge(Edge::new(
v1s,
v1e,
EdgeCurve::NurbsCurve(stripe.contact1.clone()),
)),
true,
)
},
|(eid, fwd, _, _)| (eid, fwd),
);
let arc_curve =
|sec: &crate::section::CircSection, a: Point3, b: Point3| -> Option<EdgeCurve> {
let u = a - sec.center;
let v = b - sec.center;
let n = u.cross(v);
let n = n.normalize().ok()?;
let circle = brepkit_math::curves::Circle3D::new(sec.center, n, sec.radius).ok()?;
Some(EdgeCurve::Circle(circle))
};
let end_curve = stripe
.sections
.last()
.and_then(|sec| arc_curve(sec, p1_end, p2_end))
.unwrap_or(EdgeCurve::Line);
let start_curve = stripe
.sections
.first()
.and_then(|sec| arc_curve(sec, p2_start, p1_start))
.unwrap_or(EdgeCurve::Line);
let e1 = topo.add_edge(Edge::new(v1e, v2e, end_curve));
let (e2, e2_fwd) = adopt2.map_or_else(
|| {
(
topo.add_edge(Edge::new(
v2e,
v2s,
EdgeCurve::NurbsCurve(stripe.contact2.clone()),
)),
true,
)
},
|(eid, fwd, _, _)| (eid, fwd),
);
let e3 = topo.add_edge(Edge::new(v2s, v1s, start_curve));
let wire = Wire::new(
vec![
OrientedEdge::new(e0, e0_fwd),
OrientedEdge::new(e1, true),
OrientedEdge::new(e2, e2_fwd),
OrientedEdge::new(e3, true),
],
true,
)?;
let wire_id = topo.add_wire(wire);
let face = Face::new(wire_id, Vec::new(), stripe.surface.clone());
let face_id = topo.add_face(face);
Ok(BlendFaceInfo {
face: face_id,
cross_end: (e1, v1e, v2e),
cross_start: (e3, v2s, v1s),
})
}
pub struct BlendFaceInfo {
pub face: FaceId,
pub cross_end: (brepkit_topology::edge::EdgeId, VertexId, VertexId),
pub cross_start: (brepkit_topology::edge::EdgeId, VertexId, VertexId),
}
pub fn notch_face_corner_with_arc(
topo: &mut Topology,
face_id: FaceId,
arc: (brepkit_topology::edge::EdgeId, VertexId, VertexId),
) -> Result<Option<FaceId>, BlendError> {
let (arc_eid, va, vb) = arc;
let wire_id = topo.face(face_id)?.outer_wire();
let oes = topo.wire(wire_id)?.edges().to_vec();
let n = oes.len();
if n < 3 {
return Ok(None);
}
let ends = |oe: &OrientedEdge| -> Result<(VertexId, VertexId), BlendError> {
let e = topo.edge(oe.edge())?;
Ok((oe.oriented_start(e), oe.oriented_end(e)))
};
for i in 0..n {
let j = (i + 1) % n;
let (s0, e0) = ends(&oes[i])?;
let (s1, e1) = ends(&oes[j])?;
if e0 != s1 || e0 == va || e0 == vb {
continue;
}
let fwd = s0 == va && e1 == vb;
let rev = s0 == vb && e1 == va;
if !(fwd || rev) {
continue;
}
let both_straight = [oes[i].edge(), oes[j].edge()].iter().all(|&eid| {
topo.edge(eid)
.is_ok_and(|e| matches!(e.curve(), EdgeCurve::Line))
});
if !both_straight {
continue;
}
let mut new_oes: Vec<OrientedEdge> = Vec::with_capacity(n - 1);
for (k, oe) in oes.iter().enumerate() {
if k == i {
new_oes.push(OrientedEdge::new(arc_eid, fwd));
} else if k != j {
new_oes.push(*oe);
}
}
let new_wire = topo.add_wire(Wire::new(new_oes, true)?);
let (surface, reversed, inners) = {
let f = topo.face(face_id)?;
(
f.surface().clone(),
f.is_reversed(),
f.inner_wires().to_vec(),
)
};
let new_face = if reversed {
Face::new_reversed(new_wire, inners, surface)
} else {
Face::new(new_wire, inners, surface)
};
let nf = topo.add_face(new_face);
return Ok(Some(nf));
}
Ok(None)
}
pub struct PlaneAdapter {
pub origin: Point3,
pub u_dir: Vec3,
pub v_dir: Vec3,
pub norm: Vec3,
}
impl PlaneAdapter {
#[must_use]
pub fn from_normal_and_d(normal: Vec3, d: f64) -> Self {
let origin = Point3::new(normal.x() * d, normal.y() * d, normal.z() * d);
let ref_vec = if normal.x().abs() < 0.9 {
Vec3::new(1.0, 0.0, 0.0)
} else {
Vec3::new(0.0, 1.0, 0.0)
};
let u_dir = normal
.cross(ref_vec)
.normalize()
.unwrap_or(Vec3::new(1.0, 0.0, 0.0));
let v_dir = normal
.cross(u_dir)
.normalize()
.unwrap_or(Vec3::new(0.0, 1.0, 0.0));
Self {
origin,
u_dir,
v_dir,
norm: normal,
}
}
}
impl ParametricSurface for PlaneAdapter {
fn evaluate(&self, u: f64, v: f64) -> Point3 {
self.origin + self.u_dir * u + self.v_dir * v
}
fn normal(&self, _u: f64, _v: f64) -> Vec3 {
self.norm
}
fn project_point(&self, point: Point3) -> (f64, f64) {
let d = point - self.origin;
(d.dot(self.u_dir), d.dot(self.v_dir))
}
fn partial_u(&self, _u: f64, _v: f64) -> Vec3 {
self.u_dir
}
fn partial_v(&self, _u: f64, _v: f64) -> Vec3 {
self.v_dir
}
}
pub struct FlippedNormalSurface<'a> {
inner: &'a dyn ParametricSurface,
}
impl<'a> FlippedNormalSurface<'a> {
#[must_use]
pub const fn new(inner: &'a dyn ParametricSurface) -> Self {
Self { inner }
}
}
impl ParametricSurface for FlippedNormalSurface<'_> {
fn evaluate(&self, u: f64, v: f64) -> Point3 {
self.inner.evaluate(u, v)
}
fn normal(&self, u: f64, v: f64) -> Vec3 {
-self.inner.normal(u, v)
}
fn project_point(&self, point: Point3) -> (f64, f64) {
self.inner.project_point(point)
}
fn partial_u(&self, u: f64, v: f64) -> Vec3 {
self.inner.partial_u(u, v)
}
fn partial_v(&self, u: f64, v: f64) -> Vec3 {
self.inner.partial_v(u, v)
}
}
#[must_use]
pub fn surface_ref_or_adapter<'a>(
surface: &'a FaceSurface,
adapter_slot: &'a mut Option<PlaneAdapter>,
) -> &'a dyn ParametricSurface {
if let FaceSurface::Plane { normal, d } = surface {
let adapter = adapter_slot.insert(PlaneAdapter::from_normal_and_d(*normal, *d));
return adapter as &dyn ParametricSurface;
}
match surface {
FaceSurface::Plane { .. } => {
adapter_slot.insert(PlaneAdapter::from_normal_and_d(
Vec3::new(0.0, 0.0, 1.0),
0.0,
)) as &dyn ParametricSurface
}
FaceSurface::Cylinder(c) => c as &dyn ParametricSurface,
FaceSurface::Cone(c) => c as &dyn ParametricSurface,
FaceSurface::Sphere(s) => s as &dyn ParametricSurface,
FaceSurface::Torus(t) => t as &dyn ParametricSurface,
FaceSurface::Nurbs(n) => n as &dyn ParametricSurface,
}
}