Correction in _cleave_connected_region and polygon_triangulate and some few tweaks

This commit is contained in:
RonaldoCMP
2021-11-04 12:09:29 +00:00
parent 10604cd20b
commit 6bd1dd918f
6 changed files with 105 additions and 98 deletions

View File

@@ -414,22 +414,22 @@ function _old_cleave_connected_region(region) =
/// Internal Function: _cleave_connected_region(region, eps)
/// Description:
/// Given a region that is connected and has its outer border in region[0],
/// produces a polygon with the same points that has overlapping connected paths
/// to join internal holes to the outer border. Output is a single path.
/// It expect that region[0] be a simple closed CW path and that each hole,
/// region[i] for i>0, be a simple closed CCW path.
/// The paths are also supposed to be disjoint except for common vertices and
/// common edges but no crossing.
/// This function implements an extension of the algorithm discussed in:
/// https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
/// Given a region that is connected and has its outer border in region[0],
/// produces a overlapping connected path to join internal holes to
/// the outer border without adding points. Output is a single non-simple polygon.
/// Requirements:
/// It expects that all region paths be simple closed paths, with region[0] CW and
/// the other paths CCW and encircled by region[0]. The input region paths are also
/// supposed to be disjoint except for common vertices and common edges but with
/// no crossings. It may return `undef` if these conditions are not met.
/// This function implements an extension of the algorithm discussed in:
/// https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
function _cleave_connected_region(region, eps=EPSILON) =
len(region)==1 ? region[0] :
let(
outer = deduplicate(clockwise_polygon(region[0])), //
holes = [for(i=[1:1:len(region)-1]) // possibly unneeded
let(poly=region[i]) //
deduplicate( ccw_polygon(poly) ) ], //
outer = deduplicate(region[0]), //
holes = [for(i=[1:1:len(region)-1]) // deduplication possibly unneeded
deduplicate( region[i] ) ], //
extridx = [for(li=holes) max_index(column(li,0)) ],
// the right extreme vertex for each hole sorted by decreasing x values
extremes = sort( [for(i=idx(holes)) [ i, extridx[i], -holes[i][extridx[i]].x] ], idx=2 )
@@ -439,7 +439,7 @@ function _cleave_connected_region(region, eps=EPSILON) =
// connect the hole paths one at a time to the outer path.
// 'extremes' is the list of the right extreme vertex of each hole sorted by decreasing abscissas
// see _cleave_connected_region(region, eps)
// see: _cleave_connected_region(region, eps)
function _polyHoles(outer, holes, extremes, eps=EPSILON, n=0) =
let(
extr = extremes[n], //
@@ -447,17 +447,17 @@ function _polyHoles(outer, holes, extremes, eps=EPSILON, n=0) =
ipt = extr[1], // index of the hole point with maximum abscissa
brdg = _bridge(hole[ipt], outer, eps) // the index of a point in outer to bridge hole[ipt] to
)
assert(brdg!=undef, "Error: check input polygon restrictions")
brdg == undef ? undef :
let(
l = len(outer),
lh = len(hole),
// the new outer polygon bridging the hole to the old outer
npoly =
approx(outer[brdg], hole[ipt], eps)
? [ for(i=[brdg: 1: brdg+l]) outer[i%l] ,
for(i=[ipt+1:1: ipt+lh-1]) hole[i%lh] ]
: [ for(i=[brdg: 1: brdg+l]) outer[i%l] ,
for(i=[ipt:1: ipt+lh]) hole[i%lh] ]
? [ for(i=[brdg: 1: brdg+l]) outer[i%l] ,
for(i=[ipt+1: 1: ipt+lh-1]) hole[i%lh] ]
: [ for(i=[brdg: 1: brdg+l]) outer[i%l] ,
for(i=[ipt: 1: ipt+lh]) hole[i%lh] ]
)
n==len(holes)-1 ? npoly :
_polyHoles(npoly, holes, extremes, eps, n+1);
@@ -472,13 +472,13 @@ function _bridge(pt, outer,eps) =
let(
l = len(outer),
crxs =
[for( i=idx(outer) )
let( edge = select(outer,i,i+1) )
let( edges = pair(outer,wrap=true) )
[for( i = idx(edges) )
let( edge = edges[i] )
// consider just descending outer edges at right of pt crossing ordinate pt.y
if( (edge[0].y> pt.y)
&& (edge[1].y<=pt.y)
&& ( norm(edge[1]-pt)<eps // accepts touching vertices
|| _tri_class([pt, edge[0], edge[1]], eps)>0 ) )
if( (edge[0].y > pt.y+eps)
&& (edge[1].y <= pt.y)
&& _is_at_left(pt, [edge[1], edge[0]], eps) )
[ i,
// the point of edge with ordinate pt.y
abs(pt.y-edge[1].y)<eps ? edge[1] :
@@ -487,22 +487,21 @@ function _bridge(pt, outer,eps) =
]
]
)
assert(crxs!=[], "Error: check input polygon restrictions")
crxs == [] ? undef :
let(
// the intersection point nearest to pt
// the intersection point of the nearest edge to pt with minimum slope
minX = min([for(p=crxs) p[1].x]),
crxcand = [for(crx=crxs) if(crx[1].x < minX+eps) crx ],
nearest = min_index([for(crx=crxcand) outer[crx[0]].y]),
crxcand = [for(crx=crxs) if(crx[1].x < minX+eps) crx ], // nearest edges
nearest = min_index([for(crx=crxcand)
(outer[crx[0]].x - pt.x) / (outer[crx[0]].y - pt.y) ]), // minimum slope
proj = crxcand[nearest],
vert0 = outer[proj[0]], // the two vertices of the nearest crossing edge
vert1 = outer[(proj[0]+1)%l],
isect = proj[1] // the intersection point
)
// if pt touches the middle of an outer edge -> error
assert( ! approx(pt,isect,eps) || approx(pt,vert0,eps) || approx(pt,vert1,eps),
"There is a forbidden self_intersection" )
norm(pt-vert0) < eps ? proj[0] : // if pt touches an outer vertex, return its index
norm(pt-vert1) < eps ? (proj[0]+1)%l :
norm(pt-vert1) < eps ? (proj[0]+1)%l : // if pt touches an outer vertex, return its index
// as vert0.y > pt.y then pt!=vert0
norm(pt-isect) < eps ? undef : // if pt touches the middle of an outer edge -> error
let(
// the edge [vert0, vert1] necessarily satisfies vert0.y > vert1.y
// indices of candidates to an outer bridge point
@@ -553,13 +552,15 @@ function _bridge(pt, outer,eps) =
function vnf_from_region(region, transform, reverse=false) =
let (
regions = region_parts(force_region(region)),
vnfs = [
for (rgn = regions) let(
cleaved = path3d(_cleave_connected_region(rgn)),
face = is_undef(transform)? cleaved : apply(transform,cleaved),
faceidxs = reverse? [for (i=[len(face)-1:-1:0]) i] : [for (i=[0:1:len(face)-1]) i]
) [face, [faceidxs]]
],
vnfs =
[ for (rgn = regions)
let( cleaved = path3d(_cleave_connected_region(rgn)) )
assert( cleaved, "The region is invalid")
let(
face = is_undef(transform)? cleaved : apply(transform,cleaved),
faceidxs = reverse? [for (i=[len(face)-1:-1:0]) i] : [for (i=[0:1:len(face)-1]) i]
) [face, [faceidxs]]
],
outvnf = vnf_merge(vnfs)
)
vnf_triangulate(outvnf);
@@ -667,9 +668,13 @@ function _link_indicator(l,imin,imax) =
function vnf_triangulate(vnf) =
let(
verts = vnf[0],
faces = [for (face=vnf[1]) each len(face)==3 ? [face] :
polygon_triangulate(verts, face)]
) [verts, faces];
faces = [for (face=vnf[1])
each (len(face)==3 ? [face] :
let( tris = polygon_triangulate(verts, face) )
assert( tris!=undef, "Some `vnf` face cannot be triangulated.")
tris ) ]
)
[verts, faces];