use brepkit_math::nurbs::surface::NurbsSurface;
use brepkit_math::vec::{Point3, Vec3};
use brepkit_topology::Topology;
use brepkit_topology::edge::EdgeId;
use brepkit_topology::face::{Face, FaceId, FaceSurface};
use brepkit_topology::vertex::VertexId;
use brepkit_topology::wire::{OrientedEdge, Wire, WireId};
use crate::dot_normal_point;
pub fn ring_point_positions(
topo: &Topology,
ring: &[VertexId],
) -> Result<Vec<Point3>, crate::OperationsError> {
ring.iter()
.map(|&vid| -> Result<Point3, crate::OperationsError> { Ok(topo.vertex(vid)?.point()) })
.collect()
}
const CAP_PLANARITY_TOL: f64 = 1e-6;
fn ring_scale(verts: &[Point3]) -> f64 {
let c = verts[0];
verts.iter().map(|p| (*p - c).length()).fold(0.0, f64::max)
}
fn ring_is_planar(cap_verts: &[Point3], outward: Vec3) -> bool {
let plane_pt = cap_verts[0];
let max_dev = cap_verts
.iter()
.map(|p| (*p - plane_pt).dot(outward).abs())
.fold(0.0, f64::max);
max_dev <= CAP_PLANARITY_TOL * ring_scale(cap_verts)
}
fn bilinear_cap_patch(corners: &[Point3]) -> Result<NurbsSurface, brepkit_math::MathError> {
NurbsSurface::new(
1,
1,
vec![0.0, 0.0, 1.0, 1.0],
vec![0.0, 0.0, 1.0, 1.0],
vec![vec![corners[0], corners[1]], vec![corners[3], corners[2]]],
vec![vec![1.0, 1.0], vec![1.0, 1.0]],
)
}
pub fn outward_normal(verts: &[Point3], toward: Vec3) -> Result<Vec3, crate::OperationsError> {
let n = crate::winding::newell_normal(verts).normalize()?;
Ok(if n.dot(toward) < 0.0 { -n } else { n })
}
pub fn build_cap_face(
topo: &mut Topology,
outer_ring_edges: &[EdgeId],
inner_wires: Vec<WireId>,
cap_verts: &[Point3],
outward: Vec3,
start_role: bool,
) -> Result<FaceId, crate::OperationsError> {
let n = outer_ring_edges.len();
if n < 3 || cap_verts.len() != n {
return Err(crate::OperationsError::InvalidInput {
reason: "cap ring must have at least 3 vertices matching its edge count".into(),
});
}
let ring_wire = |topo: &mut Topology, flip: bool| -> Result<_, crate::OperationsError> {
let edges: Vec<OrientedEdge> = if start_role == flip {
(0..n)
.map(|i| OrientedEdge::new(outer_ring_edges[i], true))
.collect()
} else {
(0..n)
.rev()
.map(|i| OrientedEdge::new(outer_ring_edges[i], false))
.collect()
};
Ok(topo.add_wire(Wire::new(edges, true).map_err(crate::OperationsError::Topology)?))
};
if ring_is_planar(cap_verts, outward) {
let wid = ring_wire(topo, false)?;
let surface = FaceSurface::Plane {
normal: outward,
d: dot_normal_point(outward, cap_verts[0]),
};
return Ok(topo.add_face(Face::new(wid, inner_wires, surface)));
}
if !inner_wires.is_empty() {
return Err(crate::OperationsError::InvalidInput {
reason: "cap with holes on a non-planar section boundary is not supported".into(),
});
}
if n != 4 {
return Err(crate::OperationsError::InvalidInput {
reason: "cap for a non-planar section boundary with more than 4 edges is not supported"
.into(),
});
}
let surf = bilinear_cap_patch(cap_verts).map_err(crate::OperationsError::Math)?;
let reversed = surf
.normal(0.5, 0.5)
.map(|nrm| nrm.dot(outward) < 0.0)
.unwrap_or(false);
let wid = ring_wire(topo, reversed)?;
let mut face = Face::new(wid, vec![], FaceSurface::Nurbs(surf));
if reversed {
face.set_reversed(true);
}
Ok(topo.add_face(face))
}