use super::analytic::{
CircleCurve, ConeSurface, CylinderSurface, DegenerateCurve, HyperbolaCurve, LineCurve,
ParabolaCurve, PlaneSurface, SphereSurface, TorusSurface,
};
use super::nurbs::NurbsError;
use super::sampled::GeometryLayoutError;
use super::{PlacedCurve, PlacedSurface, SolvedCurveGeometry, SolvedSurfaceGeometry};
use crate::features::FinitePoint3;
use crate::scalar::{NonZeroLength, PositiveLength, PositiveReal};
#[derive(Debug, Clone, PartialEq)]
pub enum ScaleRefusal {
Field(&'static str),
ControlPoints(NurbsError),
Samples(GeometryLayoutError),
Translation,
}
enum ScalingError {
Geometry(ScaleRefusal),
Resource(cadmpeg_core::CodecError),
}
impl From<ScaleRefusal> for ScalingError {
fn from(error: ScaleRefusal) -> Self {
Self::Geometry(error)
}
}
impl From<cadmpeg_core::CodecError> for ScalingError {
fn from(error: cadmpeg_core::CodecError) -> Self {
Self::Resource(error)
}
}
fn scaled_point(
point: FinitePoint3,
scale: PositiveReal,
field: &'static str,
) -> Result<FinitePoint3, ScaleRefusal> {
point.scaled(scale).ok_or(ScaleRefusal::Field(field))
}
fn scaled_positive(
length: PositiveLength,
scale: PositiveReal,
field: &'static str,
) -> Result<PositiveLength, ScaleRefusal> {
PositiveLength::new(length.get() * scale.get()).ok_or(ScaleRefusal::Field(field))
}
fn scaled_nonzero(
length: NonZeroLength,
scale: PositiveReal,
field: &'static str,
) -> Result<NonZeroLength, ScaleRefusal> {
NonZeroLength::new(length.get() * scale.get()).ok_or(ScaleRefusal::Field(field))
}
impl SolvedSurfaceGeometry {
pub fn scaled(
&self,
ctx: &cadmpeg_core::decode::DecodeContext<'_>,
scale: PositiveReal,
) -> Result<Result<Self, ScaleRefusal>, cadmpeg_core::CodecError> {
self.clone().scaled_owned(ctx, scale)
}
}
impl SolvedCurveGeometry {
pub fn scaled(
&self,
ctx: &cadmpeg_core::decode::DecodeContext<'_>,
scale: PositiveReal,
) -> Result<Result<Self, ScaleRefusal>, cadmpeg_core::CodecError> {
self.clone().scaled_owned(ctx, scale)
}
}
impl SolvedCurveGeometry {
pub fn scaled_owned(
mut self,
ctx: &cadmpeg_core::decode::DecodeContext<'_>,
scale: PositiveReal,
) -> Result<Result<Self, ScaleRefusal>, cadmpeg_core::CodecError> {
match self.scale_in_place(ctx, scale) {
Ok(()) => Ok(Ok(self)),
Err(ScalingError::Geometry(error)) => Ok(Err(error)),
Err(ScalingError::Resource(error)) => Err(error),
}
}
fn scale_in_place(
&mut self,
ctx: &cadmpeg_core::decode::DecodeContext<'_>,
scale: PositiveReal,
) -> Result<(), ScalingError> {
let scaled = match self {
Self::Line(line) => Self::Line(LineCurve::new(
scaled_point(
line.origin(),
scale,
"scaled LineCurve.origin must be finite",
)?,
line.direction(),
)),
Self::Circle(circle) => {
let center =
scaled_point(circle.center(), scale, "CircleCurve.center must be finite")?;
let radius = scaled_positive(
circle.radius(),
scale,
"CircleCurve.radius must be positive and finite",
)?;
Self::Circle(CircleCurve::new(center, *circle.frame(), radius))
}
Self::Ellipse(ellipse) => {
Self::Ellipse(ellipse.scaled(scale).map_err(ScaleRefusal::Field)?)
}
Self::Parabola(parabola) => {
let vertex = scaled_point(
parabola.vertex(),
scale,
"ParabolaCurve.vertex must be finite",
)?;
let focal_distance = scaled_positive(
parabola.focal_distance(),
scale,
"ParabolaCurve.focal_distance must be positive and finite",
)?;
Self::Parabola(ParabolaCurve::new(
vertex,
*parabola.frame(),
focal_distance,
))
}
Self::Hyperbola(hyperbola) => {
let center = scaled_point(
hyperbola.center(),
scale,
"HyperbolaCurve.center must be finite",
)?;
let major_radius = scaled_positive(
hyperbola.major_radius(),
scale,
"HyperbolaCurve.major_radius must be positive and finite",
)?;
let minor_radius = scaled_positive(
hyperbola.minor_radius(),
scale,
"HyperbolaCurve.minor_radius must be positive and finite",
)?;
Self::Hyperbola(HyperbolaCurve::new(
center,
*hyperbola.frame(),
major_radius,
minor_radius,
))
}
Self::Degenerate(degenerate) => Self::Degenerate(DegenerateCurve::new(scaled_point(
degenerate.point(),
scale,
"DegenerateCurve.point must be finite",
)?)),
Self::Nurbs(value) => {
value
.scale_points(scale)
.map_err(ScaleRefusal::ControlPoints)?;
return Ok(());
}
Self::Polyline(value) => {
value.scale_points(scale).map_err(ScaleRefusal::Samples)?;
return Ok(());
}
Self::Transformed(placed) => {
placed.scale_in_place(ctx, scale)?;
return Ok(());
}
Self::Composite { .. } | Self::Unknown { .. } => return Ok(()),
};
*self = scaled;
Ok(())
}
}
impl SolvedSurfaceGeometry {
pub fn scaled_owned(
mut self,
ctx: &cadmpeg_core::decode::DecodeContext<'_>,
scale: PositiveReal,
) -> Result<Result<Self, ScaleRefusal>, cadmpeg_core::CodecError> {
match self.scale_in_place(ctx, scale) {
Ok(()) => Ok(Ok(self)),
Err(ScalingError::Geometry(error)) => Ok(Err(error)),
Err(ScalingError::Resource(error)) => Err(error),
}
}
fn scale_in_place(
&mut self,
ctx: &cadmpeg_core::decode::DecodeContext<'_>,
scale: PositiveReal,
) -> Result<(), ScalingError> {
let scaled = match self {
Self::Plane(plane) => Self::Plane(PlaneSurface::new(
scaled_point(plane.origin(), scale, "PlaneSurface.origin must be finite")?,
*plane.frame(),
)),
Self::Cylinder(cylinder) => {
let origin = scaled_point(
cylinder.origin(),
scale,
"CylinderSurface.origin must be finite",
)?;
let radius = scaled_positive(
cylinder.radius(),
scale,
"CylinderSurface.radius must be positive and finite",
)?;
Self::Cylinder(CylinderSurface::new(origin, *cylinder.frame(), radius))
}
Self::Cone(cone) => {
let origin =
scaled_point(cone.origin(), scale, "ConeSurface.origin must be finite")?;
let radius = cone.radius().scaled(scale).ok_or(ScaleRefusal::Field(
"ConeSurface.radius must be nonnegative and finite",
))?;
Self::Cone(ConeSurface::new(
origin,
*cone.frame(),
radius,
cone.ratio(),
cone.half_angle(),
))
}
Self::Sphere(sphere) => {
let center = scaled_point(
sphere.center(),
scale,
"SphereSurface.center must be finite",
)?;
let radius = scaled_nonzero(
sphere.radius(),
scale,
"SphereSurface.radius must be finite and nonzero",
)?;
Self::Sphere(SphereSurface::new(center, *sphere.frame(), radius))
}
Self::Torus(torus) => {
let center =
scaled_point(torus.center(), scale, "TorusSurface.center must be finite")?;
let major_radius = scaled_positive(
torus.major_radius(),
scale,
"TorusSurface.major_radius must be positive and finite",
)?;
let minor_radius = scaled_nonzero(
torus.minor_radius(),
scale,
"TorusSurface.minor_radius must be finite and nonzero",
)?;
Self::Torus(TorusSurface::new(
center,
*torus.frame(),
major_radius,
minor_radius,
))
}
Self::Nurbs(value) => {
value
.scale_points(scale)
.map_err(ScaleRefusal::ControlPoints)?;
return Ok(());
}
Self::Polygonal(value) => {
value.scale_points(scale).map_err(ScaleRefusal::Samples)?;
return Ok(());
}
Self::Transformed(placed) => {
placed.scale_in_place(ctx, scale)?;
return Ok(());
}
Self::Unknown { .. } => return Ok(()),
};
*self = scaled;
Ok(())
}
}
impl PlacedCurve {
fn scale_in_place(
&mut self,
ctx: &cadmpeg_core::decode::DecodeContext<'_>,
scale: PositiveReal,
) -> Result<(), ScalingError> {
let _depth = ctx.enter_nested("IR geometry unit scaling nesting")?;
if let SolvedCurveGeometry::Transformed(placed) = self.basis.as_mut() {
placed.scale_in_place(ctx, scale)?;
} else {
self.basis.scale_in_place(ctx, scale)?;
}
self.transform = self
.transform
.scaled_translation(scale)
.ok_or(ScaleRefusal::Translation)?;
Ok(())
}
}
impl PlacedSurface {
fn scale_in_place(
&mut self,
ctx: &cadmpeg_core::decode::DecodeContext<'_>,
scale: PositiveReal,
) -> Result<(), ScalingError> {
let _depth = ctx.enter_nested("IR geometry unit scaling nesting")?;
if let SolvedSurfaceGeometry::Transformed(placed) = self.basis.as_mut() {
placed.scale_in_place(ctx, scale)?;
} else {
self.basis.scale_in_place(ctx, scale)?;
}
self.transform = self
.transform
.scaled_translation(scale)
.ok_or(ScaleRefusal::Translation)?;
Ok(())
}
}
#[cfg(test)]
mod tests;