1use gizmo_math::Vec3;
2use std::collections::{HashMap, HashSet};
3
4const EPSILON: f32 = 1e-4;
5
6#[derive(Clone, Debug)]
7pub struct HullFace {
8 pub v: [usize; 3],
9 pub normal: Vec3,
10 pub outside_set: Vec<usize>,
11}
12
13impl HullFace {
14 pub fn new(a: usize, b: usize, c: usize, points: &[Vec3]) -> Self {
15 let pa = points[a];
16 let pb = points[b];
17 let pc = points[c];
18
19 let mut normal = (pb - pa).cross(pc - pa);
20 let len = normal.length();
21 if len > 1e-8 {
22 normal /= len;
23 } else {
24 normal = Vec3::Y;
25 }
26
27 Self {
28 v: [a, b, c],
29 normal,
30 outside_set: Vec::new(),
31 }
32 }
33
34 pub fn distance(&self, p: Vec3, points: &[Vec3]) -> f32 {
35 (p - points[self.v[0]]).dot(self.normal)
36 }
37}
38
39pub struct ConvexHull {
40 pub vertices: Vec<Vec3>,
41 pub faces: Vec<[u32; 3]>,
42}
43
44pub fn compute_convex_hull(points: &[Vec3]) -> ConvexHull {
46 if points.len() < 4 {
47 return ConvexHull {
49 vertices: points.to_vec(),
50 faces: vec![],
51 };
52 }
53
54 let pts = points;
55
56 let mut min_x = 0;
58 let mut max_x = 0;
59 let mut min_y = 0;
60 let mut max_y = 0;
61 let mut min_z = 0;
62 let mut max_z = 0;
63
64 for i in 1..pts.len() {
65 let p = pts[i];
66 if p.x < pts[min_x].x {
67 min_x = i;
68 }
69 if p.x > pts[max_x].x {
70 max_x = i;
71 }
72 if p.y < pts[min_y].y {
73 min_y = i;
74 }
75 if p.y > pts[max_y].y {
76 max_y = i;
77 }
78 if p.z < pts[min_z].z {
79 min_z = i;
80 }
81 if p.z > pts[max_z].z {
82 max_z = i;
83 }
84 }
85
86 let extremes = [min_x, max_x, min_y, max_y, min_z, max_z];
87
88 let mut max_dist_sq = -1.0;
90 let mut p0 = 0;
91 let mut p1 = 0;
92 for &e1 in &extremes {
93 for &e2 in &extremes {
94 let dist_sq = (pts[e1] - pts[e2]).length_squared();
95 if dist_sq > max_dist_sq {
96 max_dist_sq = dist_sq;
97 p0 = e1;
98 p1 = e2;
99 }
100 }
101 }
102
103 if max_dist_sq < 1e-8 {
104 return ConvexHull {
105 vertices: vec![pts[0]],
106 faces: vec![],
107 };
108 }
109
110 let mut max_dist_line = -1.0;
112 let mut p2 = 0;
113 let line_dir = (pts[p1] - pts[p0]).normalize();
114 for i in 0..pts.len() {
115 let p = pts[i];
116 let v = p - pts[p0];
117 let proj = v.dot(line_dir);
118 let dist_sq = (v - line_dir * proj).length_squared();
119 if dist_sq > max_dist_line {
120 max_dist_line = dist_sq;
121 p2 = i;
122 }
123 }
124
125 if max_dist_line < 1e-8 {
126 return ConvexHull {
127 vertices: vec![pts[p0], pts[p1]],
128 faces: vec![],
129 };
130 }
131
132 let plane_normal = (pts[p1] - pts[p0]).cross(pts[p2] - pts[p0]).normalize();
134 let mut max_dist_plane = -1.0;
135 let mut p3 = 0;
136 for i in 0..pts.len() {
137 let dist = (pts[i] - pts[p0]).dot(plane_normal).abs();
138 if dist > max_dist_plane {
139 max_dist_plane = dist;
140 p3 = i;
141 }
142 }
143
144 if max_dist_plane < 1e-8 {
145 let mut dedup = pts.to_vec();
147 dedup.dedup_by(|a, b| {
148 (a.x - b.x).abs() < 1e-4 && (a.y - b.y).abs() < 1e-4 && (a.z - b.z).abs() < 1e-4
149 });
150 return ConvexHull {
151 vertices: dedup,
152 faces: vec![],
153 };
154 }
155
156 let mut faces = Vec::new();
158
159 let mut f0 = HullFace::new(p0, p1, p2, pts);
162 if f0.distance(pts[p3], pts) > 0.0 {
163 f0 = HullFace::new(p0, p2, p1, pts);
164 }
165
166 let mut f1 = HullFace::new(p0, p3, p1, pts);
167 if f1.distance(pts[p2], pts) > 0.0 {
168 f1 = HullFace::new(p0, p1, p3, pts);
169 }
170
171 let mut f2 = HullFace::new(p1, p3, p2, pts);
172 if f2.distance(pts[p0], pts) > 0.0 {
173 f2 = HullFace::new(p1, p2, p3, pts);
174 }
175
176 let mut f3 = HullFace::new(p2, p3, p0, pts);
177 if f3.distance(pts[p1], pts) > 0.0 {
178 f3 = HullFace::new(p2, p0, p3, pts);
179 }
180
181 faces.push(f0);
182 faces.push(f1);
183 faces.push(f2);
184 faces.push(f3);
185
186 for i in 0..pts.len() {
188 if i == p0 || i == p1 || i == p2 || i == p3 {
189 continue;
190 }
191 let p = pts[i];
192
193 let mut best_face = usize::MAX;
194 let mut max_d = EPSILON;
195
196 for (f_idx, f) in faces.iter().enumerate() {
197 let d = f.distance(p, pts);
198 if d > max_d {
199 max_d = d;
200 best_face = f_idx;
201 }
202 }
203
204 if best_face != usize::MAX {
205 faces[best_face].outside_set.push(i);
206 }
207 }
208
209 loop {
211 let mut active_face_idx = usize::MAX;
213 for (i, f) in faces.iter().enumerate() {
214 if !f.outside_set.is_empty() {
215 active_face_idx = i;
216 break;
217 }
218 }
219
220 if active_face_idx == usize::MAX {
221 break; }
223
224 let mut best_pt = 0;
226 let mut max_d = -1.0;
227 let active_face = &faces[active_face_idx];
228
229 for &idx in &active_face.outside_set {
230 let d = active_face.distance(pts[idx], pts);
231 if d > max_d {
232 max_d = d;
233 best_pt = idx;
234 }
235 }
236
237 let p = pts[best_pt];
238
239 let mut visible_faces = Vec::new();
241 for (i, f) in faces.iter().enumerate() {
242 if f.distance(p, pts) > EPSILON {
243 visible_faces.push(i);
244 }
245 }
246
247 let mut edge_counts = HashMap::new();
250 for &f_idx in &visible_faces {
251 let f = &faces[f_idx];
252 let edges = [(f.v[0], f.v[1]), (f.v[1], f.v[2]), (f.v[2], f.v[0])];
253 for &(u, v) in &edges {
254 *edge_counts.entry((u, v)).or_insert(0) += 1;
255 }
256 }
257
258 let mut horizon_edges = Vec::new();
259 for (&(u, v), &count) in &edge_counts {
260 if count == 1 && !edge_counts.contains_key(&(v, u)) {
261 horizon_edges.push((u, v));
262 }
263 }
264
265 let mut orphaned_points = Vec::new();
267 for &f_idx in &visible_faces {
268 for &idx in &faces[f_idx].outside_set {
269 if idx != best_pt {
270 orphaned_points.push(idx);
271 }
272 }
273 }
274
275 let mut new_faces = Vec::new();
277 for (i, f) in faces.into_iter().enumerate() {
278 if !visible_faces.contains(&i) {
279 new_faces.push(f);
280 }
281 }
282 faces = new_faces;
283
284 let mut new_face_indices = Vec::new();
286 for &(u, v) in &horizon_edges {
287 let new_face = HullFace::new(u, v, best_pt, pts);
288 faces.push(new_face);
289 new_face_indices.push(faces.len() - 1);
290 }
291
292 for idx in orphaned_points {
294 let pt = pts[idx];
295 let mut best_face = usize::MAX;
296 let mut best_d = EPSILON;
297
298 for &f_idx in &new_face_indices {
299 let d = faces[f_idx].distance(pt, pts);
300 if d > best_d {
301 best_d = d;
302 best_face = f_idx;
303 }
304 }
305
306 if best_face != usize::MAX {
307 faces[best_face].outside_set.push(idx);
308 }
309 }
310 }
311
312 let mut vertex_set = HashSet::new();
314 for f in &faces {
315 vertex_set.insert(f.v[0]);
316 vertex_set.insert(f.v[1]);
317 vertex_set.insert(f.v[2]);
318 }
319
320 let mut out_vertices = Vec::new();
321 let mut old_to_new = HashMap::new();
322
323 for old_idx in vertex_set {
324 out_vertices.push(pts[old_idx]);
325 old_to_new.insert(old_idx, (out_vertices.len() - 1) as u32);
326 }
327
328 let mut out_faces = Vec::new();
329 for f in &faces {
330 out_faces.push([
331 old_to_new[&f.v[0]],
332 old_to_new[&f.v[1]],
333 old_to_new[&f.v[2]],
334 ]);
335 }
336
337 ConvexHull {
338 vertices: out_vertices,
339 faces: out_faces,
340 }
341}