mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Expose path_cut_points()
This commit is contained in:
parent
b15b0ffc88
commit
68a26db513
5 changed files with 111 additions and 108 deletions
|
@ -982,7 +982,7 @@ module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed)
|
||||||
);
|
);
|
||||||
distOK = is_def(n) || (min(distances)>=0 && max(distances)<=length);
|
distOK = is_def(n) || (min(distances)>=0 && max(distances)<=length);
|
||||||
assert(distOK,"Cannot fit all of the copies");
|
assert(distOK,"Cannot fit all of the copies");
|
||||||
cutlist = _path_cut_points(path, distances, closed, direction=true);
|
cutlist = path_cut_points(path, distances, closed, direction=true);
|
||||||
planar = len(path[0])==2;
|
planar = len(path[0])==2;
|
||||||
if (true) for(i=[0:1:len(cutlist)-1]) {
|
if (true) for(i=[0:1:len(cutlist)-1]) {
|
||||||
$pos = cutlist[i][0];
|
$pos = cutlist[i][0];
|
||||||
|
|
|
@ -332,7 +332,7 @@ module stroke(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dummy=assert(trim1<path_length(path)-trim2, "Path is too short for endcap(s). Try a smaller width, or set endcap_length to a smaller value.");
|
dummy=assert(trim1<path_length(path)-trim2, "Path is too short for endcap(s). Try a smaller width, or set endcap_length to a smaller value.");
|
||||||
pathcut = _path_cut_points(path, [trim1, path_length(path)-trim2], closed=false);
|
pathcut = path_cut_points(path, [trim1, path_length(path)-trim2], closed=false);
|
||||||
pathcut_su = _cut_to_seg_u_form(pathcut,path);
|
pathcut_su = _cut_to_seg_u_form(pathcut,path);
|
||||||
path2 = _path_cut_getpaths(path, pathcut, closed=false)[1];
|
path2 = _path_cut_getpaths(path, pathcut, closed=false)[1];
|
||||||
widths = _path_select(width, pathcut_su[0][0], pathcut_su[0][1], pathcut_su[1][0], pathcut_su[1][1]);
|
widths = _path_select(width, pathcut_su[0][0], pathcut_su[0][1], pathcut_su[1][0], pathcut_su[1][1]);
|
||||||
|
|
205
paths.scad
205
paths.scad
|
@ -514,7 +514,7 @@ function resample_path(path, n, spacing, closed=true) =
|
||||||
// Add last point later
|
// Add last point later
|
||||||
n = is_def(n) ? n-(closed?0:1) : round(length/spacing),
|
n = is_def(n) ? n-(closed?0:1) : round(length/spacing),
|
||||||
distlist = lerpn(0,length,n,false),
|
distlist = lerpn(0,length,n,false),
|
||||||
cuts = _path_cut_points(path, distlist, closed=closed)
|
cuts = path_cut_points(path, distlist, closed=closed)
|
||||||
)
|
)
|
||||||
[ each column(cuts,0),
|
[ each column(cuts,0),
|
||||||
if (!closed) last(path) // Then add last point here
|
if (!closed) last(path) // Then add last point here
|
||||||
|
@ -709,47 +709,109 @@ function path_torsion(path, closed=false) =
|
||||||
|
|
||||||
// Section: Breaking paths up into subpaths
|
// Section: Breaking paths up into subpaths
|
||||||
|
|
||||||
/// Internal Function: _path_cut_points()
|
|
||||||
///
|
|
||||||
/// Usage:
|
// Function: path_cut()
|
||||||
/// cuts = _path_cut_points(path, dists, [closed=], [direction=]);
|
// Topics: Paths
|
||||||
///
|
// See Also: split_path_at_self_crossings()
|
||||||
/// Description:
|
// Usage:
|
||||||
/// Cuts a path at a list of distances from the first point in the path. Returns a list of the cut
|
// path_list = path_cut(path, cutdist, [closed]);
|
||||||
/// points and indices of the next point in the path after that point. So for example, a return
|
// Description:
|
||||||
/// value entry of [[2,3], 5] means that the cut point was [2,3] and the next point on the path after
|
// Given a list of distances in `cutdist`, cut the path into
|
||||||
/// this point is path[5]. If the path is too short then _path_cut_points returns undef. If you set
|
// subpaths at those lengths, returning a list of paths.
|
||||||
/// `direction` to true then `_path_cut_points` will also return the tangent vector to the path and a normal
|
// If the input path is closed then the final path will include the
|
||||||
/// vector to the path. It tries to find a normal vector that is coplanar to the path near the cut
|
// original starting point. The list of cut distances must be
|
||||||
/// point. If this fails it will return a normal vector parallel to the xy plane. The output with
|
// in ascending order and should not include the endpoints: 0
|
||||||
/// direction vectors will be `[point, next_index, tangent, normal]`.
|
// or len(path). If you repeat a distance you will get an
|
||||||
/// .
|
// empty list in that position in the output. If you give an
|
||||||
/// If you give the very last point of the path as a cut point then the returned index will be
|
// empty cutdist array you will get the input path as output
|
||||||
/// one larger than the last index (so it will not be a valid index). If you use the closed
|
// (without the final vertex doubled in the case of a closed path).
|
||||||
/// option then the returned index will be equal to the path length for cuts along the closing
|
// Arguments:
|
||||||
/// path segment, and if you give a point equal to the path length you will get an
|
// path = path of any dimension or a 1-region
|
||||||
/// index of len(path)+1 for the index.
|
// cutdist = Distance or list of distances where path is cut
|
||||||
///
|
// closed = If true, treat the path as a closed polygon. Default: false
|
||||||
/// Arguments:
|
// Example(2D,NoAxes):
|
||||||
/// path = path to cut
|
// path = circle(d=100);
|
||||||
/// dists = distances where the path should be cut (a list) or a scalar single distance
|
// segs = path_cut(path, [50, 200], closed=true);
|
||||||
/// ---
|
// rainbow(segs) stroke($item, endcaps="butt", width=3);
|
||||||
/// closed = set to true if the curve is closed. Default: false
|
function path_cut(path,cutdist,closed) =
|
||||||
/// direction = set to true to return direction vectors. Default: false
|
is_num(cutdist) ? path_cut(path,[cutdist],closed) :
|
||||||
///
|
is_1region(path) ? path_cut(path[0], cutdist, default(closed,true)):
|
||||||
/// Example(NORENDER):
|
let(closed=default(closed,false))
|
||||||
/// square=[[0,0],[1,0],[1,1],[0,1]];
|
assert(is_bool(closed))
|
||||||
/// _path_cut_points(square, [.5,1.5,2.5]); // Returns [[[0.5, 0], 1], [[1, 0.5], 2], [[0.5, 1], 3]]
|
assert(is_vector(cutdist))
|
||||||
/// _path_cut_points(square, [0,1,2,3]); // Returns [[[0, 0], 1], [[1, 0], 2], [[1, 1], 3], [[0, 1], 4]]
|
assert(last(cutdist)<path_length(path,closed=closed),"Cut distances must be smaller than the path length")
|
||||||
/// _path_cut_points(square, [0,0.8,1.6,2.4,3.2], closed=true); // Returns [[[0, 0], 1], [[0.8, 0], 1], [[1, 0.6], 2], [[0.6, 1], 3], [[0, 0.8], 4]]
|
assert(cutdist[0]>0, "Cut distances must be strictly positive")
|
||||||
/// _path_cut_points(square, [0,0.8,1.6,2.4,3.2]); // Returns [[[0, 0], 1], [[0.8, 0], 1], [[1, 0.6], 2], [[0.6, 1], 3], undef]
|
let(
|
||||||
function _path_cut_points(path, dists, closed=false, direction=false) =
|
cutlist = path_cut_points(path,cutdist,closed=closed)
|
||||||
|
)
|
||||||
|
_path_cut_getpaths(path, cutlist, closed);
|
||||||
|
|
||||||
|
|
||||||
|
function _path_cut_getpaths(path, cutlist, closed) =
|
||||||
|
let(
|
||||||
|
cuts = len(cutlist)
|
||||||
|
)
|
||||||
|
[
|
||||||
|
[ each list_head(path,cutlist[0][1]-1),
|
||||||
|
if (!approx(cutlist[0][0], path[cutlist[0][1]-1])) cutlist[0][0]
|
||||||
|
],
|
||||||
|
for(i=[0:1:cuts-2])
|
||||||
|
cutlist[i][0]==cutlist[i+1][0] && cutlist[i][1]==cutlist[i+1][1] ? []
|
||||||
|
:
|
||||||
|
[ if (!approx(cutlist[i][0], select(path,cutlist[i][1]))) cutlist[i][0],
|
||||||
|
each slice(path, cutlist[i][1], cutlist[i+1][1]-1),
|
||||||
|
if (!approx(cutlist[i+1][0], select(path,cutlist[i+1][1]-1))) cutlist[i+1][0],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
if (!approx(cutlist[cuts-1][0], select(path,cutlist[cuts-1][1]))) cutlist[cuts-1][0],
|
||||||
|
each select(path,cutlist[cuts-1][1],closed ? 0 : -1)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Function: path_cut_points()
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// cuts = path_cut_points(path, cutdist, [closed=], [direction=]);
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Cuts a path at a list of distances from the first point in the path. Returns a list of the cut
|
||||||
|
// points and indices of the next point in the path after that point. So for example, a return
|
||||||
|
// value entry of [[2,3], 5] means that the cut point was [2,3] and the next point on the path after
|
||||||
|
// this point is path[5]. If the path is too short then path_cut_points returns undef. If you set
|
||||||
|
// `direction` to true then `path_cut_points` will also return the tangent vector to the path and a normal
|
||||||
|
// vector to the path. It tries to find a normal vector that is coplanar to the path near the cut
|
||||||
|
// point. If this fails it will return a normal vector parallel to the xy plane. The output with
|
||||||
|
// direction vectors will be `[point, next_index, tangent, normal]`.
|
||||||
|
// .
|
||||||
|
// If you give the very last point of the path as a cut point then the returned index will be
|
||||||
|
// one larger than the last index (so it will not be a valid index). If you use the closed
|
||||||
|
// option then the returned index will be equal to the path length for cuts along the closing
|
||||||
|
// path segment, and if you give a point equal to the path length you will get an
|
||||||
|
// index of len(path)+1 for the index.
|
||||||
|
//
|
||||||
|
// Arguments:
|
||||||
|
// path = path to cut
|
||||||
|
// cutdist = distances where the path should be cut (a list) or a scalar single distance
|
||||||
|
// ---
|
||||||
|
// closed = set to true if the curve is closed. Default: false
|
||||||
|
// direction = set to true to return direction vectors. Default: false
|
||||||
|
//
|
||||||
|
// Example(NORENDER):
|
||||||
|
// square=[[0,0],[1,0],[1,1],[0,1]];
|
||||||
|
// path_cut_points(square, [.5,1.5,2.5]); // Returns [[[0.5, 0], 1], [[1, 0.5], 2], [[0.5, 1], 3]]
|
||||||
|
// path_cut_points(square, [0,1,2,3]); // Returns [[[0, 0], 1], [[1, 0], 2], [[1, 1], 3], [[0, 1], 4]]
|
||||||
|
// path_cut_points(square, [0,0.8,1.6,2.4,3.2], closed=true); // Returns [[[0, 0], 1], [[0.8, 0], 1], [[1, 0.6], 2], [[0.6, 1], 3], [[0, 0.8], 4]]
|
||||||
|
// path_cut_points(square, [0,0.8,1.6,2.4,3.2]); // Returns [[[0, 0], 1], [[0.8, 0], 1], [[1, 0.6], 2], [[0.6, 1], 3], undef]
|
||||||
|
function path_cut_points(path, cutdist, closed=false, direction=false) =
|
||||||
let(long_enough = len(path) >= (closed ? 3 : 2))
|
let(long_enough = len(path) >= (closed ? 3 : 2))
|
||||||
assert(long_enough,len(path)<2 ? "Two points needed to define a path" : "Closed path must include three points")
|
assert(long_enough,len(path)<2 ? "Two points needed to define a path" : "Closed path must include three points")
|
||||||
is_num(dists) ? _path_cut_points(path, [dists],closed, direction)[0] :
|
is_num(cutdist) ? path_cut_points(path, [cutdist],closed, direction)[0] :
|
||||||
assert(is_vector(dists))
|
assert(is_vector(cutdist))
|
||||||
assert(is_increasing(dists), "Cut distances must be an increasing list")
|
assert(is_increasing(cutdist), "Cut distances must be an increasing list")
|
||||||
let(cuts = _path_cut_points_recurse(path,dists,closed))
|
let(cuts = path_cut_points_recurse(path,cutdist,closed))
|
||||||
!direction
|
!direction
|
||||||
? cuts
|
? cuts
|
||||||
: let(
|
: let(
|
||||||
|
@ -759,7 +821,7 @@ function _path_cut_points(path, dists, closed=false, direction=false) =
|
||||||
hstack(cuts, list_to_matrix(dir,1), list_to_matrix(normals,1));
|
hstack(cuts, list_to_matrix(dir,1), list_to_matrix(normals,1));
|
||||||
|
|
||||||
// Main recursive path cut function
|
// Main recursive path cut function
|
||||||
function _path_cut_points_recurse(path, dists, closed=false, pind=0, dtotal=0, dind=0, result=[]) =
|
function path_cut_points_recurse(path, dists, closed=false, pind=0, dtotal=0, dind=0, result=[]) =
|
||||||
dind == len(dists) ? result :
|
dind == len(dists) ? result :
|
||||||
let(
|
let(
|
||||||
lastpt = len(result)==0? [] : last(result)[0], // location of last cut point
|
lastpt = len(result)==0? [] : last(result)[0], // location of last cut point
|
||||||
|
@ -768,7 +830,7 @@ function _path_cut_points_recurse(path, dists, closed=false, pind=0, dtotal=0, d
|
||||||
? [lerp(lastpt,select(path,pind),(dists[dind]-dtotal)/dpartial),pind]
|
? [lerp(lastpt,select(path,pind),(dists[dind]-dtotal)/dpartial),pind]
|
||||||
: _path_cut_single(path, dists[dind]-dtotal-dpartial, closed, pind)
|
: _path_cut_single(path, dists[dind]-dtotal-dpartial, closed, pind)
|
||||||
)
|
)
|
||||||
_path_cut_points_recurse(path, dists, closed, nextpoint[1], dists[dind],dind+1, concat(result, [nextpoint]));
|
path_cut_points_recurse(path, dists, closed, nextpoint[1], dists[dind],dind+1, concat(result, [nextpoint]));
|
||||||
|
|
||||||
|
|
||||||
// Search for a single cut point in the path
|
// Search for a single cut point in the path
|
||||||
|
@ -826,65 +888,6 @@ function _path_cuts_dir(path, cuts, closed=false, eps=1e-2) =
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
// Function: path_cut()
|
|
||||||
// Topics: Paths
|
|
||||||
// See Also: split_path_at_self_crossings()
|
|
||||||
// Usage:
|
|
||||||
// path_list = path_cut(path, cutdist, [closed]);
|
|
||||||
// Description:
|
|
||||||
// Given a list of distances in `cutdist`, cut the path into
|
|
||||||
// subpaths at those lengths, returning a list of paths.
|
|
||||||
// If the input path is closed then the final path will include the
|
|
||||||
// original starting point. The list of cut distances must be
|
|
||||||
// in ascending order and should not include the endpoints: 0
|
|
||||||
// or len(path). If you repeat a distance you will get an
|
|
||||||
// empty list in that position in the output. If you give an
|
|
||||||
// empty cutdist array you will get the input path as output
|
|
||||||
// (without the final vertex doubled in the case of a closed path).
|
|
||||||
// Arguments:
|
|
||||||
// path = path of any dimension or a 1-region
|
|
||||||
// cutdist = Distance or list of distances where path is cut
|
|
||||||
// closed = If true, treat the path as a closed polygon. Default: false
|
|
||||||
// Example(2D,NoAxes):
|
|
||||||
// path = circle(d=100);
|
|
||||||
// segs = path_cut(path, [50, 200], closed=true);
|
|
||||||
// rainbow(segs) stroke($item, endcaps="butt", width=3);
|
|
||||||
function path_cut(path,cutdist,closed) =
|
|
||||||
is_num(cutdist) ? path_cut(path,[cutdist],closed) :
|
|
||||||
is_1region(path) ? path_cut(path[0], cutdist, default(closed,true)):
|
|
||||||
let(closed=default(closed,false))
|
|
||||||
assert(is_bool(closed))
|
|
||||||
assert(is_vector(cutdist))
|
|
||||||
assert(last(cutdist)<path_length(path,closed=closed),"Cut distances must be smaller than the path length")
|
|
||||||
assert(cutdist[0]>0, "Cut distances must be strictly positive")
|
|
||||||
let(
|
|
||||||
cutlist = _path_cut_points(path,cutdist,closed=closed)
|
|
||||||
)
|
|
||||||
_path_cut_getpaths(path, cutlist, closed);
|
|
||||||
|
|
||||||
|
|
||||||
function _path_cut_getpaths(path, cutlist, closed) =
|
|
||||||
let(
|
|
||||||
cuts = len(cutlist)
|
|
||||||
)
|
|
||||||
[
|
|
||||||
[ each list_head(path,cutlist[0][1]-1),
|
|
||||||
if (!approx(cutlist[0][0], path[cutlist[0][1]-1])) cutlist[0][0]
|
|
||||||
],
|
|
||||||
for(i=[0:1:cuts-2])
|
|
||||||
cutlist[i][0]==cutlist[i+1][0] && cutlist[i][1]==cutlist[i+1][1] ? []
|
|
||||||
:
|
|
||||||
[ if (!approx(cutlist[i][0], select(path,cutlist[i][1]))) cutlist[i][0],
|
|
||||||
each slice(path, cutlist[i][1], cutlist[i+1][1]-1),
|
|
||||||
if (!approx(cutlist[i+1][0], select(path,cutlist[i+1][1]-1))) cutlist[i+1][0],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
if (!approx(cutlist[cuts-1][0], select(path,cutlist[cuts-1][1]))) cutlist[cuts-1][0],
|
|
||||||
each select(path,cutlist[cuts-1][1],closed ? 0 : -1)
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
// internal function
|
// internal function
|
||||||
// converts pathcut output form to a [segment, u]
|
// converts pathcut output form to a [segment, u]
|
||||||
// form list that works withi path_select
|
// form list that works withi path_select
|
||||||
|
|
|
@ -845,8 +845,8 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false)
|
||||||
assert(d_first<path_length(revresult),str("Path ",i," is too short for specified cut distance ",d_first))
|
assert(d_first<path_length(revresult),str("Path ",i," is too short for specified cut distance ",d_first))
|
||||||
assert(d_next<path_length(nextpath), str("Path ",i+1," is too short for specified cut distance ",d_next))
|
assert(d_next<path_length(nextpath), str("Path ",i+1," is too short for specified cut distance ",d_next))
|
||||||
let(
|
let(
|
||||||
firstcut = _path_cut_points(revresult, d_first, direction=true),
|
firstcut = path_cut_points(revresult, d_first, direction=true),
|
||||||
nextcut = _path_cut_points(nextpath, d_next, direction=true)
|
nextcut = path_cut_points(nextpath, d_next, direction=true)
|
||||||
)
|
)
|
||||||
assert(!loop || nextcut[1] < len(revresult)-1-firstcut[1], "Path is too short to close the loop")
|
assert(!loop || nextcut[1] < len(revresult)-1-firstcut[1], "Path is too short to close the loop")
|
||||||
let(
|
let(
|
||||||
|
@ -1197,8 +1197,8 @@ function _stroke_end(width,left, right, spec) =
|
||||||
90-vector_angle([newright[1],newright[0],newleft[0]])/2,
|
90-vector_angle([newright[1],newright[0],newleft[0]])/2,
|
||||||
jointleft = 8*cutleft/cos(leftangle)/(1+4*bez_k),
|
jointleft = 8*cutleft/cos(leftangle)/(1+4*bez_k),
|
||||||
jointright = 8*cutright/cos(rightangle)/(1+4*bez_k),
|
jointright = 8*cutright/cos(rightangle)/(1+4*bez_k),
|
||||||
pathcutleft = _path_cut_points(newleft,abs(jointleft)),
|
pathcutleft = path_cut_points(newleft,abs(jointleft)),
|
||||||
pathcutright = _path_cut_points(newright,abs(jointright)),
|
pathcutright = path_cut_points(newright,abs(jointright)),
|
||||||
leftdelete = intright? pathcutleft[1] : pathcutleft[1] + pathclip[1] -1,
|
leftdelete = intright? pathcutleft[1] : pathcutleft[1] + pathclip[1] -1,
|
||||||
rightdelete = intright? pathcutright[1] + pathclip[1] -1 : pathcutright[1],
|
rightdelete = intright? pathcutright[1] + pathclip[1] -1 : pathcutright[1],
|
||||||
leftcorner = line_intersection([pathcutleft[0], newleft[pathcutleft[1]]], [newright[0],newleft[0]]),
|
leftcorner = line_intersection([pathcutleft[0], newleft[pathcutleft[1]]], [newright[0],newleft[0]]),
|
||||||
|
|
|
@ -2966,7 +2966,7 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
|
||||||
|
|
||||||
start = center ? (path_length(path) - textlength)/2 : 0;
|
start = center ? (path_length(path) - textlength)/2 : 0;
|
||||||
|
|
||||||
pts = _path_cut_points(path, add_scalar([0, each cumsum(lsize)],start+lsize[0]/2), direction=true);
|
pts = path_cut_points(path, add_scalar([0, each cumsum(lsize)],start+lsize[0]/2), direction=true);
|
||||||
|
|
||||||
usernorm = is_def(normal);
|
usernorm = is_def(normal);
|
||||||
usetop = is_def(top);
|
usetop = is_def(top);
|
||||||
|
|
Loading…
Reference in a new issue