mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-08-05 18:57:45 +02:00
Sped up path_self_intersections() a lot.
This commit is contained in:
109
geometry.scad
109
geometry.scad
@@ -568,14 +568,33 @@ function cleanup_path(path, eps=EPSILON) = is_closed_path(path,eps=eps)? select(
|
|||||||
// for (isect=isects) translate(isect[0]) color("blue") sphere(d=10);
|
// for (isect=isects) translate(isect[0]) color("blue") sphere(d=10);
|
||||||
function path_self_intersections(path, closed=true, eps=EPSILON) =
|
function path_self_intersections(path, closed=true, eps=EPSILON) =
|
||||||
let(
|
let(
|
||||||
path = cleanup_path(path, eps=eps)
|
path = cleanup_path(path, eps=eps),
|
||||||
|
plen = len(path)
|
||||||
) [
|
) [
|
||||||
for (i = idx(path,end=closed?-2:-3), j = idx(path,start=i+1,end=closed?-1:-2)) let(
|
for (i = [0:1:plen-(closed?2:3)], j=[i+1:1:plen-(closed?1:2)]) let(
|
||||||
a = select(path,i,i+1),
|
a1 = path[i],
|
||||||
b = select(path,j,j+1),
|
a2 = path[(i+1)%plen],
|
||||||
isect = _general_line_intersection(a,b,eps=eps)
|
b1 = path[j],
|
||||||
) if ( !is_undef(isect) && isect[1]>0 && isect[1]<=1 && isect[2]>0 && isect[2]<=1)
|
b2 = path[(j+1)%plen],
|
||||||
[isect[0], i, isect[1], j, isect[2]]
|
isect =
|
||||||
|
(max(a1.x, a2.x) < min(b1.x, b2.x))? undef :
|
||||||
|
(min(a1.x, a2.x) > max(b1.x, b2.x))? undef :
|
||||||
|
(max(a1.y, a2.y) < min(b1.y, b2.y))? undef :
|
||||||
|
(min(a1.y, a2.y) > max(b1.y, b2.y))? undef :
|
||||||
|
let(
|
||||||
|
c = a1-a2,
|
||||||
|
d = b1-b2,
|
||||||
|
denom = (c.x*d.y)-(c.y*d.x)
|
||||||
|
) abs(denom)<eps? undef : let(
|
||||||
|
e = a1-b1,
|
||||||
|
t = ((e.x*d.y)-(e.y*d.x)) / denom,
|
||||||
|
u = ((e.x*c.y)-(e.y*c.x)) / denom
|
||||||
|
) [a1+t*(a2-a1), t, u]
|
||||||
|
) if (
|
||||||
|
isect != undef &&
|
||||||
|
isect[1]>eps && isect[1]<=1+eps &&
|
||||||
|
isect[2]>eps && isect[2]<=1+eps
|
||||||
|
) [isect[0], i, isect[1], j, isect[2]]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@@ -768,43 +787,43 @@ function _extreme_angle_fragment(seg, fragments, rightmost=true, eps=EPSILON) =
|
|||||||
) [foundfrag, remainder];
|
) [foundfrag, remainder];
|
||||||
|
|
||||||
|
|
||||||
// Function: assemble_path_fragments()
|
// Function: assemble_a_path_from_fragments()
|
||||||
// Usage:
|
// Usage:
|
||||||
// assemble_path_fragments(subpaths);
|
// assemble_a_path_from_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 one complete closed path, and
|
||||||
function assemble_path_fragments(fragments, rightmost=false, eps=EPSILON, _finished=[]) =
|
// remainder fragments. Returns [PATH, FRAGMENTS] where FRAGMENTS is the list of remaining
|
||||||
|
// polyline path fragments.
|
||||||
|
// Arguments:
|
||||||
|
// fragments = List of polylines to be assembled into complete polygons.
|
||||||
|
// rightmost = If true, assemble paths using rightmost turns. Leftmost if false.
|
||||||
|
// 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) =
|
||||||
len(fragments)==0? _finished :
|
len(fragments)==0? _finished :
|
||||||
let(
|
let(
|
||||||
path = fragments[0],
|
path = fragments[0],
|
||||||
newfrags = slice(fragments, 1, -1),
|
newfrags = slice(fragments, 1, -1)
|
||||||
finished = concat(_finished, [path])
|
|
||||||
) is_closed_path(path, eps=eps)? (
|
) is_closed_path(path, eps=eps)? (
|
||||||
// starting fragment is already closed
|
// starting fragment is already closed
|
||||||
assemble_path_fragments(fragments=newfrags, rightmost=rightmost, eps=eps, _finished=finished)
|
[path, newfrags]
|
||||||
) : let(
|
) : let(
|
||||||
// Find rightmost/leftmost continuation fragment
|
// Find rightmost/leftmost continuation fragment
|
||||||
extrema = _extreme_angle_fragment(
|
seg = select(path,-2,-1),
|
||||||
seg=select(path,-2,-1),
|
frags = slice(fragments,1,-1),
|
||||||
fragments=slice(fragments,1,-1),
|
extrema = _extreme_angle_fragment(seg=seg, fragments=frags, rightmost=rightmost, eps=eps),
|
||||||
rightmost=rightmost,
|
|
||||||
eps=eps
|
|
||||||
),
|
|
||||||
foundfrag = extrema[0],
|
foundfrag = extrema[0],
|
||||||
remainder = extrema[1],
|
remainder = extrema[1],
|
||||||
newfrags = remainder,
|
newfrags = remainder
|
||||||
finished = concat(_finished, [path])
|
|
||||||
) is_undef(foundfrag)? (
|
) is_undef(foundfrag)? (
|
||||||
// No remaining fragments connect! INCOMPLETE PATH!
|
// No remaining fragments connect! INCOMPLETE PATH!
|
||||||
// Treat it as complete.
|
// Treat it as complete.
|
||||||
assemble_path_fragments(fragments=newfrags, rightmost=rightmost, eps=eps, _finished=finished)
|
[path, newfrags]
|
||||||
) : is_closed_path(foundfrag, eps=eps)? (
|
) : is_closed_path(foundfrag, eps=eps)? (
|
||||||
let(
|
let(
|
||||||
newfrags = concat([path], remainder),
|
newfrags = concat([path], remainder)
|
||||||
finished = concat(_finished, [foundfrag])
|
|
||||||
)
|
)
|
||||||
// Found fragment is already closed
|
// Found fragment is already closed
|
||||||
assemble_path_fragments(fragments=newfrags, rightmost=rightmost, eps=eps, _finished=finished)
|
[foundfrag, newfrags]
|
||||||
) : let(
|
) : let(
|
||||||
fragend = select(foundfrag,-1),
|
fragend = select(foundfrag,-1),
|
||||||
hits = [for (i = idx(path,end=-2)) if(approx(path[i],fragend,eps=eps)) i]
|
hits = [for (i = idx(path,end=-2)) if(approx(path[i],fragend,eps=eps)) i]
|
||||||
@@ -814,15 +833,47 @@ function assemble_path_fragments(fragments, rightmost=false, eps=EPSILON, _finis
|
|||||||
hitidx = select(hits,-1),
|
hitidx = select(hits,-1),
|
||||||
newpath = slice(path,0,hitidx+1),
|
newpath = slice(path,0,hitidx+1),
|
||||||
newfrags = concat(len(newpath)>1? [newpath] : [], remainder),
|
newfrags = concat(len(newpath)>1? [newpath] : [], remainder),
|
||||||
finished = concat(_finished,[concat(slice(path,hitidx,-2),foundfrag)])
|
outpath = concat(slice(path,hitidx,-2), foundfrag)
|
||||||
)
|
)
|
||||||
assemble_path_fragments(fragments=newfrags, rightmost=rightmost, eps=eps, _finished=finished)
|
[outpath, newfrags]
|
||||||
) : let(
|
) : let(
|
||||||
// Path still incomplete. Continue building it.
|
// Path still incomplete. Continue building it.
|
||||||
newpath = concat(path, slice(foundfrag, 1, -1)),
|
newpath = concat(path, slice(foundfrag, 1, -1)),
|
||||||
newfrags = concat([newpath], remainder)
|
newfrags = concat([newpath], remainder)
|
||||||
)
|
)
|
||||||
assemble_path_fragments(fragments=newfrags, rightmost=rightmost, eps=eps, _finished=_finished);
|
assemble_a_path_from_fragments(
|
||||||
|
fragments=newfrags,
|
||||||
|
rightmost=rightmost,
|
||||||
|
eps=eps
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Function: assemble_path_fragments()
|
||||||
|
// Usage:
|
||||||
|
// assemble_path_fragments(subpaths);
|
||||||
|
// Description:
|
||||||
|
// Given a list of incomplete paths, assembles them together into complete closed paths if it can.
|
||||||
|
// Arguments:
|
||||||
|
// fragments = List of polylines to be assembled into complete polygons.
|
||||||
|
// rightmost = If true, assemble paths using rightmost turns. Leftmost if false.
|
||||||
|
// eps = The epsilon error value to determine whether two points coincide. Default: `EPSILON` (1e-9)
|
||||||
|
function assemble_path_fragments(fragments, rightmost=true, eps=EPSILON, _finished=[]) =
|
||||||
|
len(fragments)==0? _finished :
|
||||||
|
let(
|
||||||
|
result = assemble_a_path_from_fragments(
|
||||||
|
fragments=fragments,
|
||||||
|
rightmost=rightmost,
|
||||||
|
eps=eps
|
||||||
|
),
|
||||||
|
newpath = result[0],
|
||||||
|
remainder = result[1],
|
||||||
|
finished = concat(_finished, [newpath])
|
||||||
|
) assemble_path_fragments(
|
||||||
|
fragments=remainder,
|
||||||
|
rightmost=rightmost, eps=eps,
|
||||||
|
_finished=finished
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function: simplify_path()
|
// Function: simplify_path()
|
||||||
|
Reference in New Issue
Block a user