#![allow(clippy::wildcard_imports)]
use super::*;
pub(super) fn check_carrier_reachability(ir: &CadIr, findings: &mut Vec<Finding>) {
let mut surfaces = ir
.model
.faces
.iter()
.map(|face| face.surface.0.as_str())
.collect::<HashSet<_>>();
let mut curves = ir
.model
.edges
.iter()
.filter_map(|edge| edge.curve.as_ref().map(|id| id.0.as_str()))
.collect::<HashSet<_>>();
let pcurves = ir
.model
.coedges
.iter()
.filter_map(|coedge| coedge.pcurve.as_ref().map(|id| id.0.as_str()))
.collect::<HashSet<_>>();
let points = ir
.model
.vertices
.iter()
.map(|vertex| vertex.point.0.as_str())
.collect::<HashSet<_>>();
for procedural in &ir.model.procedural_surfaces {
surfaces.insert(&procedural.surface.0);
match &procedural.definition {
ProceduralSurfaceDefinition::Extrusion { directrix, .. }
| ProceduralSurfaceDefinition::Revolution { directrix, .. } => {
curves.insert(&directrix.0);
}
ProceduralSurfaceDefinition::Sweep { profile, spine } => {
curves.extend([profile.0.as_str(), spine.0.as_str()]);
}
ProceduralSurfaceDefinition::Offset { support, .. } => {
surfaces.insert(&support.0);
}
ProceduralSurfaceDefinition::Ruled { first, second } => {
curves.extend([first.0.as_str(), second.0.as_str()]);
}
ProceduralSurfaceDefinition::Blend {
supports, spine, ..
} => {
for support in supports.iter().flatten() {
surfaces.insert(&support.surface.0);
}
if let Some(spine) = spine {
curves.insert(&spine.0);
}
}
ProceduralSurfaceDefinition::Unknown { .. } => {}
}
}
for procedural in &ir.model.procedural_curves {
curves.insert(&procedural.curve.0);
match &procedural.definition {
ProceduralCurveDefinition::Exact | ProceduralCurveDefinition::Helix { .. } => {}
ProceduralCurveDefinition::Compound { components, .. } => {
curves.extend(components.iter().map(|component| component.0.as_str()));
}
ProceduralCurveDefinition::Intersection { context } => {
for side in &context.sides {
if let Some(surface) = &side.surface {
surfaces.insert(&surface.0);
}
}
}
ProceduralCurveDefinition::ThreeSurfaceIntersection { context, third, .. } => {
for side in context.sides.iter().chain(std::iter::once(third)) {
if let Some(surface) = &side.surface {
surfaces.insert(&surface.0);
}
}
}
ProceduralCurveDefinition::SurfaceCurve { context, .. } => {
for side in &context.sides {
if let Some(surface) = &side.surface {
surfaces.insert(&surface.0);
}
}
}
ProceduralCurveDefinition::Silhouette {
context,
cast_surface,
..
} => {
surfaces.insert(&cast_surface.0);
for side in &context.sides {
if let Some(surface) = &side.surface {
surfaces.insert(&surface.0);
}
}
}
ProceduralCurveDefinition::SurfaceOffset { context, base, .. } => {
curves.insert(&base.0);
for side in &context.sides {
if let Some(surface) = &side.surface {
surfaces.insert(&surface.0);
}
}
}
ProceduralCurveDefinition::Spring { context, .. } => {
for side in &context.sides {
if let Some(surface) = &side.surface {
surfaces.insert(&surface.0);
}
}
}
ProceduralCurveDefinition::Deformable { bend, data, .. } => {
curves.insert(&bend.0);
if let crate::geometry::DeformableCurveData::Surface { surface } = data {
surfaces.insert(&surface.0);
}
}
ProceduralCurveDefinition::Projection {
context, source, ..
} => {
curves.insert(&source.0);
for side in &context.sides {
if let Some(surface) = &side.surface {
surfaces.insert(&surface.0);
}
}
}
ProceduralCurveDefinition::Offset {
source, support, ..
} => {
curves.insert(&source.0);
if let Some(support) = support {
surfaces.insert(&support.0);
}
}
ProceduralCurveDefinition::TwoSidedOffset { context, .. } => {
for side in &context.sides {
if let Some(surface) = &side.surface {
surfaces.insert(surface.0.as_str());
}
}
}
ProceduralCurveDefinition::VectorOffset { source, .. } => {
curves.insert(&source.0);
}
ProceduralCurveDefinition::Subset { source, .. } => {
curves.insert(&source.0);
}
ProceduralCurveDefinition::BlendSpine { blend_surface } => {
if let Some(surface) = blend_surface {
surfaces.insert(&surface.0);
}
}
ProceduralCurveDefinition::Unknown { .. } => {}
}
}
for link in ir.unknowns.iter().flat_map(|record| &record.links) {
surfaces.insert(link);
curves.insert(link);
}
for (kind, id) in ir
.model
.surfaces
.iter()
.filter(|entity| !surfaces.contains(entity.id.0.as_str()))
.map(|entity| ("surface", entity.id.0.as_str()))
.chain(
ir.model
.curves
.iter()
.filter(|entity| !curves.contains(entity.id.0.as_str()))
.map(|entity| ("curve", entity.id.0.as_str())),
)
.chain(
ir.model
.pcurves
.iter()
.filter(|entity| !pcurves.contains(entity.id.0.as_str()))
.map(|entity| ("pcurve", entity.id.0.as_str())),
)
.chain(
ir.model
.points
.iter()
.filter(|entity| !points.contains(entity.id.0.as_str()))
.map(|entity| ("point", entity.id.0.as_str())),
)
{
findings.push(Finding {
check: Check::CarrierReachability,
severity: Severity::Error,
message: format!("orphan {kind} carrier"),
entity: Some(id.into()),
});
}
}
pub(super) fn check_parameter_domains(ir: &CadIr, findings: &mut Vec<Finding>) {
let curves = ir
.model
.curves
.iter()
.map(|curve| (curve.id.0.as_str(), &curve.geometry))
.collect::<HashMap<_, _>>();
for edge in &ir.model.edges {
let Some([start, end]) = edge.param_range else {
continue;
};
let mut valid = start.is_finite() && end.is_finite() && start <= end;
if let Some(curve) = edge.curve.as_ref().and_then(|id| curves.get(id.0.as_str())) {
let tau = std::f64::consts::TAU;
match curve {
CurveGeometry::Circle { .. } | CurveGeometry::Ellipse { .. } => {
valid &= (0.0..tau).contains(&start) && end - start <= tau;
}
CurveGeometry::Nurbs(nurbs) => {
if let (Some(first), Some(last)) = (nurbs.knots.first(), nurbs.knots.last()) {
valid &= start >= *first && end <= *last;
}
}
_ => {}
}
}
if !valid {
findings.push(Finding {
check: Check::ParameterDomain,
severity: Severity::Error,
message: "edge parameter range is outside its canonical carrier domain".into(),
entity: Some(edge.id.0.clone()),
});
}
}
}