allow non-convex polygons in linear_extrude

This commit is contained in:
moehriegitt 2018-11-10 17:57:33 +01:00
parent 1d955a5131
commit f51e7fb820
14 changed files with 439 additions and 254 deletions

View file

@ -170,11 +170,11 @@ TEST_STL.stl := \
TEST_STL.jsgz := \
$(addprefix test-out/,$(notdir $(TEST_STL.scad:.scad=.js.gz)))
FAIL_STL.stl := \
$(addprefix test-out/,$(notdir $(FAIL_STL.scad:.scad=.stl)))
FAIL_STL := \
$(addprefix test-out/fail-,$(notdir $(FAIL_STL.scad:.scad=.stl)))
FAIL_STL.jsgz := \
$(addprefix test-out/,$(notdir $(FAIL_STL.scad:.scad=.js.gz)))
FAIL_JS := \
$(addprefix test-out/fail-,$(notdir $(FAIL_JS.scad:.scad=.js)))
######################################################################
# header files
@ -395,10 +395,10 @@ test-stl: $(TEST_STL.stl)
test-js: $(TEST_STL.jsgz)
.PHONY: fail-stl
fail-stl: $(FAIL_STL.stl)
fail-stl: $(FAIL_STL)
.PHONY: fail-js
fail-js: $(FAIL_STL.jsgz)
fail-js: $(FAIL_JS)
.PHONY: test-jsgz
test-jsgz: test-js
@ -418,6 +418,10 @@ test-out/%.stl: scad-test/%.scad hob3l.exe
$(HOB3L) $< -o $@.new.stl
mv $@.new.stl $@
test-out/fail-%.stl: scad-test/%.scad hob3l.exe
! $(HOB3L) $< -o $@.new.stl
echo >| $@
test-out/%.stl: $(SCAD_DIR)/%.scad hob3l.exe
openscad $< -o $@.new.csg
$(HOB3L) $@.new.csg -o $@.new.stl
@ -430,6 +434,10 @@ test-out/%.js: scad-test/%.scad hob3l.exe
mv $@.new2.js $@
rm $@.new.js
test-out/fail-%.js: scad-test/%.scad hob3l.exe
! $(HOB3L) $< -o $@.new.js
echo >| $@
test-out/%.js: $(SCAD_DIR)/%.scad hob3l.exe
openscad $< -o $@.new.csg
$(HOB3L) $@.new.csg -o $@.new.js

View file

@ -701,10 +701,6 @@ is not usually helpful in 3D space). This means that the
`--dump-csg3` output cannot be read back as input file. The XOR node
is represented by a `hob3l_xor` functor.
BUG: Currently, this has the same restriction as the `polyhedron`: it
cannot correctly handle non-convex polygons. (Actually, it works
surprisingly well most of the time, but it is really still broken.)
_OpenSCAD compatibility_:
* `slices`:

View file

