Merge pull request #720 from adrianVmariano/master

fix bug in is_valid_region (some invalid regions weren't identified)
This commit is contained in:
Revar Desmera 2021-11-04 21:39:02 -07:00 committed by GitHub
commit 4fde5e358a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 22 deletions

View file

@ -49,14 +49,22 @@ function is_region(x) = is_list(x) && is_path(x.x);
// Arguments:
// region = region to check
// eps = tolerance for geometric comparisons. Default: `EPSILON` = 1e-9
// Example(2D,noaxes): Nested squares form a region
// region = [for(i=[3:2:10]) square(i,center=true)];
// rainbow(region)stroke($item, width=.1,closed=true);
// back(6)text(is_valid_region(region) ? "region" : "non-region", size=2,halign="center");
// Example(2D,noaxes): Two non-intersecting squares make a valid region:
// region = [square(10), right(11,square(8))];
// rainbow(region)stroke($item, width=.1,closed=true);
// back(12)text(is_valid_region(region) ? "region" : "non-region", size=2);
// Example(2D,noaxes): Nested squares form a region
// region = [for(i=[3:2:10]) square(i,center=true)];
// rainbow(region)stroke($item, width=.1,closed=true);
// back(6)text(is_valid_region(region) ? "region" : "non-region", size=2,halign="center");
// Example(2D,noaxes): Also a region:
// region= [square(10,center=true), square(5,center=true), right(10,square(7))];
// rainbow(region)stroke($item, width=.1,closed=true);
// back(8)text(is_valid_region(region) ? "region" : "non-region", size=2);
// Example(2D,noaxes): The squares cross each other, so not a region
// object = [square(10), move([8,8], square(8))];
// rainbow(object)stroke($item, width=.1,closed=true);
// back(17)text(is_valid_region(object) ? "region" : "non-region", size=2);
// Example(2D,noaxes): Not a region due to a self-intersecting (non-simple) hourglass path
// object = [move([-2,-2],square(14)), [[0,0],[10,0],[0,10],[10,10]]];
// rainbow(object)stroke($item, width=.1,closed=true);
@ -69,10 +77,6 @@ function is_region(x) = is_list(x) && is_path(x.x);
// region = [square(10), move([10,10], square(8))];
// rainbow(region)stroke($item, width=.1,closed=true);
// back(12)text(is_valid_region(region) ? "region" : "non-region", size=2);
// Example(2D,noaxes): The squares cross each other, so not a region
// object = [square(10), move([8,8], square(8))];
// rainbow(object)stroke($item, width=.1,closed=true);
// back(17)text(is_valid_region(object) ? "region" : "non-region", size=2);
// Example(2D,noaxes): A union is one way to fix the above example and get a region. (Note that union is run here on two simple paths, which are valid regions themselves and hence acceptable inputs to union.
// region = union([square(10), move([8,8], square(8))]);
// rainbow(region)stroke($item, width=.1,closed=true);
@ -92,27 +96,60 @@ function is_region(x) = is_list(x) && is_path(x.x);
// stroke(object[0], width=0.1,closed=true);
// color("red")dashed_stroke(object[1], width=0.1,closed=true);
// back(12)text(is_valid_region(object) ? "region" : "non-region", size=2);
// Example(2D,noaxes): Crossing at vertices is also bad
// object = [square(10), [[10,0],[0,10],[8,13],[13,8]]];
// rainbow(object)stroke($item, width=.1,closed=true);
// back(14)text(is_valid_region(object) ? "region" : "non-region", size=2);
function is_valid_region(region, eps=EPSILON) =
let(region=force_region(region))
assert(is_region(region), "Input is not a region")
[for(p=region) if (!is_path_simple(p,closed=true,eps=eps)) 1] == []
&&
[for(i=[0:1:len(region)-2])
let( isect = _region_region_intersections([region[i]], list_tail(region,i+1), eps=eps))
each [
// check for intersection points not at the end of a segment
for(pts=flatten(isect[0])) if (pts[2]!=0 && pts[2]!=1) 1,
// check for full segment
for(seg=pair(flatten(isect[0])))
if (seg[0][0]==seg[1][0] // same path
&& seg[0][1]==seg[1][1] // same segment
&& seg[0][2]==0 && seg[1][2]==1) // both ends
1]
] ==[];
if (_polygon_crosses_region(list_tail(region,i+1),region[i], eps=eps)) 1] == [];
// internal function:
// returns true if the polygon crosses the region so that part of the
// polygon is inside the region and part is outside.
function _polygon_crosses_region(region, poly, eps=EPSILON) =
let(
subpaths = flatten(split_region_at_region_crossings(region,[poly],eps=eps)[1])
)
[for(path=subpaths)
let(isect=
[for (subpath = subpaths)
let(
midpt = mean([subpath[0], subpath[1]]),
rel = point_in_region(midpt,region,eps=eps)
)
rel
])
if (!all_equal(isect) || isect[0]==0) 1 ] != [];
// Function: is_region_simple()
// Usage:
// bool = is_region_simple(region, [eps]);
// Description:
// We extend the notion of the simple path to regions: a simple region is entirely
// non-self-intersecting, meaning that it is formed from a list of simple polygons that
// don't intersect each other at all---not even with corner contact points.
// Regions with corner contact are valid but may fail CGA. Simple regions
// should not create problems with CGAL.
// Arguments:
// region = region to check
// eps = tolerance for geometric comparisons. Default: `EPSILON` = 1e-9
function is_simple_region(region, eps=EPSILON) =
let(region=force_region(region))
assert(is_region(region), "Input is not a region")
[for(p=region) if (!is_path_simple(p,closed=true,eps=eps)) 1] == []
&&
[for(i=[0:1:len(region)-2])
if (_region_region_intersections([region[i]], list_tail(region,i+1), eps=eps)[0][0] != []) 1
] ==[];
// Function: make_region()
// Usage:
// r_fixed = make_region(r, [nonzero], [eps]);

View file

@ -1311,7 +1311,7 @@ module _rod(spec, length, tolerance, orient=UP, spin=0, anchor=CENTER)
// range (variability) of the thread heights. Smaller numbers give tigher tolerances. It must be a value from
// 4-8, so an allowed (loose) tolerance is "7G". The default ISO tolerance is "6H".
// .
// The $slop parameter determines extra gaps left to account for printing overextrusion. It defaults to 0.2.
// The $slop parameter determines extra gaps left to account for printing overextrusion. It defaults to 0.
// Arguments:
// name = screw specification, e.g. "M5x1" or "#8-32"
// diameter = outside diameter of nut (flat to flat dimension)
@ -1322,7 +1322,7 @@ module _rod(spec, length, tolerance, orient=UP, spin=0, anchor=CENTER)
// spec = screw specification from `screw_info()`. If you specify this you can omit all the preceeding parameters.
// details = toggle some details in rendering. Default: false
// tolerance = nut tolerance. Determines actual nut thread geometry based on nominal sizing. Default is "2B" for UTS and "6H" for ISO.
// $slop = extra space left to account for printing over-extrusion. Default: 0.2
// $slop = extra space left to account for printing over-extrusion. Default: 0
// Example: A metric and UTS nut
// inch=25.4;
// nut("3/8", 5/8*inch, 1/4*inch);