index/objects/geometry/
line.rs

1use wasm_bindgen::prelude::*;
2
3use crate::{objects::vector_object::VectorObjectBuilder, utils::point2d::Point2D};
4
5use super::tipable::Tipable;
6
7/// A Line is a straight one-dimensional figure that extends infinitely in both directions.
8/// It is defined by two points.
9#[wasm_bindgen]
10#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct Line {
12    /// The starting point of the line as Point2D.
13    pub start: Point2D,
14    /// The ending point of the line as Point2D.
15    pub end: Point2D,
16}
17
18#[wasm_bindgen]
19impl Line {
20    /// Creates a new Line object from two points.
21    #[wasm_bindgen(constructor, return_description = "A line connecting the start and end points.")]
22    pub fn new(start: Point2D, end: Point2D) -> Line {
23        Line { start, end }
24    }
25
26    /// Gets a VectorObjectBuilder with the line's points.
27    #[wasm_bindgen(getter, return_description = "A vector object builder with the line's points.")]
28    pub fn vector_object_builder(
29        &self,
30    ) -> VectorObjectBuilder {
31        VectorObjectBuilder::default()
32            .move_point(&self.start)
33            .line_to(&self.end)
34    }
35
36    /// Returns the length of the line.
37    #[wasm_bindgen(getter, return_description = "The length of the line.")]
38    pub fn length(&self) -> f32 {
39        Point2D::distance(&self.start, &self.end)
40    }
41
42    /// Returns the midpoint of the line as a Point2D.
43    #[wasm_bindgen(return_description = "The midpoint of the line.")]
44    pub fn midpoint(&self) -> Point2D {
45        (self.start + self.end) * 0.5
46    }
47
48    /// Returns the slope of the line.
49    #[wasm_bindgen(return_description = "The slope of the line.")]
50    pub fn slope(&self) -> f32 {
51        (self.end.y - self.start.y) / (self.end.x - self.start.x)
52    }
53
54    /// Returns the y-intercept of the line.
55    #[wasm_bindgen(return_description = "The y-intercept of the line.")]
56    pub fn y_intercept(&self) -> f32 {
57        self.start.y - self.slope() * self.start.x
58    }
59
60    /// Returns the normal slope of the line.
61    #[wasm_bindgen(return_description = "The normal slope of the line.")]
62    pub fn normal_slope(&self) -> f32 {
63        -1.0 / self.slope()
64    }
65
66    /// Returns the perpendicular line of the line at a given Point2D.
67    #[wasm_bindgen(return_description = "The perpendicular line of the line at the given point.")]
68    pub fn perpendicular_line(
69        &self,
70        #[wasm_bindgen(param_description = "The point to create the perpendicular line at.")]
71        point: Point2D
72    ) -> Line {
73        let slope = self.normal_slope();
74        let y_intercept = point.y - slope * point.x;
75        Line::new(point, Point2D::new(0.0, y_intercept))
76    }
77
78    /// Returns the intersection Point2D of the line with another line, if it exists.
79    #[wasm_bindgen(return_description = "The intersection point of the two lines if it exists.")]
80    pub fn intersection(
81        &self,
82        #[wasm_bindgen(param_description = "The other line to intersect with.")]
83        other: &Line
84    ) -> Option<Point2D> {
85        let x = (other.y_intercept() - self.y_intercept()) / (self.slope() - other.slope());
86        let y = self.slope() * x + self.y_intercept();
87        if x.is_nan() || y.is_nan() {
88            None
89        } else {
90            Some(Point2D::new(x, y))
91        }
92    }
93
94    /// Returns whether the line contains a given Point2D.
95    #[wasm_bindgen(return_description = "Whether the line contains the given point.")]
96    pub fn contains(
97        &self,
98        #[wasm_bindgen(param_description = "The point to check if it is contained.")]
99        point: Point2D
100    ) -> bool {
101        let x_min = self.start.x.min(self.end.x);
102        let x_max = self.start.x.max(self.end.x);
103        let y_min = self.start.y.min(self.end.y);
104        let y_max = self.start.y.max(self.end.y);
105        point.x >= x_min && point.x <= x_max && point.y >= y_min && point.y <= y_max
106    }
107
108    /// Returns the distance from the line to a given Point2D.
109    #[wasm_bindgen(return_description = "The distance from the line to the given point.")]
110    pub fn distance_to_point(
111        &self,
112        #[wasm_bindgen(param_description = "The point to calculate the distance to.")]
113        point: Point2D
114    ) -> f32 {
115        let a = self.start.y - self.end.y;
116        let b = self.end.x - self.start.x;
117        let c = self.start.x * self.end.y - self.end.x * self.start.y;
118        (a * point.x + b * point.y + c).abs() / (a * a + b * b).sqrt()
119    }
120
121    /// Creates a VectorObjectBuilder with the line's points and a tip at the start.
122    #[wasm_bindgen(return_description = "A VectorObjectBuilder with the line's points and a tip at the start.")]
123    pub fn with_tip_at_the_start(
124        &self,
125        #[wasm_bindgen(param_description = "The shape of the tip. The shape must be pointing to the right and centered to (0, 0), this function will rotate and move it to the correct angle.")]
126        tip_shape: VectorObjectBuilder,
127    ) -> VectorObjectBuilder {
128        let mut builder = self.vector_object_builder();
129        builder = builder.add_child(self.tip_at_start(tip_shape));
130        builder
131    }
132
133    /// Creates a VectorObjectBuilder with the line's points and a tip at the end.
134    #[wasm_bindgen(return_description = "A VectorObjectBuilder with the line's points and a tip at the end.")]
135    pub fn with_tip_at_the_end(
136        &self,
137        #[wasm_bindgen(param_description = "The shape of the tip. The shape must be pointing to the right and centered to (0, 0), this function will rotate and move it to the correct angle.")]
138        tip_shape: VectorObjectBuilder,
139    ) -> VectorObjectBuilder {
140        let mut builder = self.vector_object_builder();
141        builder = builder.add_child(self.tip_at_end(tip_shape));
142        builder
143    }
144
145    /// Creates a VectorObjectBuilder with the line's points and tips at both ends.
146    #[wasm_bindgen(return_description = "A VectorObjectBuilder with the line's points and tips at both ends.")]
147    pub fn with_tips_at_both_ends(
148        &self,
149        #[wasm_bindgen(param_description = "The shape of the tip. The shape must be pointing to the right, this function will rotate and move it to the correct angle.")]
150        tip_shape: VectorObjectBuilder,
151    ) -> VectorObjectBuilder {
152        let mut builder = self.vector_object_builder();
153        builder = builder.add_child(self.tip_at_start(tip_shape.clone()));
154        builder = builder.add_child(self.tip_at_end(tip_shape));
155        builder
156    }
157}
158
159impl Tipable for Line {
160    fn angle_at_end(&self) -> f32 {
161        self.slope().atan()
162    }
163    fn angle_at_start(&self) -> f32 {
164        self.slope().atan() + std::f32::consts::PI
165    }
166    fn end(&self) -> Point2D {
167        self.end
168    }
169    fn start(&self) -> Point2D {
170        self.start
171    }
172}