csg 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ #include "mesh.h"
2
+ #include "poly.h"
3
+
4
+ #ifndef __CMD_AUDIT_H
5
+ #define __CMD_AUDIT_H
6
+
7
+ int cmd_audit(int argc, char *argv[]);
8
+
9
+ #endif
@@ -1,11 +1,13 @@
1
1
  #include <string.h>
2
2
  #include "dbg.h"
3
3
 
4
+ #include "util.h"
4
5
  #include "commands.h"
5
6
  #include "mesh.h"
6
7
  #include "bsp.h"
7
8
  #include "bsp_mesh.h"
8
9
  #include "export.h"
10
+ #include "cmd_audit.h"
9
11
 
10
12
  typedef bsp_node_t* (*bsp_binary_op)(bsp_node_t *, bsp_node_t *);
11
13
 
@@ -26,14 +28,14 @@ bsp_node_t* bsp_binary_operation(char *path1, char *path2, bsp_binary_op op) {
26
28
  check(file1 != NULL, "Failed to read mesh from '%s'", path1);
27
29
  log_info("Loaded file: %s %d facets", path1, file1->poly_count(file1));
28
30
  bsp1 = mesh_to_bsp(file1);
29
- check_mem(bsp1);
31
+ assert_mem(bsp1);
30
32
 
31
33
  // Read 2
32
34
  file2 = mesh_read_file(path2);
33
35
  check(file2 != NULL, "Failed to read mesh from '%s'", path2);
34
36
  log_info("Loaded file: %s %d facets", path2, file2->poly_count(file2));
35
37
  bsp2 = mesh_to_bsp(file2);
36
- check_mem(bsp2);
38
+ assert_mem(bsp2);
37
39
 
38
40
  // Operate
39
41
  result = op(bsp1, bsp2);
@@ -89,11 +91,54 @@ MAKE_CSG_COMMAND(intersect);
89
91
  MAKE_CSG_COMMAND(union);
90
92
  MAKE_CSG_COMMAND(subtract);
91
93
 
94
+ // Commands that exist only when built with `DEBUG` defined
95
+ // these generally don't do anything useful
96
+ #ifdef DEBUG
97
+ int cmd_DEBUG_bsp(int argc, char **argv) {
98
+ const char *suffix = ".bsp.stl";
99
+ char *name = argv[0];
100
+ char *out_name = NULL;
101
+ mesh_t *in = NULL;
102
+ mesh_t *out = NULL;
103
+ bsp_node_t *bsp = NULL;
104
+ check(argc >= 1, "Too few args");
105
+ assert_mem(out_name = calloc(strlen(name) + strlen(suffix) + 1, 1));
106
+ check(sprintf(out_name, "%s%s", name, suffix) == (strlen(name) + strlen(suffix)), "Failed to build out name.");
107
+
108
+ check(in = mesh_read_file(name), "Failed to READ");
109
+ check(bsp = in->to_bsp(in), "Failed to BSP");
110
+ check(out = bsp_to_mesh(bsp, 0), "Failed to BSP->mesh wrap");
111
+ bsp = NULL; // Make it obvs that out now holds the ref
112
+
113
+ log_info("Read: [%s]", argv[0]);
114
+ log_info("Poly Count: [%d]", in->poly_count(in));
115
+ log_info("BSP: [%p]", out);
116
+ log_info("BSP Poly Count: [%d]", out->poly_count(out));
117
+ log_info("Write: [%s](%d)", out_name, out->write(out, out_name, "STL"));
118
+
119
+ if(out != NULL) out->destroy(out);
120
+ if(in != NULL) in->destroy(in);
121
+ if(out_name != NULL) free(out_name);
122
+ return 0;
123
+ error:
124
+ if(out != NULL) out->destroy(out);
125
+ if(in != NULL) in->destroy(in);
126
+ if(out_name != NULL) free(out_name);
127
+ return -1;
128
+ }
129
+ #endif
130
+
92
131
  // Available commands
93
132
  const cmd_t commands[] = {
94
133
  {"intersect", "Intersect two geometries", cmd_intersect},
95
134
  {"subtract", "Subtract two geometries", cmd_subtract},
96
135
  {"union", "Union two geometries", cmd_union},
136
+ {"audit", "Audit a mesh on disk for errors", cmd_audit},
137
+
138
+ #ifdef DEBUG
139
+ {"bsp", "Identity through BSP", cmd_DEBUG_bsp},
140
+ #endif
141
+
97
142
  {NULL, NULL, NULL}
98
143
  };
99
144
 
@@ -1,9 +1,9 @@
1
+ #include "util.h"
1
2
  #include "export.h"
2
3
  #include "bsp_mesh.h"
3
4
 
4
5
  stl_object *stl_from_polys(klist_t(poly) *polygons) {
5
6
  stl_object *stl = stl_alloc(NULL, polygons->size);
6
- check_mem(stl);
7
7
 
8
8
  kliter_t(poly) *iter = kl_begin(polygons);
9
9
  stl_facet *facet = stl->facets;
@@ -49,13 +49,11 @@ bsp_node_t *stl_to_bsp(stl_object *stl) {
49
49
  poly = poly_make_triangle(stl->facets[i].vertices[0],
50
50
  stl->facets[i].vertices[1],
51
51
  stl->facets[i].vertices[2]);
52
- check_mem(polys);
53
52
  *kl_pushp(poly, polys) = poly;
54
53
  }
55
54
  check(polys->size == stl->facet_count, "Wrong number of faces generated.");
56
55
 
57
56
  tree = bsp_build(NULL, polys, 1);
58
- check_mem(tree);
59
57
 
60
58
  kl_destroy(poly, polys);
61
59
  return tree;
@@ -77,33 +75,56 @@ mesh_t* bsp_to_mesh(bsp_node_t* bsp, int copy) {
77
75
  return NEW(bsp_mesh_t, "BSP", input);
78
76
  }
79
77
 
80
- klist_t(poly)* polys_to_tris(klist_t(poly) *dst, klist_t(poly) *src) {
78
+ klist_t(poly) *poly_to_tris(klist_t(poly)* dst, poly_t *poly) {
81
79
  klist_t(poly) *result = dst;
82
80
  if(result == NULL) result = kl_init(poly);
83
81
 
84
- kliter_t(poly) *iter = NULL;
85
- for(iter = kl_begin(src); iter != kl_end(src); iter = kl_next(iter)) {
86
- poly_t *poly = kl_val(iter);
87
- int vertex_count = poly_vertex_count(poly);
82
+ int vertex_count = poly_vertex_count(poly);
88
83
 
89
- // Copy triangles, split higher-vertex polygons into triangle fans.
90
- if(vertex_count == 3) {
91
- poly_t *copy = clone_poly(poly);
92
- check_mem(copy);
93
- *kl_pushp(poly, result) = copy;
94
- }
95
- else if(vertex_count > 3) {
96
- float3 *v_cur, *v_prev;
97
- for(int i = 2; i < vertex_count; i++) {
98
- v_cur = &poly->vertices[i];
99
- v_prev = &poly->vertices[i - 1];
100
- poly_t *tri = poly_make_triangle(poly->vertices[0], *v_prev, *v_cur);
101
- check_mem(tri);
84
+ // Copy triangles, split higher-vertex polygons into triangle fans.
85
+ if(vertex_count == 3) {
86
+ *kl_pushp(poly, result) = clone_poly(poly);
87
+ }
88
+ else if(vertex_count > 3) {
89
+ float3 *v_cur, *v_prev;
90
+ for(int i = 2; i < vertex_count; i++) {
91
+ v_cur = &poly->vertices[i];
92
+ v_prev = &poly->vertices[i - 1];
93
+ poly_t *tri = poly_make_triangle(poly->vertices[0], *v_prev, *v_cur);
94
+
95
+ // If we don't create a valid polygon, don't include it in the result.
96
+ if(tri != NULL) {
102
97
  *kl_pushp(poly, result) = tri;
103
98
  }
104
- } else {
105
- sentinel("polygon(%p) has less than three(%d) vertices.", poly, poly_vertex_count(poly));
99
+ else {
100
+ #ifdef DEBUG
101
+ log_warn("Failed to build triangle:\n(%f, %f, %f)\n(%f, %f, %f)\n(%f, %f, %f)",
102
+ FLOAT3_FORMAT(poly->vertices[0]), FLOAT3_FORMAT(*v_prev), FLOAT3_FORMAT(*v_cur));
103
+ log_warn("Original:");
104
+ poly_print(poly, dbg_get_log());
105
+ #endif
106
+ }
106
107
  }
108
+ } else {
109
+ sentinel("polygon(%p) has less than three(%d) vertices.", poly, poly_vertex_count(poly));
110
+ }
111
+
112
+ return result;
113
+ error:
114
+ if(result != dst) if(result != NULL) kl_destroy(poly, result);
115
+ return NULL;
116
+ }
117
+
118
+ klist_t(poly)* polys_to_tris(klist_t(poly) *dst, klist_t(poly) *src) {
119
+ klist_t(poly) *result = dst;
120
+ if(result == NULL) result = kl_init(poly);
121
+
122
+ kliter_t(poly) *iter = NULL;
123
+ for(iter = kl_begin(src); iter != kl_end(src); iter = kl_next(iter)) {
124
+ poly_t *poly = kl_val(iter);
125
+ check(poly_to_tris(result, poly) != NULL,
126
+ "Failed to tesselate %p(%d) into triangles.",
127
+ poly, poly_vertex_count(poly));
107
128
  }
108
129
 
109
130
  return result;
@@ -15,6 +15,7 @@ stl_object *bsp_to_stl(bsp_node_t *tree);
15
15
  bsp_node_t *stl_to_bsp(stl_object *stl);
16
16
  bsp_node_t *mesh_to_bsp(mesh_t *mesh);
17
17
  mesh_t* bsp_to_mesh(bsp_node_t *tree, int copy);
18
+ klist_t(poly) *poly_to_tris(klist_t(poly)* dst, poly_t *src);
18
19
  klist_t(poly)* polys_to_tris(klist_t(poly)* dst, klist_t(poly)* src);
19
20
 
20
21
  #endif
data/src/poly.c CHANGED
@@ -1,14 +1,16 @@
1
+ #include <stdio.h>
1
2
  #include <assert.h>
3
+ #include <stdbool.h>
2
4
 
3
5
  #include "poly.h"
6
+ #include "util.h"
7
+ #include "export.h"
4
8
 
5
9
  poly_t *alloc_poly(void) {
6
10
  poly_t *poly = malloc(sizeof(poly_t));
7
- check_mem(poly);
11
+ assert_mem(poly);
8
12
  poly_init(poly);
9
13
  return poly;
10
- error:
11
- return NULL;
12
14
  }
13
15
 
14
16
  void free_poly(poly_t *p, int free_self) {
@@ -20,6 +22,24 @@ void free_poly(poly_t *p, int free_self) {
20
22
  if(free_self) free(p);
21
23
  }
22
24
 
25
+ void poly_print(poly_t *p, FILE *stream) {
26
+ fprintf(stream, "Poly(%p) Verts: %d Area: %f:\n", p, poly_vertex_count(p), poly_area(p));
27
+ for(int i = 0; i < poly_vertex_count(p); i++) {
28
+ fprintf(stream,"\tV[%d]: (%f, %f, %f)\n", i, FLOAT3_FORMAT(p->vertices[i]));
29
+ }
30
+ }
31
+
32
+ void poly_print_with_plane_info(poly_t *p, poly_t *plane, FILE *stream) {
33
+ fprintf(stream, "Poly(%p) w(%f) Verts: %d Area: %f:\n", p, p->w, poly_vertex_count(p), poly_area(p));
34
+ for(int i = 0; i < poly_vertex_count(p); i++) {
35
+ float3 diff = FLOAT3_INIT;
36
+ f3_sub(&diff, p->vertices[i], plane->vertices[0]);
37
+ float distance = f3_dot(plane->normal, diff);
38
+ fprintf(stream,"\tV[%d]: (%f, %f, %f) [%s] - %f from plane\n",
39
+ i, FLOAT3_FORMAT(p->vertices[i]), poly_classify_vertex_string(plane, p->vertices[i]), distance);
40
+ }
41
+ }
42
+
23
43
  poly_t *poly_init(poly_t *poly) {
24
44
  poly->vertex_count = 0;
25
45
  poly->vertex_max = POLY_MAX_VERTS;
@@ -28,8 +48,7 @@ poly_t *poly_init(poly_t *poly) {
28
48
  }
29
49
 
30
50
  poly_t *clone_poly(poly_t *poly) {
31
- poly_t *copy = NULL;
32
- check_mem(copy = alloc_poly());
51
+ poly_t *copy = alloc_poly();
33
52
  memcpy(copy, poly, sizeof(poly_t));
34
53
 
35
54
  // Either point the clone at its own copied
@@ -41,14 +60,11 @@ poly_t *clone_poly(poly_t *poly) {
41
60
  // We can lean on the `copy->*` memebers
42
61
  // since they would have been memcpy'd over
43
62
  copy->vertices = malloc(poly_vertex_max(copy) * sizeof(float3));
44
- check_mem(copy->vertices);
63
+ assert_mem(copy->vertices);
45
64
  memcpy(copy->vertices, poly->vertices, poly_vertex_max(copy) * sizeof(float3));
46
65
  }
47
66
 
48
67
  return copy;
49
- error:
50
- if(copy != NULL) free_poly(copy, 1);
51
- return NULL;
52
68
  }
53
69
 
54
70
  int poly_update(poly_t *poly) {
@@ -70,6 +86,65 @@ int poly_update(poly_t *poly) {
70
86
  return 0;
71
87
  }
72
88
 
89
+ // Return two times the area of a triangle.
90
+ // Avoids the division in half unless it's required to avoid
91
+ // failing `f > 0.0` when area is used as a predicate
92
+ float poly_triangle_2area(poly_t *triangle) {
93
+ if(poly_vertex_count(triangle) != 3) return NAN;
94
+
95
+ return triangle_2area(
96
+ triangle->vertices[0],
97
+ triangle->vertices[1],
98
+ triangle->vertices[2]
99
+ );
100
+ }
101
+
102
+ // The actual area of a triangle `triangle`
103
+ // Works through poly_triangle_2area
104
+ float poly_triangle_area(poly_t *triangle) {
105
+ return 0.5 * poly_triangle_2area(triangle);
106
+ }
107
+
108
+ float poly_area(poly_t *poly) {
109
+ return poly_2area(poly) / 2.0;
110
+ }
111
+
112
+ float poly_2area(poly_t *poly) {
113
+ float area2 = 0.0;
114
+ const int vertex_count = poly_vertex_count(poly);
115
+
116
+ // Sanity check that we have at least a polygon
117
+ if(vertex_count < 3) return NAN;
118
+
119
+ // Before we get into this tesselating bullshit, is this just a triangle?
120
+ if(vertex_count == 3) return poly_triangle_2area(poly);
121
+
122
+ // Break the poly into a triangle fan and sum the 2areas of the components
123
+ // Note that i = 2 on first iteration so that `i - 1` is defined and != 0
124
+ // This starts the loop on the first triangle in the poly.
125
+ // Since we're only caring about the magnitude of the cross inside
126
+ // triangle_2area, the vertex order doesn't matter.
127
+ for(int i = 2; i < vertex_count; i++) {
128
+ area2 += triangle_2area(
129
+ poly->vertices[0], // Root vertex
130
+ poly->vertices[i - 1], // Previous vertex
131
+ poly->vertices[i] // Current vertex
132
+ );
133
+ }
134
+
135
+
136
+ return area2;
137
+ }
138
+
139
+ bool poly_has_area(poly_t *poly) {
140
+ float area = poly_2area(poly);
141
+ check_debug(!isnan(area), "Polygon(%p) area is NaN", poly);
142
+
143
+ return area > 0.0;
144
+ error:
145
+ return false;
146
+ }
147
+
73
148
  int poly_vertex_count(poly_t *poly) {
74
149
  return poly->vertex_count;
75
150
  }
@@ -91,7 +166,7 @@ int poly_vertex_expand(poly_t *poly) {
91
166
  // Not using realloc because the original buffer may be struct-owned
92
167
  int new_size = poly->vertex_max * 2;
93
168
  float3 *new_verts = malloc(new_size * sizeof(float3));
94
- check_mem(new_verts);
169
+ assert_mem(new_verts);
95
170
 
96
171
  memcpy(new_verts, poly->vertices, poly->vertex_max * sizeof(float3));
97
172
  poly->vertex_max = new_size;
@@ -105,17 +180,28 @@ int poly_vertex_expand(poly_t *poly) {
105
180
  poly->vertices = new_verts;
106
181
 
107
182
  return 0;
108
- error:
109
- if(new_verts != NULL) free(new_verts);
110
- return -1;
111
183
  }
112
184
 
113
- // add a vertex to the end of the polygon vertex list
114
- int poly_push_vertex(poly_t *poly, float3 v) {
185
+ // add a vertex to the end of the polygon vertex list, if
186
+ // `guard` is true, a check will be performed to reject
187
+ // verts that cause 0-length edges to appear.
188
+ bool poly_push_vertex_guarded(poly_t *poly, float3 v, bool guard) {
115
189
  if(poly_vertex_available(poly) == 0) {
116
190
  poly_vertex_expand(poly);
117
191
  }
118
192
 
193
+ // We only need to perform zero-length-edge checks if we are
194
+ // actually going to create an edge through this push.
195
+ if(guard && (poly_vertex_count(poly) > 0)) {
196
+ int last_idx = poly_vertex_count(poly) - 1;
197
+ bool duplicate_first = !(f3_distance2(poly->vertices[0], v) > 0.0);
198
+ bool duplicate_last = !(f3_distance2(poly->vertices[last_idx], v) > 0.0);
199
+
200
+ // Fail out the addition if we're adding a duplucate first or last vertex
201
+ // as the new last vert. This would create an edge of length zero.
202
+ if(duplicate_first || duplicate_last) return false;
203
+ }
204
+
119
205
  // Dat assignment copy
120
206
  poly->vertices[poly->vertex_count][0] = v[0];
121
207
  poly->vertices[poly->vertex_count][1] = v[1];
@@ -126,9 +212,20 @@ int poly_push_vertex(poly_t *poly, float3 v) {
126
212
  check(poly_update(poly) == 0, "Failed to update polygon during poly_push_vertex(%p)", poly);
127
213
  }
128
214
 
129
- return 0;
215
+ return true;
130
216
  error:
131
- return -1;
217
+ return false;
218
+ }
219
+
220
+ // The default interface to pushing a vertex, force the guard to on
221
+ bool poly_push_vertex(poly_t *poly, float3 v) {
222
+ return poly_push_vertex_guarded(poly, v, true);
223
+ }
224
+
225
+ // Unsafe poly push, forces the guard off, allowing 0-length edges
226
+ // to form. Useful in the `audit` command
227
+ bool poly_push_vertex_unsafe(poly_t *poly, float3 v) {
228
+ return poly_push_vertex_guarded(poly, v, false);
132
229
  }
133
230
 
134
231
  int poly_classify_vertex(poly_t *poly, float3 v) {
@@ -138,6 +235,21 @@ int poly_classify_vertex(poly_t *poly, float3 v) {
138
235
  return COPLANAR;
139
236
  }
140
237
 
238
+ const char* poly_classify_vertex_string(poly_t *poly, float3 v) {
239
+ const char *classification = "UNKNOWN";
240
+ switch(poly_classify_vertex(poly, v)) {
241
+ case FRONT:
242
+ classification = "FRONT";
243
+ break;
244
+ case BACK:
245
+ classification = "BACK";
246
+ break;
247
+ case COPLANAR:
248
+ classification = "COPLANAR";
249
+ }
250
+ return classification;
251
+ }
252
+
141
253
  int poly_classify_poly(poly_t *this, poly_t *other) {
142
254
  int front, back;
143
255
  int count = poly_vertex_count(other);
@@ -180,6 +292,10 @@ int poly_split(poly_t *divider, poly_t *poly, poly_t **front, poly_t **back) {
180
292
  int count = poly_vertex_count(poly);
181
293
  for(i = 0; i < count; i++) {
182
294
  j = (i + 1) % count;
295
+
296
+ // Fill v_cur[..] and v_next[..] with the values of
297
+ // the current (i) and next (j) vertex (x,y,z) data
298
+ // from `poly`
183
299
  for(int k = 0; k < 3; k++) {
184
300
  v_cur[k] = poly->vertices[i][k];
185
301
  v_next[k] = poly->vertices[j][k];
@@ -204,31 +320,38 @@ int poly_split(poly_t *divider, poly_t *poly, poly_t **front, poly_t **back) {
204
320
  float t = divider->w;
205
321
  t = t - f3_dot(divider->normal, v_cur);
206
322
  t = t / f3_dot(divider->normal, diff);
323
+ t = clampf(t, 0.0, 1.0);
207
324
 
208
325
  float3 mid_f = {v_cur[0], v_cur[1], v_cur[2]};
209
326
  f3_interpolate(&mid_f, v_cur, v_next, t);
210
327
 
211
- check(poly_push_vertex(*front, mid_f) == 0,
212
- "Failed to push midpoint to front poly(%p)", front);
213
- check(poly_push_vertex(*back, mid_f) == 0,
214
- "Failed to push midpoint to back poly(%p):", back);
328
+ poly_push_vertex(*front, mid_f);
329
+ poly_push_vertex(*back, mid_f);
215
330
  }
216
331
  }
217
332
 
333
+ // Clear any polygons that are not finished by this point
334
+ if((*front != NULL) && (poly_vertex_count(*front) < 3)) {
335
+ free_poly(*front, true);
336
+ *front = NULL;
337
+ }
338
+
339
+ if((*back != NULL) && (poly_vertex_count(*back) < 3)) {
340
+ free_poly(*back, true);
341
+ *back = NULL;
342
+ }
343
+
218
344
  return 0;
219
- error:
220
- return -1;
221
345
  }
222
346
 
223
- poly_t *poly_make_triangle(float3 a, float3 b, float3 c) {
224
- poly_t *p = NULL;
225
- check_mem(p = alloc_poly());
347
+ poly_t *poly_make_triangle_guarded(float3 a, float3 b, float3 c, bool guard) {
348
+ poly_t *p = alloc_poly();
226
349
 
227
- check(poly_push_vertex(p, a) == 0,
350
+ check_debug(poly_push_vertex_guarded(p, a, guard),
228
351
  "Failed to add vertex a to poly(%p): (%f, %f, %f)", p, FLOAT3_FORMAT(a));
229
- check(poly_push_vertex(p, b) == 0,
352
+ check_debug(poly_push_vertex_guarded(p, b, guard),
230
353
  "Failed to add vertex b to poly(%p): (%f, %f, %f)", p, FLOAT3_FORMAT(b));
231
- check(poly_push_vertex(p, c) == 0,
354
+ check_debug(poly_push_vertex_guarded(p, c, guard),
232
355
  "Failed to add vertex c to poly(%p): (%f, %f, %f)", p, FLOAT3_FORMAT(c));
233
356
 
234
357
  return p;
@@ -237,6 +360,14 @@ error:
237
360
  return NULL;
238
361
  }
239
362
 
363
+ poly_t *poly_make_triangle(float3 a, float3 b, float3 c) {
364
+ return poly_make_triangle_guarded(a, b, c, true);
365
+ }
366
+
367
+ poly_t *poly_make_triangle_unsafe(float3 a, float3 b, float3 c) {
368
+ return poly_make_triangle_guarded(a, b, c, false);
369
+ }
370
+
240
371
  poly_t *poly_invert(poly_t *poly) {
241
372
  f3_scale(&poly->normal, -1.0);
242
373
  poly->w *= -1.0;
@@ -262,3 +393,45 @@ poly_t *poly_invert(poly_t *poly) {
262
393
 
263
394
  return poly;
264
395
  }
396
+
397
+ // Compute the length of the lognest edge squared
398
+ float poly_max_edge_length2(poly_t *poly) {
399
+ const int count = poly_vertex_count(poly);
400
+ float longest = -INFINITY;
401
+
402
+ for(int i = 0; i < count; i++) {
403
+ int j = (i + 1) % count;
404
+ float d2 = f3_distance2(poly->vertices[i], poly->vertices[j]);
405
+
406
+ longest = (d2 > longest) ? d2 : longest;
407
+ }
408
+
409
+ return longest;
410
+ }
411
+
412
+ float poly_min_edge_length2(poly_t *poly) {
413
+ const int count = poly_vertex_count(poly);
414
+ float min = INFINITY;
415
+
416
+ for(int i = 0; i < count; i++) {
417
+ int j = (i + 1) % count;
418
+ float d2 = f3_distance2(poly->vertices[i], poly->vertices[j]);
419
+
420
+ min = (d2 < min) ? d2 : min;
421
+ }
422
+
423
+ return min;
424
+ }
425
+
426
+ float triangle_2area(float3 a, float3 b, float3 c) {
427
+ float3 b_a = FLOAT3_INIT;
428
+ float3 c_a = FLOAT3_INIT;
429
+
430
+ f3_sub(&b_a, b, a);
431
+ f3_sub(&c_a, c, a);
432
+
433
+ float3 cross = FLOAT3_INIT;
434
+ f3_cross(&cross, b_a, c_a);
435
+
436
+ return f3_magnitude(&cross);
437
+ }