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#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "type", rename = "Point")]
10pub struct Point {
11 pub guid: String, pub name: String, #[serde(rename = "x")]
14 _x: f32, #[serde(rename = "y")]
16 _y: f32, #[serde(rename = "z")]
18 _z: f32, pub width: f32, pub pointcolor: Color, #[serde(default = "Xform::identity")]
22 pub xform: Xform, }
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 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 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 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 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 pub fn jsonload(json_data: &str) -> Result<Self, Box<dyn std::error::Error>> {
88 Ok(serde_json::from_str(json_data)?)
89 }
90
91 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 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 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 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 pub fn distance(&self, p: &Point) -> f32 {
124 self.distance_with_min(p, 1e-12)
125 }
126
127 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 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 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 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
222impl 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
250impl 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
286impl 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;