index/objects/geometry/
arc.rs

1use wasm_bindgen::prelude::*;
2
3use crate::{objects::vector_object::VectorObjectBuilder, utils::{bezier::AnchorsAndHandles, point2d::{Path2D, Point2D}}};
4
5use super::tipable::Tipable;
6
7/// An Arc is a portion of the circumference of a circle.
8#[wasm_bindgen]
9#[derive(Clone, Debug)]
10pub struct Arc {
11    /// The center point of the arc as a Point2D.
12    center: Point2D,
13    /// The radius of the arc.
14    radius: f32,
15    /// The start angle of the arc in radians.
16    start_angle: f32,
17    /// The end angle of the arc in radians.
18    end_angle: f32,
19}
20
21#[wasm_bindgen]
22impl Arc {
23    /// Creates a new Arc object from a center point, radius, start angle, and end angle.
24    #[wasm_bindgen(constructor, return_description = "An arc.")]
25    pub fn new(
26        #[wasm_bindgen(param_description = "The center point of the arc as a Point2D.")]
27        center: Point2D,
28        #[wasm_bindgen(param_description = "The radius of the arc.")]
29        radius: f32,
30        #[wasm_bindgen(param_description = "The start angle of the arc in radians.")]
31        start_angle: f32,
32        #[wasm_bindgen(param_description = "The end angle of the arc in radians.")]
33        end_angle: f32,
34    ) -> Arc {
35        Arc {
36            center,
37            radius,
38            start_angle,
39            end_angle,
40        }
41    }
42
43    /// Creates a new vector object builder with the arc's points.
44    #[wasm_bindgen(return_description = "A VectorObjectBuilder representing the arc.")]
45    pub fn vector_object_builder(&self, samples: Option<usize>) -> VectorObjectBuilder {
46        let samples = samples.unwrap_or(15);
47        let anchors = (0..samples)
48            .map(|i| self.start_angle + (self.end_angle - self.start_angle) * i as f32 / (samples - 1) as f32)
49            .map(|angle| Point2D::new(angle.cos(), angle.sin()))
50            .collect::<Vec<Point2D>>();
51        let dtheta = (self.end_angle - self.start_angle) / (samples - 1) as f32;
52        let tangent_vectors = anchors.iter().map(|point| {
53            Point2D::new(-point.y, point.x)
54        }).collect::<Vec<Point2D>>();
55        let factor = 4.0 / 3.0 * (dtheta / 4.0).tan();
56        let handles1 = anchors[0..samples - 1].iter().zip(tangent_vectors[0..samples - 1].iter()).map(|(point, tangent)| {
57            *point + *tangent * factor
58        }).collect::<Vec<Point2D>>();
59        let handles2 = anchors[1..samples].iter().zip(tangent_vectors[1..samples].iter()).map(|(point, tangent)| {
60            *point - *tangent * factor
61        }).collect::<Vec<Point2D>>();
62        let path = Path2D::from_anchors_and_handles(&AnchorsAndHandles::new(
63            anchors[0..samples - 1].to_vec(),
64            handles1,
65            handles2,
66            anchors[1..samples].to_vec(),
67        ).unwrap());
68        VectorObjectBuilder::default()
69            .set_path(path)
70            .scale(self.radius, self.radius, None, None)
71            .shift(self.center.x, self.center.y, None)
72            .actual_path_as_path(None, None)
73    }
74
75    /// Returns the center point of the arc.
76    #[wasm_bindgen(getter, return_description = "The center point of the arc as a Point2D.")]
77    pub fn center(&self) -> Point2D {
78        self.center
79    }
80
81    /// Returns the radius of the arc.
82    #[wasm_bindgen(getter, return_description = "The radius of the arc.")]
83    pub fn radius(&self) -> f32 {
84        self.radius
85    }
86
87    /// Returns the start angle of the arc in radians.
88    #[wasm_bindgen(getter, return_description = "The start angle of the arc in radians.")]
89    pub fn start_angle(&self) -> f32 {
90        self.start_angle
91    }
92
93    /// Returns the end angle of the arc in radians.
94    #[wasm_bindgen(getter, return_description = "The end angle of the arc in radians.")]
95    pub fn end_angle(&self) -> f32 {
96        self.end_angle
97    }
98
99    /// Returns a VectorObjectBuilder representing the arc and a tip at the start of the arc.
100    #[wasm_bindgen(return_description = "A VectorObjectBuilder representing the arc and a tip at the start of the arc.")]
101    pub fn start_tip_vector_object_builder(
102        &self,
103        #[wasm_bindgen(param_description = "The tip shape as a VectorObjectBuilder to add to the start of the arc. It must be pointing to the right and centered at (0, 0). This function will rotate and move it to the correct angle.")]
104        tip_shape: VectorObjectBuilder,
105        #[wasm_bindgen(param_description = "The number of samples to use to create the arc, by default 15.")]
106        samples: Option<usize>
107    ) -> VectorObjectBuilder {
108        let mut builder = self.vector_object_builder(samples);
109        builder = builder.add_child(self.tip_at_start(tip_shape));
110        builder
111    }
112
113    /// Returns a VectorObjectBuilder representing the arc and a tip at the end of the arc.
114    #[wasm_bindgen(return_description = "A VectorObjectBuilder representing the arc and a tip at the end of the arc.")]
115    pub fn end_tip_vector_object_builder(
116        &self,
117        #[wasm_bindgen(param_description = "The tip shape as a VectorObjectBuilder to add to the end of the arc. It must be pointing to the right and centered at (0, 0). This function will rotate and move it to the correct angle.")]
118        tip_shape: VectorObjectBuilder,
119        #[wasm_bindgen(param_description = "The number of samples to use to create the arc.")]
120        samples: Option<usize>
121    ) -> VectorObjectBuilder {
122        let mut builder = self.vector_object_builder(samples);
123        builder = builder.add_child(self.tip_at_end(tip_shape));
124        builder
125    }
126
127    /// Returns a VectorObjectBuilder representing the arc and tips at both ends of the arc.
128    #[wasm_bindgen(return_description = "A VectorObjectBuilder representing the arc and tips at both ends of the arc.")]
129    pub fn both_tips_vector_object_builder(
130        &self,
131        #[wasm_bindgen(param_description = "The tip shape as a VectorObjectBuilder to add to the start of the arc. It must be pointing to the right and centered at (0, 0). This function will rotate and move it to the correct angle.")]
132        tip_shape: VectorObjectBuilder,
133        #[wasm_bindgen(param_description = "The number of samples to use to create the arc.")]
134        samples: Option<usize>
135    ) -> VectorObjectBuilder {
136        let mut builder = self.vector_object_builder(samples);
137        builder = builder.add_child(self.tip_at_start(tip_shape.clone()));
138        builder = builder.add_child(self.tip_at_end(tip_shape));
139        builder
140    }
141
142    /// Returns the point on the arc at a given t progress value.
143    #[wasm_bindgen(return_description = "The Point2D on the arc at the given t value.")]
144    pub fn point_at(
145        &self,
146        #[wasm_bindgen(param_description = "The t value to evaluate the polynomial at. A number between 0 and 1.")]
147        t: f32,
148    ) -> Point2D {
149        let angle = self.start_angle + (self.end_angle - self.start_angle) * t;
150        let x = self.center.x + self.radius * angle.cos();
151        let y = self.center.y + self.radius * angle.sin();
152        Point2D::new(x, y)
153    }
154
155    /// Returns the length of the arc.
156    #[wasm_bindgen(return_description = "The length of the arc.")]
157    pub fn length(&self) -> f32 {
158        let angle = self.end_angle - self.start_angle;
159        angle.abs() * self.radius
160    }
161}
162
163impl Tipable for Arc {
164    fn start(&self) -> Point2D {
165        self.point_at(0.0)
166    }
167
168    fn end(&self) -> Point2D {
169        self.point_at(1.0)
170    }
171
172    fn angle_at_end(&self) -> f32 {
173        self.end_angle + std::f32::consts::PI / 2.0
174    }
175
176    fn angle_at_start(&self) -> f32 {
177        self.start_angle - std::f32::consts::PI / 2.0
178    }
179}
180
181/// A Circle is a set of all points in a plane that are at a given distance from a given point, the center.
182#[wasm_bindgen]
183#[derive(Clone, Debug)]
184pub struct Circle {
185    /// The center Point2D of the circle.
186    center: Point2D,
187    /// The radius of the circle.
188    radius: f32,
189}
190
191#[wasm_bindgen]
192impl Circle {
193    /// Creates a new Circle from a center Point2D and a radius.
194    #[wasm_bindgen(constructor, return_description = "A circle.")]
195    pub fn new(
196        #[wasm_bindgen(param_description = "The center point of the circle as a Point2D.")]
197        center: Point2D,
198        #[wasm_bindgen(param_description = "The radius of the circle.")]
199        radius: f32,
200    ) -> Circle {
201        Circle { center, radius }
202    }
203    /// Creates an Arc from the circle.
204    #[wasm_bindgen(getter, return_description = "An Arc representing the circle.")]
205    pub fn arc(&self) -> Arc {
206        Arc::new(self.center, self.radius, 0.0, 2.0 * std::f32::consts::PI)
207    }
208    /// Creates a new VectorObjectBuilder from the circle.
209    #[wasm_bindgen(return_description = "A VectorObjectBuilder representing the circle.")]
210    pub fn vector_object_builder(
211        &self,
212        #[wasm_bindgen(param_description = "The number of samples to use to create the circle, by default 15.")]
213        samples: Option<usize>
214    ) -> VectorObjectBuilder {
215        self.arc().vector_object_builder(samples).close()
216    }
217    /// Returns the center point of the circle as a Point2D.
218    #[wasm_bindgen(getter, return_description = "The center point of the circle.")]
219    pub fn center(&self) -> Point2D {
220        self.center
221    }
222    /// Returns the radius of the circle.
223    #[wasm_bindgen(getter, return_description = "The radius of the circle.")]
224    pub fn radius(&self) -> f32 {
225        self.radius
226    }
227    /// Returns the circumference of the circle.
228    #[wasm_bindgen(return_description = "The circumference of the circle.")]
229    pub fn circumference(&self) -> f32 {
230        2.0 * std::f32::consts::PI * self.radius
231    }
232    /// Returns the area of the circle.
233    #[wasm_bindgen(return_description = "The area of the circle.")]
234    pub fn area(&self) -> f32 {
235        std::f32::consts::PI * self.radius.powi(2)
236    }
237}