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);
|
||||
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;
|
||||
if (true) for(i=[0:1:len(cutlist)-1]) {
|
||||
$pos = cutlist[i][0];
|
||||
|
|
|
@ -332,7 +332,7 @@ module stroke(
|
|||
}
|
||||
} 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.");
|
||||
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);
|
||||
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]);
|
||||
|
|
205
paths.scad
205
paths.scad
|
@ -514,7 +514,7 @@ function resample_path(path, n, spacing, closed=true) =
|
|||
// Add last point later
|
||||
n = is_def(n) ? n-(closed?0:1) : round(length/spacing),
|
||||
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),
|
||||
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
|
||||
|
||||
/// Internal Function: _path_cut_points()
|
||||
///
|
||||
/// Usage:
|
||||
/// cuts = _path_cut_points(path, dists, [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
|
||||
/// dists = 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, dists, closed=false, direction=false) =
|
||||
|
||||
|
||||
// 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)
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
|
||||
// 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))
|
||||
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] :
|
||||
assert(is_vector(dists))
|
||||
assert(is_increasing(dists), "Cut distances must be an increasing list")
|
||||
let(cuts = _path_cut_points_recurse(path,dists,closed))
|
||||
is_num(cutdist) ? path_cut_points(path, [cutdist],closed, direction)[0] :
|
||||
assert(is_vector(cutdist))
|
||||
assert(is_increasing(cutdist), "Cut distances must be an increasing list")
|
||||
let(cuts = path_cut_points_recurse(path,cutdist,closed))
|
||||
!direction
|
||||
? cuts
|
||||
: 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));
|
||||
|
||||
// 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 :
|
||||
let(
|
||||
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]
|
||||
: _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
|
||||
|
@ -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
|
||||
// converts pathcut output form to a [segment, u]
|
||||
// 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_next<path_length(nextpath), str("Path ",i+1," is too short for specified cut distance ",d_next))
|
||||
let(
|
||||
firstcut = _path_cut_points(revresult, d_first, direction=true),
|
||||
nextcut = _path_cut_points(nextpath, d_next, direction=true)
|
||||
firstcut = path_cut_points(revresult, d_first, 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")
|
||||
let(
|
||||
|
@ -1197,8 +1197,8 @@ function _stroke_end(width,left, right, spec) =
|
|||
90-vector_angle([newright[1],newright[0],newleft[0]])/2,
|
||||
jointleft = 8*cutleft/cos(leftangle)/(1+4*bez_k),
|
||||
jointright = 8*cutright/cos(rightangle)/(1+4*bez_k),
|
||||
pathcutleft = _path_cut_points(newleft,abs(jointleft)),
|
||||
pathcutright = _path_cut_points(newright,abs(jointright)),
|
||||
pathcutleft = path_cut_points(newleft,abs(jointleft)),
|
||||
pathcutright = path_cut_points(newright,abs(jointright)),
|
||||
leftdelete = intright? pathcutleft[1] : pathcutleft[1] + pathclip[1] -1,
|
||||
rightdelete = intright? pathcutright[1] + pathclip[1] -1 : pathcutright[1],
|
||||
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;
|
||||
|
||||
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);
|
||||
usetop = is_def(top);
|
||||
|
|
Loading…
Reference in a new issue