use crate::coords::CoordSystem;
use crate::error::{Error, Result};
use crate::math::{BoundingBox, Vec3};
use crate::param::{ParameterSet, Scalar, ScalarPoint};
use super::geom::{dist_point_segment, tol};
fn polyline_dist(points: &[ScalarPoint], p: Vec3, cs: CoordSystem) -> f64 {
let pts: Vec<Vec3> = points.iter().map(|q| cs.to_cartesian(q.value())).collect();
match pts.len() {
0 => f64::INFINITY,
1 => (p - pts[0]).norm(),
_ => pts
.windows(2)
.map(|w| dist_point_segment(p, w[0], w[1]))
.fold(f64::INFINITY, f64::min),
}
}
fn polyline_bbox(points: &[ScalarPoint], cs: CoordSystem) -> BoundingBox {
BoundingBox::from_points(points.iter().map(|q| cs.to_cartesian(q.value())))
}
fn polyline_scale(points: &[ScalarPoint]) -> f64 {
points
.iter()
.flat_map(|q| q.value().0)
.fold(0.0, |m, c| m.max(c.abs()))
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Curve {
pub points: Vec<ScalarPoint>,
}
impl Curve {
pub fn new<P: Into<ScalarPoint>>(points: impl IntoIterator<Item = P>) -> Self {
Curve {
points: points.into_iter().map(Into::into).collect(),
}
}
pub(crate) fn inside(&self, _p: Vec3) -> bool {
false
}
pub(crate) fn bbox(&self, cs: CoordSystem) -> BoundingBox {
polyline_bbox(&self.points, cs)
}
pub(crate) fn dimension(&self) -> usize {
if self.points.len() >= 2 { 1 } else { 0 }
}
pub(crate) fn update(&mut self, p: &ParameterSet) -> Result<()> {
self.points.iter_mut().try_for_each(|q| q.update(p))
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Wire {
pub points: Vec<ScalarPoint>,
pub radius: Scalar,
}
impl Wire {
pub fn new<P: Into<ScalarPoint>>(
points: impl IntoIterator<Item = P>,
radius: impl Into<Scalar>,
) -> Self {
Wire {
points: points.into_iter().map(Into::into).collect(),
radius: radius.into(),
}
}
pub(crate) fn inside(&self, p: Vec3, cs: CoordSystem) -> bool {
let r = self.radius.value();
polyline_dist(&self.points, p, cs) <= r + tol(polyline_scale(&self.points), r)
}
pub(crate) fn bbox(&self, cs: CoordSystem) -> BoundingBox {
let b = polyline_bbox(&self.points, cs);
if b.is_empty() {
b
} else {
b.expanded(self.radius.value())
}
}
pub(crate) fn dimension(&self) -> usize {
if self.radius.value() > 0.0 {
3
} else if self.points.len() >= 2 {
1
} else {
0
}
}
pub(crate) fn update(&mut self, p: &ParameterSet) -> Result<()> {
self.points.iter_mut().try_for_each(|q| q.update(p))?;
self.radius.update(p)?;
if self.radius.value() < 0.0 {
return Err(Error::Invalid(format!(
"negative wire radius {}",
self.radius.value()
)));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct PointShape {
pub point: ScalarPoint,
}
impl PointShape {
pub fn new(p: impl Into<ScalarPoint>) -> Self {
PointShape { point: p.into() }
}
pub(crate) fn inside(&self, _p: Vec3) -> bool {
false
}
pub(crate) fn bbox(&self, cs: CoordSystem) -> BoundingBox {
let q = cs.to_cartesian(self.point.value());
BoundingBox::from_corners(q, q)
}
pub(crate) fn update(&mut self, p: &ParameterSet) -> Result<()> {
self.point.update(p)
}
}