mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Added path_self_intersections(), decompose_path(), and fixed inverted t result in _general_line_intersection()
This commit is contained in:
parent
ccfed6b306
commit
3846a367ee
1 changed files with 91 additions and 9 deletions
100
geometry.scad
100
geometry.scad
|
@ -125,18 +125,18 @@ function line_normal(p1,p2) =
|
||||||
|
|
||||||
// 2D Line intersection from two segments.
|
// 2D Line intersection from two segments.
|
||||||
// This function returns [p,t,u] where p is the intersection point of
|
// This function returns [p,t,u] where p is the intersection point of
|
||||||
// the lines defined by the two segments, t is the bezier parameter
|
// the lines defined by the two segments, t is the proportional distance
|
||||||
// for the intersection point on s1 and u is the bezier parameter for
|
// of the intersection point along s1, and u is the proportional distance
|
||||||
// the intersection point on s2. The bezier parameter runs over [0,1]
|
// of the intersection point along s2. The proportional values run over
|
||||||
// for each segment, so if it is in this range, then the intersection
|
// the range of 0 to 1 for each segment, so if it is in this range, then
|
||||||
// lies on the segment. Otherwise it lies somewhere on the extension
|
// the intersection lies on the segment. Otherwise it lies somewhere on
|
||||||
// of the segment.
|
// the extension of the segment.
|
||||||
function _general_line_intersection(s1,s2,eps=EPSILON) =
|
function _general_line_intersection(s1,s2,eps=EPSILON) =
|
||||||
let(
|
let(
|
||||||
denominator = det2([s1[0],s2[0]]-[s1[1],s2[1]])
|
denominator = det2([s1[0],s2[0]]-[s1[1],s2[1]])
|
||||||
) approx(denominator,0,eps=eps)? [undef,undef,undef] : let(
|
) approx(denominator,0,eps=eps)? [undef,undef,undef] : let(
|
||||||
t = det2([s1[0],s2[0]]-s2) / denominator,
|
t = det2([s1[0],s2[0]]-s2) / denominator,
|
||||||
u = det2([s1[0],s1[0]]-[s1[1],s2[1]]) /denominator
|
u = det2([s1[0],s1[0]]-[s2[0],s1[1]]) / denominator
|
||||||
) [s1[0]+t*(s1[1]-s1[0]), t, u];
|
) [s1[0]+t*(s1[1]-s1[0]), t, u];
|
||||||
|
|
||||||
|
|
||||||
|
@ -544,6 +544,88 @@ function close_path(path, eps=EPSILON) = is_closed_path(path,eps=eps)? path : co
|
||||||
function cleanup_path(path, eps=EPSILON) = is_closed_path(path,eps=eps)? select(path,0,-2) : path;
|
function cleanup_path(path, eps=EPSILON) = is_closed_path(path,eps=eps)? select(path,0,-2) : path;
|
||||||
|
|
||||||
|
|
||||||
|
// Function: path_self_intersections()
|
||||||
|
// Usage:
|
||||||
|
// isects = path_self_intersections(path, [eps]);
|
||||||
|
// Description:
|
||||||
|
// Locates all self intersections of the given path. Returns a list of intersections, where
|
||||||
|
// each intersection is a list like [POINT, SEGNUM1, PROPORTION1, SEGNUM2, PROPORTION2] where
|
||||||
|
// POINT is the coordinates of the intersection point, SEGNUMs are the integer indices of the
|
||||||
|
// intersecting segments along the path, and the PROPORTIONS are the 0.0 to 1.0 proportions
|
||||||
|
// of how far along those segments they intersect at. A proportion of 0.0 indicates the start
|
||||||
|
// of the segment, and a proportion of 1.0 indicates the end of the segment.
|
||||||
|
// Arguments:
|
||||||
|
// path = The path to find self intersections of.
|
||||||
|
// closed = If true, treat path like a closed polygon. Default: true
|
||||||
|
// eps = The epsilon error value to determine whether two points coincide. Default: `EPSILON` (1e-9)
|
||||||
|
// Example(2D):
|
||||||
|
// path = [
|
||||||
|
// [-100,100], [0,-50], [100,100], [100,-100], [0,50], [-100,-100]
|
||||||
|
// ];
|
||||||
|
// isects = path_self_intersections(path, closed=true);
|
||||||
|
// // isects == [[[-33.3333, 0], 0, 0.666667, 4, 0.333333], [[33.3333, 0], 1, 0.333333, 3, 0.666667]]
|
||||||
|
// stroke(path, closed=true, width=1);
|
||||||
|
// for (isect=isects) translate(isect[0]) color("blue") sphere(d=10);
|
||||||
|
// echo(isects=isects);
|
||||||
|
function path_self_intersections(path, closed=true, eps=EPSILON) =
|
||||||
|
let(
|
||||||
|
path = cleanup_path(path, eps=eps)
|
||||||
|
) [
|
||||||
|
for (i = idx(path,end=closed?-2:-3), j = idx(path,start=i+1,end=closed?-1:-2)) let(
|
||||||
|
a = select(path,i,i+1),
|
||||||
|
b = select(path,j,j+1),
|
||||||
|
isect = _general_line_intersection(a,b,eps=eps)
|
||||||
|
) if ( !is_undef(isect) && isect[1]>0 && isect[1]<=1 && isect[2]>0 && isect[2]<=1)
|
||||||
|
[isect[0], i, isect[1], j, isect[2]]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// Function: decompose_path()
|
||||||
|
// Usage:
|
||||||
|
// splitpaths = decompose_path(path, [closed], [eps]);
|
||||||
|
// Description:
|
||||||
|
// Given a possibly self-intersecting path, splits it up into a list of non-intersecting sub-paths.
|
||||||
|
// If the given path is not a closed polygon, then the first returned subpath will not be closed.
|
||||||
|
// All other returned subpaths should be considered as closed polygons. Subpaths of crossing areas
|
||||||
|
// will have the opposite clockwise-ness from the first path returned.
|
||||||
|
// Arguments:
|
||||||
|
// path = The path to split up.
|
||||||
|
// closed = If true, treat path like a closed polygon. Default: true
|
||||||
|
// eps = The epsilon error value to determine whether two points coincide. Default: `EPSILON` (1e-9)
|
||||||
|
// Example(2D):
|
||||||
|
// path = [
|
||||||
|
// [-100,100], [0,-50], [100,100], [100,-100], [0,50], [-100,-100]
|
||||||
|
// ];
|
||||||
|
// splitpaths = decompose_path(path, closed=true);
|
||||||
|
// rainbow(splitpaths) stroke($item, closed=true, width=3);
|
||||||
|
function decompose_path(path, closed=true, eps=EPSILON) =
|
||||||
|
let(
|
||||||
|
path = cleanup_path(path, eps=eps),
|
||||||
|
isects = path_self_intersections(path, closed, eps)
|
||||||
|
) isects==[]? [path] :
|
||||||
|
let(
|
||||||
|
isect = isects[0],
|
||||||
|
plen = len(path)
|
||||||
|
) concat(
|
||||||
|
decompose_path(
|
||||||
|
let(
|
||||||
|
subpath1 = path_subselect(path, 0, 0, isect[1], isect[2]),
|
||||||
|
subpath2 = path_subselect(path, isect[3], isect[4], plen-(closed?0:1), 1),
|
||||||
|
patha = cleanup_path(deduplicate(concat(subpath1, subpath2), eps=eps), eps=eps)
|
||||||
|
) patha,
|
||||||
|
closed=closed,
|
||||||
|
eps=eps
|
||||||
|
),
|
||||||
|
decompose_path(
|
||||||
|
let(
|
||||||
|
subpath3 = path_subselect(path, isect[1], isect[2], isect[3], isect[4]),
|
||||||
|
pathb = cleanup_path(subpath3, eps=eps)
|
||||||
|
) pathb,
|
||||||
|
closed=true,
|
||||||
|
eps=eps
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Function: path_subselect()
|
// Function: path_subselect()
|
||||||
// Usage:
|
// Usage:
|
||||||
// path_subselect(path,s1,u1,s2,u2):
|
// path_subselect(path,s1,u1,s2,u2):
|
||||||
|
@ -590,7 +672,7 @@ function polygon_area(vertices) =
|
||||||
// i = The index of the point to shift to the front of the path.
|
// i = The index of the point to shift to the front of the path.
|
||||||
// Example:
|
// Example:
|
||||||
// polygon_shift([[3,4], [8,2], [0,2], [-4,0]], 2); // Returns [[0,2], [-4,0], [3,4], [8,2]]
|
// polygon_shift([[3,4], [8,2], [0,2], [-4,0]], 2); // Returns [[0,2], [-4,0], [3,4], [8,2]]
|
||||||
function polygon_shift(poly, i) =
|
function polygon_shift(poly, i) =
|
||||||
assert(i<len(poly))
|
assert(i<len(poly))
|
||||||
let(
|
let(
|
||||||
poly = cleanup_path(poly)
|
poly = cleanup_path(poly)
|
||||||
|
@ -829,7 +911,7 @@ function furthest_point(pt, points) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The list of 2D path points for the perimeter of the polygon.
|
// path = The list of 2D path points for the perimeter of the polygon.
|
||||||
function polygon_is_clockwise(path) =
|
function polygon_is_clockwise(path) =
|
||||||
let(
|
let(
|
||||||
minx = min(subindex(path,0)),
|
minx = min(subindex(path,0)),
|
||||||
lowind = search(minx, path, 0, 0),
|
lowind = search(minx, path, 0, 0),
|
||||||
lowpts = select(path, lowind),
|
lowpts = select(path, lowind),
|
||||||
|
|
Loading…
Reference in a new issue