csg 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }