Merge pull request #1065 from adrianVmariano/master

rename path functions
This commit is contained in:
Revar Desmera 2023-03-02 18:36:31 -08:00 committed by GitHub
commit 8b6e5aeefb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 164 additions and 70 deletions

View file

@ -464,6 +464,47 @@ function deduplicate_indexed(list, indices, closed=false, eps=EPSILON) =
// Function: list_wrap()
// Usage:
// list_wrap(path, [eps]);
// Description:
// Force a list to wrap around so that its last point is equal to its first point: if the first and last entries are equal, simply returns the list unchanged.
// Otherwise returns the list with the first point duplicated at the end of the list. Comparisons are done to the tolerance `eps`. Lists of length 0 or
// 1 are returned unchanged.
// Arguments:
// list = list to unwrap
// eps = epsilon for comparison. Default: EPSILON (1e-9)
// See Also: list_unwrap(), deduplicate()
function list_wrap(list, eps=EPSILON) =
assert(is_list(list))
len(list)<2 || are_ends_equal(list,eps=eps)? list : [each list, list[0]];
function cleanup_path(list,eps=EPSILON) =
echo("***** Function cleanup_path() has been replaced by list_unwrap() and will be removed in a future version *****")
list_unwrap(list,eps);
function close_path(list,eps=EPSILON) =
echo("***** Function close_path() has been replaced by list_wrap() and will be removed in a future version *****")
list_wrap(list,eps);
// Function: list_unwrap()
// Usage:
// list_unwrap(list, [eps]);
// Description:
// If a list's last point matches its first point then delete the last point. Inverse operation to {{list_wrap()}}. Note that if the first/last points
// are repeated then the output may still have the first point equal to the last point. Comparisons are done to the tolerance `eps`. If the list has
// length 0 or 1 it is returned unchanged.
// Arguments:
// list = list to unwrap
// eps = epsilon for comparison. Default: EPSILON (1e-9)
function list_unwrap(list, eps=EPSILON) =
assert(is_list(list))
len(list)>=2 && are_ends_equal(list,eps=eps)? [for (i=[0:1:len(list)-2]) list[i]] : list;
// Function: unique()
// Usage:
// ulist = unique(list);

View file

