use std::f64::consts::PI;
use crate::errors::SmoothError;
use crate::output::path::CubicSegment;
use crate::process::Processor;
use crate::process::corner::{SmoothCornerGeometry, SmoothCornerProcessor, SmoothCornerRequest};
use crate::types::{Point, Vector};
use crate::utils::{EPSILON, clamp01};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SmoothCorner {
origin: Point,
incoming_axis: Vector,
outgoing_axis: Vector,
radius: f64,
smoothing: f64,
incoming_limit: f64,
outgoing_limit: f64,
}
impl SmoothCorner {
#[must_use]
pub fn new(origin: Point, incoming_axis: Vector, outgoing_axis: Vector) -> Self {
Self {
origin,
incoming_axis,
outgoing_axis,
radius: 0.0,
smoothing: 0.0,
incoming_limit: f64::INFINITY,
outgoing_limit: f64::INFINITY,
}
}
#[must_use]
pub fn with_radius(mut self, radius: f64) -> Self {
self.radius = radius;
self
}
#[must_use]
pub fn with_smoothing(mut self, smoothing: f64) -> Self {
self.smoothing = smoothing;
self
}
#[must_use]
pub fn with_limits(mut self, incoming_limit: f64, outgoing_limit: f64) -> Self {
self.incoming_limit = incoming_limit;
self.outgoing_limit = outgoing_limit;
self
}
pub fn geometry(&self) -> Result<SmoothCornerGeometry, SmoothError> {
Ok(SmoothCornerProcessor::new(self.to_request()?)
.process()
.geometry)
}
pub fn to_cubics(&self) -> Result<Vec<CubicSegment>, SmoothError> {
Ok(SmoothCornerProcessor::new(self.to_request()?)
.process()
.cubics)
}
fn to_request(&self) -> Result<SmoothCornerRequest, SmoothError> {
validate_corner_inputs(self)?;
let incoming_axis = self
.incoming_axis
.normalized()
.ok_or(SmoothError::DegenerateAxis)?;
let outgoing_axis = self
.outgoing_axis
.normalized()
.ok_or(SmoothError::DegenerateAxis)?;
let angle = incoming_axis
.angle_between(outgoing_axis)
.ok_or(SmoothError::DegenerateAxis)?;
if angle <= EPSILON || PI - angle <= EPSILON {
return Err(SmoothError::InvalidAngle);
}
Ok(SmoothCornerRequest {
origin: self.origin,
incoming_axis,
outgoing_axis,
radius: self.radius,
smoothing: clamp01(self.smoothing),
incoming_limit: self.incoming_limit,
outgoing_limit: self.outgoing_limit,
angle,
})
}
}
fn validate_corner_inputs(corner: &SmoothCorner) -> Result<(), SmoothError> {
if !corner.origin.is_finite()
|| !corner.incoming_axis.is_finite()
|| !corner.outgoing_axis.is_finite()
|| !corner.radius.is_finite()
|| !corner.smoothing.is_finite()
|| corner.incoming_limit.is_nan()
|| corner.outgoing_limit.is_nan()
{
return Err(SmoothError::NonFiniteInput);
}
if corner.radius < 0.0 || corner.incoming_limit < 0.0 || corner.outgoing_limit < 0.0 {
return Err(SmoothError::NegativeInput);
}
Ok(())
}