@ -21,7 +21,7 @@
* space from the polygons for storing the result.
*/
extern void cp_csg2_op_reduce(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_lazy_t *r);
/**
@ -34,7 +34,7 @@ extern void cp_csg2_op_reduce(
* \p r and/or \p b are reused and cleared to construct r. This may happen
* immediately or later in cp_csg2_op_reduce().
*
* Uses \p pool for all temporary allocations (but not for constructing r).
* Uses \p tmp for all temporary allocations (but not for constructing r).
*
* This uses the algorithm of Martinez, Rueda, Feito (2009), based on a
* Bentley-Ottmann plain sweep. The algorithm is modified:
@ -74,7 +74,7 @@ extern void cp_csg2_op_reduce(
*/
extern void cp_csg2_op_lazy(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_lazy_t *r,
cp_csg2_lazy_t *b,
cp_bool_op_t op);
@ -94,7 +94,7 @@ extern void cp_csg2_op_lazy(
*/
extern void cp_csg2_op_add_layer(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_tree_t *r,
cp_csg2_tree_t *a,
size_t zi);
@ -118,7 +118,7 @@ extern void cp_csg2_op_add_layer(
*/
extern cp_csg2_poly_t *cp_csg2_flatten(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_v_obj_p_t *root);
/**
@ -134,7 +134,7 @@ extern cp_csg2_poly_t *cp_csg2_flatten(
*/
extern void cp_csg2_op_diff_layer(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_tree_t *a,
size_t zi);

View file

@ -56,7 +56,7 @@
* Additionally, the improper start case has a special case if vertices
* coincide.
*
* Uses \p pool for all temporary allocations (but not for constructing
* Uses \p tmp for all temporary allocations (but not for constructing
* point_arr or tri).
*
* Runtime: O(n log n)
@ -64,7 +64,7 @@
* Where n = number of points.
*/
extern bool cp_csg2_tri_set(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_vec2_arr_ref_t *point_arr,
cp_v_size3_t *tri,
@ -79,14 +79,14 @@ extern bool cp_csg2_tri_set(
* This uses cp_csg2_tri_set() internally, so the path is contrained
* in the way described for that function.
*
* Uses \p pool for all temporary allocations (but not for constructing g).
* Uses \p tmp for all temporary allocations (but not for constructing g).
*
* Runtime: O(n log n)
* Space: O(n)
* Where n = number of points.
*/
extern bool cp_csg2_tri_path(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_poly_t *g,
cp_csg2_path_t *s);
@ -100,17 +100,26 @@ extern bool cp_csg2_tri_path(
* the paths in one data structure, so the set of paths of the given
* polygon is contrained in the way described for that function.
*
* Uses \p pool for all temporary allocations (but not for constructing r).
* Uses \p tmp for all temporary allocations (but not for constructing r).
*
* Runtime: O(n log n)
* Space: O(n)
* Where n = number of points.
*/
extern bool cp_csg2_tri_poly(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_poly_t *g);
/**
* Same as cp_csg2_tri_poly, but triangulates a reference array of vec2.
*/
extern bool cp_csg2_tri_vec2_arr_ref(
cp_v_size3_t *tri,
cp_pool_t *tmp,
cp_err_t *t,
cp_vec2_arr_ref_t *a2);
/**
* Triangulate a given layer
*
@ -125,7 +134,7 @@ extern bool cp_csg2_tri_poly(
* tree, so the set of paths of each polygon in the tree is contrained
* in the way described for that function.
*
* Uses \p pool for all temporary allocations (but not for constructing r).
* Uses \p tmp for all temporary allocations (but not for constructing r).
*
* Runtime: O(m * n log n)
* Space: O(max(n))
@ -135,7 +144,7 @@ extern bool cp_csg2_tri_poly(
* max(n) = the maximum n among the polygons.
*/
extern bool cp_csg2_tri_layer(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_tree_t *r,
size_t zi);
@ -149,7 +158,7 @@ extern bool cp_csg2_tri_layer(
* Runtime and space: see cp_csg2_tri_layer.
*/
extern bool cp_csg2_tri_layer_diff(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_tree_t *r,
size_t zi);

View file

@ -48,7 +48,7 @@ extern void cp_csg3_tree_bb(
* Convert a SCAD AST into a CSG3 tree.
*/
extern bool cp_csg3_from_scad_tree(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg3_tree_t *r,
cp_err_t *t,
cp_scad_tree_t const *scad);

View file

@ -81,7 +81,7 @@ typedef enum {
((__x != 0) && ((__x & (__x - 1)) == 0)); \
})
#define cp_offsetof(T,F) (__builtin_offsetof(T,F))
#define cp_offsetof(T,F) (__builtin_offsetof(__typeof__(T),F))
#define cp_alignof(X) (__alignof__(X))

View file

@ -571,4 +571,65 @@ static inline bool cp_vec3_left_normal3(
return cp_vec3_right_normal3(r,b,o,a);
}
static inline cp_vec2_t *cp_vec2_arr_ref(
cp_vec2_arr_ref_t *a,
size_t i)
{
assert(i < a->count);
assert(((i * a->size) / a->size) == i);
void *r = ((char*)a->base_vec2) + (a->size * i);
return r;
}
static inline cp_loc_t cp_vec2_arr_loc(
cp_vec2_arr_ref_t *a,
size_t i)
{
assert(i < a->count);
assert(((i * a->size) / a->size) == i);
cp_loc_t *r = (void*)(((char*)a->base_loc) + (a->size * i));
return *r;
}
static inline size_t cp_vec2_arr_idx(
cp_vec2_arr_ref_t *a,
cp_vec2_t *p)
{
size_t o = CP_PTRDIFF((char*)p, (char*)a->base_vec2);
assert((o % a->size) == 0);
return o / a->size;
}
/**
* Convert to vec2 array.
*/
static inline void cp_vec2_arr_ref_from_v_vec2_loc(
cp_vec2_arr_ref_t *a,
cp_v_vec2_loc_t *v)
{
a->base_vec2 = (char*)v->data + cp_offsetof(*v->data, coord);
a->base_loc = (char*)v->data + cp_offsetof(*v->data, loc);
a->size = sizeof(*v->data);
a->count = v->size;
}
/**
* Convert to vec2 array.
*/
static inline void cp_vec2_arr_ref_from_a_vec3_loc(
cp_vec2_arr_ref_t *a,
cp_a_vec3_loc_t *v,
bool yz_plane)
{
a->base_vec2 =
(char*)v->data +
(yz_plane ?
cp_offsetof(*v->data, coord.be)
: cp_offsetof(*v->data, coord.b));
a->base_loc = (char*)v->data + cp_offsetof(*v->data, loc);
a->size = sizeof(*v->data);
a->count = v->size;
}
#endif /* __CP_MAT_H */

View file

@ -35,6 +35,10 @@ typedef union {
cp_vec2_t b;
cp_dim_t w;
};
struct {
cp_dim_t we;
cp_vec2_t be;
};
} cp_vec3_t;
typedef struct {
@ -57,6 +61,10 @@ typedef union {
struct {
cp_vec3_t b;
};
struct {
cp_dim_t we;
cp_vec3_t be;
};
} cp_vec4_t;
typedef struct {

View file

@ -86,52 +86,12 @@ typedef CP_ARR_T(cp_vec3_loc_ref_t) cp_a_vec3_loc_ref_t;
* Pointer to base of array of entries with vec2 slot plus info to access array.
*/
typedef struct {
void *base;
void *base_vec2;
void *base_loc;
size_t size;
size_t count;
} cp_vec2_arr_ref_t;
static inline cp_vec2_t *cp_vec2_arr_ref(
cp_vec2_arr_ref_t *a,
size_t i)
{
assert(i < a->count);
assert(((i * a->size) / a->size) == i);
void *r = ((char*)a->base) + (a->size * i);
return r;
}
static inline size_t cp_vec2_arr_idx(
cp_vec2_arr_ref_t *a,
cp_vec2_t *p)
{
size_t o = CP_PTRDIFF((char*)p, (char*)a->base);
assert((o % a->size) == 0);
return o / a->size;
}
static inline cp_vec2_arr_ref_t *__cp_vec2_arr_ref_set(
cp_vec2_arr_ref_t *a,
cp_vec2_arr_ref_t x)
{
*a = x;
return a;
}
#define CP_VEC2_ARR_REF(arr, slot) \
(*__cp_vec2_arr_ref_set( \
&(cp_vec2_arr_ref_t){ .base=0 }, \
({ \
__typeof__(*(arr)) *__arr = (arr); \
void *__base = __arr->data; \
cp_vec2_arr_ref_t __r = { \
.base = (char*)__base + cp_offsetof(__typeof__(__arr->data[0]), slot), \
.size = sizeof(__arr->data[0]), \
.count = __arr->size \
}; \
__r; \
})))
#define CP_V01(p) (p).v[0], (p).v[1]
#define CP_V012(p) (p).v[0], (p).v[1], (p).v[2]
#define CP_V0123(p) (p).v[0], (p).v[1], (p).v[2], (p).v[3]

View file

@ -238,6 +238,10 @@ sub gen_type_vec($$$)
$s.= " X_dim_t w;\n";
}
$s.= " };\n";
$s.= " struct {\n";
$s.= " X_dim_t we;\n";
$s.= " X_$ts->{name}_t be;\n";
$s.= " };\n";
}
$s.= "} X_$t->{name}_t;\n";
publish($oc, 'tam_h', $s);

View file

@ -184,7 +184,7 @@ typedef CP_VEC_T(event_t*) v_event_p_t;
*/
typedef struct {
/** Memory pool to use */
cp_pool_t *pool;
cp_pool_t *tmp;
/** Error output */
cp_err_t *err;
@ -221,7 +221,7 @@ typedef struct {
*/
typedef struct {
cp_csg_opt_t const *opt;
cp_pool_t *pool;
cp_pool_t *tmp;
} op_ctxt_t;
@ -483,7 +483,7 @@ static point_t *pt_new(
return CP_BOX_OF(pt, point_t, node_pt);
}
point_t *p = CP_POOL_NEW(c->pool, *p);
point_t *p = CP_POOL_NEW(c->tmp, *p);
p->v.coord = coord;
p->v.loc = loc;
p->v.color = *color;
@ -505,7 +505,7 @@ static event_t *ev_new(
bool left,
event_t *other)
{
event_t *r = CP_POOL_NEW(c->pool, *r);
event_t *r = CP_POOL_NEW(c->tmp, *r);
r->loc = loc;
r->p = p;
r->left = left;
@ -1757,7 +1757,7 @@ static bool csg2_op_v_csg2(
return false;
}
LOG("ADD\n");
cp_csg2_op_lazy(c->opt, c->pool, o, &oi, CP_OP_ADD);
cp_csg2_op_lazy(c->opt, c->tmp, o, &oi, CP_OP_ADD);
}
}
return true;
@ -1795,7 +1795,7 @@ static bool csg2_op_cut(
return false;
}
LOG("CUT\n");
cp_csg2_op_lazy(c->opt, c->pool, o, &oc, CP_OP_CUT);
cp_csg2_op_lazy(c->opt, c->tmp, o, &oc, CP_OP_CUT);
}
}
return true;
@ -1822,7 +1822,7 @@ static bool csg2_op_xor(
return false;
}
LOG("XOR\n");
cp_csg2_op_lazy(c->opt, c->pool, o, &oc, CP_OP_XOR);
cp_csg2_op_lazy(c->opt, c->tmp, o, &oc, CP_OP_XOR);
}
}
return true;
@ -1858,7 +1858,7 @@ static bool csg2_op_sub(
return false;
}
LOG("SUB\n");
cp_csg2_op_lazy(c->opt, c->pool, o, &os, CP_OP_SUB);
cp_csg2_op_lazy(c->opt, c->tmp, o, &os, CP_OP_SUB);
return true;
}
@ -1924,14 +1924,14 @@ static bool csg2_op_csg2(
* them. Any poly but r->data[0] will be left completely untouched.
*/
static void cp_csg2_op_poly(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_poly_t *o,
cp_csg2_lazy_t const *r)
{
TRACE();
/* make context */
ctxt_t c = {
.pool = pool,
.tmp = tmp,
.comb = &r->comb,
.comb_size = (1U << r->size),
};
@ -1980,7 +1980,7 @@ static void cp_csg2_op_poly(
static cp_csg2_poly_t *poly_sub(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_poly_t *a0,
cp_csg2_poly_t *a1)
{
@ -1995,11 +1995,11 @@ static cp_csg2_poly_t *poly_sub(
CP_ZERO(&o1);
csg2_op_poly(&o1, a1);
cp_csg2_op_lazy(opt, pool, &o0, &o1, CP_OP_SUB);
cp_csg2_op_lazy(opt, tmp, &o0, &o1, CP_OP_SUB);
assert(o0.size == 2);
cp_csg2_poly_t *o = CP_CLONE(a1);
cp_csg2_op_poly(pool, o, &o0);
cp_csg2_op_poly(tmp, o, &o0);
/* check that the originals really haven't changed */
assert(a0->point.size == a0_point_sz);
@ -2010,7 +2010,7 @@ static cp_csg2_poly_t *poly_sub(
static void csg2_op_diff2_poly(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_poly_t *a0,
cp_csg2_poly_t *a1)
{
@ -2019,13 +2019,13 @@ static void csg2_op_diff2_poly(
return;
}
a0->diff_above = poly_sub(opt, pool, a0, a1);
a1->diff_below = poly_sub(opt, pool, a1, a0);
a0->diff_above = poly_sub(opt, tmp, a0, a1);
a1->diff_below = poly_sub(opt, tmp, a1, a0);
}
static void csg2_op_diff2(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_t *a0,
cp_csg2_t *a1)
{
@ -2038,12 +2038,12 @@ static void csg2_op_diff2(
if (p1 == NULL) {
return;
}
csg2_op_diff2_poly(opt, pool, p0, p1);
csg2_op_diff2_poly(opt, tmp, p0, p1);
}
static void csg2_op_diff2_layer(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_layer_t *a0,
cp_csg2_layer_t *a1)
{
@ -2054,14 +2054,14 @@ static void csg2_op_diff2_layer(
if (cp_csg_add_size(a1->root) != 1) {
return;
}
csg2_op_diff2(opt, pool,
csg2_op_diff2(opt, tmp,
cp_csg2_cast(cp_csg2_t, cp_v_nth(&a0->root->add,0)),
cp_csg2_cast(cp_csg2_t, cp_v_nth(&a1->root->add,0)));
}
static void csg2_op_diff_stack(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
size_t zi,
cp_csg2_stack_t *a)
{
@ -2080,12 +2080,12 @@ static void csg2_op_diff_stack(
return;
}
csg2_op_diff2_layer(opt, pool, l0, l1);
csg2_op_diff2_layer(opt, tmp, l0, l1);
}
static void csg2_op_diff_csg2(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
size_t zi,
cp_csg2_t *a)
{
@ -2093,7 +2093,7 @@ static void csg2_op_diff_csg2(
/* only work on stacks, ignore anything else */
switch (a->type) {
case CP_CSG2_STACK:
csg2_op_diff_stack(opt, pool, zi, cp_csg2_cast(cp_csg2_stack_t, a));
csg2_op_diff_stack(opt, tmp, zi, cp_csg2_cast(cp_csg2_stack_t, a));
return;
default:
return;
@ -2116,14 +2116,14 @@ static void csg2_op_diff_csg2(
* space from the polygons for storing the result.
*/
extern void cp_csg2_op_reduce(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_lazy_t *r)
{
TRACE();
if (r->size <= 1) {
return;
}
cp_csg2_op_poly(pool, r->data[0], r);
cp_csg2_op_poly(tmp, r->data[0], r);
if (r->data[0]->point.size == 0) {
CP_ZERO(r);
return;
@ -2142,7 +2142,7 @@ extern void cp_csg2_op_reduce(
* \p r and/or \p b are reused and cleared to construct r. This may happen
* immediately or later in cp_csg2_op_reduce().
*
* Uses \p pool for all temporary allocations (but not for constructing r).
* Uses \p tmp for all temporary allocations (but not for constructing r).
*
* This uses the algorithm of Martinez, Rueda, Feito (2009), based on a
* Bentley-Ottmann plain sweep. The algorithm is modified:
@ -2182,7 +2182,7 @@ extern void cp_csg2_op_reduce(
*/
extern void cp_csg2_op_lazy(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_lazy_t *r,
cp_csg2_lazy_t *b,
cp_bool_op_t op)
@ -2217,11 +2217,11 @@ extern void cp_csg2_op_lazy(
/* otherwise reduce the larger one */
if (r->size > b->size) {
cp_csg2_op_reduce(pool, r);
cp_csg2_op_reduce(tmp, r);
assert(r->size <= 1);
}
else {
cp_csg2_op_reduce(pool, b);
cp_csg2_op_reduce(tmp, b);
assert(b->size <= 1);
}
}
@ -2264,7 +2264,7 @@ extern void cp_csg2_op_lazy(
*/
extern void cp_csg2_op_add_layer(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_tree_t *r,
cp_csg2_tree_t *a,
size_t zi)
@ -2275,14 +2275,14 @@ extern void cp_csg2_op_add_layer(
op_ctxt_t c = {
.opt = opt,
.pool = pool,
.tmp = tmp,
};
cp_csg2_lazy_t ol;
CP_ZERO(&ol);
bool ok __unused = csg2_op_csg2(&c, zi, &ol, a->root);
assert(ok && "Unexpected object in tree.");
cp_csg2_op_reduce(pool, &ol);
cp_csg2_op_reduce(tmp, &ol);
cp_csg2_poly_t *o = ol.data[0];
if (o != NULL) {
@ -2321,20 +2321,20 @@ extern void cp_csg2_op_add_layer(
*/
extern cp_csg2_poly_t *cp_csg2_flatten(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_v_obj_p_t *root)
{
TRACE();
op_ctxt_t c = {
.opt = opt,
.pool = pool,
.tmp = tmp,
};
cp_csg2_lazy_t ol;
CP_ZERO(&ol);
bool ok __unused = csg2_op_v_csg2(&c, 0, &ol, root);
assert(ok && "Unexpected object in tree.");
cp_csg2_op_reduce(pool, &ol);
cp_csg2_op_reduce(tmp, &ol);
return ol.data[0];
}
@ -2352,7 +2352,7 @@ extern cp_csg2_poly_t *cp_csg2_flatten(
*/
extern void cp_csg2_op_diff_layer(
cp_csg_opt_t const *opt,
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg2_tree_t *a,
size_t zi)
{
@ -2360,7 +2360,7 @@ extern void cp_csg2_op_diff_layer(
cp_csg2_stack_t *s __unused = cp_csg2_cast(*s, a->root);
assert(zi < s->layer.size);
csg2_op_diff_csg2(opt, pool, zi, a->root);
csg2_op_diff_csg2(opt, tmp, zi, a->root);
}
/**

View file

@ -1083,24 +1083,24 @@ static bool transition(
}
static bool csg2_tri_v_csg2(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_v_obj_p_t *r,
size_t zi);
static bool csg2_tri_layer(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_layer_t *r)
{
if (r->root == NULL) {
return true;
}
return csg2_tri_v_csg2(pool, t, &r->root->add, r->zi);
return csg2_tri_v_csg2(tmp, t, &r->root->add, r->zi);
}
static bool csg2_tri_stack(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_stack_t *r,
size_t zi)
@ -1109,37 +1109,37 @@ static bool csg2_tri_stack(
if ((l == NULL) || (l->root == NULL)) {
return true;
}
return csg2_tri_layer(pool, t, l);
return csg2_tri_layer(tmp, t, l);
}
static bool csg2_tri_sub(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg_sub_t *r,
size_t zi)
{
return
csg2_tri_v_csg2(pool, t, &r->add->add, zi) &&
csg2_tri_v_csg2(pool, t, &r->sub->add, zi);
csg2_tri_v_csg2(tmp, t, &r->add->add, zi) &&
csg2_tri_v_csg2(tmp, t, &r->sub->add, zi);
}
static bool csg2_tri_add(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg_add_t *r,
size_t zi)
{
return csg2_tri_v_csg2(pool, t, &r->add, zi);
return csg2_tri_v_csg2(tmp, t, &r->add, zi);
}
static bool csg2_tri_cut(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg_cut_t *r,
size_t zi)
{
for (cp_v_each(i, &r->cut)) {
if (!csg2_tri_v_csg2(pool, t, &r->cut.data[i]->add, zi)) {
if (!csg2_tri_v_csg2(tmp, t, &r->cut.data[i]->add, zi)) {
return false;
}
}
@ -1147,13 +1147,13 @@ static bool csg2_tri_cut(
}
static bool csg2_tri_xor(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg_xor_t *r,
size_t zi)
{
for (cp_v_each(i, &r->xor)) {
if (!csg2_tri_v_csg2(pool, t, &r->xor.data[i]->add, zi)) {
if (!csg2_tri_v_csg2(tmp, t, &r->xor.data[i]->add, zi)) {
return false;
}
}
@ -1161,42 +1161,42 @@ static bool csg2_tri_xor(
}
static bool csg2_tri_csg2(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_t *r,
size_t zi)
{
switch (r->type) {
case CP_CSG2_POLY:
return cp_csg2_tri_poly(pool, t, cp_csg2_cast(cp_csg2_poly_t, r));
return cp_csg2_tri_poly(tmp, t, cp_csg2_cast(cp_csg2_poly_t, r));
case CP_CSG2_STACK:
return csg2_tri_stack(pool, t, cp_csg2_cast(cp_csg2_stack_t, r), zi);
return csg2_tri_stack(tmp, t, cp_csg2_cast(cp_csg2_stack_t, r), zi);
case CP_CSG_ADD:
return csg2_tri_add(pool, t, cp_csg_cast(cp_csg_add_t, r), zi);
return csg2_tri_add(tmp, t, cp_csg_cast(cp_csg_add_t, r), zi);
case CP_CSG_XOR:
return csg2_tri_xor(pool, t, cp_csg_cast(cp_csg_xor_t, r), zi);
return csg2_tri_xor(tmp, t, cp_csg_cast(cp_csg_xor_t, r), zi);
case CP_CSG_SUB:
return csg2_tri_sub(pool, t, cp_csg_cast(cp_csg_sub_t, r), zi);
return csg2_tri_sub(tmp, t, cp_csg_cast(cp_csg_sub_t, r), zi);
case CP_CSG_CUT:
return csg2_tri_cut(pool, t, cp_csg_cast(cp_csg_cut_t, r), zi);
return csg2_tri_cut(tmp, t, cp_csg_cast(cp_csg_cut_t, r), zi);
}
CP_DIE("2D object type: %#x", r->type);
}
static bool csg2_tri_v_csg2(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_v_obj_p_t *r,
size_t zi)
{
for (cp_v_each(i, r)) {
if (!csg2_tri_csg2(pool, t, cp_csg2_cast(cp_csg2_t, cp_v_nth(r,i)), zi)) {
if (!csg2_tri_csg2(tmp, t, cp_csg2_cast(cp_csg2_t, cp_v_nth(r,i)), zi)) {
return false;
}
}
@ -1204,24 +1204,24 @@ static bool csg2_tri_v_csg2(
}
static bool csg2_tri_diff_v_csg2(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_v_obj_p_t *r,
size_t zi);
static bool csg2_tri_diff_layer(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_layer_t *r)
{
if (r->root == NULL) {
return true;
}
return csg2_tri_diff_v_csg2(pool, t, &r->root->add, r->zi);
return csg2_tri_diff_v_csg2(tmp, t, &r->root->add, r->zi);
}
static bool csg2_tri_diff_stack(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_stack_t *r,
size_t zi)
@ -1230,21 +1230,21 @@ static bool csg2_tri_diff_stack(
if (l == NULL) {
return true;
}
return csg2_tri_diff_layer(pool, t, l);
return csg2_tri_diff_layer(tmp, t, l);
}
static bool csg2_tri_diff_poly(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_poly_t *g)
{
if (g->diff_above != NULL) {
if (!cp_csg2_tri_poly(pool, t, g->diff_above)) {
if (!cp_csg2_tri_poly(tmp, t, g->diff_above)) {
return false;
}
}
if (g->diff_below != NULL) {
if (!cp_csg2_tri_poly(pool, t, g->diff_below)) {
if (!cp_csg2_tri_poly(tmp, t, g->diff_below)) {
return false;
}
}
@ -1252,29 +1252,29 @@ static bool csg2_tri_diff_poly(
}
static bool csg2_tri_diff_csg2(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_t *r,
size_t zi)
{
switch (r->type) {
case CP_CSG2_POLY:
return csg2_tri_diff_poly(pool, t, cp_csg2_cast(cp_csg2_poly_t, r));
return csg2_tri_diff_poly(tmp, t, cp_csg2_cast(cp_csg2_poly_t, r));
case CP_CSG2_STACK:
return csg2_tri_diff_stack(pool, t, cp_csg2_cast(cp_csg2_stack_t, r), zi);
return csg2_tri_diff_stack(tmp, t, cp_csg2_cast(cp_csg2_stack_t, r), zi);
default:
return true;
}
}
static bool csg2_tri_diff_v_csg2(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_v_obj_p_t *r,
size_t zi)
{
for (cp_v_each(i, r)) {
if (!csg2_tri_diff_csg2(pool, t, cp_csg2_cast(cp_csg2_t, cp_v_nth(r,i)), zi)) {
if (!csg2_tri_diff_csg2(tmp, t, cp_csg2_cast(cp_csg2_t, cp_v_nth(r,i)), zi)) {
return false;
}
}
@ -1332,7 +1332,7 @@ static bool csg2_tri_diff_v_csg2(
* Additionally, the improper start case has a special case if vertices
* coincide.
*
* Uses \p pool for all temporary allocations (but not for constructing
* Uses \p tmp for all temporary allocations (but not for constructing
* point_arr or tri).
*
* Runtime: O(n log n)
@ -1340,7 +1340,7 @@ static bool csg2_tri_diff_v_csg2(
* Where n = number of points.
*/
extern bool cp_csg2_tri_set(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_vec2_arr_ref_t *point_arr,
cp_v_size3_t *tri,
@ -1363,7 +1363,7 @@ extern bool cp_csg2_tri_set(
/* allocate list cells */
size_t list_size = node->size * 2;
list_t *list_data = CP_POOL_NEW_ARR(pool, *list_data, list_size);
list_t *list_data = CP_POOL_NEW_ARR(tmp, *list_data, list_size);
assert(cp_mem_is0(list_data, sizeof(*list_data) * list_size));
/* init context */
@ -1421,14 +1421,14 @@ extern bool cp_csg2_tri_set(
* This uses cp_csg2_tri_set() internally, so the path is contrained
* in the way described for that function.
*
* Uses \p pool for all temporary allocations (but not for constructing g).
* Uses \p tmp for all temporary allocations (but not for constructing g).
*
* Runtime: O(n log n)
* Space: O(n)
* Where n = number of points.
*/
extern bool cp_csg2_tri_path(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_poly_t *g,
cp_csg2_path_t *s)
@ -1436,8 +1436,8 @@ extern bool cp_csg2_tri_path(
size_t n = s->point_idx.size;
/* allocate */
node_t *node = CP_POOL_NEW_ARR(pool, *node, n);
edge_t *edge = CP_POOL_NEW_ARR(pool, *edge, n);
node_t *node = CP_POOL_NEW_ARR(tmp, *node, n);
edge_t *edge = CP_POOL_NEW_ARR(tmp, *edge, n);
/* Init nodes and edges and insert into X structure 'px'
* To do multiple paths in one go, this would need to be
@ -1455,7 +1455,9 @@ extern bool cp_csg2_tri_path(
cp_a_csg2_3node_t a = CP_A_INIT_WITH(node, n);
return cp_csg2_tri_set(pool, t, &CP_VEC2_ARR_REF(&g->point, coord), &g->triangle, &a);
cp_vec2_arr_ref_t a2;
cp_vec2_arr_ref_from_v_vec2_loc(&a2, &g->point);
return cp_csg2_tri_set(tmp, t, &a2, &g->triangle, &a);
}
/**
@ -1467,14 +1469,14 @@ extern bool cp_csg2_tri_path(
* the paths in one data structure, so the set of paths of the given
* polygon is contrained in the way described for that function.
*
* Uses \p pool for all temporary allocations (but not for constructing r).
* Uses \p tmp for all temporary allocations (but not for constructing r).
*
* Runtime: O(n log n)
* Space: O(n)
* Where n = number of points.
*/
extern bool cp_csg2_tri_poly(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_poly_t *g)
{
@ -1489,8 +1491,8 @@ extern bool cp_csg2_tri_poly(
}
/* allocate */
node_t *node = CP_POOL_NEW_ARR(pool, *node, n);
edge_t *edge = CP_POOL_NEW_ARR(pool, *edge, n);
node_t *node = CP_POOL_NEW_ARR(tmp, *node, n);
edge_t *edge = CP_POOL_NEW_ARR(tmp, *edge, n);
/* make edges */
size_t m = g->path.size;
@ -1523,13 +1525,57 @@ extern bool cp_csg2_tri_poly(
cp_a_csg2_3node_t a = CP_A_INIT_WITH(node, n);
/* run the triangulation algorithm */
if (!cp_csg2_tri_set(pool, t, &CP_VEC2_ARR_REF(&g->point, coord), &g->triangle, &a)) {
cp_vec2_arr_ref_t a2;
cp_vec2_arr_ref_from_v_vec2_loc(&a2, &g->point);
if (!cp_csg2_tri_set(tmp, t, &a2, &g->triangle, &a)) {
return false;
}
assert(g->triangle.size <= tri_cnt);
return true;
}
/**
* Same as cp_csg2_tri_poly, but triangulates a reference array of vec2.
*/
extern bool cp_csg2_tri_vec2_arr_ref(
cp_v_size3_t *tri,
cp_pool_t *tmp,
cp_err_t *t,
cp_vec2_arr_ref_t *a2)
{
/* count edges */
size_t n = a2->count;
if (n < 2) {
return true;
}
/* allocate */
node_t *node = CP_POOL_NEW_ARR(tmp, *node, n);
edge_t *edge = CP_POOL_NEW_ARR(tmp, *edge, n);
/* make edges */
for (cp_size_each(j, n)) {
node_t *p = &node[j];
p->loc = cp_vec2_arr_loc(a2, j);
p->coord = cp_vec2_arr_ref(a2, j);
p->out = &edge[j];
p->in = &edge[cp_wrap_sub1(j,n)];
}
/* Expect n triangles (that's about in the middle of the worst case, and
* slightly larger than what's expected). */
cp_v_clear(tri, n);
cp_a_csg2_3node_t a = CP_A_INIT_WITH(node, n);
/* run the triangulation algorithm */
if (!cp_csg2_tri_set(tmp, t, a2, tri, &a)) {
return false;
}
assert(tri->size <= n);
return true;
}
/**
* Triangulate a given layer
*
@ -1544,7 +1590,7 @@ extern bool cp_csg2_tri_poly(
* tree, so the set of paths of each polygon in the tree is contrained
* in the way described for that function.
*
* Uses \p pool for all temporary allocations (but not for constructing r).
* Uses \p tmp for all temporary allocations (but not for constructing r).
*
* Runtime: O(m * n log n)
* Space: O(max(n))
@ -1554,7 +1600,7 @@ extern bool cp_csg2_tri_poly(
* max(n) = the maximum n among the polygons.
*/
extern bool cp_csg2_tri_layer(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_tree_t *r,
size_t zi)
@ -1562,7 +1608,7 @@ extern bool cp_csg2_tri_layer(
if (r->root == NULL) {
return true;
}
return csg2_tri_csg2(pool, t, r->root, zi);
return csg2_tri_csg2(tmp, t, r->root, zi);
}
/**
@ -1574,7 +1620,7 @@ extern bool cp_csg2_tri_layer(
* Runtime and space: see cp_csg2_tri_layer.
*/
extern bool cp_csg2_tri_layer_diff(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_err_t *t,
cp_csg2_tree_t *r,
size_t zi)
@ -1582,5 +1628,5 @@ extern bool cp_csg2_tri_layer_diff(
if (r->root == NULL) {
return true;
}
return csg2_tri_diff_csg2(pool, t, r->root, zi);
return csg2_tri_diff_csg2(tmp, t, r->root, zi);
}

View file

@ -22,12 +22,13 @@
#define TRI_RIGHT 2
typedef struct {
cp_pool_t *tmp;
cp_mat3wi_t const *mat;
cp_gc_t gc;
} mat_ctxt_t;
typedef struct {
cp_pool_t *pool;
cp_pool_t *tmp;
cp_csg3_tree_t *tree;
cp_csg_opt_t const *opt;
cp_err_t *err;
@ -693,6 +694,99 @@ static size_t get_fn(
return fn;
}
/**
* Ensure that all paths of the polygon run clockwise.
*
* If a path needs to be reversed, do it.
*
* Return whether any path needed reversal.
*/
static bool polygon_make_clockwise(
cp_csg2_poly_t *p)
{
bool rev = false;
for (cp_v_each(i, &p->path)) {
cp_csg2_path_t *q = &cp_v_nth(&p->path, i);
double sum = 0;
for (cp_v_each(j0, &q->point_idx)) {
size_t j1 = cp_wrap_add1(j0, q->point_idx.size);
size_t j2 = cp_wrap_add1(j1, q->point_idx.size);
sum += cp_vec2_right_cross3_z(
&cp_v_nth(&p->point, cp_v_nth(&q->point_idx, j0)).coord,
&cp_v_nth(&p->point, cp_v_nth(&q->point_idx, j1)).coord,
&cp_v_nth(&p->point, cp_v_nth(&q->point_idx, j2)).coord);
}
assert(!cp_eq(sum, 0));
if (sum < 0) {
rev = true;
cp_v_reverse(&q->point_idx, 0, -(size_t)1);
}
}
return rev;
}
#if 0
/**
* Whether a sequence of 3 points is convex.
*
* Returns a bitmask:
* bit 0: XY is concave
* bit 1: XY is collinear
* bit 2: XY is convex
* bit 3: YZ is concave
* bit 4: YZ is collinear
* bit 5: YZ is convex
*/
static int vec3_face_normal3_z(
cp_vec3_t const *a,
cp_vec3_t const *o,
cp_vec3_t const *b)
{
return
(1 << (1 + cp_vec2_right_normal3_z(&a->b, &o->b, &b->b))) |
(1 << (4 + cp_vec2_right_normal3_z(&a->be, &o->be, &b->be)));
}
#endif
static void face_from_tri_or_poly(
size_t *k,
cp_csg3_poly_t *o,
cp_v_size3_t *tri,
cp_loc_t loc,
size_t fn,
bool rev,
bool top)
{
size_t j_offset = top ? o->point.size - fn : 0;
cp_csg3_face_t *f;
if (tri->size > 0) {
/* from triangulation */
for (cp_v_each(i, tri)) {
f = &cp_v_nth(&o->face, (*k)++);
cp_v_init0(&f->point, 3);
for (cp_size_each(j, 3)) {
cp_vec3_loc_ref_t *v = &cp_v_nth(&f->point, j);
v->ref = &cp_v_nth(&o->point, cp_v_nth(tri, i).p[j] + j_offset);
v->loc = loc;
}
face_basics(f, rev ^ top, loc);
}
}
else {
/* from convex path */
f = &cp_v_nth(&o->face, (*k)++);
cp_v_init0(&f->point, fn);
for (cp_size_each(j, fn)) {
cp_vec3_loc_ref_t *v = &cp_v_nth(&f->point, j);
v->ref = &cp_v_nth(&o->point, j + j_offset);
v->loc = loc;
}
face_basics(f, rev ^ top, loc);
}
}
/**
* From an array of points in the rough shape of a tower,
* make a polyhedron. 'Tower' means the shape consists
@ -719,15 +813,62 @@ static size_t get_fn(
*
* This also runs xform and minmax, but not make_edges.
*/
static void faces_from_tower(
static bool faces_n_edges_from_tower(
cp_csg3_poly_t *o,
ctxt_t *c,
cp_mat3wi_t const *m,
cp_loc_t loc,
size_t fn,
size_t fnz,
bool rev,
unsigned tri_side)
unsigned tri_side,
bool may_need_tri)
{
/* FIXME:
* To cope with non-convex bottom and top:
*
* * Check here whether top/bottom will be non-convex.
*
* * Assume the face will be broken into triangles, so face
* count is known.
*
* * Instead of '1', use computed face count in init0.
*
* * Instead of normal face construction, use a callback
* based call into csg2-triangle module. That module
* needs to be changed so that 'add_triangle' can be
* a callback.
*/
unsigned orient = 0;
bool need_tri = false;
if (may_need_tri) {
for (cp_size_each(i, fn)) {
size_t j = cp_wrap_add1(i, fn);
size_t k = cp_wrap_add1(j, fn);
orient |= 1U << (1 + cp_vec2_right_normal3_z(
&cp_v_nth(&o->point, i).coord.b,
&cp_v_nth(&o->point, j).coord.b,
&cp_v_nth(&o->point, k).coord.b));
if ((orient & 5) == 5) { /* both directions in path */
need_tri = true;
break;
}
}
}
/* check whether rev was passed correctly */
cp_v_size3_t tri = {0};
if (need_tri) {
cp_vec2_arr_ref_t a2;
cp_vec2_arr_ref_from_a_vec3_loc(&a2, &o->point, false);
assert(a2.count >= fn);
a2.count = fn;
if (!cp_csg2_tri_vec2_arr_ref(&tri, c->tmp, c->err, &a2)) {
return false;
}
}
/* reverse based on determinant */
if (m->d < 0) {
rev = !rev;
@ -743,36 +884,24 @@ static void faces_from_tower(
/* generate faces */
size_t k = 0;
size_t bt_cnt = tri.size ? tri.size : 1U; /* faces in bottom (and top) */
cp_v_init0(&o->face,
1U + /* bottom */
!!has_top + /* top */
(bt_cnt * (
1U + /* bottom */
!!has_top)) + /* top */
((fnz - 2) * fn * (1U + !!tri_side)) + /* rings */
(fn * (1U + !!(tri_side && has_top)))); /* roof */
/* bottom */
cp_csg3_face_t *f = &cp_v_nth(&o->face, k++);
cp_v_init0(&f->point, fn);
for (cp_size_each(j, fn)) {
cp_vec3_loc_ref_t *v = &cp_v_nth(&f->point, j);
v->ref = &cp_v_nth(&o->point, j);
v->loc = loc;
}
face_basics(f, rev, loc);
face_from_tri_or_poly(&k, o, &tri, loc, fn, rev, false);
/* top */
if (has_top) {
/* top */
f = &cp_v_nth(&o->face, k++);
cp_v_init0(&f->point, fn);
for (cp_size_each(j, fn)) {
cp_vec3_loc_ref_t *v = &cp_v_nth(&f->point, j);
v->ref = &cp_v_nth(&o->point, o->point.size - j - 1);
v->loc = loc;
}
face_basics(f, rev, loc);
face_from_tri_or_poly(&k, o, &tri, loc, fn, rev, true);
}
/* sides */
cp_csg3_face_t *f;
for (cp_size_each(i, fnz, 1, !has_top)) {
size_t k1 = i * fn;
size_t k0 = k1 - fn;
@ -817,6 +946,7 @@ static void faces_from_tower(
}
assert(o->face.size == k);
return poly_make_edges(o, c);
}
static void set_vec3_loc(
@ -830,8 +960,9 @@ static void set_vec3_loc(
p->loc = loc;
}
static void csg3_poly_make_sphere(
static bool csg3_poly_make_sphere(
cp_csg3_poly_t *o,
ctxt_t *c,
cp_mat3wi_t const *m,
cp_scad_sphere_t const *s,
size_t fn)
@ -856,8 +987,8 @@ static void csg3_poly_make_sphere(
p += fn;
}
/* make faces */
faces_from_tower(o, m, s->loc, fn, fnz, true, TRI_NONE);
/* make faces and edges */
return faces_n_edges_from_tower(o, c, m, s->loc, fn, fnz, true, TRI_NONE, false);
}
static bool csg3_from_sphere(
@ -887,11 +1018,11 @@ static bool csg3_from_sphere(
size_t fn = get_fn(c->opt, s->_fn, true);
if (fn > 0) {
/* all faces are convex */
cp_csg3_poly_t *o = cp_csg3_new_obj(*o, s->loc, mo->gc);
cp_v_push(r, cp_obj(o));
csg3_poly_make_sphere(o, m, s, fn);
if (!poly_make_edges(o, c)) {
if (!csg3_poly_make_sphere(o, c, m, s, fn)) {
return msg(c, CP_ERR_FAIL, NULL, NULL,
" Internal Error: 'sphere' polyhedron construction algorithm is broken.\n");
}
@ -949,6 +1080,7 @@ static bool csg3_from_polyhedron(
s->faces.size);
}
/* FIXME: possibly concave faces */
cp_csg3_poly_t *o = cp_csg3_new_obj(*o, s->loc, m->gc);
cp_v_push(r, cp_obj(o));
@ -1010,37 +1142,6 @@ static void xform_2d(
}
}
/**
* Ensure that all paths of the polygon run clockwise.
*
* If a path needs to be reversed, do it.
*
* Return whether any path needed reversal.
*/
static bool polygon_clockwise(
cp_csg2_poly_t *p)
{
bool rev = false;
for (cp_v_each(i, &p->path)) {
cp_csg2_path_t *q = &cp_v_nth(&p->path, i);
double sum = 0;
for (cp_v_each(j0, &q->point_idx)) {
size_t j1 = cp_wrap_add1(j0, q->point_idx.size);
size_t j2 = cp_wrap_add1(j1, q->point_idx.size);
sum += cp_vec2_right_cross3_z(
&cp_v_nth(&p->point, cp_v_nth(&q->point_idx, j0)).coord,
&cp_v_nth(&p->point, cp_v_nth(&q->point_idx, j1)).coord,
&cp_v_nth(&p->point, cp_v_nth(&q->point_idx, j2)).coord);
}
assert(!cp_eq(sum, 0));
if (sum < 0) {
rev = true;
cp_v_reverse(&q->point_idx, 0, -(size_t)1);
}
}
return rev;
}
static bool csg3_from_polygon(
bool *no,
cp_v_obj_p_t *r,
@ -1098,7 +1199,7 @@ static bool csg3_from_polygon(
}
/* normalise to paths to be clockwise */
(void)polygon_clockwise(o);
(void)polygon_make_clockwise(o);
return true;
}
@ -1144,6 +1245,7 @@ static bool csg3_from_cube(
}
/* make points */
/* all faces are convex */
cp_csg3_poly_t *o = cp_csg3_new_obj(*o, s->loc, mo->gc);
cp_v_push(r, cp_obj(o));
@ -1160,11 +1262,8 @@ static bool csg3_from_cube(
set_vec3_loc(&cp_v_nth(&o->point, i), !(i&1)^!(i&2), !(i&2), !(i&4), s->loc);
}
/* make faces */
faces_from_tower(o, m, s->loc, 4, 2, false, TRI_NONE);
/* make edges */
if (!poly_make_edges(o, c)) {
/* make faces & edges */
if (!faces_n_edges_from_tower(o, c, m, s->loc, 4, 2, false, TRI_NONE, false)) {
return msg(c, CP_ERR_FAIL, NULL, NULL,
" Internal Error: 'cube' polyhedron construction algorithm is broken.\n");
}
@ -1228,7 +1327,7 @@ static bool csg3_from_circle(
mn.mat = m;
xform_2d(&mn, o);
bool rev __unused = polygon_clockwise(o);
bool rev __unused = polygon_make_clockwise(o);
assert(!rev);
return true;
@ -1277,9 +1376,11 @@ static bool csg3_from_square(
cp_csg2_poly_t *o = cp_csg2_new(*o, s->loc);
cp_v_push(r, cp_obj(o));
cp_csg2_path_t *path = cp_v_push0(&o->path);
cp_v_init0(&o->path, 1);
cp_csg2_path_t *path = &cp_v_nth(&o->path, 0);
cp_v_init0(&o->point, 4);
for (cp_size_each(i, 4)) {
cp_vec2_loc_t *p = cp_v_push0(&o->point);
cp_vec2_loc_t *p = &cp_v_nth(&o->point, i);
p->coord.x = cp_dim(!!(i & 1));
p->coord.y = cp_dim(!!(i & 2));
p->loc = s->loc;
@ -1296,7 +1397,7 @@ static bool csg3_from_square(
cp_v_push(&path->point_idx, 3);
cp_v_push(&path->point_idx, 1);
bool rev __unused = polygon_clockwise(o);
bool rev __unused = polygon_make_clockwise(o);
assert(!rev);
return true;
@ -1311,6 +1412,7 @@ static bool csg3_poly_cylinder(
cp_scale_t r2,
size_t fn)
{
/* all faces are convex */
cp_csg3_poly_t *o = cp_csg3_new_obj(*o, s->loc, mo->gc);
cp_v_push(r, cp_obj(o));
@ -1332,11 +1434,8 @@ static bool csg3_poly_cylinder(
}
}
/* make faces */
faces_from_tower(o, m, s->loc, fn, 2, false, TRI_NONE);
/* make edges */
if (!poly_make_edges(o, c)) {
/* make faces & edges */
if (!faces_n_edges_from_tower(o, c, m, s->loc, fn, 2, false, TRI_NONE, false)) {
return msg(c, CP_ERR_FAIL, NULL, NULL,
" Internal Error: 'cylinder' polyhedron construction algorithm is broken.\n");
}
@ -1489,8 +1588,8 @@ static bool csg3_from_linext(
}
/* get polygon */
cp_csg2_poly_t *p = cp_csg2_flatten(c->opt, c->pool, &rc);
cp_pool_clear(c->pool);
cp_csg2_poly_t *p = cp_csg2_flatten(c->opt, c->tmp, &rc);
cp_pool_clear(c->tmp);
/* empty? */
if ((p == NULL) || (p->path.size == 0)) {
@ -1513,7 +1612,7 @@ static bool csg3_from_linext(
tri = TRI_LEFT;
}
/* Use 3D XOR to handle 2D XOR semantics of polygon paths */
cp_v_csg_add_p_t *xo = NULL;
if (p->path.size >= 2) {
cp_csg_xor_t *xor = cp_csg_new(*xor, s->loc);
@ -1521,17 +1620,13 @@ static bool csg3_from_linext(
xo = &xor->xor;
}
/* FIXME:
* Some paths are negative and cannot simply be generated as a separate
* linext, but must be considered as a single one.
* Identify which ones are negative (or use XOR on linear extrusions).
*/
for (cp_v_each(i, &p->path)) {
cp_csg2_path_t const *q = &cp_v_nth(&p->path, i);
size_t pcnt = q->point_idx.size;
size_t tcnt = (zcnt * pcnt) + is_cone;
/* possibly concave faces: handled by faces_n_edge_from_tower. */
cp_csg3_poly_t *o = cp_csg3_new_obj(*o, s->loc, mo->gc);
if (xo != NULL) {
cp_csg_add_t *o2 = cp_csg_new(*o2, s->loc);
@ -1567,17 +1662,12 @@ static bool csg3_from_linext(
w->loc = s->loc;
}
faces_from_tower(o, m, s->loc, pcnt, s->slices + 1, true, tri);
if (!poly_make_edges(o, c)) {
if (!faces_n_edges_from_tower(o, c, m, s->loc, pcnt, s->slices + 1, true, tri, true)) {
return msg(c, CP_ERR_FAIL, NULL, NULL,
" Internal Error: 'linear_extrude' polyhedron construction algorithm is broken.\n");
}
}
/* FIXME: continue */
(void)r;
return true;
}
@ -1901,7 +1991,7 @@ extern void cp_csg3_tree_bb(
* Convert a SCAD AST into a CSG3 tree.
*/
extern bool cp_csg3_from_scad_tree(
cp_pool_t *pool,
cp_pool_t *tmp,
cp_csg3_tree_t *r,
cp_err_t *t,
cp_scad_tree_t const *scad)
@ -1911,7 +2001,7 @@ extern bool cp_csg3_from_scad_tree(
assert(t != NULL);
assert(r->opt != NULL);
ctxt_t c = {
.pool = pool,
.tmp = tmp,
.tree = r,
.opt = r->opt,
.err = t,

View file

@ -163,9 +163,12 @@ TEST_STL.scad := \
scad-test/test13.scad \
scad-test/test14.scad \
scad-test/test31d.scad \
scad-test/test30a.scad
scad-test/test30a.scad \
scad-test/test13b.scad
FAIL_STL.scad := \
scad-test/chain1.scad \
scad-test/linext5.scad \
scad-test/test13b.scad
FAIL_JS.scad := \
scad-test/chain1.scad \
scad-test/linext5.scad