@ -285,7 +285,7 @@ module stroke(
for (path = paths) {
pathvalid = is_path(path,[2,3]) || same_shape(path,[[0,0]]) || same_shape(path,[[0,0,0]]);
assert(pathvalid,"The path argument must be a list of 2D or 3D points, or a region.");
path = deduplicate( closed? close_path(path) : path );
path = deduplicate( closed? list_wrap(path) : path );
check4 = assert(is_num(width) || len(width)==len(path),
"width must be a number or a vector the same length as the path (or all components of a region)");
@ -576,7 +576,7 @@ function dashed_stroke(path, dashpat=[3,3], closed=false, fit=true, mindash=0.5)
each dashed_stroke(p, dashpat, closed=true, fit=fit)
] :
let(
path = closed? close_path(path) : path,
path = closed? list_wrap(path) : path,
dashpat = len(dashpat)%2==0? dashpat : concat(dashpat,[0]),
plen = path_length(path),
dlen = sum(dashpat),

View file

@ -2217,8 +2217,8 @@ function align_polygon(reference, poly, angles, cp, trans, return_ind=false) =
// rot(90, p=pentagon(r=4))); // returns false
function are_polygons_equal(poly1, poly2, eps=EPSILON) =
let(
poly1 = cleanup_path(poly1),
poly2 = cleanup_path(poly2),
poly1 = list_unwrap(poly1),
poly2 = list_unwrap(poly2),
l1 = len(poly1),
l2 = len(poly2)
) l1 != l2 ? false :

View file

@ -87,24 +87,6 @@ function force_path(path, name="path") =
: path;
// Function: close_path()
// Usage:
// close_path(path);
// Description:
// If a path's last point does not coincide with its first point, closes the path so it does.
function close_path(path, eps=EPSILON) =
are_ends_equal(path,eps=eps)? path : concat(path,[path[0]]);
// Function: cleanup_path()
// Usage:
// cleanup_path(path);
// Description:
// If a path's last point coincides with its first point, deletes the last point in the path.
function cleanup_path(path, eps=EPSILON) =
are_ends_equal(path,eps=eps)? [for (i=[0:1:len(path)-2]) path[i]] : path;
/// Internal Function: _path_select()
/// Usage:
/// _path_select(path,s1,u1,s2,u2,[closed]):
@ -260,7 +242,7 @@ function path_length_fractions(path, closed) =
/// for (isect=isects) translate(isect[0]) color("blue") sphere(d=10);
function _path_self_intersections(path, closed=true, eps=EPSILON) =
let(
path = closed ? close_path(path,eps=eps) : path,
path = closed ? list_wrap(path,eps=eps) : path,
plen = len(path)
)
[ for (i = [0:1:plen-3]) let(
@ -919,7 +901,7 @@ function split_path_at_self_crossings(path, closed=true, eps=EPSILON) =
assert(is_path(path,2), "Must give a 2D path")
assert(is_bool(closed))
let(
path = cleanup_path(path, eps=eps),
path = list_unwrap(path, eps=eps),
isects = deduplicate(
eps=eps,
concat(
@ -1030,7 +1012,7 @@ function polygon_parts(poly, nonzero=false, eps=EPSILON) =
assert(is_path(poly,2), "Must give 2D polygon")
assert(is_bool(nonzero))
let(
poly = cleanup_path(poly, eps=eps),
poly = list_unwrap(poly, eps=eps),
tagged = _tag_self_crossing_subpaths(poly, nonzero=nonzero, closed=true, eps=eps),
kept = [for (sub = tagged) if(sub[0] == "O") sub[1]],
outregion = _assemble_path_fragments(kept, eps=eps)
@ -1158,7 +1140,7 @@ function _assemble_path_fragments(fragments, eps=EPSILON, _finished=[]) =
l_area = abs(polygon_area(result_l[0])),
r_area = abs(polygon_area(result_r[0])),
result = l_area < r_area? result_l : result_r,
newpath = cleanup_path(result[0]),
newpath = list_unwrap(result[0]),
remainder = result[1],
finished = min(l_area,r_area)<eps ? _finished : concat(_finished, [newpath])
) _assemble_path_fragments(

View file

@ -421,7 +421,7 @@ function _region_region_intersections(region1, region2, closed1=true,closed2=tru
intersections = [
for(p1=idx(region1))
let(
path = closed1?close_path(region1[p1]):region1[p1]
path = closed1?list_wrap(region1[p1]):region1[p1]
)
for(i = [0:1:len(path)-2])
let(
@ -442,7 +442,7 @@ function _region_region_intersections(region1, region2, closed1=true,closed2=tru
// further tests can be discarded.
for(p2=idx(region2))
let(
poly = closed2?close_path(region2[p2]):region2[p2],
poly = closed2?list_wrap(region2[p2]):region2[p2],
signs = [for(v=poly*seg_normal) abs(v-ref) < eps ? 0 : sign(v-ref) ]
)
if(max(signs)>=0 && min(signs)<=0) // some edge intersects line [a1,a2]

View file

@ -678,7 +678,7 @@ function smooth_path(path, tangents, size, relsize, splinesteps=10, uniform=fals
bez = path_to_bezpath(path, tangents=tangents, size=size, relsize=relsize, uniform=uniform, closed=default(closed,false)),
smoothed = bezpath_curve(bez,splinesteps=splinesteps)
)
closed ? cleanup_path(smoothed) : smoothed;
closed ? list_unwrap(smoothed) : smoothed;
function _scalar_to_vector(value,length,varname) =
@ -815,7 +815,7 @@ function path_join(paths,joint=0,k=0.5,relocate=true,closed=false)=
let(
paths = !closed || len(paths)>1
? paths
: [close_path(paths[0])],
: [list_wrap(paths[0])],
N = len(paths) + (closed?0:-1),
k = _scalar_to_vector(k,N),
repjoint = is_num(joint) || (is_vector(joint,2) && len(paths)!=3),
@ -828,7 +828,7 @@ function path_join(paths,joint=0,k=0.5,relocate=true,closed=false)=
)
assert(bad_j==[], str("Invalid joint values at indices ",bad_j))
let(result=_path_join(paths,joint,k, relocate=relocate, closed=closed))
closed ? cleanup_path(result) : result;
closed ? list_unwrap(result) : result;
function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false) =
let(

View file

@ -771,7 +771,7 @@ function linear_sweep(
trgns = [
for (rgn = regions) [
for (path = rgn) let(
p = cleanup_path(path),
p = list_unwrap(path),
path = is_undef(maxseg)? p : [
for (seg = pair(p,true)) each
let( steps = ceil(norm(seg.y - seg.x) / maxseg) )
@ -783,7 +783,7 @@ function linear_sweep(
vnf = vnf_join([
for (rgn = regions)
for (pathnum = idx(rgn)) let(
p = cleanup_path(rgn[pathnum]),
p = list_unwrap(rgn[pathnum]),
path = is_undef(maxseg)? p : [
for (seg=pair(p,true)) each
let(steps=ceil(norm(seg.y-seg.x)/maxseg))
@ -1078,12 +1078,75 @@ module rotate_sweep(
// Example:
// poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]];
// spiral_sweep(poly, h=200, r=50, turns=3, $fn=36);
function _taperfunc(x) =
function _taperfunc_orig_1d(x,L) =
x>1 ? 1 : x<0 ? 0:
let(higofs = pow(0.05,2)) // Smallest hig scale is the square root of this value
let(
higofs = pow(0.05,2) // Smallest hig scale is the square root of this value
)
sqrt((1-higofs)*x+higofs);
function _taperfunc_orig(x,L) =
let(s=_taperfunc_orig_1d(x))
x>1 ? [1,1]
: x<0 ? [0,0]
: [lerp(s,1,.25),s];
function _taperfunc_ellipse(x) =
sqrt(1-(1-x)^2);
function _taperfunc_linear(x) =
x>1 ? 1 : x<0 ? 0 : x;
function _taperfunc_ogive_width(x,L) =
let( minscale = .2,
r=(L^2+(1-minscale^2))/2/(1-minscale),
scale = sqrt(r^2-(L*(1-x))^2) -(r-1)
)
x>1 ? [1,1]
: x<0 ? [0,0]
: [scale,1];
function _taperfunc_ogive_width_circle(x,L,h) =
let( minscale = .2,
r=(L^2+(1-minscale^2))/2/(1-minscale),
scale = sqrt(r^2-(L*(1-x))^2) -(r-1),
vscale = x*L>h ? h : sqrt(h^2-(x*L-h)^2)
)
x>1 ? [1,1]
: x<0.02 ? [0,0]
: [scale,vscale/h];
function _taperfunc_ogive_height(x,L) =
let( minscale = .1,L=3*L,
r=(L^2+(1-minscale^2))/2/(1-minscale),
scale = sqrt(r^2-(L*(1-x))^2) -(r-1)
)
x>1 ? [1,1]
: x<0 ? [0,0] //minscale,0]
: [1,scale];
function _taperfunc_ogive(x,L) =
let( minscale = .3,
r=(L^2+(1-minscale^2))/2/(1-minscale),
scale = sqrt(r^2-(L*(1-x))^2) -(r-1)
)
x>1 ? [1,1]
: x<0 ? [0,0]
: [scale,scale];
function _taperfunc_ogive_orig(x,L) =
let( minscale = .3,
r=(L^2+(1-minscale^2))/2/(1-minscale),
scale = sqrt(r^2-(L*(1-x))^2) -(r-1)
)
x>1 ? [1,1]
: x<0 ? [0,0]
: [lerp(_taperfunc_orig_1d(x),1,.25),scale];
function _taperfunc_cut(x,L) = x>1 ? [1,1] : [0,0];
function _taperfunc(x,L,h) = _taperfunc_ogive_width_circle(x,L,h);
//function _taperfunc(x,L,h) = _taperfunc_orig(x,L);
//function _taperfunc(x,L,h) = _taperfunc_ogive_width(x,L);
function _taperfunc(x,L,h) = _taperfunc_orig(x,L);
function _ss_polygon_r(N,theta) =
let( alpha = 360/N )
cos(alpha/2)/(cos(posmod(theta,alpha)-alpha/2));
@ -1144,16 +1207,16 @@ function spiral_sweep(poly, h, r, turns=1, taper, center, r1, r2, d, d1, d2, tap
skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h)),
points = [
for (a = interp_ang) let (
hsc = a<tapercut1 ? _taperfunc((a-minang)/taperang1)
: a>tapercut2 ? _taperfunc((maxang-a)/taperang2)
: 1,
hsc = a<tapercut1 ? _taperfunc((a-minang)/taperang1,abs(taper1),xmax-xmin)
: a>tapercut2 ? _taperfunc((maxang-a)/taperang2,abs(taper2),xmax-xmin)
: [1,1],
u = a/(360*turns),
r = lerp(r1,r2,u),
mat = affine3d_zrot(dir*a)
* affine3d_translate([_ss_polygon_r(sides,dir*a)*r, 0, h * (u-0.5)])
* affine3d_xrot(90)
* skewmat
* scale([hsc,lerp(hsc,1,0.25),1], cp=[internal ? xmax : xmin, yctr, 0]),
* scale([hsc.y,hsc.x,1], cp=[internal ? xmax : xmin, yctr, 0]),
pts = apply(mat, poly)
) pts
],
@ -3425,8 +3488,8 @@ function _textured_linear_sweep(
: [ceil(6*plen/h), 6],
obases = resample_path(path, n=counts.x * samples, closed=true),
onorms = path_normals(obases, closed=true),
bases = close_path(obases),
norms = close_path(onorms),
bases = list_wrap(obases),
norms = list_wrap(onorms),
vnf = is_vnf(texture)
? let( // VNF tile texture
row_vnf = vnf_join([
@ -3511,8 +3574,8 @@ function _textured_linear_sweep(
: [ceil(6*plen/h), 6],
obases = resample_path(path, n=counts.x * samples, closed=true),
onorms = path_normals(obases, closed=true),
bases = close_path(obases),
norms = close_path(onorms),
bases = list_wrap(obases),
norms = list_wrap(onorms),
nupath = [
for (j = [0:1:counts.x-1], vert = tpath) let(
part = (j + vert.x) * samples,
@ -3699,8 +3762,8 @@ function _textured_revolution(
is_vector(tex_size,2)? max(1,round(plen/tex_size.y)) : 6,
obases = resample_path(path, n=counts_y * samples + (closed?0:1), closed=closed),
onorms = path_normals(obases, closed=closed),
rbases = closed? close_path(obases) : obases,
rnorms = closed? close_path(onorms) : onorms,
rbases = closed? list_wrap(obases) : obases,
rnorms = closed? list_wrap(onorms) : onorms,
bases = xrot(90, p=path3d(rbases)),
norms = xrot(90, p=path3d(rnorms)),
vnf = is_vnf(texture)
@ -3766,8 +3829,8 @@ function _textured_revolution(
is_vector(tex_size,2)? max(1,round(plen/tex_size.y)) : 6,
obases = resample_path(path, n=counts_y * samples + (closed?0:1), closed=closed),
onorms = path_normals(obases, closed=closed),
bases = closed? close_path(obases) : obases,
norms = closed? close_path(onorms) : onorms,
bases = closed? list_wrap(obases) : obases,
norms = closed? list_wrap(onorms) : onorms,
ppath = is_vnf(texture)
? [ // VNF tile texture
for (j = [0:1:counts_y-1])
@ -3816,8 +3879,8 @@ function _textured_revolution(
is_vector(tex_size,2)? max(1,round(plen/tex_size.y)) : 6,
obases = resample_path(rgn[0], n=counts_y * samples + (closed?0:1), closed=closed),
onorms = path_normals(obases, closed=closed),
rbases = closed? close_path(obases) : obases,
rnorms = closed? close_path(onorms) : onorms,
rbases = closed? list_wrap(obases) : obases,
rnorms = closed? list_wrap(onorms) : onorms,
bases = xrot(90, p=path3d(rbases)),
norms = xrot(90, p=path3d(rnorms)),
caps_vnf = vnf_join([

View file

@ -81,6 +81,26 @@ test_unique_count();
module test_list_wrap() {
assert(list_wrap([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]);
assert(list_wrap([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]);
assert(list_wrap([])==[]);
assert(list_wrap([3])==[3]);
}
test_list_wrap();
module test_list_unwrap() {
assert(list_unwrap([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9]]);
assert(list_unwrap([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9]]);
assert(list_unwrap([])==[]);
assert(list_unwrap([3])==[3]);
}
test_list_unwrap();
module test_is_increasing() {
assert(is_increasing([1,2,3,4]) == true);
assert(is_increasing([1,2,2,2]) == true);

View file

@ -32,21 +32,6 @@ module force_path() {
test_is_1region();
module test_close_path() {
assert(close_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]);
assert(close_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]);
}
test_close_path();
module test_cleanup_path() {
assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9]]);
assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9]]);
}
test_cleanup_path();
module test_path_merge_collinear() {
path = [[-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30]];
assert(path_merge_collinear(path) == [[-20,-20], [-10,-20], [20,10], [20,20], [15,30]]);

View file

@ -1217,7 +1217,7 @@ module generic_threaded_rod(
* frame_map(x=[0,0,1], y=[1,0,0]) // Map profile to 3d, parallel to z axis
* scale(pitch); // scale profile by pitch
start_steps = sides / starts;
higlen = 4/32*360;//360*max(pitch/2, pmax-depth)/(2*PI*_r2);
higlen = 2/32*360;//360*max(pitch/2, pmax-depth)/(2*PI*_r2);
echo(higlen=higlen);
thread_verts = [
// Outer loop constructs a vertical column of the screw at each angle
@ -1231,10 +1231,13 @@ module generic_threaded_rod(
for (thread = [-threads/2:1:threads/2-1])
let(
tang = thread/starts * 360 + ang,
hsc = tang < -twist/2+higang1 ? _taperfunc(1-(-twist/2+higang1-tang)/higlen )
: tang > twist/2-higang2 ? _taperfunc(1-(tang-twist/2+higang2)/higlen )
: 1,
higscale=scale([lerp(hsc,1,0.25),hsc,1], cp=[0,internal ? pmax/pitch:-pdepth, 0])
hsc = tang < -twist/2+higang1 ? _taperfunc(1-(-twist/2+higang1-tang)/higlen,PI*2*_r1*higlen/360 )
: tang > twist/2-higang2 ? _taperfunc(1-(tang-twist/2+higang2)/higlen,PI*2*_r2*higlen/360 )
: [1,1],
higscale=scale([hsc.x, hsc.y,1], cp=[0,internal ? pmax/pitch:-pdepth, 0])
// higscale=scale([lerp(hsc,1,0.25),hsc,1], cp=[0,internal ? pmax/pitch:-pdepth, 0])
// higscale=scale([lerp(hsc,1,0.25),1,1], cp=[0,internal ? pmax/pitch:-pdepth, 0])
// higscale=scale([1,hsc,1], cp=[0,internal ? pmax/pitch:-pdepth, 0])
)
// The right movement finds the position of the thread along
// what will be the z axis after the profile is mapped to 3d

View file

@ -826,7 +826,7 @@ function _split_polygon_at_x(poly, x) =
if (len(out1)>=3) each split_path_at_self_crossings(out1),
if (len(out2)>=3) each split_path_at_self_crossings(out2),
],
out = [for (p=out3) if (len(p) > 2) cleanup_path(p)]
out = [for (p=out3) if (len(p) > 2) list_unwrap(p)]
) out;