mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Boolean geometry bugfixes.
This commit is contained in:
parent
88314af127
commit
1ad126f85d
1 changed files with 98 additions and 77 deletions
175
geometry.scad
175
geometry.scad
|
@ -19,11 +19,12 @@
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// point = The point to test.
|
// point = The point to test.
|
||||||
// edge = Array of two points forming the line segment to test against.
|
// edge = Array of two points forming the line segment to test against.
|
||||||
function point_on_segment2d(point, edge) =
|
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||||
point==edge[0] || point==edge[1] || // The point is an endpoint
|
function point_on_segment2d(point, edge, eps=EPSILON) =
|
||||||
|
approx(point,edge[0],eps=eps) || approx(point,edge[1],eps=eps) || // The point is an endpoint
|
||||||
sign(edge[0].x-point.x)==sign(point.x-edge[1].x) // point is in between the
|
sign(edge[0].x-point.x)==sign(point.x-edge[1].x) // point is in between the
|
||||||
&& sign(edge[0].y-point.y)==sign(point.y-edge[1].y) // edge endpoints
|
&& sign(edge[0].y-point.y)==sign(point.y-edge[1].y) // edge endpoints
|
||||||
&& point_left_of_segment2d(point, edge)==0; // and on the line defined by edge
|
&& approx(point_left_of_segment2d(point, edge),0,eps=eps); // and on the line defined by edge
|
||||||
|
|
||||||
|
|
||||||
// Function: point_left_of_segment2d()
|
// Function: point_left_of_segment2d()
|
||||||
|
@ -41,11 +42,11 @@ function point_left_of_segment2d(point, edge) =
|
||||||
|
|
||||||
|
|
||||||
// Internal non-exposed function.
|
// Internal non-exposed function.
|
||||||
function _point_above_below_segment(point, edge) =
|
function _point_above_below_segment(point, edge, eps=EPSILON) =
|
||||||
edge[0].y <= point.y? (
|
edge[0].y <= point.y+eps? (
|
||||||
(edge[1].y > point.y && point_left_of_segment2d(point, edge) > 0)? 1 : 0
|
(edge[1].y > point.y-eps && point_left_of_segment2d(point, edge) > eps)? 1 : 0
|
||||||
) : (
|
) : (
|
||||||
(edge[1].y <= point.y && point_left_of_segment2d(point, edge) < 0)? -1 : 0
|
(edge[1].y <= point.y+eps && point_left_of_segment2d(point, edge) < eps)? -1 : 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,11 +132,13 @@ function line_normal(p1,p2) =
|
||||||
// for each segment, so if it is in this range, then the intersection
|
// for each segment, so if it is in this range, then the intersection
|
||||||
// lies on the segment. Otherwise it lies somewhere on the extension
|
// lies on the segment. Otherwise it lies somewhere on the extension
|
||||||
// of the segment.
|
// of the segment.
|
||||||
function _general_line_intersection(s1,s2) =
|
function _general_line_intersection(s1,s2,eps=EPSILON) =
|
||||||
let( denominator = det2([s1[0],s2[0]]-[s1[1],s2[1]]),
|
let(
|
||||||
t=det2([s1[0],s2[0]]-s2)/denominator,
|
denominator = det2([s1[0],s2[0]]-[s1[1],s2[1]])
|
||||||
u=det2([s1[0],s1[0]]-[s1[1],s2[1]])/denominator)
|
) approx(denominator,0,eps=eps)? [undef,undef,undef] : let(
|
||||||
[denominator==0 ? undef : s1[0]+t*(s1[1]-s1[0]),t,u];
|
t = det2([s1[0],s2[0]]-s2) / denominator,
|
||||||
|
u = det2([s1[0],s1[0]]-[s1[1],s2[1]]) /denominator
|
||||||
|
) [s1[0]+t*(s1[1]-s1[0]), t, u];
|
||||||
|
|
||||||
|
|
||||||
// Function: line_intersection()
|
// Function: line_intersection()
|
||||||
|
@ -147,7 +150,8 @@ function _general_line_intersection(s1,s2) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// l1 = First 2D line, given as a list of two 2D points on the line.
|
// l1 = First 2D line, given as a list of two 2D points on the line.
|
||||||
// l2 = Second 2D line, given as a list of two 2D points on the line.
|
// l2 = Second 2D line, given as a list of two 2D points on the line.
|
||||||
function line_intersection(l1,l2) = let( isect = _general_line_intersection(l1,l2)) isect[0];
|
function line_intersection(l1,l2,eps=EPSILON) =
|
||||||
|
let(isect = _general_line_intersection(l1,l2,eps=eps)) isect[0];
|
||||||
|
|
||||||
|
|
||||||
// Function: segment_intersection()
|
// Function: segment_intersection()
|
||||||
|
@ -159,10 +163,10 @@ function line_intersection(l1,l2) = let( isect = _general_line_intersection(l1,l
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// s1 = First 2D segment, given as a list of the two 2D endpoints of the line segment.
|
// s1 = First 2D segment, given as a list of the two 2D endpoints of the line segment.
|
||||||
// s2 = Second 2D segment, given as a list of the two 2D endpoints of the line segment.
|
// s2 = Second 2D segment, given as a list of the two 2D endpoints of the line segment.
|
||||||
function segment_intersection(s1,s2) =
|
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||||
|
function segment_intersection(s1,s2,eps=EPSILON) =
|
||||||
let(
|
let(
|
||||||
isect = _general_line_intersection(s1,s2),
|
isect = _general_line_intersection(s1,s2,eps=eps)
|
||||||
eps=EPSILON
|
|
||||||
) isect[1]<0-eps || isect[1]>1+eps || isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0];
|
) isect[1]<0-eps || isect[1]>1+eps || isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0];
|
||||||
|
|
||||||
|
|
||||||
|
@ -175,10 +179,10 @@ function segment_intersection(s1,s2) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// line = The unbounded 2D line, defined by two 2D points on the line.
|
// line = The unbounded 2D line, defined by two 2D points on the line.
|
||||||
// segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment.
|
// segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment.
|
||||||
function line_segment_intersection(line,segment) =
|
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||||
|
function line_segment_intersection(line,segment,eps=EPSILON) =
|
||||||
let(
|
let(
|
||||||
isect = _general_line_intersection(line,segment),
|
isect = _general_line_intersection(line,segment,eps=eps)
|
||||||
eps = EPSILON
|
|
||||||
) isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0];
|
) isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0];
|
||||||
|
|
||||||
|
|
||||||
|
@ -342,7 +346,7 @@ function is_path(x) = is_list(x) && is_vector(x.x);
|
||||||
// is_closed_path(path, [eps]);
|
// is_closed_path(path, [eps]);
|
||||||
// Description:
|
// Description:
|
||||||
// Returns true if the first and last points in the given path are coincident.
|
// Returns true if the first and last points in the given path are coincident.
|
||||||
function is_closed_path(path, eps=1e-6) = approx(path[0], path[len(path)-1], eps=eps);
|
function is_closed_path(path, eps=EPSILON) = approx(path[0], path[len(path)-1], eps=eps);
|
||||||
|
|
||||||
|
|
||||||
// Function: close_path(path)
|
// Function: close_path(path)
|
||||||
|
@ -350,7 +354,7 @@ function is_closed_path(path, eps=1e-6) = approx(path[0], path[len(path)-1], eps
|
||||||
// close_path(path);
|
// close_path(path);
|
||||||
// Description:
|
// Description:
|
||||||
// If a path's last point does not coincide with its first point, closes the path so it does.
|
// If a path's last point does not coincide with its first point, closes the path so it does.
|
||||||
function close_path(path) = approx(path[0],path[len(path)-1])? path : concat(path,[path[0]]);
|
function close_path(path, eps=EPSILON) = is_closed_path(path,eps=eps)? path : concat(path,[path[0]]);
|
||||||
|
|
||||||
|
|
||||||
// Function path_subselect()
|
// Function path_subselect()
|
||||||
|
@ -385,7 +389,7 @@ function path_subselect(path,s1,u1,s2,u2) =
|
||||||
// assemble_path_fragments(subpaths);
|
// assemble_path_fragments(subpaths);
|
||||||
// Description:
|
// Description:
|
||||||
// Given a list of incomplete paths, assembles them together into complete closed paths if it can.
|
// Given a list of incomplete paths, assembles them together into complete closed paths if it can.
|
||||||
function assemble_path_fragments(subpaths,_finished=[]) =
|
function assemble_path_fragments(subpaths,eps=EPSILON,_finished=[]) =
|
||||||
len(subpaths)<=1? concat(_finished, subpaths) :
|
len(subpaths)<=1? concat(_finished, subpaths) :
|
||||||
let(
|
let(
|
||||||
path = subpaths[0],
|
path = subpaths[0],
|
||||||
|
@ -393,7 +397,7 @@ function assemble_path_fragments(subpaths,_finished=[]) =
|
||||||
for (i=[1:1:len(subpaths)-1], rev1=[0,1], rev2=[0,1]) let(
|
for (i=[1:1:len(subpaths)-1], rev1=[0,1], rev2=[0,1]) let(
|
||||||
idx1 = rev1? 0 : len(path)-1,
|
idx1 = rev1? 0 : len(path)-1,
|
||||||
idx2 = rev2? len(subpaths[i])-1 : 0
|
idx2 = rev2? len(subpaths[i])-1 : 0
|
||||||
) if (approx(path[idx1], subpaths[i][idx2])) [
|
) if (approx(path[idx1], subpaths[i][idx2], eps=eps)) [
|
||||||
i, concat(
|
i, concat(
|
||||||
rev1? reverse(path) : path,
|
rev1? reverse(path) : path,
|
||||||
select(rev2? reverse(subpaths[i]) : subpaths[i], 1,-1)
|
select(rev2? reverse(subpaths[i]) : subpaths[i], 1,-1)
|
||||||
|
@ -403,12 +407,14 @@ function assemble_path_fragments(subpaths,_finished=[]) =
|
||||||
) len(matches)==0? (
|
) len(matches)==0? (
|
||||||
assemble_path_fragments(
|
assemble_path_fragments(
|
||||||
select(subpaths,1,-1),
|
select(subpaths,1,-1),
|
||||||
concat(_finished, [path])
|
eps=eps,
|
||||||
|
_finished=concat(_finished, [path])
|
||||||
)
|
)
|
||||||
) : is_closed_path(matches[0][1])? (
|
) : is_closed_path(matches[0][1], eps=eps)? (
|
||||||
assemble_path_fragments(
|
assemble_path_fragments(
|
||||||
[for (i=[1:1:len(subpaths)-1]) if(i != matches[0][0]) subpaths[i]],
|
[for (i=[1:1:len(subpaths)-1]) if(i != matches[0][0]) subpaths[i]],
|
||||||
concat(_finished, [matches[0][1]])
|
eps=eps,
|
||||||
|
_finished=concat(_finished, [matches[0][1]])
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
assemble_path_fragments(
|
assemble_path_fragments(
|
||||||
|
@ -416,7 +422,8 @@ function assemble_path_fragments(subpaths,_finished=[]) =
|
||||||
[matches[0][1]],
|
[matches[0][1]],
|
||||||
[for (i = [1:1:len(subpaths)-1]) if(i != matches[0][0]) subpaths[i]]
|
[for (i = [1:1:len(subpaths)-1]) if(i != matches[0][0]) subpaths[i]]
|
||||||
),
|
),
|
||||||
_finished
|
eps=eps,
|
||||||
|
_finished=_finished
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -469,11 +476,12 @@ function simplify_path_indexed(points, path, eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// point = The point to check position of.
|
// point = The point to check position of.
|
||||||
// path = The list of 2D path points forming the perimeter of the polygon.
|
// path = The list of 2D path points forming the perimeter of the polygon.
|
||||||
function point_in_polygon(point, path) =
|
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||||
|
function point_in_polygon(point, path, eps=EPSILON) =
|
||||||
// Does the point lie on any edges? If so return 0.
|
// Does the point lie on any edges? If so return 0.
|
||||||
sum([for(i=[0:1:len(path)-1]) point_on_segment2d(point, select(path, i, i+1))?1:0])>0 ? 0 :
|
sum([for(i=[0:1:len(path)-1]) point_on_segment2d(point, select(path, i, i+1), eps=eps)?1:0])>0 ? 0 :
|
||||||
// Otherwise compute winding number and return 1 for interior, -1 for exterior
|
// Otherwise compute winding number and return 1 for interior, -1 for exterior
|
||||||
sum([for(i=[0:1:len(path)-1]) _point_above_below_segment(point, select(path, i, i+1))]) != 0 ? 1 : -1;
|
sum([for(i=[0:1:len(path)-1]) _point_above_below_segment(point, select(path, i, i+1), eps=eps)]) != 0 ? 1 : -1;
|
||||||
|
|
||||||
|
|
||||||
// Function: point_in_region()
|
// Function: point_in_region()
|
||||||
|
@ -487,10 +495,11 @@ function point_in_polygon(point, path) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// point = The point to test.
|
// point = The point to test.
|
||||||
// region = The region to test against. Given as a list of polygon paths.
|
// region = The region to test against. Given as a list of polygon paths.
|
||||||
function point_in_region(point, region, _i=0, _cnt=0) =
|
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||||
|
function point_in_region(point, region, eps=EPSILON, _i=0, _cnt=0) =
|
||||||
(_i >= len(region))? ((_cnt%2==1)? 1 : -1) : let(
|
(_i >= len(region))? ((_cnt%2==1)? 1 : -1) : let(
|
||||||
pip = point_in_polygon(point, region[_i])
|
pip = point_in_polygon(point, region[_i], eps=eps)
|
||||||
) pip==0? 0 : point_in_region(point, region, _i+1, _cnt + (pip>0? 1 : 0));
|
) approx(pip,0,eps=eps)? 0 : point_in_region(point, region, eps=eps, _i=_i+1, _cnt = _cnt + (pip>eps? 1 : 0));
|
||||||
|
|
||||||
|
|
||||||
// Function: pointlist_bounds()
|
// Function: pointlist_bounds()
|
||||||
|
@ -544,7 +553,7 @@ function is_region(x) = is_list(x) && is_path(x.x);
|
||||||
// close_region(region);
|
// close_region(region);
|
||||||
// Description:
|
// Description:
|
||||||
// Closes all paths within a given region.
|
// Closes all paths within a given region.
|
||||||
function close_region(region) = [for (path=region) close_path(path)];
|
function close_region(region, eps=EPSILON) = [for (path=region) close_path(path, eps=eps)];
|
||||||
|
|
||||||
|
|
||||||
// Function: region_path_crossings()
|
// Function: region_path_crossings()
|
||||||
|
@ -555,60 +564,68 @@ function close_region(region) = [for (path=region) close_path(path)];
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The path to find crossings on.
|
// path = The path to find crossings on.
|
||||||
// region = Region to test for crossings of.
|
// region = Region to test for crossings of.
|
||||||
function region_path_crossings(path, region) = sort([
|
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||||
|
function region_path_crossings(path, region, eps=EPSILON) = sort([
|
||||||
for (s1=enumerate(pair_wrap(path)), path=region, s2=pair_wrap(path)) let(
|
for (s1=enumerate(pair_wrap(path)), path=region, s2=pair_wrap(path)) let(
|
||||||
isect = _general_line_intersection(s1.y,s2),
|
isect = _general_line_intersection(s1[1],s2,eps=eps)
|
||||||
eps = 1e-9
|
|
||||||
) if (
|
) if (
|
||||||
!is_undef(isect) &&
|
!is_undef(isect) &&
|
||||||
isect[1] >= 0-eps && isect[1] < 1-eps &&
|
isect[1] >= 0-eps && isect[1] < 1-eps &&
|
||||||
isect[2] >= 0-eps && isect[2] < 1-eps
|
isect[2] >= 0-eps && isect[2] < 1-eps
|
||||||
) [s1.x, isect[1]]
|
) [s1[0], isect[1]]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
function _split_path_at_region_crossings(path, region, eps=1e-6) =
|
function _split_path_at_region_crossings(path, region, eps=EPSILON) =
|
||||||
let(
|
let(
|
||||||
path = deduplicate(path, eps=eps),
|
path = deduplicate(path, eps=eps),
|
||||||
region = [for (path=region) deduplicate(path, eps=eps)],
|
region = [for (path=region) deduplicate(path, eps=eps)],
|
||||||
crossings = deduplicate(concat(
|
crossings = deduplicate(
|
||||||
[[0,0]],
|
concat(
|
||||||
region_path_crossings(path, region),
|
[[0,0]],
|
||||||
[[len(path)-2,1]]
|
region_path_crossings(path, region),
|
||||||
))
|
[[len(path)-2,1]]
|
||||||
|
),
|
||||||
|
eps=eps
|
||||||
|
)
|
||||||
) [for (p = pair(crossings)) path_subselect(path, p[0][0], p[0][1], p[1][0], p[1][1])];
|
) [for (p = pair(crossings)) path_subselect(path, p[0][0], p[0][1], p[1][0], p[1][1])];
|
||||||
|
|
||||||
|
|
||||||
function _tag_subpaths(path, region) =
|
function _tag_subpaths(path, region, eps=EPSILON) =
|
||||||
let(
|
let(
|
||||||
subpaths = _split_path_at_region_crossings(path, region),
|
subpaths = _split_path_at_region_crossings(path, region, eps=eps),
|
||||||
tagged = [
|
tagged = [
|
||||||
for (subpath = subpaths) let(
|
for (sub = subpaths) let(
|
||||||
|
subpath = deduplicate(sub)
|
||||||
|
) if (len(sub)>1) let(
|
||||||
midpt = lerp(subpath[0], subpath[1], 0.5),
|
midpt = lerp(subpath[0], subpath[1], 0.5),
|
||||||
rel = point_in_region(midpt,region)
|
rel = point_in_region(midpt,region,eps=eps)
|
||||||
) rel<0? ["O", subpath] : rel>0? ["I", subpath] : let(
|
) rel<0? ["O", subpath] : rel>0? ["I", subpath] : let(
|
||||||
sidept = midpt + rot(90, planar=true, p=normalize(subpath[0][1]-subpath[0][0])*0.01),
|
vec = normalize(subpath[1]-subpath[0]),
|
||||||
rel2 = (point_in_region(sidept,region)>0) == (point_in_region(sidept,region)>0)
|
perp = rot(90, planar=true, p=vec),
|
||||||
) rel2? ["S", subpath] : ["U", subpath]
|
sidept = midpt + perp*0.01,
|
||||||
|
rel1 = point_in_polygon(sidept,path,eps=eps)>0,
|
||||||
|
rel2 = point_in_region(sidept,region,eps=eps)>0
|
||||||
|
) rel1==rel2? ["S", subpath] : ["U", subpath]
|
||||||
]
|
]
|
||||||
) tagged;
|
) tagged;
|
||||||
|
|
||||||
|
|
||||||
function _tag_region_subpaths(region1, region2) =
|
function _tag_region_subpaths(region1, region2, eps=EPSILON) =
|
||||||
[for (path=region1) each _tag_subpaths(path, region2)];
|
[for (path=region1) each _tag_subpaths(path, region2, eps=eps)];
|
||||||
|
|
||||||
|
|
||||||
function _tagged_region(region1,region2,keep1,keep2) =
|
function _tagged_region(region1,region2,keep1,keep2,eps=EPSILON) =
|
||||||
let(
|
let(
|
||||||
region1 = close_region(region1),
|
region1 = close_region(region1, eps=eps),
|
||||||
region2 = close_region(region2),
|
region2 = close_region(region2, eps=eps),
|
||||||
tagged1 = _tag_region_subpaths(region1,region2),
|
tagged1 = _tag_region_subpaths(region1, region2, eps=eps),
|
||||||
tagged2 = _tag_region_subpaths(region2,region1),
|
tagged2 = _tag_region_subpaths(region2, region1, eps=eps),
|
||||||
tagged = concat(
|
tagged = concat(
|
||||||
[for (tagpath = tagged1) if (in_list(tagpath[0], keep1)) tagpath[1]],
|
[for (tagpath = tagged1) if (in_list(tagpath[0], keep1)) tagpath[1]],
|
||||||
[for (tagpath = tagged2) if (in_list(tagpath[0], keep2)) tagpath[1]]
|
[for (tagpath = tagged2) if (in_list(tagpath[0], keep2)) tagpath[1]]
|
||||||
),
|
),
|
||||||
outregion = assemble_path_fragments(tagged)
|
outregion = assemble_path_fragments(tagged, eps=eps)
|
||||||
) outregion;
|
) outregion;
|
||||||
|
|
||||||
|
|
||||||
|
@ -624,15 +641,16 @@ function _tagged_region(region1,region2,keep1,keep2) =
|
||||||
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
||||||
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, close=true);
|
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, close=true);
|
||||||
// color("green") region(union(shape1,shape2));
|
// color("green") region(union(shape1,shape2));
|
||||||
function union(regions=[],b=undef,c=undef) =
|
function union(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
b!=undef? union(concat([regions],[b],c==undef?[]:[c])) :
|
b!=undef? union(concat([regions],[b],c==undef?[]:[c]), eps=eps) :
|
||||||
len(regions)<=1? regions[0] :
|
len(regions)<=1? regions[0] :
|
||||||
union(
|
union(
|
||||||
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
||||||
concat(
|
concat(
|
||||||
[_tagged_region(regions[0],regions[1],["O","S"],["O"])],
|
[_tagged_region(regions[0],regions[1],["O","S"],["O"], eps=eps)],
|
||||||
[for (i=[2:1:len(regions)-1]) regions[i]]
|
[for (i=[2:1:len(regions)-1]) regions[i]]
|
||||||
)
|
),
|
||||||
|
eps=eps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -649,15 +667,16 @@ function union(regions=[],b=undef,c=undef) =
|
||||||
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
||||||
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, close=true);
|
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, close=true);
|
||||||
// color("green") region(difference(shape1,shape2));
|
// color("green") region(difference(shape1,shape2));
|
||||||
function difference(regions=[],b=undef,c=undef) =
|
function difference(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
b!=undef? difference(concat([regions],[b],c==undef?[]:[c])) :
|
b!=undef? difference(concat([regions],[b],c==undef?[]:[c]), eps=eps) :
|
||||||
len(regions)<=1? regions[0] :
|
len(regions)<=1? regions[0] :
|
||||||
difference(
|
difference(
|
||||||
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
||||||
concat(
|
concat(
|
||||||
[_tagged_region(regions[0],regions[1],["O","U"],["I"])],
|
[_tagged_region(regions[0],regions[1],["O","U"],["I"], eps=eps)],
|
||||||
[for (i=[2:1:len(regions)-1]) regions[i]]
|
[for (i=[2:1:len(regions)-1]) regions[i]]
|
||||||
)
|
),
|
||||||
|
eps=eps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -673,15 +692,16 @@ function difference(regions=[],b=undef,c=undef) =
|
||||||
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
||||||
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, close=true);
|
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, close=true);
|
||||||
// color("green") region(intersection(shape1,shape2));
|
// color("green") region(intersection(shape1,shape2));
|
||||||
function intersection(regions=[],b=undef,c=undef) =
|
function intersection(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
b!=undef? intersection(concat([regions],[b],c==undef?[]:[c])) :
|
b!=undef? intersection(concat([regions],[b],c==undef?[]:[c]),eps=eps) :
|
||||||
len(regions)<=1? regions[0] :
|
len(regions)<=1? regions[0] :
|
||||||
intersection(
|
intersection(
|
||||||
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
||||||
concat(
|
concat(
|
||||||
[_tagged_region(regions[0],regions[1],["I","S"],["I"])],
|
[_tagged_region(regions[0],regions[1],["I","S"],["I"],eps=eps)],
|
||||||
[for (i=[2:1:len(regions)-1]) regions[i]]
|
[for (i=[2:1:len(regions)-1]) regions[i]]
|
||||||
)
|
),
|
||||||
|
eps=eps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -697,18 +717,19 @@ function intersection(regions=[],b=undef,c=undef) =
|
||||||
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
||||||
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, close=true);
|
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, close=true);
|
||||||
// color("green") region(exclusive_or(shape1,shape2));
|
// color("green") region(exclusive_or(shape1,shape2));
|
||||||
function exclusive_or(regions=[],b=undef,c=undef) =
|
function exclusive_or(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
b!=undef? exclusive_or(concat([regions],[b],c==undef?[]:[c])) :
|
b!=undef? exclusive_or(concat([regions],[b],c==undef?[]:[c]),eps=eps) :
|
||||||
len(regions)<=1? regions[0] :
|
len(regions)<=1? regions[0] :
|
||||||
exclusive_or(
|
exclusive_or(
|
||||||
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
||||||
concat(
|
concat(
|
||||||
[union([
|
[union([
|
||||||
difference([regions[0],regions[1]]),
|
difference([regions[0],regions[1]], eps=eps),
|
||||||
difference([regions[1],regions[0]])
|
difference([regions[1],regions[0]], eps=eps)
|
||||||
])],
|
], eps=eps)],
|
||||||
[for (i=[2:1:len(regions)-1]) regions[i]]
|
[for (i=[2:1:len(regions)-1]) regions[i]]
|
||||||
)
|
),
|
||||||
|
eps=eps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue