use brepkit_math::vec::{Point3, Vec3};
use brepkit_topology::Topology;
use brepkit_topology::face::FaceId;
use super::checks::{CheckId, EntityRef, Severity, ValidationIssue};
use crate::CheckError;
pub fn check_face_has_surface(
topo: &Topology,
face_id: FaceId,
) -> Result<Vec<ValidationIssue>, CheckError> {
let _face = topo.face(face_id)?;
Ok(vec![])
}
pub fn check_face_orientation(
topo: &Topology,
face_id: FaceId,
) -> Result<Vec<ValidationIssue>, CheckError> {
let polygon = crate::util::face_polygon(topo, face_id)?;
if polygon.len() < 3 {
return Ok(vec![]); }
let wire_normal = newell_normal(&polygon);
if wire_normal.length() < 1e-15 {
return Ok(vec![]); }
let face = topo.face(face_id)?;
let centroid = polygon_centroid(&polygon);
let surface_normal = if let Some((u, v)) = face.surface().project_point(centroid) {
let n = face.surface().normal(u, v);
if face.is_reversed() { -n } else { n }
} else {
let n = face.surface().normal(0.0, 0.0);
if face.is_reversed() { -n } else { n }
};
let dot = wire_normal.dot(surface_normal);
if dot < -0.1 {
return Ok(vec![ValidationIssue {
check: CheckId::FaceOrientationConsistency,
severity: Severity::Warning,
entity: EntityRef::Face(face_id),
description: format!("face normal inconsistent with wire winding (dot={dot:.3})"),
deviation: Some(dot.abs()),
}]);
}
Ok(vec![])
}
fn newell_normal(verts: &[Point3]) -> Vec3 {
crate::util::polygon_normal(verts)
}
fn polygon_centroid(verts: &[Point3]) -> Point3 {
let n = verts.len() as f64;
let sx: f64 = verts.iter().map(|v| v.x()).sum();
let sy: f64 = verts.iter().map(|v| v.y()).sum();
let sz: f64 = verts.iter().map(|v| v.z()).sum();
Point3::new(sx / n, sy / n, sz / n)
}