use crate::nurbs::reader::LEN_TO_MM;
use crate::nurbs::toks::Cur;
use crate::sab::Token;
use cadmpeg_ir::geometry::pcurve::PcurveNurbs;
use cadmpeg_ir::math::Point2;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum NativeChart {
Plane {
reverse_v: bool,
scale: f64,
},
Cone {
axial_scale: f64,
},
LatitudeFirst {
reverse_v: bool,
},
}
impl NativeChart {
#[must_use]
pub fn cone(cosine: f64, u_scale: f64, reverse_u: bool) -> Option<Self> {
let axial_scale = sense_sign(reverse_u) * cosine.abs() * u_scale * LEN_TO_MM;
(axial_scale.is_finite() && axial_scale != 0.0).then_some(Self::Cone { axial_scale })
}
#[must_use]
pub fn frame_point(self, native: Point2) -> Point2 {
match self {
Self::Plane { reverse_v, scale } => Point2::new(
native.u * scale * LEN_TO_MM,
native.v * sense_sign(reverse_v) * scale * LEN_TO_MM,
),
Self::Cone { axial_scale } => Point2::new(native.v, native.u * axial_scale),
Self::LatitudeFirst { reverse_v } => {
Point2::new(native.v * sense_sign(reverse_v), native.u)
}
}
}
#[must_use]
pub fn native_point(self, frame: Point2) -> Point2 {
match self {
Self::Plane { reverse_v, scale } => Point2::new(
frame.u / (scale * LEN_TO_MM),
frame.v * sense_sign(reverse_v) / (scale * LEN_TO_MM),
),
Self::Cone { axial_scale } => Point2::new(frame.v / axial_scale, frame.u),
Self::LatitudeFirst { reverse_v } => {
Point2::new(frame.v, frame.u * sense_sign(reverse_v))
}
}
}
}
#[must_use]
pub fn plane_record_chart(head: &str, tokens: &[Token]) -> Option<NativeChart> {
if head != "plane" {
return None;
}
match SupportChart::of_surface_record(head, tokens)? {
SupportChart::Native {
chart: chart @ NativeChart::Plane { .. },
..
} => Some(chart),
_ => None,
}
}
fn sense_sign(reversed: bool) -> f64 {
if reversed {
-1.0
} else {
1.0
}
}
pub(crate) fn cone_is_circular_cylinder(sine: f64, ratio: f64) -> bool {
sine.abs() <= f64::EPSILON && ratio == 1.0
}
pub(crate) fn cone_axis_reversed(sine: f64, cosine: f64, ratio: f64) -> bool {
!cone_is_circular_cylinder(sine, ratio) && sine * cosine < 0.0
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum FrameMap {
Frame,
Negated,
Torus {
opposite_major: bool,
opposite_minor: bool,
},
}
impl FrameMap {
fn apply(self, frame: Point2) -> Point2 {
match self {
Self::Frame => frame,
Self::Negated => Point2::new(-frame.u, -frame.v),
Self::Torus {
opposite_major,
opposite_minor,
} => {
let (mut u, mut v) = (frame.u, frame.v);
if opposite_major {
(u, v) = (u + std::f64::consts::PI, std::f64::consts::PI - v);
}
if opposite_minor {
v += std::f64::consts::PI;
}
Point2::new(u, v)
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum SupportChart {
Canonical,
Native { chart: NativeChart, map: FrameMap },
}
impl SupportChart {
pub(crate) fn plane(reverse_v: bool, scale: f64) -> Option<Self> {
(scale.is_finite() && scale > 0.0).then_some(Self::Native {
chart: NativeChart::Plane { reverse_v, scale },
map: FrameMap::Frame,
})
}
pub(crate) fn sphere(reverse_v: bool) -> Self {
Self::Native {
chart: NativeChart::LatitudeFirst { reverse_v },
map: FrameMap::Frame,
}
}
pub(crate) fn torus(major: f64, minor: f64, reverse_v: bool) -> Self {
Self::Native {
chart: NativeChart::LatitudeFirst { reverse_v },
map: if major < 0.0 || minor < 0.0 {
FrameMap::Torus {
opposite_major: major < 0.0,
opposite_minor: minor < 0.0,
}
} else {
FrameMap::Frame
},
}
}
pub(crate) fn cone(
sine: f64,
cosine: f64,
ratio: f64,
u_scale: f64,
reverse_u: bool,
) -> Option<Self> {
Some(Self::Native {
chart: NativeChart::cone(cosine, u_scale, reverse_u)?,
map: if cone_axis_reversed(sine, cosine, ratio) {
FrameMap::Negated
} else {
FrameMap::Frame
},
})
}
pub(crate) fn of_surface_record(head: &str, tokens: &[Token]) -> Option<Self> {
if !matches!(head, "plane" | "cone" | "sphere" | "torus") {
return Some(Self::Canonical);
}
let start = tokens
.iter()
.position(|token| matches!(token, Token::Position(_)))
.unwrap_or(tokens.len());
crate::nurbs::proc_curve::analytic_support(head, &mut Cur::at(tokens, start))
.map(|(_, chart)| chart)
}
fn neutral_point(self, native: Point2) -> Point2 {
match self {
Self::Canonical => native,
Self::Native { chart, map } => map.apply(chart.frame_point(native)),
}
}
pub(crate) fn normalize(self, pcurve: &mut PcurveNurbs) -> Option<()> {
if let Self::Native { .. } = self {
pcurve
.try_map_control_points(|_, point| {
cadmpeg_ir::units::FinitePoint2::new(self.neutral_point(point.get())).ok_or(())
})
.ok()?;
}
Some(())
}
}
#[cfg(test)]
mod tests;