use crate::output::path::{CubicSegment, SmoothPath};
use crate::process::Processor;
use crate::process::corner::{SmoothCornerGeometry, SmoothCornerProcessor, SmoothCornerRequest};
use crate::types::Point;
use crate::utils::EPSILON;
type CornerPath = (SmoothCornerGeometry, Vec<CubicSegment>);
pub(super) fn build_smooth_frame_path(points: &[Point], radius: f64, smoothing: f64) -> SmoothPath {
let corners = collect_corner_paths(points, radius, smoothing);
if corners
.iter()
.all(|(geometry, _)| geometry.radius <= EPSILON)
{
return sharp_path(points);
}
build_path_from_corners(&corners)
}
fn sharp_path(points: &[Point]) -> SmoothPath {
let mut path = SmoothPath::new();
path.move_to(points[0]);
for point in points.iter().copied().skip(1) {
path.line_to(point);
}
path.close();
path
}
fn collect_corner_paths(points: &[Point], radius: f64, smoothing: f64) -> Vec<CornerPath> {
let mut corners = Vec::with_capacity(points.len());
for index in 0..points.len() {
let prev = points[(index + points.len() - 1) % points.len()];
let origin = points[index];
let next = points[(index + 1) % points.len()];
let incoming = prev - origin;
let outgoing = next - origin;
let incoming_length = incoming.length();
let outgoing_length = outgoing.length();
let request = SmoothCornerRequest {
origin,
incoming_axis: incoming
.normalized()
.expect("输入层已保证 frame 不含退化边"),
outgoing_axis: outgoing
.normalized()
.expect("输入层已保证 frame 不含退化边"),
radius,
smoothing,
incoming_limit: incoming_length / 2.0,
outgoing_limit: outgoing_length / 2.0,
angle: incoming
.angle_between(outgoing)
.expect("输入层已保证 frame 不含退化角"),
};
let processed = SmoothCornerProcessor::new(request).process();
corners.push((processed.geometry, processed.cubics));
}
corners
}
fn build_path_from_corners(corners: &[CornerPath]) -> SmoothPath {
let mut path = SmoothPath::new();
path.move_to(corners[0].0.end);
for (geometry, cubics) in corners.iter().skip(1) {
path.line_to(geometry.start);
push_cubics(&mut path, cubics);
}
path.line_to(corners[0].0.start);
push_cubics(&mut path, &corners[0].1);
path.close();
path
}
fn push_cubics(path: &mut SmoothPath, cubics: &[CubicSegment]) {
for cubic in cubics {
path.cubic_to(cubic.ctrl1, cubic.ctrl2, cubic.to);
}
}