csg 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/csg.rb +56 -29
- data/src/bsp.c +48 -0
- data/src/bsp_mesh.h +12 -0
- data/src/commands.c +24 -23
- data/src/export.c +46 -0
- data/src/export.h +4 -0
- data/src/mesh.c +113 -0
- data/src/mesh.h +34 -0
- data/src/reader.c +49 -0
- data/src/reader.h +22 -0
- data/src/stl.c +53 -0
- data/src/stl.h +9 -2
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cab611b90101912c713c832df26091680edf4d3d
|
4
|
+
data.tar.gz: 52de356c4a37b4032ae815bef964731eb8872c07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11b9ffd8267d6cfa66c30f02ac8dc62db7debdc1aac0ebe91401d317b682cde589ac075ae5b5fc2e635904d6b36bfe2a677b0876597035b8d4eae22cf641a556
|
7
|
+
data.tar.gz: 56e441bbe90cc44e96d551dcd1224149f276164389ab4ec277cfa32998d8416901cfd8c6c384a28deb5d6d3e100f9a821462663e6bb644b73b96462158e90365
|
data/lib/csg.rb
CHANGED
@@ -9,20 +9,26 @@ module CSG
|
|
9
9
|
end
|
10
10
|
ffi_lib ['csg'] + candidates
|
11
11
|
|
12
|
-
class
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
class Mesh < FFI::ManagedStruct
|
13
|
+
# WARNING: You should probably never call `:destroy`, because it's a managed
|
14
|
+
# struct, and should get destroyed by Ruby using this very method
|
15
|
+
# inside of `Mesh.release`
|
16
|
+
layout :type, [:uint8, 4],
|
17
|
+
:init, callback([:pointer, :pointer], :int),
|
18
|
+
:destroy, callback([:pointer], :void),
|
19
|
+
:poly_count, callback([:pointer], :int),
|
20
|
+
:to_polygons, callback([:pointer], :pointer),
|
21
|
+
:to_bsp, callback([:pointer], :pointer),
|
22
|
+
:write, callback([:pointer, :string, :string], :int)
|
16
23
|
|
17
24
|
def self.release(ptr)
|
18
|
-
CSG::Native.
|
19
|
-
end
|
20
|
-
|
21
|
-
def write_file(path)
|
22
|
-
CSG::Native.stl_write_file self, path
|
25
|
+
CSG::Native.destroy_mesh ptr
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
29
|
+
attach_function :destroy_mesh, [:pointer], :void
|
30
|
+
attach_function :mesh_read_file, [:string], Mesh
|
31
|
+
|
26
32
|
class BSPNode < FFI::ManagedStruct
|
27
33
|
layout :polygons, :pointer,
|
28
34
|
:divider, :pointer,
|
@@ -34,50 +40,71 @@ module CSG
|
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
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
43
|
attach_function :free_bsp_tree, [:pointer], :void
|
42
|
-
|
43
|
-
attach_function :stl_to_bsp, [:pointer], :pointer
|
44
|
-
attach_function :bsp_to_stl, [:pointer], :pointer
|
44
|
+
attach_function :mesh_to_bsp, [Mesh], BSPNode
|
45
45
|
|
46
46
|
attach_function :bsp_union, [:pointer, :pointer], :pointer
|
47
47
|
attach_function :bsp_subtract, [:pointer, :pointer], :pointer
|
48
48
|
attach_function :bsp_intersect, [:pointer, :pointer], :pointer
|
49
|
+
|
50
|
+
attach_function :bsp_to_mesh, [:pointer, :int], :pointer
|
49
51
|
end
|
52
|
+
end
|
50
53
|
|
54
|
+
module CSG
|
51
55
|
class Solid
|
52
|
-
attr_reader :
|
56
|
+
attr_reader :mesh
|
53
57
|
|
54
58
|
def initialize(opts)
|
55
59
|
if opts[:file]
|
56
60
|
load_from_file opts[:file]
|
57
|
-
elsif opts[:
|
58
|
-
@
|
61
|
+
elsif opts[:mesh]
|
62
|
+
@mesh = opts[:mesh]
|
59
63
|
end
|
60
|
-
raise ArgumentError.new "Failed to load
|
64
|
+
raise ArgumentError.new "Failed to load mesh with: #{opts.inspect}" unless @mesh
|
61
65
|
end
|
62
66
|
|
63
67
|
def load_from_file(path)
|
64
68
|
File.stat(path) # Stat before load to raise a sane "Does not exist" error
|
65
|
-
|
66
|
-
|
69
|
+
mesh_ptr = CSG::Native::mesh_read_file path
|
70
|
+
if not mesh_ptr.null?
|
71
|
+
@mesh = CSG::Native::Mesh.new mesh_ptr
|
72
|
+
else
|
73
|
+
raise Exception.new("Failed to produce Mesh from #{path}")
|
74
|
+
end
|
67
75
|
end
|
68
76
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
77
|
+
def write(path)
|
78
|
+
rc = mesh[:write].call(mesh, path, "STL")
|
79
|
+
raise Exception.new("Failed to write to '#{path}") if rc != 0
|
72
80
|
end
|
73
81
|
|
74
|
-
# Build the CSG methods with ManagedStruct wrappers
|
75
82
|
[:intersect, :subtract, :union].each do |name|
|
76
83
|
define_method name do |solid|
|
77
|
-
|
78
|
-
|
79
|
-
|
84
|
+
# I'm so paranoid because ruby will gladly FFI through a
|
85
|
+
# *NULL and explode if you ask, and that's much worse.
|
86
|
+
raise Exception.new("The calling mesh is a NULL pointer") if mesh.null?
|
87
|
+
raise Exception.new("The parameter mesh is a NULL pointer") if solid.mesh.null?
|
88
|
+
|
89
|
+
raise Exception.new("My BSP tree is NULL.") if (my_bsp_ptr = mesh[:to_bsp].call mesh).null?
|
90
|
+
# Assign ASAP in case the exception triggers an unwind, and I want the GC to know about this
|
91
|
+
my_bsp = CSG::Native::BSPNode.new(my_bsp_ptr)
|
92
|
+
|
93
|
+
raise Exception.new("My BSP tree is NULL.") if (their_bsp_ptr = solid.mesh[:to_bsp].call solid.mesh).null?
|
94
|
+
their_bsp = CSG::Native::BSPNode.new(their_bsp_ptr)
|
95
|
+
|
96
|
+
result_ptr = CSG::Native.send "bsp_#{name}", my_bsp, their_bsp
|
97
|
+
raise Exception.new("Result of #{name} is NULL") if result_ptr.null?
|
98
|
+
|
99
|
+
# We will not wrap the result in a CSG::Native::BSPNode because
|
100
|
+
# to avoid garbage collection, and we'll manage this pointer
|
101
|
+
# inside of the CSG::Native::Mesh object we get with
|
102
|
+
# bsp_to_mesh(.., 0) - which will not clone the input parameter
|
103
|
+
result_mesh_ptr = CSG::Native.bsp_to_mesh result_ptr, 0
|
104
|
+
raise Exception.new("Failed to wrap BSPNode(#{result_mesh_ptr} pointer as a Mesh, got NULL") if result_mesh_ptr.null?
|
105
|
+
CSG::Solid.new :mesh => CSG::Native::Mesh.new(result_mesh_ptr)
|
80
106
|
end
|
81
107
|
end
|
108
|
+
|
82
109
|
end
|
83
110
|
end
|
data/src/bsp.c
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
#include "bsp.h"
|
4
4
|
#include "dbg.h"
|
5
|
+
#include "export.h"
|
6
|
+
#include "bsp_mesh.h"
|
5
7
|
|
6
8
|
bsp_node_t *alloc_bsp_node(void) {
|
7
9
|
bsp_node_t *node = NULL;
|
@@ -627,3 +629,49 @@ error:
|
|
627
629
|
if(result != NULL) free_bsp_tree(result);
|
628
630
|
return NULL;
|
629
631
|
}
|
632
|
+
|
633
|
+
int bsp_count_polygons(bsp_node_t *tree) {
|
634
|
+
if(tree == NULL) return 0;
|
635
|
+
|
636
|
+
return (tree->polygons == NULL) ? 0 : tree->polygons->size +
|
637
|
+
bsp_count_polygons(tree->front) +
|
638
|
+
bsp_count_polygons(tree->back);
|
639
|
+
}
|
640
|
+
|
641
|
+
// bsp_node_t tree backed mesh methods and mesh prototype
|
642
|
+
int bsp_mesh_init(void *self, void *data) {
|
643
|
+
bsp_mesh_t *mesh = (bsp_mesh_t*)self;
|
644
|
+
if(data == NULL) {
|
645
|
+
check_mem(mesh->bsp = alloc_bsp_node());
|
646
|
+
}
|
647
|
+
else {
|
648
|
+
mesh->bsp = (bsp_node_t*)data;
|
649
|
+
}
|
650
|
+
return 0;
|
651
|
+
error:
|
652
|
+
return -1;
|
653
|
+
}
|
654
|
+
|
655
|
+
void bsp_mesh_destroy(void *self) {
|
656
|
+
bsp_mesh_t *mesh = (bsp_mesh_t*)self;
|
657
|
+
free_bsp_tree(mesh->bsp);
|
658
|
+
free(self);
|
659
|
+
}
|
660
|
+
|
661
|
+
int bsp_mesh_poly_count(void *self) {
|
662
|
+
bsp_mesh_t *mesh = (bsp_mesh_t*)self;
|
663
|
+
return bsp_count_polygons(mesh->bsp);
|
664
|
+
}
|
665
|
+
|
666
|
+
klist_t(poly)* bsp_mesh_to_polygons(void *self) {
|
667
|
+
bsp_mesh_t *mesh = (bsp_mesh_t*)self;
|
668
|
+
return bsp_to_polygons(mesh->bsp, 0, NULL);
|
669
|
+
}
|
670
|
+
|
671
|
+
// bsp_mesh_t prototype definition
|
672
|
+
mesh_t bsp_mesh_t_Proto = {
|
673
|
+
.init = bsp_mesh_init,
|
674
|
+
.destroy = bsp_mesh_destroy,
|
675
|
+
.poly_count = bsp_mesh_poly_count,
|
676
|
+
.to_polygons = bsp_mesh_to_polygons,
|
677
|
+
};
|
data/src/bsp_mesh.h
ADDED
data/src/commands.c
CHANGED
@@ -2,8 +2,9 @@
|
|
2
2
|
#include "dbg.h"
|
3
3
|
|
4
4
|
#include "commands.h"
|
5
|
-
#include "
|
5
|
+
#include "mesh.h"
|
6
6
|
#include "bsp.h"
|
7
|
+
#include "bsp_mesh.h"
|
7
8
|
#include "export.h"
|
8
9
|
|
9
10
|
typedef bsp_node_t* (*bsp_binary_op)(bsp_node_t *, bsp_node_t *);
|
@@ -12,39 +13,39 @@ typedef bsp_node_t* (*bsp_binary_op)(bsp_node_t *, bsp_node_t *);
|
|
12
13
|
// in `path1` and `path1` defined by an operation `op` from` bsp.h
|
13
14
|
// Result is freshly allocated, and needs to be freed with `free_bsp_tree()`
|
14
15
|
bsp_node_t* bsp_binary_operation(char *path1, char *path2, bsp_binary_op op) {
|
15
|
-
|
16
|
+
mesh_t *file1 = NULL;
|
16
17
|
bsp_node_t *bsp1 = NULL;
|
17
18
|
|
18
|
-
|
19
|
+
mesh_t *file2 = NULL;
|
19
20
|
bsp_node_t *bsp2 = NULL;
|
20
21
|
|
21
22
|
bsp_node_t *result = NULL;
|
22
23
|
|
23
24
|
// Read 1
|
24
|
-
file1 =
|
25
|
-
check(file1 != NULL, "Failed to read
|
26
|
-
log_info("Loaded file: %s %d facets", path1, file1->
|
27
|
-
bsp1 =
|
25
|
+
file1 = mesh_read_file(path1);
|
26
|
+
check(file1 != NULL, "Failed to read mesh from '%s'", path1);
|
27
|
+
log_info("Loaded file: %s %d facets", path1, file1->poly_count(file1));
|
28
|
+
bsp1 = mesh_to_bsp(file1);
|
28
29
|
check_mem(bsp1);
|
29
30
|
|
30
31
|
// Read 2
|
31
|
-
file2 =
|
32
|
-
check(file2 != NULL, "Failed to read
|
33
|
-
log_info("Loaded file: %s %d facets", path2, file2->
|
34
|
-
bsp2 =
|
32
|
+
file2 = mesh_read_file(path2);
|
33
|
+
check(file2 != NULL, "Failed to read mesh from '%s'", path2);
|
34
|
+
log_info("Loaded file: %s %d facets", path2, file2->poly_count(file2));
|
35
|
+
bsp2 = mesh_to_bsp(file2);
|
35
36
|
check_mem(bsp2);
|
36
37
|
|
37
38
|
// Operate
|
38
39
|
result = op(bsp1, bsp2);
|
39
40
|
|
40
|
-
if(file1 != NULL)
|
41
|
-
if(file2 != NULL)
|
41
|
+
if(file1 != NULL) file1->destroy(file1);
|
42
|
+
if(file2 != NULL) file2->destroy(file2);
|
42
43
|
if(bsp1 != NULL) free_bsp_tree(bsp1);
|
43
44
|
if(bsp2 != NULL) free_bsp_tree(bsp2);
|
44
45
|
return result;
|
45
46
|
error:
|
46
|
-
if(file1 != NULL)
|
47
|
-
if(file2 != NULL)
|
47
|
+
if(file1 != NULL) file1->destroy(file1);
|
48
|
+
if(file2 != NULL) file2->destroy(file2);
|
48
49
|
if(bsp1 != NULL) free_bsp_tree(bsp1);
|
49
50
|
if(bsp2 != NULL) free_bsp_tree(bsp2);
|
50
51
|
if(result != NULL) free_bsp_tree(result);
|
@@ -61,23 +62,23 @@ error:
|
|
61
62
|
#define MAKE_CSG_COMMAND(name) \
|
62
63
|
int cmd_##name(int argc, char **argv) { \
|
63
64
|
bsp_node_t *result = NULL; \
|
64
|
-
|
65
|
+
mesh_t *out = NULL; \
|
65
66
|
char *out_path = "./out.stl"; \
|
66
67
|
\
|
67
68
|
check(argc >= 2, "At least two input files required."); \
|
68
69
|
if(argc > 2) out_path = argv[2]; \
|
69
70
|
\
|
70
|
-
result = bsp_binary_operation(argv[0], argv[1], bsp_##name);
|
71
|
-
|
71
|
+
result = bsp_binary_operation(argv[0], argv[1], bsp_##name); \
|
72
|
+
check(result != NULL, "Binary operation" #name "failed."); \
|
73
|
+
out = NEW(bsp_mesh_t, "BSP", result); \
|
72
74
|
log_info("Writing output to %s", out_path); \
|
73
|
-
check(
|
75
|
+
check(out->write(out, out_path, "STL") == 0, "Failed to write STL to %s", out_path); \
|
74
76
|
\
|
75
|
-
|
76
|
-
if(out != NULL) stl_free(out); \
|
77
|
+
out->destroy(out); \
|
77
78
|
return 0; \
|
78
79
|
error: \
|
79
|
-
if(
|
80
|
-
if(
|
80
|
+
if(out != NULL) out->destroy(out); \
|
81
|
+
else if(result != NULL) free_bsp_tree(result); \
|
81
82
|
return -1; \
|
82
83
|
}
|
83
84
|
|
data/src/export.c
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#include "export.h"
|
2
|
+
#include "bsp_mesh.h"
|
2
3
|
|
3
4
|
stl_object *stl_from_polys(klist_t(poly) *polygons) {
|
4
5
|
stl_object *stl = stl_alloc(NULL, polygons->size);
|
@@ -65,3 +66,48 @@ error:
|
|
65
66
|
}
|
66
67
|
return NULL;
|
67
68
|
}
|
69
|
+
|
70
|
+
bsp_node_t *mesh_to_bsp(mesh_t *mesh) {
|
71
|
+
return mesh->to_bsp(mesh);
|
72
|
+
}
|
73
|
+
|
74
|
+
mesh_t* bsp_to_mesh(bsp_node_t* bsp, int copy) {
|
75
|
+
bsp_node_t* input = (copy == 0) ? bsp : clone_bsp_tree(bsp);
|
76
|
+
|
77
|
+
return NEW(bsp_mesh_t, "BSP", input);
|
78
|
+
}
|
79
|
+
|
80
|
+
klist_t(poly)* polys_to_tris(klist_t(poly) *dst, klist_t(poly) *src) {
|
81
|
+
klist_t(poly) *result = dst;
|
82
|
+
if(result == NULL) result = kl_init(poly);
|
83
|
+
|
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);
|
88
|
+
|
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);
|
102
|
+
*kl_pushp(poly, result) = tri;
|
103
|
+
}
|
104
|
+
} else {
|
105
|
+
sentinel("polygon(%p) has less than three(%d) vertices.", poly, poly_vertex_count(poly));
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
return result;
|
110
|
+
error:
|
111
|
+
if(result != dst) if(result != NULL) kl_destroy(poly, result);
|
112
|
+
return NULL;
|
113
|
+
}
|
data/src/export.h
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
#include "stl.h"
|
6
6
|
#include "poly.h"
|
7
7
|
#include "bsp.h"
|
8
|
+
#include "mesh.h"
|
8
9
|
|
9
10
|
#ifndef __EXPORT_H
|
10
11
|
#define __EXPORT_H
|
@@ -12,5 +13,8 @@
|
|
12
13
|
stl_object *stl_from_polys(klist_t(poly) *polygons);
|
13
14
|
stl_object *bsp_to_stl(bsp_node_t *tree);
|
14
15
|
bsp_node_t *stl_to_bsp(stl_object *stl);
|
16
|
+
bsp_node_t *mesh_to_bsp(mesh_t *mesh);
|
17
|
+
mesh_t* bsp_to_mesh(bsp_node_t *tree, int copy);
|
18
|
+
klist_t(poly)* polys_to_tris(klist_t(poly)* dst, klist_t(poly)* src);
|
15
19
|
|
16
20
|
#endif
|
data/src/mesh.c
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
#include "dbg.h"
|
2
|
+
|
3
|
+
#include "stl.h"
|
4
|
+
#include "export.h"
|
5
|
+
#include "mesh.h"
|
6
|
+
#include "reader.h"
|
7
|
+
#include "bsp.h"
|
8
|
+
|
9
|
+
int mesh_init(void *self, void *unused) {
|
10
|
+
if(self == NULL) return -1;
|
11
|
+
return 0;
|
12
|
+
}
|
13
|
+
|
14
|
+
char* mesh_describe(void *self) {
|
15
|
+
mesh_t *mesh = (mesh_t*)self;
|
16
|
+
return mesh->type;
|
17
|
+
}
|
18
|
+
|
19
|
+
void free_mesh(void *self) {
|
20
|
+
if(self != NULL) free(self);
|
21
|
+
return;
|
22
|
+
}
|
23
|
+
|
24
|
+
void destroy_mesh(void *self) {
|
25
|
+
mesh_t *mesh = (mesh_t*)self;
|
26
|
+
if(mesh->destroy != NULL) {
|
27
|
+
mesh->destroy(mesh);
|
28
|
+
}
|
29
|
+
else {
|
30
|
+
free_mesh(mesh);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
int _default_poly_count(void *self) {
|
35
|
+
return 0;
|
36
|
+
}
|
37
|
+
|
38
|
+
klist_t(poly)* _default_to_polygons(void *self) {
|
39
|
+
return NULL;
|
40
|
+
}
|
41
|
+
|
42
|
+
int _default_write(void *self, char *path, char type[4]) {
|
43
|
+
int rc = -1;
|
44
|
+
mesh_t *mesh = (mesh_t*)self;
|
45
|
+
stl_object *stl = NULL;
|
46
|
+
klist_t(poly) *polys = NULL;
|
47
|
+
klist_t(poly) *triangles = NULL;
|
48
|
+
|
49
|
+
// This is the only format we speak right now
|
50
|
+
if(strncmp(type, "STL", 3) != 0) goto error;
|
51
|
+
|
52
|
+
check((polys = mesh->to_polygons(mesh)) != NULL, "Failed to get polygons from mesh %p", mesh);
|
53
|
+
|
54
|
+
triangles = polys_to_tris(NULL, polys);
|
55
|
+
check(triangles != NULL, "Failed to convert polygons to trianges.");
|
56
|
+
|
57
|
+
// The default output format is STL
|
58
|
+
stl = stl_from_polys(triangles);
|
59
|
+
check(stl != NULL, "Failed to generate STL object for write.");
|
60
|
+
rc = stl_write_file(stl, path);
|
61
|
+
|
62
|
+
if(stl != NULL) stl_free(stl);
|
63
|
+
if(polys != NULL) kl_destroy(poly, polys);
|
64
|
+
if(triangles != NULL) kl_destroy(poly, triangles);
|
65
|
+
return rc;
|
66
|
+
error:
|
67
|
+
if(stl != NULL) stl_free(stl);
|
68
|
+
if(polys != NULL) kl_destroy(poly, polys);
|
69
|
+
if(triangles != NULL) kl_destroy(poly, triangles);
|
70
|
+
return -1;
|
71
|
+
}
|
72
|
+
|
73
|
+
bsp_node_t* _default_to_bsp(void *self) {
|
74
|
+
mesh_t *mesh = (mesh_t*)self;
|
75
|
+
bsp_node_t *bsp = NULL;
|
76
|
+
klist_t(poly)* polys = NULL;
|
77
|
+
|
78
|
+
check((polys = mesh->to_polygons(mesh)) != NULL,
|
79
|
+
"Failed to convert mesh %p to poly list", mesh);
|
80
|
+
|
81
|
+
check((bsp = bsp_build(NULL, polys, 1)) != NULL,
|
82
|
+
"Failed to build BSP tree from %zd polygons of %p", polys->size, self);
|
83
|
+
|
84
|
+
kl_destroy(poly, polys);
|
85
|
+
return bsp;
|
86
|
+
error:
|
87
|
+
if(polys != NULL) kl_destroy(poly, polys);
|
88
|
+
if(bsp != NULL) free_bsp_tree(bsp);
|
89
|
+
return NULL;
|
90
|
+
}
|
91
|
+
|
92
|
+
void *alloc_mesh(size_t size, mesh_t proto, char type[4], void *data) {
|
93
|
+
if(proto.init == NULL) proto.init = mesh_init;
|
94
|
+
if(proto.destroy == NULL) proto.destroy = free_mesh;
|
95
|
+
if(proto.poly_count == NULL) proto.poly_count = _default_poly_count;
|
96
|
+
if(proto.to_polygons == NULL) proto.to_polygons = _default_to_polygons;
|
97
|
+
if(proto.write == NULL) proto.write = _default_write;
|
98
|
+
if(proto.to_bsp == NULL) proto.to_bsp = _default_to_bsp;
|
99
|
+
|
100
|
+
mesh_t *m = calloc(1, size);
|
101
|
+
*m = proto;
|
102
|
+
strncpy(m->type, type, 3);
|
103
|
+
|
104
|
+
check(m->init(m, data) != -1, "Failed to initialize %p(%s, %p)", m, m->type, data);
|
105
|
+
return m;
|
106
|
+
error:
|
107
|
+
if(m != NULL) m->destroy(m);
|
108
|
+
return NULL;
|
109
|
+
}
|
110
|
+
|
111
|
+
mesh_t *mesh_read_file(char *path) {
|
112
|
+
return reader_load(path);
|
113
|
+
}
|
data/src/mesh.h
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
|
3
|
+
#include "poly.h"
|
4
|
+
#include "bsp.h"
|
5
|
+
|
6
|
+
#ifndef __MESH_H
|
7
|
+
#define __MESH_H
|
8
|
+
|
9
|
+
typedef struct s_mesh_t {
|
10
|
+
char type[4];
|
11
|
+
|
12
|
+
int (*init)(void *self, void *data);
|
13
|
+
void (*destroy)(void *self);
|
14
|
+
|
15
|
+
int (*poly_count)(void *self);
|
16
|
+
klist_t(poly)* (*to_polygons)(void *self);
|
17
|
+
bsp_node_t* (*to_bsp)(void *self);
|
18
|
+
|
19
|
+
int (*write)(void *self, char *path, char type[4]);
|
20
|
+
} mesh_t;
|
21
|
+
|
22
|
+
int mesh_init(void *self, void *data);
|
23
|
+
void free_mesh(void *self);
|
24
|
+
void destroy_mesh(void *self);
|
25
|
+
|
26
|
+
void *alloc_mesh(size_t size, mesh_t proto, char type[4], void *data);
|
27
|
+
|
28
|
+
#define NEW(T, N, D) alloc_mesh(sizeof(T), T##_Proto, N, D)
|
29
|
+
#define _(M) proto.M
|
30
|
+
|
31
|
+
// Load wrapper to read better
|
32
|
+
mesh_t *mesh_read_file(char *path);
|
33
|
+
|
34
|
+
#endif
|
data/src/reader.c
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#include "stl.h"
|
2
|
+
#include "reader.h"
|
3
|
+
|
4
|
+
// API
|
5
|
+
mesh_t* reader_load(char *path) {
|
6
|
+
mesh_t *mesh = NULL;
|
7
|
+
|
8
|
+
// Walk the readers list and return the first thing that passes
|
9
|
+
// the test and loads a mesh_t != NULL
|
10
|
+
const reader_t *r = NULL;
|
11
|
+
for(r = readers; r->name != NULL; r++) {
|
12
|
+
if(r->test(path) == 1) {
|
13
|
+
if((mesh = r->load(path))) {
|
14
|
+
break;
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
return mesh;
|
20
|
+
}
|
21
|
+
|
22
|
+
// Wrappers
|
23
|
+
int _stl_predicate(char *path) {
|
24
|
+
// TODO: Valdiate the STL file instead of blindly trying a read.
|
25
|
+
// Ideally, try to read a line, and pass if binary, if it's text
|
26
|
+
// pass only if it begins with /^solid/i
|
27
|
+
return 1;
|
28
|
+
}
|
29
|
+
|
30
|
+
mesh_t* _stl_reader(char *path) {
|
31
|
+
stl_object *stl = NULL;
|
32
|
+
mesh_t *mesh = NULL;
|
33
|
+
|
34
|
+
check((stl = stl_read_file(path, 1)) != NULL,
|
35
|
+
"Failed to read '%s' as STL", path);
|
36
|
+
check((mesh = NEW(stl_mesh_t, "STL", stl)) != NULL,
|
37
|
+
"Failed to create mesh from STL(%p)", stl);
|
38
|
+
|
39
|
+
return mesh;
|
40
|
+
error:
|
41
|
+
return NULL;
|
42
|
+
}
|
43
|
+
|
44
|
+
// List of readers that should be tried in order
|
45
|
+
// {NULL, NULL, NULL} terminated.
|
46
|
+
const reader_t readers[] = {
|
47
|
+
{"STL", _stl_predicate, _stl_reader},
|
48
|
+
{NULL, NULL, NULL}
|
49
|
+
};
|
data/src/reader.h
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#include "mesh.h"
|
2
|
+
|
3
|
+
#ifndef __READER_H
|
4
|
+
#define __READER_H
|
5
|
+
|
6
|
+
// API
|
7
|
+
mesh_t* reader_load(char *path);
|
8
|
+
|
9
|
+
// Types
|
10
|
+
typedef int (reader_predicate_t)(char *path);
|
11
|
+
typedef mesh_t* (reader_loader_t)(char *path);
|
12
|
+
|
13
|
+
typedef struct s_reader_t {
|
14
|
+
char *name;
|
15
|
+
reader_predicate_t *test;
|
16
|
+
reader_loader_t *load;
|
17
|
+
} reader_t;
|
18
|
+
|
19
|
+
// Exported list of readers
|
20
|
+
extern const reader_t readers[];
|
21
|
+
|
22
|
+
#endif
|
data/src/stl.c
CHANGED
@@ -330,3 +330,56 @@ error:
|
|
330
330
|
if(fd != -1) close(fd);
|
331
331
|
return NULL;
|
332
332
|
}
|
333
|
+
|
334
|
+
// Mesh type prototype methods
|
335
|
+
int stl_mesh_init(void *self, void *data) {
|
336
|
+
stl_mesh_t *mesh = (stl_mesh_t*)self;
|
337
|
+
if(data == NULL) {
|
338
|
+
check_mem(mesh->stl = stl_alloc(NULL, 0));
|
339
|
+
}
|
340
|
+
else {
|
341
|
+
mesh->stl = (stl_object*)data;
|
342
|
+
}
|
343
|
+
return 0;
|
344
|
+
error:
|
345
|
+
return -1;
|
346
|
+
}
|
347
|
+
|
348
|
+
void stl_mesh_destroy(void *self) {
|
349
|
+
stl_mesh_t *mesh = (stl_mesh_t*)self;
|
350
|
+
stl_free(mesh->stl);
|
351
|
+
free(self);
|
352
|
+
}
|
353
|
+
|
354
|
+
int stl_mesh_poly_count(void *self) {
|
355
|
+
stl_mesh_t *mesh = (stl_mesh_t*)self;
|
356
|
+
return mesh->stl->facet_count;
|
357
|
+
}
|
358
|
+
|
359
|
+
klist_t(poly)* stl_mesh_to_polygons(void *self) {
|
360
|
+
stl_mesh_t *mesh = (stl_mesh_t*)self;
|
361
|
+
int count = mesh->_(poly_count)(mesh);
|
362
|
+
klist_t(poly)* polys = count > 0 ? kl_init(poly) : NULL;
|
363
|
+
|
364
|
+
for(int i = 0; i < count; i++) {
|
365
|
+
poly_t *poly = poly_make_triangle(mesh->stl->facets[i].vertices[0],
|
366
|
+
mesh->stl->facets[i].vertices[1],
|
367
|
+
mesh->stl->facets[i].vertices[2]);
|
368
|
+
check_mem(poly);
|
369
|
+
*kl_pushp(poly, polys) = poly;
|
370
|
+
}
|
371
|
+
|
372
|
+
|
373
|
+
return polys;
|
374
|
+
error:
|
375
|
+
if(polys != NULL) kl_destroy(poly, polys);
|
376
|
+
return NULL;
|
377
|
+
}
|
378
|
+
|
379
|
+
// Mesh type definitions
|
380
|
+
mesh_t stl_mesh_t_Proto = {
|
381
|
+
.init = stl_mesh_init,
|
382
|
+
.destroy = stl_mesh_destroy,
|
383
|
+
.poly_count = stl_mesh_poly_count,
|
384
|
+
.to_polygons = stl_mesh_to_polygons
|
385
|
+
};
|
data/src/stl.h
CHANGED
@@ -4,12 +4,11 @@
|
|
4
4
|
|
5
5
|
#include "klist.h"
|
6
6
|
#include "vector.h"
|
7
|
+
#include "mesh.h"
|
7
8
|
|
8
9
|
#ifndef __STL_H
|
9
10
|
#define __STL_H
|
10
11
|
|
11
|
-
|
12
|
-
|
13
12
|
// File format structs
|
14
13
|
typedef struct s_stl_facet {
|
15
14
|
float3 normal;
|
@@ -57,4 +56,12 @@ KLIST_INIT(stl_object, stl_object*, mp_stl_free)
|
|
57
56
|
#define mp_std_free(x) free(kl_val(x))
|
58
57
|
KLIST_INIT(stl_facet, stl_facet*, mp_std_free)
|
59
58
|
|
59
|
+
// mesh_t type and prototype
|
60
|
+
extern mesh_t stl_mesh_t_Proto;
|
61
|
+
|
62
|
+
typedef struct s_stl_mesh_t {
|
63
|
+
mesh_t proto;
|
64
|
+
stl_object *stl;
|
65
|
+
} stl_mesh_t;
|
66
|
+
|
60
67
|
#endif
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yaroslav Shirokov
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-12-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -40,20 +40,26 @@ files:
|
|
40
40
|
- src/commands.c
|
41
41
|
- src/dbg.c
|
42
42
|
- src/export.c
|
43
|
+
- src/mesh.c
|
43
44
|
- src/poly.c
|
45
|
+
- src/reader.c
|
44
46
|
- src/stl.c
|
45
47
|
- src/vector.c
|
46
48
|
- src/bsp.h
|
49
|
+
- src/bsp_mesh.h
|
47
50
|
- src/commands.h
|
48
51
|
- src/dbg.h
|
49
52
|
- src/export.h
|
50
53
|
- src/klist.h
|
54
|
+
- src/mesh.h
|
51
55
|
- src/poly.h
|
56
|
+
- src/reader.h
|
52
57
|
- src/stl.h
|
53
58
|
- src/vector.h
|
54
59
|
- ext/Rakefile
|
55
60
|
homepage: https://github.com/sshirokov/csgtool/
|
56
|
-
licenses:
|
61
|
+
licenses:
|
62
|
+
- MIT
|
57
63
|
metadata: {}
|
58
64
|
post_install_message:
|
59
65
|
rdoc_options: []
|