Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

1use bone_types::{Length, Parameter, Point2, Tolerance, UnitVec2, Vec2}; 2use core::f64::consts::TAU; 3use uom::si::length::millimeter; 4 5use crate::KernelError; 6use crate::aabb::Aabb2; 7use crate::closest::ClosestPoint2; 8use crate::curvature::Curvature; 9use crate::curve2::{Curve2, Curve2Kind}; 10 11#[derive(Copy, Clone, Debug, PartialEq)] 12pub struct Circle2 { 13 center: Point2, 14 radius: Length, 15} 16 17impl Circle2 { 18 pub fn new(center: Point2, radius: Length, tolerance: Tolerance) -> Result<Self, KernelError> { 19 if radius.get::<millimeter>() < tolerance.value() { 20 return Err(KernelError::DegenerateCircle); 21 } 22 Ok(Self { center, radius }) 23 } 24 25 #[must_use] 26 pub(crate) const fn from_raw(center: Point2, radius: Length) -> Self { 27 Self { center, radius } 28 } 29 30 #[must_use] 31 pub const fn center(self) -> Point2 { 32 self.center 33 } 34 35 #[must_use] 36 pub const fn radius(self) -> Length { 37 self.radius 38 } 39 40 #[must_use] 41 pub fn radius_mm(self) -> f64 { 42 self.radius.get::<millimeter>() 43 } 44} 45 46impl Curve2 for Circle2 { 47 fn evaluate(&self, t: Parameter) -> Point2 { 48 let (cx, cy) = self.center.coords_mm(); 49 let r = self.radius_mm(); 50 let theta = TAU * t.value(); 51 Point2::from_mm(cx + r * theta.cos(), cy + r * theta.sin()) 52 } 53 54 fn derivative(&self, t: Parameter) -> Vec2 { 55 let r = self.radius_mm(); 56 let theta = TAU * t.value(); 57 Vec2::from_mm(-r * TAU * theta.sin(), r * TAU * theta.cos()) 58 } 59 60 fn tangent(&self, t: Parameter) -> UnitVec2 { 61 let theta = TAU * t.value(); 62 UnitVec2::new_unchecked(-theta.sin(), theta.cos()) 63 } 64 65 fn curvature(&self, _t: Parameter) -> Curvature { 66 Curvature::from_radius(self.radius) 67 } 68 69 fn bounding_box(&self) -> Aabb2 { 70 let (cx, cy) = self.center.coords_mm(); 71 let r = self.radius_mm(); 72 Aabb2::from_corners( 73 Point2::from_mm(cx - r, cy - r), 74 Point2::from_mm(cx + r, cy + r), 75 ) 76 } 77 78 fn closest_point(&self, p: Point2, tolerance: Tolerance) -> ClosestPoint2 { 79 let (cx, cy) = self.center.coords_mm(); 80 let (px, py) = p.coords_mm(); 81 let dx = px - cx; 82 let dy = py - cy; 83 let dist = (dx * dx + dy * dy).sqrt(); 84 let r = self.radius_mm(); 85 if dist < tolerance.value() { 86 let projected = Point2::from_mm(cx + r, cy); 87 let distance = Length::new::<millimeter>(r); 88 return ClosestPoint2::new(Parameter::new(0.0), projected, distance); 89 } 90 let theta = dy.atan2(dx); 91 let t_raw = theta / TAU; 92 let t = t_raw.rem_euclid(1.0); 93 let projected = Point2::from_mm(cx + r * theta.cos(), cy + r * theta.sin()); 94 let distance = Length::new::<millimeter>((dist - r).abs()); 95 ClosestPoint2::new(Parameter::new(t), projected, distance) 96 } 97 98 fn as_kind(&self) -> Curve2Kind { 99 Curve2Kind::Circle(*self) 100 } 101} 102 103impl core::fmt::Display for Circle2 { 104 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 105 write!( 106 f, 107 "circle2{{ c={}, r={} mm }}", 108 self.center, 109 self.radius.get::<millimeter>(), 110 ) 111 } 112}