Skip to main content

roughr/
geometry.rs

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
87/// Raises the order from a quadratic bezier to a cubic bezier curve.
88pub 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}