Skip to main content

smooth_frame/input/
corner.rs

1use std::f64::consts::PI;
2
3use crate::errors::SmoothError;
4use crate::output::path::CubicSegment;
5use crate::process::Processor;
6use crate::process::corner::{SmoothCornerGeometry, SmoothCornerProcessor, SmoothCornerRequest};
7use crate::types::{Point, Vector};
8use crate::utils::{EPSILON, clamp01};
9
10/// 任意凸角的 Sketch-like smooth corner 输入入口。
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub struct SmoothCorner {
13    origin: Point,
14    incoming_axis: Vector,
15    outgoing_axis: Vector,
16    radius: f64,
17    smoothing: f64,
18    incoming_limit: f64,
19    outgoing_limit: f64,
20}
21
22impl SmoothCorner {
23    /// 创建一个任意凸角的 smooth corner。
24    ///
25    /// `incoming_axis` 从角点指向上一条边,`outgoing_axis` 从角点指向下一条边。
26    #[must_use]
27    pub fn new(origin: Point, incoming_axis: Vector, outgoing_axis: Vector) -> Self {
28        Self {
29            origin,
30            incoming_axis,
31            outgoing_axis,
32            radius: 0.0,
33            smoothing: 0.0,
34            incoming_limit: f64::INFINITY,
35            outgoing_limit: f64::INFINITY,
36        }
37    }
38
39    /// 设置核心圆半径。
40    #[must_use]
41    pub fn with_radius(mut self, radius: f64) -> Self {
42        self.radius = radius;
43        self
44    }
45
46    /// 设置 Sketch-like smoothing,生成前会 clamp 到 `[0, 1]`。
47    #[must_use]
48    pub fn with_smoothing(mut self, smoothing: f64) -> Self {
49        self.smoothing = smoothing;
50        self
51    }
52
53    /// 设置 incoming / outgoing 两侧可占用的最大长度。
54    #[must_use]
55    pub fn with_limits(mut self, incoming_limit: f64, outgoing_limit: f64) -> Self {
56        self.incoming_limit = incoming_limit;
57        self.outgoing_limit = outgoing_limit;
58        self
59    }
60
61    /// 解析 smooth corner 的几何参数。
62    pub fn geometry(&self) -> Result<SmoothCornerGeometry, SmoothError> {
63        Ok(SmoothCornerProcessor::new(self.to_request()?)
64            .process()
65            .geometry)
66    }
67
68    /// 生成最多 3 段 cubic Bezier。
69    ///
70    /// 半径为 0 或被限制压缩到 0 时返回空数组。
71    pub fn to_cubics(&self) -> Result<Vec<CubicSegment>, SmoothError> {
72        Ok(SmoothCornerProcessor::new(self.to_request()?)
73            .process()
74            .cubics)
75    }
76
77    // 校验输入并转换成处理层使用的规范化请求。
78    fn to_request(&self) -> Result<SmoothCornerRequest, SmoothError> {
79        validate_corner_inputs(self)?;
80
81        let incoming_axis = self
82            .incoming_axis
83            .normalized()
84            .ok_or(SmoothError::DegenerateAxis)?;
85        let outgoing_axis = self
86            .outgoing_axis
87            .normalized()
88            .ok_or(SmoothError::DegenerateAxis)?;
89        let angle = incoming_axis
90            .angle_between(outgoing_axis)
91            .ok_or(SmoothError::DegenerateAxis)?;
92
93        if angle <= EPSILON || PI - angle <= EPSILON {
94            return Err(SmoothError::InvalidAngle);
95        }
96
97        Ok(SmoothCornerRequest {
98            origin: self.origin,
99            incoming_axis,
100            outgoing_axis,
101            radius: self.radius,
102            smoothing: clamp01(self.smoothing),
103            incoming_limit: self.incoming_limit,
104            outgoing_limit: self.outgoing_limit,
105            angle,
106        })
107    }
108}
109
110// 校验 smooth corner 输入是否为有限且非负。
111fn validate_corner_inputs(corner: &SmoothCorner) -> Result<(), SmoothError> {
112    if !corner.origin.is_finite()
113        || !corner.incoming_axis.is_finite()
114        || !corner.outgoing_axis.is_finite()
115        || !corner.radius.is_finite()
116        || !corner.smoothing.is_finite()
117        || corner.incoming_limit.is_nan()
118        || corner.outgoing_limit.is_nan()
119    {
120        return Err(SmoothError::NonFiniteInput);
121    }
122    if corner.radius < 0.0 || corner.incoming_limit < 0.0 || corner.outgoing_limit < 0.0 {
123        return Err(SmoothError::NegativeInput);
124    }
125    Ok(())
126}