csg 0.0.0

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.
data/Makefile ADDED
@@ -0,0 +1,41 @@
1
+ #-*- mode:makefile-gmake; -*-
2
+ ROOT = $(shell pwd)
3
+ TARGET = csgtool
4
+
5
+ INCLUDE += -I$(ROOT)/src
6
+ SOURCES = $(wildcard $(ROOT)/src/*.c)
7
+
8
+ OBJS = $(patsubst %.c,%.o,$(SOURCES))
9
+ CPPFLAGS = $(OPTCPPFLAGS)
10
+ LIBS = -lm $(OPTLIBS)
11
+ CFLAGS = -g -std=c99 $(INCLUDE) -Wall -Werror $(OPTFLAGS)
12
+
13
+ ifeq ($(shell uname),Darwin)
14
+ LIB_TARGET = libcsg.dylib
15
+ else
16
+ LIB_TARGET = libcsg.so
17
+ endif
18
+
19
+ .DEFAULT_GOAL = all
20
+ all: $(TARGET) $(LIB_TARGET)
21
+
22
+ clean:
23
+ make -C tests clean
24
+ rm -rf $(OBJS) $(TARGET) $(TARGET).o $(TARGET).new $(LIB_TARGET)
25
+
26
+ test:
27
+ @make -C tests clean test
28
+
29
+ .PHONY: all clean test libcsg
30
+
31
+ $(TARGET): $(TARGET).o $(OBJS)
32
+ $(CC) $(CFLAGS) $^ $(LIBS) -o $@.new
33
+ mv $@.new $@
34
+
35
+ $(LIB_TARGET): $(OBJS)
36
+ $(CC) -shared $(OBJS) $(LIBS) -o $(LIB_TARGET)
37
+
38
+ libcsg: $(LIB_TARGET)
39
+
40
+ %.o: %.c
41
+ $(CC) -fPIC $(CFLAGS) -o $@ -c $^
data/ext/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+
2
+ desc 'build library'
3
+ task :default do
4
+ root = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)), ".."))
5
+ system("make -C #{root} libcsg")
6
+ end
data/lib/csg.rb ADDED
@@ -0,0 +1,83 @@
1
+ require 'ffi'
2
+
3
+ module CSG
4
+ module Native
5
+ extend FFI::Library
6
+
7
+ candidates = ['.bundle', '.so', '.dylib', ''].map do |ext|
8
+ File.join(File.expand_path("../..", __FILE__), 'libcsg' + ext)
9
+ end
10
+ ffi_lib ['csg'] + candidates
11
+
12
+ class STLObject < FFI::ManagedStruct
13
+ layout :header, [:uint8, 80],
14
+ :facet_count, :uint32,
15
+ :stl_facet, :pointer
16
+
17
+ def self.release(ptr)
18
+ CSG::Native.stl_free ptr
19
+ end
20
+
21
+ def write_file(path)
22
+ CSG::Native.stl_write_file self, path
23
+ end
24
+ end
25
+
26
+ class BSPNode < FFI::ManagedStruct
27
+ layout :polygons, :pointer,
28
+ :divider, :pointer,
29
+ :front, :pointer,
30
+ :back, :pointer
31
+
32
+ def self.release(ptr)
33
+ CSG::Native.free_bsp_tree ptr
34
+ end
35
+ end
36
+
37
+ attach_function :stl_read_file, [:string, :bool], :pointer
38
+ attach_function :stl_write_file, [:pointer, :string], :int
39
+
40
+ attach_function :stl_free, [:pointer], :void
41
+ attach_function :free_bsp_tree, [:pointer], :void
42
+
43
+ attach_function :stl_to_bsp, [:pointer], :pointer
44
+ attach_function :bsp_to_stl, [:pointer], :pointer
45
+
46
+ attach_function :bsp_union, [:pointer, :pointer], :pointer
47
+ attach_function :bsp_subtract, [:pointer, :pointer], :pointer
48
+ attach_function :bsp_intersect, [:pointer, :pointer], :pointer
49
+ end
50
+
51
+ class Solid
52
+ attr_reader :tree
53
+
54
+ def initialize(opts)
55
+ if opts[:file]
56
+ load_from_file opts[:file]
57
+ elsif opts[:tree]
58
+ @tree = opts[:tree]
59
+ end
60
+ raise ArgumentError.new "Failed to load tree with: #{opts.inspect}" unless @tree
61
+ end
62
+
63
+ def load_from_file(path)
64
+ File.stat(path) # Stat before load to raise a sane "Does not exist" error
65
+ stl = CSG::Native::STLObject.new CSG::Native.stl_read_file(path, false)
66
+ @tree = CSG::Native::BSPNode.new CSG::Native.stl_to_bsp(stl)
67
+ end
68
+
69
+ def to_stl
70
+ ptr = CSG::Native.bsp_to_stl tree
71
+ CSG::Native::STLObject.new(ptr)
72
+ end
73
+
74
+ # Build the CSG methods with ManagedStruct wrappers
75
+ [:intersect, :subtract, :union].each do |name|
76
+ define_method name do |solid|
77
+ ptr = CSG::Native.send "bsp_#{name}", tree, solid.tree
78
+ tree = CSG::Native::BSPNode.new(ptr)
79
+ CSG::Solid.new :tree => tree
80
+ end
81
+ end
82
+ end
83
+ end
data/src/bsp.c ADDED
@@ -0,0 +1,555 @@
1
+ #include <assert.h>
2
+
3
+ #include "bsp.h"
4
+ #include "dbg.h"
5
+
6
+ bsp_node_t *alloc_bsp_node(void) {
7
+ bsp_node_t *node = NULL;
8
+ check_mem(node = calloc(1, sizeof(bsp_node_t)));
9
+ node->polygons = kl_init(poly);
10
+ return node;
11
+ error:
12
+ return NULL;
13
+ }
14
+
15
+ bsp_node_t *clone_bsp_tree(bsp_node_t *tree) {
16
+ bsp_node_t *copy = alloc_bsp_node();
17
+ check_mem(copy);
18
+
19
+ kliter_t(poly) *iter = kl_begin(tree->polygons);
20
+ for(; iter != kl_end(tree->polygons); iter = kl_next(iter)) {
21
+ poly_t *poly_copy = clone_poly(kl_val(iter));
22
+ check_mem(poly_copy);
23
+ *kl_pushp(poly, copy->polygons) = poly_copy;
24
+ }
25
+
26
+ free_poly(copy->divider, 1);
27
+ copy->divider = clone_poly(tree->divider);
28
+ check_mem(copy->divider);
29
+
30
+ if(tree->front != NULL) {
31
+ copy->front = clone_bsp_tree(tree->front);
32
+ check_mem(copy->front);
33
+ }
34
+ if(tree->back != NULL) {
35
+ copy->back = clone_bsp_tree(tree->back);
36
+ check_mem(copy->back);
37
+ }
38
+
39
+ return copy;
40
+ error:
41
+ if(copy != NULL) free_bsp_tree(copy);
42
+ return NULL;
43
+ }
44
+
45
+ void free_bsp_node(bsp_node_t *node) {
46
+ if(node == NULL) return;
47
+ kl_destroy(poly, node->polygons);
48
+ free_poly(node->divider, 1);
49
+ free(node);
50
+ }
51
+
52
+ void free_bsp_tree(bsp_node_t *tree) {
53
+ if(tree == NULL) return;
54
+ if(tree->front != NULL) free_bsp_tree(tree->front);
55
+ if(tree->back != NULL) free_bsp_tree(tree->back);
56
+ free_bsp_node(tree);
57
+ }
58
+
59
+ // Put the polygon in the the appropriate list
60
+ // and increment the counter assosiated with it.
61
+ // A polygon can end up in muliple lists, but not
62
+ // all of them.
63
+ int bsp_subdivide(poly_t *divider, poly_t *poly,
64
+ poly_t **coplanar_front, int *n_cp_front,
65
+ poly_t **coplanar_back, int *n_cp_back,
66
+ poly_t **front, int *n_front,
67
+ poly_t **back, int *n_back) {
68
+ switch(poly_classify_poly(divider, poly)) {
69
+ case FRONT:
70
+ front[*n_front] = poly;
71
+ *n_front += 1;
72
+ break;
73
+ case BACK:
74
+ back[*n_back] = poly;
75
+ *n_back += 1;
76
+ break;
77
+ case COPLANAR:
78
+ if(f3_dot(divider->normal, poly->normal) > 0) {
79
+ coplanar_front[*n_cp_front] = poly;
80
+ *n_cp_front += 1;
81
+ }
82
+ else {
83
+ coplanar_back[*n_cp_back] = poly;
84
+ *n_cp_back += 1;
85
+ }
86
+ break;
87
+ case SPANNING: {
88
+ poly_t *f = NULL;
89
+ poly_t *b = NULL;
90
+ check(poly_split(divider, poly, &f, &b) == 0,
91
+ "Failed to split polygon(%p) with divider(%p)", poly, divider);
92
+ front[*n_front] = f;
93
+ *n_front += 1;
94
+
95
+ back[*n_back] = b;
96
+ *n_back += 1;
97
+ break;
98
+ }
99
+ }
100
+
101
+ return 0;
102
+ error:
103
+ return -1;
104
+ }
105
+
106
+ bsp_node_t *bsp_build(bsp_node_t *node, klist_t(poly) *polygons, int copy) {
107
+ poly_t **polys = NULL;
108
+ check_mem(polys = malloc(sizeof(poly_t*) * polygons->size));
109
+
110
+ kliter_t(poly) *iter = NULL;
111
+ int i = 0;
112
+ for(iter = kl_begin(polygons); iter != kl_end(polygons); i++, iter = kl_next(iter)) {
113
+ poly_t *poly = NULL;
114
+ poly = copy ? clone_poly(kl_val(iter)) : kl_val(iter);
115
+ check(poly != NULL, "Failed to make poly array. Item %d is NULL in list %p. (Copy: %s)",
116
+ i, polygons, copy ? "true" : "false");
117
+ polys[i] = poly;
118
+ }
119
+
120
+ check((node = bsp_build_array(node, polys, polygons->size)),
121
+ "Failed to build node from list(%p) of %zd polys", polygons, polygons->size);
122
+ free(polys);
123
+
124
+ return node;
125
+ error:
126
+ if(polys) free(polys);
127
+ return NULL;
128
+ }
129
+ bsp_node_t *bsp_build_array(bsp_node_t *node, poly_t **polygons, size_t n_polys) {
130
+ int rc = 0;
131
+
132
+ // Polygon lists and counters
133
+ int n_coplanar = 0;
134
+ int n_front = 0;
135
+ int n_back = 0;
136
+ poly_t **coplanar = NULL;
137
+ poly_t **front_p = NULL;
138
+ poly_t **back_p = NULL;
139
+
140
+ // Iterators
141
+ poly_t *poly = NULL;
142
+ size_t poly_i = 0;
143
+
144
+ if(node == NULL) {
145
+ // Allocate a node if we weren't given one. It's the nice
146
+ // thing to do for people.
147
+ node = alloc_bsp_node();
148
+ check_mem(node);
149
+ }
150
+
151
+ if(n_polys == 0) return node;
152
+
153
+ if(node->divider == NULL) {
154
+ // Add the divider to the list of coplanar polygons
155
+ // and advance the iterator to the next polygon
156
+ // if we have not yet picked the divider for this node.
157
+ // This avoids having to rely on an explicit
158
+ // test of this node against itself in the loop below.
159
+ *kl_pushp(poly, node->polygons) = polygons[0];
160
+ poly_i += 1;
161
+
162
+ node->divider = clone_poly(polygons[0]);
163
+ check_mem(node->divider);
164
+ }
165
+
166
+
167
+ check_mem(coplanar = malloc(sizeof(poly_t*) * n_polys));
168
+ check_mem(front_p = malloc(sizeof(poly_t*) * n_polys));
169
+ check_mem(back_p = malloc(sizeof(poly_t*) * n_polys));
170
+ for(; poly_i < n_polys; poly_i++) {
171
+ poly = polygons[poly_i];
172
+ rc = bsp_subdivide(node->divider, poly,
173
+ coplanar, &n_coplanar,
174
+ coplanar, &n_coplanar,
175
+ front_p, &n_front,
176
+ back_p, &n_back);
177
+ check(rc == 0, "Failed to subdivide: %p => %p", node->divider, poly);
178
+ }
179
+
180
+ // Store the coplanar nodes in this node's polygon list
181
+ // and free the container, letting the list destructor
182
+ // clean up
183
+ int i = 0;
184
+ for(i = 0; i < n_coplanar; i++) {
185
+ *kl_pushp(poly, node->polygons) = coplanar[i];
186
+ }
187
+ free(coplanar);
188
+ coplanar = NULL;
189
+
190
+ if((n_front > 0)) {
191
+ if(node->front == NULL) node->front = alloc_bsp_node();
192
+ check_mem(node->front);
193
+ check(bsp_build_array(node->front, front_p, n_front) != NULL,
194
+ "Failed to build front tree of bsp_node_array(%p)", node);
195
+ }
196
+ if((n_back > 0)) {
197
+ if(node->back == NULL) node->back = alloc_bsp_node();
198
+ check_mem(node->back);
199
+ check(bsp_build_array(node->back, back_p, n_back) != NULL,
200
+ "Failed to build back tree of bsp_node(%p)", node);
201
+ }
202
+ free(front_p);
203
+ free(back_p);
204
+ front_p = back_p = NULL;
205
+
206
+ return node;
207
+ error:
208
+ if(coplanar) free(coplanar);
209
+ if(back_p) free(back_p);
210
+ if(front_p) free(front_p);
211
+ return NULL;
212
+ }
213
+
214
+ int bsp_copy_node_polygons(bsp_node_t *node, int make_triangles, klist_t(poly) *dst) {
215
+ int copied = 0;
216
+ kliter_t(poly) *iter = kl_begin(node->polygons);
217
+ for(;iter != kl_end(node->polygons); iter = kl_next(iter)) {
218
+ poly_t *poly = kl_val(iter);
219
+ int vertex_count = poly_vertex_count(poly);
220
+ if(!make_triangles || vertex_count == 3) {
221
+ poly_t *copy = clone_poly(poly);
222
+ check_mem(copy);
223
+ *kl_pushp(poly, dst) = copy;
224
+ }
225
+ else if(vertex_count > 3){
226
+ // Start with the third vertex and build triangles
227
+ // in in the form (v0, v_prev, v_cur)
228
+ float3 *v_cur, *v_prev;
229
+ for(int i = 2; i < vertex_count; i++) {
230
+ v_cur = &poly->vertices[i];
231
+ v_prev = &poly->vertices[i - 1];
232
+ poly_t *tri = poly_make_triangle(poly->vertices[0], *v_prev, *v_cur);
233
+ check_mem(tri);
234
+ *kl_pushp(poly, dst) = tri;
235
+ }
236
+ }
237
+ else {
238
+ sentinel("polygon(%p) has less than three(%d) vertices.", poly, poly_vertex_count(poly));
239
+ }
240
+ copied++;
241
+ }
242
+ return copied;
243
+ error:
244
+ return -1;
245
+ }
246
+
247
+ klist_t(poly) *bsp_to_polygons(bsp_node_t *tree, int make_triangles, klist_t(poly) *dst) {
248
+ klist_t(poly) *polygons = dst ? dst : kl_init(poly);
249
+
250
+ if(tree->back != NULL) {
251
+ bsp_to_polygons(tree->back, make_triangles, polygons);
252
+ }
253
+
254
+ int rc = bsp_copy_node_polygons(tree, make_triangles, polygons);
255
+ check(rc == tree->polygons->size, "bsp_copy_node_polygons() did not copy all polygons");
256
+
257
+ if(tree->front != NULL) {
258
+ bsp_to_polygons(tree->front, make_triangles, polygons);
259
+ }
260
+
261
+ return polygons;
262
+ error:
263
+ // Only clean up the polygons list if we initialized it on error
264
+ if(dst == NULL) kl_destroy(poly, polygons);
265
+ return NULL;
266
+ }
267
+
268
+ bsp_node_t *bsp_invert(bsp_node_t *tree) {
269
+ // Invert every polygon in this node
270
+ poly_t *poly = NULL;
271
+ kliter_t(poly) *iter = kl_begin(tree->polygons);
272
+ for(; iter != kl_end(tree->polygons); iter = kl_next(iter)) {
273
+ poly = poly_invert(kl_val(iter));
274
+ check(poly != NULL, "Failed to invert polygon %p", kl_val(iter));
275
+ }
276
+
277
+ // Invert the divider
278
+ if(tree->divider) {
279
+ poly = poly_invert(tree->divider);
280
+ check(poly != NULL, "Failed to inverts bsp_node_t(%p)->divider(%p)", tree, tree->divider);
281
+ }
282
+
283
+ // Invert the front and back trees
284
+ bsp_node_t *node;
285
+ if(tree->front) {
286
+ node = bsp_invert(tree->front);
287
+ check(node != NULL, "Failed to invert back tree of bsp_node_t(%p)", tree->front);
288
+ }
289
+ if(tree->back) {
290
+ node = bsp_invert(tree->back);
291
+ check(node != NULL, "Failed to invert back tree of bsp_node_t(%p)", tree->back);
292
+ }
293
+
294
+ // Swap front and back trees
295
+ node = tree->front;
296
+ tree->front = tree->back;
297
+ tree->back = node;
298
+
299
+ return tree;
300
+ error:
301
+ return NULL;
302
+ }
303
+
304
+ klist_t(poly) *bsp_clip_polygon_array(bsp_node_t *node, poly_t **polygons, size_t n_polys, klist_t(poly) *dst) {
305
+ klist_t(poly) *result = dst != NULL ? dst : kl_init(poly);
306
+ size_t i;
307
+ poly_t *p = NULL;
308
+ int rc = -1;
309
+
310
+ poly_t **poly_buffer = NULL;
311
+ poly_t **front_array = NULL;
312
+ poly_t **back_array = NULL;
313
+ int n_front = 0;
314
+ int n_back = 0;
315
+
316
+ // Let's end this quick if there's nothing to do.
317
+ if(n_polys == 0) return result;
318
+
319
+ if(node->divider != NULL) {
320
+ check_mem(poly_buffer = malloc((sizeof(poly_t*) * n_polys) * 2));
321
+ front_array = poly_buffer;
322
+ back_array = poly_buffer + n_polys;
323
+ // Sort this node's polygons into the front or back
324
+ for(i = 0; i < n_polys; i++) {
325
+ p = polygons[i];
326
+ rc = bsp_subdivide(node->divider, p,
327
+ front_array, &n_front, back_array, &n_back,
328
+ front_array, &n_front, back_array, &n_back);
329
+ check(rc != -1, "Failed to subdivide poly %p", p);
330
+ }
331
+
332
+ int i;
333
+ poly_t *copy = NULL;
334
+ // Recur to the front tree, or copy my current front nodes to result.
335
+ if(node->front) {
336
+ result = bsp_clip_polygon_array(node->front, front_array, n_front, result);
337
+ check(result != NULL, "Failed to clip front tree");
338
+ }
339
+ else {
340
+ for(i = 0; i < n_front; i++) {
341
+ copy = clone_poly(front_array[i]);
342
+ check_mem(copy);
343
+ *kl_pushp(poly, result) = copy;
344
+ }
345
+ }
346
+
347
+ // Repeat for the back tree
348
+ if(node->back) {
349
+ result = bsp_clip_polygon_array(node->back, back_array, n_back, result);
350
+ check(result != NULL, "Failed to clip back tree");
351
+ }
352
+
353
+ if(poly_buffer) free(poly_buffer);
354
+ // Clean up the result halves, now that they're copied into `result`
355
+ }
356
+ else {
357
+ // If we don't have a divider we just copy out the polygons
358
+ for(i = 0; i < n_polys; i++) {
359
+ check_mem(p = clone_poly(polygons[i]));
360
+ *kl_pushp(poly, result) = p;
361
+ }
362
+ }
363
+
364
+ return result;
365
+ error:
366
+ if(poly_buffer) free(poly_buffer);
367
+ if(result) kl_destroy(poly, result);
368
+ return NULL;
369
+ }
370
+
371
+ klist_t(poly) *bsp_clip_polygons(bsp_node_t *node, klist_t(poly) *polygons, klist_t(poly) *dst) {
372
+ klist_t(poly) *result = dst != NULL ? dst : kl_init(poly);
373
+ kliter_t(poly) *iter = NULL;
374
+ poly_t *p = NULL;
375
+ int rc = -1;
376
+
377
+ poly_t **poly_buffer = NULL;
378
+ poly_t **front_array = NULL;
379
+ poly_t **back_array = NULL;
380
+ int n_front = 0;
381
+ int n_back = 0;
382
+
383
+ // Let's end this quick if there's nothing to do.
384
+ if(polygons->size == 0) return result;
385
+
386
+ if(node->divider != NULL) {
387
+ check_mem(poly_buffer = malloc(sizeof(poly_t*) * polygons->size * 2));
388
+ front_array = poly_buffer;
389
+ back_array = poly_buffer + polygons->size;
390
+ // Sort this node's polygons into the front or back
391
+ for(iter = kl_begin(polygons); iter != kl_end(polygons); iter = kl_next(iter)) {
392
+ rc = bsp_subdivide(node->divider, kl_val(iter),
393
+ front_array, &n_front, back_array, &n_back,
394
+ front_array, &n_front, back_array, &n_back);
395
+ check(rc != -1, "Failed to subdivide poly %p", kl_val(iter));
396
+ }
397
+
398
+ int i;
399
+ poly_t *copy = NULL;
400
+ // Recur to the front tree, or copy my current front nodes to result.
401
+ if(node->front) {
402
+ result = bsp_clip_polygon_array(node->front, front_array, n_front, result);
403
+ check(result != NULL, "Failed to clip front tree");
404
+ }
405
+ else {
406
+ for(i = 0; i < n_front; i++) {
407
+ copy = clone_poly(front_array[i]);
408
+ check_mem(copy);
409
+ *kl_pushp(poly, result) = copy;
410
+ }
411
+ }
412
+
413
+ // Repeat for the back tree
414
+ if(node->back) {
415
+ result = bsp_clip_polygon_array(node->back, back_array, n_back, result);
416
+ check(result != NULL, "Failed to clip back tree");
417
+ }
418
+
419
+ if(poly_buffer) free(poly_buffer);
420
+ // Clean up the result halves, now that they're copied into `result`
421
+ }
422
+ else {
423
+ // If we don't have a divider we just copy out the polygons
424
+ for(iter = kl_begin(polygons); iter != kl_end(polygons); iter = kl_next(iter)) {
425
+ check_mem(p = clone_poly(kl_val(iter)));
426
+ *kl_pushp(poly, result) = p;
427
+ }
428
+ }
429
+
430
+ return result;
431
+ error:
432
+ if(poly_buffer) free(poly_buffer);
433
+ if(result) kl_destroy(poly, result);
434
+ return NULL;
435
+ }
436
+
437
+ bsp_node_t *bsp_clip(bsp_node_t *us, bsp_node_t *them) {
438
+ klist_t(poly) *new = bsp_clip_polygons(them, us->polygons, NULL);
439
+ kl_destroy(poly, us->polygons);
440
+ us->polygons = new;
441
+
442
+ if(us->front != NULL) bsp_clip(us->front, them);
443
+ if(us->back != NULL) bsp_clip(us->back, them);
444
+
445
+ return us;
446
+ }
447
+
448
+ bsp_node_t *bsp_subtract(bsp_node_t *tree_a, bsp_node_t *tree_b) {
449
+ bsp_node_t *a = NULL;
450
+ bsp_node_t *b = NULL;
451
+ bsp_node_t *result = NULL;
452
+ klist_t(poly) *b_polys = NULL;
453
+
454
+ check_mem(a = clone_bsp_tree(tree_a));
455
+ check_mem(b = clone_bsp_tree(tree_b));
456
+
457
+ check(bsp_invert(a) != NULL, "Failed to invert A");
458
+ check(bsp_clip(a, b) != NULL, "Failed to clip(A, B)");
459
+ check(bsp_clip(b, a) != NULL, "Failed clip(B, A)");
460
+ check(bsp_invert(b) != NULL, "Failed to invert B")
461
+ check(bsp_clip(b, a) != NULL, "Failed to clio(B, A)");
462
+ check(bsp_invert(b) != NULL, "Failed to invert B")
463
+ b_polys = bsp_to_polygons(b, 0, NULL);
464
+ check(b_polys != NULL, "Failed to get polygons from B");
465
+ check(bsp_build(a, b_polys, 1) == a, "Failed to add nodes from B into tree A");
466
+ check(bsp_invert(a) != NULL, "Failed to invert A")
467
+
468
+ // TODO: Build a more balanced trees from the polys of
469
+ // a instead of cloning a tree with potential gaps.
470
+ result = clone_bsp_tree(a);
471
+ check_mem(result);
472
+
473
+ if(b_polys != NULL) kl_destroy(poly, b_polys);
474
+ if(a != NULL) free_bsp_tree(a);
475
+ if(b != NULL) free_bsp_tree(b);
476
+ return result;
477
+ error:
478
+ if(b_polys != NULL) kl_destroy(poly, b_polys);
479
+ if(a != NULL) free_bsp_tree(a);
480
+ if(b != NULL) free_bsp_tree(b);
481
+ if(result != NULL) free_bsp_tree(result);
482
+ return NULL;
483
+ }
484
+
485
+ bsp_node_t *bsp_union(bsp_node_t *tree_a, bsp_node_t *tree_b) {
486
+ bsp_node_t *a = NULL;
487
+ bsp_node_t *b = NULL;
488
+ bsp_node_t *result = NULL;
489
+ klist_t(poly) *b_polys = NULL;
490
+
491
+ check_mem(a = clone_bsp_tree(tree_a));
492
+ check_mem(b = clone_bsp_tree(tree_b));
493
+
494
+ check(bsp_clip(a, b) != NULL, "Failed to clip(A, B)");
495
+ check(bsp_clip(b, a) != NULL, "Failed clip(B, A)");
496
+ check(bsp_invert(b) != NULL, "Failed to invert B")
497
+ check(bsp_clip(b, a) != NULL, "Failed to clio(B, A)");
498
+ check(bsp_invert(b) != NULL, "Failed to invert B")
499
+ b_polys = bsp_to_polygons(b, 0, NULL);
500
+ check(b_polys != NULL, "Failed to get polygons from B");
501
+ check(bsp_build(a, b_polys, 1) == a, "Failed to add nodes from B into tree A");
502
+
503
+ // TODO: Build a more balanced trees from the polys of
504
+ // a instead of cloning a tree with potential gaps.
505
+ result = clone_bsp_tree(a);
506
+ check_mem(result);
507
+
508
+ if(b_polys != NULL) kl_destroy(poly, b_polys);
509
+ if(a != NULL) free_bsp_tree(a);
510
+ if(b != NULL) free_bsp_tree(b);
511
+ return result;
512
+ error:
513
+ if(b_polys != NULL) kl_destroy(poly, b_polys);
514
+ if(a != NULL) free_bsp_tree(a);
515
+ if(b != NULL) free_bsp_tree(b);
516
+ if(result != NULL) free_bsp_tree(result);
517
+ return NULL;
518
+ }
519
+
520
+ bsp_node_t *bsp_intersect(bsp_node_t *tree_a, bsp_node_t *tree_b) {
521
+ bsp_node_t *a = NULL;
522
+ bsp_node_t *b = NULL;
523
+ bsp_node_t *result = NULL;
524
+ klist_t(poly) *b_polys = NULL;
525
+
526
+ check_mem(a = clone_bsp_tree(tree_a));
527
+ check_mem(b = clone_bsp_tree(tree_b));
528
+
529
+ check(bsp_invert(a) != NULL, "Failed to invert A");
530
+ check(bsp_clip(b, a) != NULL, "Failed clip(B, A)");
531
+ check(bsp_invert(b) != NULL, "Failed to invert B");
532
+ check(bsp_clip(a, b) != NULL, "Failed to clip(A, B)");
533
+ check(bsp_clip(b, a) != NULL, "Failed to clio(B, A)");
534
+
535
+ b_polys = bsp_to_polygons(b, 0, NULL);
536
+ check(b_polys != NULL, "Failed to get polygons from B");
537
+ check(bsp_build(a, b_polys, 1) == a, "Failed to add nodes from B into tree A");
538
+ check(bsp_invert(a) == a, "Failed to invert tree A");
539
+
540
+ // TODO: Build a more balanced trees from the polys of
541
+ // a instead of cloning a tree with potential gaps.
542
+ result = clone_bsp_tree(a);
543
+ check_mem(result);
544
+
545
+ if(b_polys != NULL) kl_destroy(poly, b_polys);
546
+ if(a != NULL) free_bsp_tree(a);
547
+ if(b != NULL) free_bsp_tree(b);
548
+ return result;
549
+ error:
550
+ if(b_polys != NULL) kl_destroy(poly, b_polys);
551
+ if(a != NULL) free_bsp_tree(a);
552
+ if(b != NULL) free_bsp_tree(b);
553
+ if(result != NULL) free_bsp_tree(result);
554
+ return NULL;
555
+ }