use axiolid_core::{Point2, Point3, Scalar, Vec3};
use axiolid_curve::Curve2;
use axiolid_model::{GeometryGraph, GeometryNode, NodeId};
use axiolid_profile::{Contour, Profile};
pub(super) fn extrusion(
graph: &GeometryGraph,
profile: NodeId,
direction: Vec3,
depth: Scalar,
) -> Option<Vec<Point3>> {
let GeometryNode::Profile(profile) = graph.get(profile)? else {
return None;
};
if !depth.is_finite() || depth <= 0.0 || !direction.is_finite() {
return None;
}
let offset = direction.try_normalize()? * depth;
let outline = outline(profile)?;
let base = outline.iter().map(|p| Point3::new(p.x, p.y, 0.0));
let top = base.clone().map(|p| p + offset);
Some(base.chain(top).collect())
}
pub(super) fn block(x: Scalar, y: Scalar, z: Scalar) -> Option<[Point3; 8]> {
if ![x, y, z].iter().all(|v| v.is_finite() && *v > 0.0) {
return None;
}
let (hx, hy, hz) = (x / 2.0, y / 2.0, z / 2.0);
Some([
Point3::new(-hx, -hy, -hz),
Point3::new(hx, -hy, -hz),
Point3::new(-hx, hy, -hz),
Point3::new(hx, hy, -hz),
Point3::new(-hx, -hy, hz),
Point3::new(hx, -hy, hz),
Point3::new(-hx, hy, hz),
Point3::new(hx, hy, hz),
])
}
fn outline(profile: &Profile) -> Option<Vec<Point2>> {
match profile {
Profile::Rectangle(rectangle) => {
let rounded = rectangle.outer_radius.is_some_and(|r| r != 0.0);
let (x, y) = (rectangle.x, rectangle.y);
if rounded || !(x.is_finite() && y.is_finite() && x > 0.0 && y > 0.0) {
return None;
}
let (hx, hy) = (x / 2.0, y / 2.0);
Some(vec![
Point2::new(-hx, -hy),
Point2::new(hx, -hy),
Point2::new(hx, hy),
Point2::new(-hx, hy),
])
}
Profile::Contour(contour) => straight_contour(&contour.outer),
Profile::Derived { basis, transform } => Some(
outline(basis)?
.into_iter()
.map(|p| transform.transform_point2(p))
.collect(),
),
_ => None,
}
}
fn straight_contour(contour: &Contour) -> Option<Vec<Point2>> {
let mut points = Vec::with_capacity(contour.segments.len() * 2);
for segment in &contour.segments {
let Curve2::Line(line) = &segment.curve else {
return None;
};
let domain = segment.domain;
points.push(line.origin + line.direction * domain.start);
points.push(line.origin + line.direction * domain.end);
}
let finite = points.iter().all(|p| p.is_finite());
(finite && contour.segments.len() >= 3).then_some(points)
}