index/objects/geometry/
rectangle.rs

1use wasm_bindgen::prelude::*;
2
3use crate::{objects::vector_object::VectorObjectBuilder, utils::{bounding_box::BoundingBox, point2d::Point2D}};
4
5/// A Rectangle is a quadrilateral with four right angles.
6#[wasm_bindgen]
7pub struct Rectangle {
8    /// The BoundingBox of the rectangle.
9    bbox: BoundingBox,
10    /// The rotation of the rectangle, if any.
11    rotation: Option<f32>,
12}
13
14#[wasm_bindgen]
15impl Rectangle {
16    /// Creates a new Rectangle from a BoundingBox and an optional rotation.
17    #[wasm_bindgen(constructor, return_description = "A new rectangle.")]
18    pub fn new(
19        #[wasm_bindgen(param_description = "The BoundingBox of the rectangle.")]
20        bbox: BoundingBox,
21        #[wasm_bindgen(param_description = "The rotation of the rectangle, if any.")]
22        rotation: Option<f32>
23    ) -> Rectangle {
24        Rectangle { bbox, rotation }
25    }
26    /// Gets a VectorObjectBuilder with the rectangle's points.
27    #[wasm_bindgen(getter, return_description = "A VectorObjectBuilder representing the rectangle.")]
28    pub fn vector_object_builder(&self) -> VectorObjectBuilder {
29        let mut builder = VectorObjectBuilder::default()
30            .move_point(&self.top_left())
31            .line_to(&self.top_right())
32            .line_to(&self.bottom_right())
33            .line_to(&self.bottom_left())
34            .close();
35        if let Some(rotation) = self.rotation {
36            builder = builder.rotate(rotation, Some(self.center()), None);
37        }
38        builder
39    }
40    /// Returns the position of the rectangle.
41    #[wasm_bindgen(getter, return_description = "The position of the rectangle.")]
42    pub fn position(&self) -> Point2D {
43        Point2D::new(self.bbox.min_x(), self.bbox.min_y())
44    }
45    /// Returns the size of the rectangle.
46    #[wasm_bindgen(getter, return_description = "The size of the rectangle.")]
47    pub fn bbox(&self) -> BoundingBox {
48        self.bbox.clone()
49    }
50    /// Returns the rotation of the rectangle.
51    #[wasm_bindgen(getter, return_description = "The rotation of the rectangle.")]
52    pub fn rotation(&self) -> Option<f32> {
53        self.rotation
54    }
55    /// Returns the top left corner of the rectangle as a Point2D.
56    #[wasm_bindgen(getter, return_description = "The top left corner of the rectangle.")]
57    pub fn top_left(&self) -> Point2D {
58        self.position()
59    }
60    /// Returns the top right corner of the rectangle as a Point2D.
61    #[wasm_bindgen(getter, return_description = "The top right corner of the rectangle.")]
62    pub fn top_right(&self) -> Point2D {
63        self.position() + Point2D::new(self.bbox.width(), 0.0)
64    }
65    /// Returns the bottom left corner of the rectangle as a Point2D.
66    #[wasm_bindgen(getter, return_description = "The bottom left corner of the rectangle.")]
67    pub fn bottom_left(&self) -> Point2D {
68        self.position() + Point2D::new(0.0, self.bbox.height())
69    }
70    /// Returns the bottom right corner of the rectangle as a Point2D.
71    #[wasm_bindgen(getter, return_description = "The bottom right corner of the rectangle.")]
72    pub fn bottom_right(&self) -> Point2D {
73        self.position() + Point2D::new(self.bbox.width(), self.bbox.height())
74    }
75    /// Returns the top of the rectangle as a Point2D.
76    #[wasm_bindgen(getter, return_description = "The top of the rectangle.")]
77    pub fn top(&self) -> Point2D {
78        self.position() + Point2D::new(self.bbox.width() / 2.0, 0.0)
79    }
80    /// Returns the bottom of the rectangle as a Point2D.
81    #[wasm_bindgen(getter, return_description = "The bottom of the rectangle.")]
82    pub fn bottom(&self) -> Point2D {
83        self.position() + Point2D::new(self.bbox.width() / 2.0, self.bbox.height())
84    }
85    /// Returns the left of the rectangle as a Point2D.
86    #[wasm_bindgen(getter, return_description = "The left of the rectangle.")]
87    pub fn left(&self) -> Point2D {
88        self.position() + Point2D::new(0.0, self.bbox.height() / 2.0)
89    }
90    /// Returns the right of the rectangle as a Point2D.
91    #[wasm_bindgen(getter, return_description = "The right of the rectangle.")]
92    pub fn right(&self) -> Point2D {
93        self.position() + Point2D::new(self.bbox.width(), self.bbox.height() / 2.0)
94    }
95    /// Returns the area of the rectangle.
96    #[wasm_bindgen(getter, return_description = "The area of the rectangle.")]
97    pub fn area(&self) -> f32 {
98        self.bbox.width() * self.bbox.height()
99    }
100    /// Returns the center of the rectangle as a Point2D.
101    #[wasm_bindgen(getter, return_description = "The center of the rectangle.")]
102    pub fn center(&self) -> Point2D {
103        self.bbox.center()
104    }
105}
106
107/// A square is a rectangle with equal width and height.
108#[wasm_bindgen]
109#[derive(Clone, Debug)]
110pub struct Square {
111    /// The center point of the square.
112    center: Point2D,
113    /// The side length of the square.
114    side_length: f32,
115    /// The rotation of the square.
116    rotation: Option<f32>,
117}
118
119#[wasm_bindgen]
120impl Square {
121    /// Creates a new Square from a center point, side length, and optional rotation.
122    #[wasm_bindgen(constructor, return_description = "A square.")]
123    pub fn new(
124        #[wasm_bindgen(param_description = "The center point of the square as a Point2D.")]
125        center: Point2D,
126        #[wasm_bindgen(param_description = "The side length of the square.")]
127        side_length: f32,
128        #[wasm_bindgen(param_description = "The rotation of the square, if any.")]
129        rotation: Option<f32>
130    ) -> Square {
131        Square { center, side_length, rotation }
132    }
133    /// Creates a new Rectangle from the square.
134    #[wasm_bindgen(getter, return_description = "A Rectangle representing the square.")]
135    pub fn rectangle(&self) -> Result<Rectangle, JsError> {
136        if self.side_length <= 0.0 {
137            return Err(JsError::new("The side length must be positive."));
138        }
139        Ok(Rectangle::new(
140            BoundingBox::new(
141                self.center.x - self.side_length / 2.0,
142                self.center.y - self.side_length / 2.0,
143                self.side_length,
144                self.side_length,
145            ).unwrap(),
146            self.rotation,
147        ))
148    }
149    /// Creates a VectorObjectBuilder from the square.
150    #[wasm_bindgen(getter, return_description = "A VectorObjectBuilder representing the square.")]
151    pub fn vector_object_builder(&self) -> Result<VectorObjectBuilder, JsError> {
152        let rectangle = self.rectangle()?;
153        Ok(rectangle.vector_object_builder())
154    }
155    /// Returns the center point of the square.
156    #[wasm_bindgen(getter, return_description = "The center point of the square as a Point2D.")]
157    pub fn center(&self) -> Point2D {
158        self.center
159    }
160    /// Returns the side length of the square.
161    #[wasm_bindgen(getter, return_description = "The side length of the square as a Point2D.")]
162    pub fn side_length(&self) -> f32 {
163        self.side_length
164    }
165    /// Returns the rotation of the square.
166    #[wasm_bindgen(getter, return_description = "The rotation of the square, if any.")]
167    pub fn rotation(&self) -> Option<f32> {
168        self.rotation
169    }
170    /// Returns the area of the square.
171    #[wasm_bindgen(getter, return_description = "The area of the square.")]
172    pub fn area(&self) -> f32 {
173        self.side_length * self.side_length
174    }
175    /// Returns the perimeter of the square.
176    #[wasm_bindgen(getter, return_description = "The perimeter of the square.")]
177    pub fn perimeter(&self) -> f32 {
178        4.0 * self.side_length
179    }
180}