1use euclid::default::Point2D;
2use euclid::{Angle, Translation2D, Trig, Vector2D};
3use num_traits::{Float, FromPrimitive};
4
5use crate::core::_c;
6
7#[derive(Clone, Debug, PartialEq)]
8pub struct Line<F: Float + Trig> {
9 pub start_point: Point2D<F>,
10 pub end_point: Point2D<F>,
11}
12
13#[derive(Clone, Debug, PartialEq)]
14pub struct BezierQuadratic<F: Float + Trig> {
15 pub start: Point2D<F>,
16 pub cp: Point2D<F>,
17 pub end: Point2D<F>,
18}
19
20#[derive(Clone, Debug, PartialEq)]
21pub struct BezierCubic<F: Float + Trig> {
22 pub start: Point2D<F>,
23 pub cp1: Point2D<F>,
24 pub cp2: Point2D<F>,
25 pub end: Point2D<F>,
26}
27
28impl<F: Float + Trig> Line<F> {
29 pub fn from(points: &[Point2D<F>]) -> Self {
30 Line {
31 start_point: points[0],
32 end_point: points[1],
33 }
34 }
35 pub fn as_points(&self) -> Vec<Point2D<F>> {
36 vec![self.start_point, self.end_point]
37 }
38
39 pub fn length(&self) -> F {
40 (self.end_point - self.start_point).length()
41 }
42
43 pub fn rotate(&mut self, center: &Point2D<F>, degrees: F) {
44 let angle = Angle::radians(degrees.to_radians());
45 let translation = Translation2D::new(-center.x, -center.y);
46 let transformation = translation
47 .to_transform()
48 .then_rotate(angle)
49 .then_translate(Vector2D::new(center.x, center.y));
50 self.start_point = transformation.transform_point(self.start_point);
51 self.end_point = transformation.transform_point(self.end_point);
52 }
53}
54
55pub fn rotate_points<F: Float + Trig>(
56 points: &[Point2D<F>],
57 center: &Point2D<F>,
58 degrees: F,
59) -> Vec<Point2D<F>> {
60 let angle = Angle::radians(degrees.to_radians());
61 let translation = Translation2D::new(-center.x, -center.y);
62 let transformation = translation
63 .to_transform()
64 .then_rotate(angle)
65 .then_translate(Vector2D::new(center.x, center.y));
66 points
67 .iter()
68 .map(|&p| transformation.transform_point(p))
69 .collect::<Vec<Point2D<F>>>()
70}
71
72pub fn rotate_lines<F: Float + Trig>(
73 lines: &[Line<F>],
74 center: &Point2D<F>,
75 degrees: F,
76) -> Vec<Line<F>> {
77 lines
78 .iter()
79 .cloned()
80 .map(|mut l| {
81 l.rotate(center, degrees);
82 l
83 })
84 .collect::<Vec<Line<F>>>()
85}
86
87pub fn convert_bezier_quadratic_to_cubic<F: Float + FromPrimitive + Trig>(
89 bezier_quadratic: BezierQuadratic<F>,
90) -> BezierCubic<F> {
91 let cubic_x1 = bezier_quadratic.start.x
92 + _c::<F>(2.0 / 3.0) * (bezier_quadratic.cp.x - bezier_quadratic.start.x);
93 let cubic_y1 = bezier_quadratic.start.y
94 + _c::<F>(2.0 / 3.0) * (bezier_quadratic.cp.y - bezier_quadratic.start.y);
95 let cubic_x2 = bezier_quadratic.end.x
96 + _c::<F>(2.0 / 3.0) * (bezier_quadratic.cp.x - bezier_quadratic.end.x);
97 let cubic_y2 = bezier_quadratic.end.y
98 + _c::<F>(2.0 / 3.0) * (bezier_quadratic.cp.y - bezier_quadratic.end.y);
99
100 BezierCubic {
101 start: bezier_quadratic.start,
102 cp1: Point2D::new(cubic_x1, cubic_y1),
103 cp2: Point2D::new(cubic_x2, cubic_y2),
104 end: bezier_quadratic.end,
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use euclid::default::Point2D;
111 #[test]
112 fn line_length() {
113 let l = super::Line::from(&[Point2D::new(1.0, 1.0), Point2D::new(2.0, 2.0)]);
114 assert_eq!(l.length(), f32::sqrt(2.0));
115 }
116}