Skip to main content

pite_script/
vec2.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2use pyo3::prelude::*;
3use std::sync::Arc;
4
5#[pyclass(name = "Vec2", subclass)]
6#[derive(Clone)]
7pub struct Vec2 {
8    pub(crate) x: f64,
9    pub(crate) y: f64,
10    pub(crate) target: Option<(Arc<crate::ScriptHost>, String)>,
11}
12
13impl std::fmt::Debug for Vec2 {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        f.debug_struct("Vec2")
16            .field("x", &self.x())
17            .field("y", &self.y())
18            .finish()
19    }
20}
21
22impl Vec2 {
23    pub fn x(&self) -> f64 {
24        if let Some((host, node)) = &self.target {
25            if let Some((x, _)) = host.get_node_position(node) {
26                return x;
27            }
28        }
29        self.x
30    }
31
32    pub fn y(&self) -> f64 {
33        if let Some((host, node)) = &self.target {
34            if let Some((_, y)) = host.get_node_position(node) {
35                return y;
36            }
37        }
38        self.y
39    }
40
41    pub fn set_target(&mut self, host: Arc<crate::ScriptHost>, node: String) {
42        if let Some((x, y)) = host.get_node_position(&node) {
43            self.x = x;
44            self.y = y;
45        }
46        self.target = Some((host, node));
47    }
48}
49
50impl<'py> pyo3::FromPyObject<'_, 'py> for Vec2 {
51    type Error = PyErr;
52    fn extract(ob: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {
53        let b: pyo3::PyRef<'py, Vec2> = ob.extract()?;
54        Ok(b.clone())
55    }
56}
57
58#[pymethods]
59impl Vec2 {
60    #[new]
61    #[pyo3(signature = (x = 0.0, y = 0.0))]
62    pub fn new(x: f64, y: f64) -> Self {
63        Self { x, y, target: None }
64    }
65
66    #[getter]
67    pub fn get_x(&self) -> f64 {
68        self.x()
69    }
70
71    #[setter]
72    pub fn set_x(&mut self, val: f64) {
73        self.x = val;
74        if let Some((host, node)) = &self.target {
75            let cur_y = self.y();
76            self.y = cur_y;
77            host.set_node_position(node, val, cur_y);
78        }
79    }
80
81    #[getter]
82    pub fn get_y(&self) -> f64 {
83        self.y()
84    }
85
86    #[setter]
87    pub fn set_y(&mut self, val: f64) {
88        self.y = val;
89        if let Some((host, node)) = &self.target {
90            let cur_x = self.x();
91            self.x = cur_x;
92            host.set_node_position(node, cur_x, val);
93        }
94    }
95
96    fn __repr__(&self) -> String {
97        format!("Vec2({}, {})", self.x(), self.y())
98    }
99
100    fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
101        let (ox, oy) = if let Ok(other_vec) = other.extract::<Vec2>() {
102            (other_vec.x(), other_vec.y())
103        } else if let Ok((x, y)) = other.extract::<(f64, f64)>() {
104            (x, y)
105        } else {
106            return false;
107        };
108        (self.x() - ox).abs() < 1e-6 && (self.y() - oy).abs() < 1e-6
109    }
110
111    fn __add__(&self, other: &Bound<'_, PyAny>) -> PyResult<Vec2> {
112        let (ox, oy) = if let Ok(other_vec) = other.extract::<Vec2>() {
113            (other_vec.x(), other_vec.y())
114        } else if let Ok(t) = other.extract::<(f64, f64)>() {
115            t
116        } else {
117            return Err(pyo3::exceptions::PyTypeError::new_err(
118                "unsupported operand type for +: 'Vec2' and non-vector",
119            ));
120        };
121        Ok(Vec2 {
122            x: self.x() + ox,
123            y: self.y() + oy,
124            target: None,
125        })
126    }
127
128    fn __sub__(&self, other: &Bound<'_, PyAny>) -> PyResult<Vec2> {
129        let (ox, oy) = if let Ok(other_vec) = other.extract::<Vec2>() {
130            (other_vec.x(), other_vec.y())
131        } else if let Ok(t) = other.extract::<(f64, f64)>() {
132            t
133        } else {
134            return Err(pyo3::exceptions::PyTypeError::new_err(
135                "unsupported operand type for -: 'Vec2' and non-vector",
136            ));
137        };
138        Ok(Vec2 {
139            x: self.x() - ox,
140            y: self.y() - oy,
141            target: None,
142        })
143    }
144
145    fn __mul__(&self, scalar: f64) -> Vec2 {
146        Vec2 {
147            x: self.x() * scalar,
148            y: self.y() * scalar,
149            target: None,
150        }
151    }
152
153    fn __rmul__(&self, scalar: f64) -> Vec2 {
154        Vec2 {
155            x: self.x() * scalar,
156            y: self.y() * scalar,
157            target: None,
158        }
159    }
160
161    fn __truediv__(&self, scalar: f64) -> PyResult<Vec2> {
162        if scalar == 0.0 {
163            return Err(pyo3::exceptions::PyZeroDivisionError::new_err(
164                "division by zero",
165            ));
166        }
167        Ok(Vec2 {
168            x: self.x() / scalar,
169            y: self.y() / scalar,
170            target: None,
171        })
172    }
173
174    fn __iadd__(&mut self, other: &Bound<'_, PyAny>) -> PyResult<()> {
175        let (ox, oy) = if let Ok(other_vec) = other.extract::<Vec2>() {
176            (other_vec.x(), other_vec.y())
177        } else if let Ok(t) = other.extract::<(f64, f64)>() {
178            t
179        } else {
180            return Err(pyo3::exceptions::PyTypeError::new_err(
181                "unsupported operand type for +=: 'Vec2' and non-vector",
182            ));
183        };
184        let cur_x = self.x();
185        let cur_y = self.y();
186        self.set_x(cur_x + ox);
187        self.set_y(cur_y + oy);
188        Ok(())
189    }
190
191    fn __isub__(&mut self, other: &Bound<'_, PyAny>) -> PyResult<()> {
192        let (ox, oy) = if let Ok(other_vec) = other.extract::<Vec2>() {
193            (other_vec.x(), other_vec.y())
194        } else if let Ok(t) = other.extract::<(f64, f64)>() {
195            t
196        } else {
197            return Err(pyo3::exceptions::PyTypeError::new_err(
198                "unsupported operand type for -=: 'Vec2' and non-vector",
199            ));
200        };
201        let cur_x = self.x();
202        let cur_y = self.y();
203        self.set_x(cur_x - ox);
204        self.set_y(cur_y - oy);
205        Ok(())
206    }
207
208    fn __imul__(&mut self, scalar: f64) {
209        let cur_x = self.x();
210        let cur_y = self.y();
211        self.set_x(cur_x * scalar);
212        self.set_y(cur_y * scalar);
213    }
214
215    fn __itruediv__(&mut self, scalar: f64) -> PyResult<()> {
216        if scalar == 0.0 {
217            return Err(pyo3::exceptions::PyZeroDivisionError::new_err(
218                "division by zero",
219            ));
220        }
221        let cur_x = self.x();
222        let cur_y = self.y();
223        self.set_x(cur_x / scalar);
224        self.set_y(cur_y / scalar);
225        Ok(())
226    }
227
228    fn __len__(&self) -> usize {
229        2
230    }
231
232    fn __getitem__(&self, idx: isize) -> PyResult<f64> {
233        match idx {
234            0 | -2 => Ok(self.x()),
235            1 | -1 => Ok(self.y()),
236            _ => Err(pyo3::exceptions::PyIndexError::new_err(
237                "Vec2 index out of range",
238            )),
239        }
240    }
241
242    fn __setitem__(&mut self, idx: isize, val: f64) -> PyResult<()> {
243        match idx {
244            0 | -2 => {
245                self.set_x(val);
246                Ok(())
247            }
248            1 | -1 => {
249                self.set_y(val);
250                Ok(())
251            }
252            _ => Err(pyo3::exceptions::PyIndexError::new_err(
253                "Vec2 index out of range",
254            )),
255        }
256    }
257
258    fn __iter__(slf: Bound<'_, Self>) -> PyResult<Bound<'_, pyo3::types::PyIterator>> {
259        let x = slf.borrow().x();
260        let y = slf.borrow().y();
261        let tuple = pyo3::types::PyTuple::new(slf.py(), [x, y])?;
262        tuple.try_iter()
263    }
264
265    fn length(&self) -> f64 {
266        let x = self.x();
267        let y = self.y();
268        (x * x + y * y).sqrt()
269    }
270
271    fn normalized(&self) -> Vec2 {
272        let len = self.length();
273        if len > 1e-6 {
274            let x = self.x();
275            let y = self.y();
276            Vec2 {
277                x: x / len,
278                y: y / len,
279                target: None,
280            }
281        } else {
282            Vec2 {
283                x: 0.0,
284                y: 0.0,
285                target: None,
286            }
287        }
288    }
289
290    fn to_tuple(&self) -> (f64, f64) {
291        (self.x(), self.y())
292    }
293}
294
295impl From<(f64, f64)> for Vec2 {
296    fn from(t: (f64, f64)) -> Self {
297        Self {
298            x: t.0,
299            y: t.1,
300            target: None,
301        }
302    }
303}