session_rust/
point.rs

1use crate::{Color, Vector, Xform};
2use serde::{ser::Serialize as SerTrait, Deserialize, Serialize};
3use std::fmt;
4use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign};
5use uuid::Uuid;
6
7/// A 3D point with visual properties and JSON serialization support.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "type", rename = "Point")]
10pub struct Point {
11    pub guid: String, // Unique identifier
12    pub name: String, // Name of the point
13    #[serde(rename = "x")]
14    _x: f32, // X coordinate (private)
15    #[serde(rename = "y")]
16    _y: f32, // Y coordinate (private)
17    #[serde(rename = "z")]
18    _z: f32, // Z coordinate (private)
19    pub width: f32,   // Width of the point
20    pub pointcolor: Color, // Color of the point
21    #[serde(default = "Xform::identity")]
22    pub xform: Xform, // Transformation matrix
23}
24
25impl Default for Point {
26    fn default() -> Self {
27        Self {
28            _x: 0.0,
29            _y: 0.0,
30            _z: 0.0,
31            guid: Uuid::new_v4().to_string(),
32            name: "my_point".to_string(),
33            pointcolor: Color::white(),
34            width: 1.0,
35            xform: Xform::identity(),
36        }
37    }
38}
39
40impl Point {
41    /// Creates a new Point with specified coordinates.
42    pub fn new(x: f32, y: f32, z: f32) -> Self {
43        Self {
44            _x: x,
45            _y: y,
46            _z: z,
47            ..Default::default()
48        }
49    }
50
51    /// Getters for coordinates
52    pub fn x(&self) -> f32 {
53        self._x
54    }
55    pub fn y(&self) -> f32 {
56        self._y
57    }
58    pub fn z(&self) -> f32 {
59        self._z
60    }
61
62    /// Setters for coordinates
63    pub fn set_x(&mut self, v: f32) {
64        self._x = v;
65    }
66    pub fn set_y(&mut self, v: f32) {
67        self._y = v;
68    }
69    pub fn set_z(&mut self, v: f32) {
70        self._z = v;
71    }
72
73    ///////////////////////////////////////////////////////////////////////////////////////////
74    // JSON
75    ///////////////////////////////////////////////////////////////////////////////////////////
76
77    /// Serializes the Point to a JSON string.
78    pub fn jsondump(&self) -> Result<String, Box<dyn std::error::Error>> {
79        let mut buf = Vec::new();
80        let formatter = serde_json::ser::PrettyFormatter::with_indent(b"    ");
81        let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
82        SerTrait::serialize(self, &mut ser)?;
83        Ok(String::from_utf8(buf)?)
84    }
85
86    /// Deserializes a Point from a JSON string.
87    pub fn jsonload(json_data: &str) -> Result<Self, Box<dyn std::error::Error>> {
88        Ok(serde_json::from_str(json_data)?)
89    }
90
91    /// Serializes the Point to a JSON file.
92    pub fn to_json(&self, filepath: &str) -> Result<(), Box<dyn std::error::Error>> {
93        let json = self.jsondump()?;
94        std::fs::write(filepath, json)?;
95        Ok(())
96    }
97
98    /// Deserializes a Point from a JSON file.
99    pub fn from_json(filepath: &str) -> Result<Self, Box<dyn std::error::Error>> {
100        let json = std::fs::read_to_string(filepath)?;
101        Self::jsonload(&json)
102    }
103
104    ///////////////////////////////////////////////////////////////////////////////////////////
105    // Details
106    ///////////////////////////////////////////////////////////////////////////////////////////
107
108    /// Check if the points are in counter-clockwise order.
109    pub fn ccw(a: &Point, b: &Point, c: &Point) -> bool {
110        (c._y - a._y) * (b._x - a._x) > (b._y - a._y) * (c._x - a._x)
111    }
112
113    /// Calculate the mid point between this point and another point.
114    pub fn mid_point(&self, p: &Point) -> Point {
115        Point::new(
116            (self._x + p._x) / 2.0,
117            (self._y + p._y) / 2.0,
118            (self._z + p._z) / 2.0,
119        )
120    }
121
122    /// Calculate the distance between this point and another point.
123    pub fn distance(&self, p: &Point) -> f32 {
124        self.distance_with_min(p, 1e-12)
125    }
126
127    /// Calculate the distance between this point and another point with custom minimum.
128    pub fn distance_with_min(&self, p: &Point, double_min: f32) -> f32 {
129        let mut dx = (self[0] - p[0]).abs();
130        let mut dy = (self[1] - p[1]).abs();
131        let mut dz = (self[2] - p[2]).abs();
132
133        // Reorder coordinates to put largest in dx
134        if dy >= dx && dy >= dz {
135            std::mem::swap(&mut dx, &mut dy);
136        } else if dz >= dx && dz >= dy {
137            std::mem::swap(&mut dx, &mut dz);
138        }
139
140        if dx > double_min {
141            dy /= dx;
142            dz /= dx;
143            dx * (1.0 + dy * dy + dz * dz).sqrt()
144        } else if dx > 0.0 && dx.is_finite() {
145            dx
146        } else {
147            0.0
148        }
149    }
150
151    /// Calculate the area of a polygon.
152    pub fn area(points: &[Point]) -> f32 {
153        let n = points.len();
154        let mut area = 0.0;
155
156        for i in 0..n {
157            let j = (i + 1) % n;
158            area += points[i][0] * points[j][1];
159            area -= points[j][0] * points[i][1];
160        }
161
162        area.abs() / 2.0
163    }
164
165    /// Calculate the centroid of a quadrilateral.
166    pub fn centroid_quad(vertices: &[Point]) -> Result<Point, &'static str> {
167        if vertices.len() != 4 {
168            return Err("Polygon must have exactly 4 vertices.");
169        }
170
171        let mut total_area = 0.0;
172        let mut centroid_sum = Vector::new(0.0, 0.0, 0.0);
173
174        for i in 0..4 {
175            let p0 = &vertices[i];
176            let p1 = &vertices[(i + 1) % 4];
177            let p2 = &vertices[(i + 2) % 4];
178
179            let tri_area =
180                ((p0[0] * (p1[1] - p2[1]) + p1[0] * (p2[1] - p0[1]) + p2[0] * (p0[1] - p1[1]))
181                    .abs())
182                    / 2.0;
183            total_area += tri_area;
184
185            let tri_centroid = Vector::new(
186                (p0[0] + p1[0] + p2[0]) / 3.0,
187                (p0[1] + p1[1] + p2[1]) / 3.0,
188                (p0[2] + p1[2] + p2[2]) / 3.0,
189            );
190            centroid_sum += tri_centroid * tri_area;
191        }
192
193        let result = centroid_sum / total_area;
194        Ok(Point::new(result.x(), result.y(), result.z()))
195    }
196}
197
198impl fmt::Display for Point {
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        use crate::tolerance::TOL;
201        write!(
202            f,
203            "Point(x={}, y={}, z={})",
204            TOL.format_number(self._x as f64, None),
205            TOL.format_number(self._y as f64, None),
206            TOL.format_number(self._z as f64, None)
207        )
208    }
209}
210
211impl PartialEq for Point {
212    fn eq(&self, other: &Self) -> bool {
213        self.name == other.name
214            && (self._x * 1000000.0).round() == (other._x * 1000000.0).round()
215            && (self._y * 1000000.0).round() == (other._y * 1000000.0).round()
216            && (self._z * 1000000.0).round() == (other._z * 1000000.0).round()
217            && (self.width * 1000000.0).round() == (other.width * 1000000.0).round()
218            && self.pointcolor == other.pointcolor
219    }
220}
221
222///////////////////////////////////////////////////////////////////////////////////////////
223// Indexing operators
224///////////////////////////////////////////////////////////////////////////////////////////
225
226impl Index<usize> for Point {
227    type Output = f32;
228
229    fn index(&self, index: usize) -> &Self::Output {
230        match index {
231            0 => &self._x,
232            1 => &self._y,
233            2 => &self._z,
234            _ => panic!("Index out of range"),
235        }
236    }
237}
238
239impl IndexMut<usize> for Point {
240    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
241        match index {
242            0 => &mut self._x,
243            1 => &mut self._y,
244            2 => &mut self._z,
245            _ => panic!("Index out of range"),
246        }
247    }
248}
249
250///////////////////////////////////////////////////////////////////////////////////////////
251// No-copy operators
252///////////////////////////////////////////////////////////////////////////////////////////
253
254impl MulAssign<f32> for Point {
255    fn mul_assign(&mut self, rhs: f32) {
256        self._x *= rhs;
257        self._y *= rhs;
258        self._z *= rhs;
259    }
260}
261
262impl DivAssign<f32> for Point {
263    fn div_assign(&mut self, rhs: f32) {
264        self._x /= rhs;
265        self._y /= rhs;
266        self._z /= rhs;
267    }
268}
269
270impl AddAssign<Vector> for Point {
271    fn add_assign(&mut self, rhs: Vector) {
272        self._x += rhs.x();
273        self._y += rhs.y();
274        self._z += rhs.z();
275    }
276}
277
278impl SubAssign<Vector> for Point {
279    fn sub_assign(&mut self, rhs: Vector) {
280        self._x -= rhs.x();
281        self._y -= rhs.y();
282        self._z -= rhs.z();
283    }
284}
285
286///////////////////////////////////////////////////////////////////////////////////////////
287// Copy operators
288///////////////////////////////////////////////////////////////////////////////////////////
289
290impl Mul<f32> for Point {
291    type Output = Point;
292
293    fn mul(self, rhs: f32) -> Self::Output {
294        Point::new(self._x * rhs, self._y * rhs, self._z * rhs)
295    }
296}
297
298impl Div<f32> for Point {
299    type Output = Point;
300
301    fn div(self, rhs: f32) -> Self::Output {
302        Point::new(self._x / rhs, self._y / rhs, self._z / rhs)
303    }
304}
305
306impl Sub<Point> for Point {
307    type Output = Vector;
308
309    fn sub(self, rhs: Point) -> Self::Output {
310        Vector::new(self._x - rhs._x, self._y - rhs._y, self._z - rhs._z)
311    }
312}
313
314impl Add<Vector> for Point {
315    type Output = Point;
316
317    fn add(self, rhs: Vector) -> Self::Output {
318        Point::new(self._x + rhs.x(), self._y + rhs.y(), self._z + rhs.z())
319    }
320}
321
322impl Sub<Vector> for Point {
323    type Output = Point;
324
325    fn sub(self, rhs: Vector) -> Self::Output {
326        Point::new(self._x - rhs.x(), self._y - rhs.y(), self._z - rhs.z())
327    }
328}
329
330#[cfg(test)]
331#[path = "point_test.rs"]
332mod point_test;