diff --git a/arrays.scad b/arrays.scad index 4636cda..3fddebb 100644 --- a/arrays.scad +++ b/arrays.scad @@ -75,8 +75,9 @@ function select(list, start, end=undef) = // slice([3,4,5,6,7,8,9], 6, -1); // Returns [9] // slice([3,4,5,6,7,8,9], 2, -2); // Returns [5,6,7,8] function slice(arr,st,end) = let( - s=st<0?(len(arr)+st):st, - e=end<0?(len(arr)+end+1):end + l=len(arr), + s=st<0?(l+st):st, + e=end<0?(l+end+1):end ) [for (i=[s:1:e-1]) if (e>s) arr[i]]; diff --git a/paths.scad b/paths.scad index 0add502..382e647 100644 --- a/paths.scad +++ b/paths.scad @@ -549,8 +549,37 @@ function decompose_path(path, closed=true, eps=EPSILON) = path = cleanup_path(path, eps=eps), tagged = _tag_self_crossing_subpaths(path, closed=closed, eps=eps), kept = [for (sub = tagged) if(sub[0] == "O") sub[1]], - outregion = assemble_path_fragments(kept, eps=eps) - ) outregion; + completed = [for (frag=kept) if(is_closed_path(frag)) frag], + incomplete = [for (frag=kept) if(!is_closed_path(frag)) frag], + defrag = _path_fast_defragment(incomplete, eps=eps), + completed2 = assemble_path_fragments(defrag, eps=eps) + ) concat(completed2,completed); + + +function _path_fast_defragment(fragments, eps=EPSILON, _done=[]) = + len(fragments)==0? _done : + let( + path = fragments[0], + endpt = select(path,-1), + extenders = [ + for (i = [1:1:len(fragments)-1]) let( + test1 = approx(endpt,fragments[i][0],eps=eps), + test2 = approx(endpt,select(fragments[i],-1),eps=eps) + ) if (test1 || test2) (test1? i : -1) + ] + ) len(extenders) == 1 && extenders[0] >= 0? _path_fast_defragment( + fragments=[ + concat(select(path,0,-2),fragments[extenders[0]]), + for (i = [1:1:len(fragments)-1]) + if (i != extenders[0]) fragments[i] + ], + eps=eps, + _done=_done + ) : _path_fast_defragment( + fragments=[for (i = [1:1:len(fragments)-1]) fragments[i]], + eps=eps, + _done=concat(_done,[deduplicate(path,closed=true,eps=eps)]) + ); function _extreme_angle_fragment(seg, fragments, rightmost=true, eps=EPSILON) = @@ -596,33 +625,29 @@ function _extreme_angle_fragment(seg, fragments, rightmost=true, eps=EPSILON) = // Arguments: // fragments = List of polylines to be assembled into complete polygons. // rightmost = If true, assemble paths using rightmost turns. Leftmost if false. +// startfrag = The fragment to start with. Default: 0 // eps = The epsilon error value to determine whether two points coincide. Default: `EPSILON` (1e-9) -function assemble_a_path_from_fragments(fragments, rightmost=true, eps=EPSILON) = +function assemble_a_path_from_fragments(fragments, rightmost=true, startfrag=0, eps=EPSILON) = len(fragments)==0? _finished : let( - path = fragments[0], - newfrags = slice(fragments, 1, -1) + path = fragments[startfrag], + newfrags = [for (i=idx(fragments)) if (i!=startfrag) fragments[i]] ) is_closed_path(path, eps=eps)? ( // starting fragment is already closed [path, newfrags] ) : let( // Find rightmost/leftmost continuation fragment seg = select(path,-2,-1), - frags = slice(fragments,1,-1), - extrema = _extreme_angle_fragment(seg=seg, fragments=frags, rightmost=rightmost, eps=eps), + extrema = _extreme_angle_fragment(seg=seg, fragments=newfrags, rightmost=rightmost, eps=eps), foundfrag = extrema[0], - remainder = extrema[1], - newfrags = remainder + remainder = extrema[1] ) is_undef(foundfrag)? ( // No remaining fragments connect! INCOMPLETE PATH! // Treat it as complete. - [path, newfrags] + [path, remainder] ) : is_closed_path(foundfrag, eps=eps)? ( - let( - newfrags = concat([path], remainder) - ) // Found fragment is already closed - [foundfrag, newfrags] + [foundfrag, concat([path], remainder)] ) : let( fragend = select(foundfrag,-1), hits = [for (i = idx(path,end=-2)) if(approx(path[i],fragend,eps=eps)) i] @@ -661,19 +686,15 @@ function assemble_path_fragments(fragments, eps=EPSILON, _finished=[]) = minxidx = min_index([ for (frag=fragments) min(subindex(frag,0)) ]), - promoted = [ - fragments[minxidx], - for (i=idx(fragments)) - if (i!=minxidx) - fragments[i] - ], result_l = assemble_a_path_from_fragments( - fragments=promoted, + fragments=fragments, + startfrag=minxidx, rightmost=false, eps=eps ), result_r = assemble_a_path_from_fragments( - fragments=promoted, + fragments=fragments, + startfrag=minxidx, rightmost=true, eps=eps ), diff --git a/version.scad b/version.scad index 869fb01..66d3e9d 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,251]; +BOSL_VERSION = [2,0,252]; // Section: BOSL Library Version Functions