use kurbo::{Arc, BezPath, Point, Shape, Vec2};
const ARC_TOLERANCE: f64 = 0.1;
pub fn arc_path(center: Point, radius: f64, start_angle: f64, sweep_angle: f64) -> BezPath {
let arc = Arc::new(
center,
Vec2::new(radius, radius),
start_angle,
sweep_angle,
0.0,
);
arc.to_path(ARC_TOLERANCE)
}
#[cfg(test)]
mod tests {
use super::*;
use kurbo::PathEl;
use std::f64::consts::PI;
#[test]
fn arc_path_starts_with_a_move_to() {
let path = arc_path(Point::new(10.0, 10.0), 5.0, 0.0, PI / 2.0);
assert!(matches!(path.elements().first(), Some(PathEl::MoveTo(_))));
}
#[test]
fn arc_path_produces_curve_segments_for_a_quarter_turn() {
let path = arc_path(Point::new(0.0, 0.0), 10.0, 0.0, PI / 2.0);
let curve_count = path
.elements()
.iter()
.filter(|el| matches!(el, PathEl::CurveTo(..) | PathEl::QuadTo(..)))
.count();
assert!(curve_count >= 1, "expected at least one curve segment");
}
#[test]
fn full_circle_produces_more_segments_than_a_quarter_turn() {
let quarter = arc_path(Point::new(0.0, 0.0), 10.0, 0.0, PI / 2.0);
let full = arc_path(Point::new(0.0, 0.0), 10.0, 0.0, 2.0 * PI);
let count = |p: &BezPath| {
p.elements()
.iter()
.filter(|el| matches!(el, PathEl::CurveTo(..) | PathEl::QuadTo(..)))
.count()
};
assert!(count(&full) > count(&quarter));
}
#[test]
fn arc_path_endpoints_lie_on_the_circle() {
let center = Point::new(3.0, 4.0);
let radius = 7.0;
let path = arc_path(center, radius, 0.0, PI);
let start = match path.elements().first() {
Some(PathEl::MoveTo(p)) => *p,
other => panic!("expected MoveTo, got {other:?}"),
};
let end = match path.elements().last() {
Some(PathEl::CurveTo(_, _, p)) => *p,
Some(PathEl::QuadTo(_, p)) => *p,
Some(PathEl::LineTo(p)) => *p,
other => panic!("expected a terminal segment, got {other:?}"),
};
let dist = |p: Point| ((p.x - center.x).powi(2) + (p.y - center.y).powi(2)).sqrt();
assert!((dist(start) - radius).abs() < 1e-6);
assert!((dist(end) - radius).abs() < 1e-6);
}
}