Removed range(), rangex(). Added count().

This commit is contained in:
Garth Minette 2021-04-07 20:57:45 -07:00
parent a4c099e670
commit 8bd3ef8a74
13 changed files with 75 additions and 170 deletions

View file

@ -209,7 +209,7 @@ function list_tail(list, from=1) =
// If given a string, explodes it into a list of single letters.
// Arguments:
// l = The value to expand.
// See Also: scalar_vec3(), force_list(), range(), rangex()
// See Also: scalar_vec3(), force_list(), rangex()
// Example:
// l1 = list([3:2:9]); // Returns: [3,5,7,9]
// l2 = list([3,4,5]); // Returns: [3,4,5]
@ -396,7 +396,7 @@ function list_decreasing(list) =
// Usage:
// list = repeat(val, n);
// Topics: List Handling
// See Also: range(), rangex()
// See Also: count(), lerpn()
// Description:
// Generates a list or array of `n` copies of the given value `val`.
// If the count `n` is given as a list of counts, then this creates a
@ -416,89 +416,21 @@ function repeat(val, n, i=0) =
[for (j=[1:1:n[i]]) repeat(val, n, i+1)];
// Function: range()
// Function: count()
// Usage:
// list = range(n, <s=>, <e=>);
// list = range(n, <s=>, <step=>);
// list = range(e=, <step=>);
// list = range(s=, e=, <step=>);
// Topics: List Handling
// list = count(n,<s>,<step>);
// Description:
// Returns a list, counting up from starting value `s`, by `step` increments,
// until either `n` values are in the list, or it reaches the end value `e`.
// If both `n` and `e` are given, returns `n` values evenly spread from `s`
// to `e`, and `step` is ignored.
// Creates a list of `n` numbers, starting at `s`, incrementing by `step` each time.
// Arguments:
// n = Desired number of values in returned list, if given.
// ---
// s = Starting value. Default: 0
// e = Ending value to stop at, if given.
// step = Amount to increment each value. Default: 1
// See Also: repeat(), rangex()
// n = The length of the list of numbers to create.
// s = The starting value of the list of numbers.
// step = The amount to increment successive numbers in the list.
// Example:
// a = range(4); // Returns [0,1,2,3]
// b = range(n=4, step=2); // Returns [0,2,4,6]
// c = range(n=4, s=3, step=3); // Returns [3,6,9,12]
// d = range(n=5, s=0, e=10); // Returns [0, 2.5, 5, 7.5, 10]
// e = range(e=3); // Returns [0,1,2,3]
// f = range(e=7, step=2); // Returns [0,2,4,6]
// g = range(s=3, e=5); // Returns [3,4,5]
// h = range(s=3, e=8, step=2); // Returns [3,5,7]
// i = range(s=4, e=8.3, step=2); // Returns [4,6,8]
function range(n, s=0, e, step) =
assert( is_undef(n) || is_finite(n), "Parameter `n` must be a number.")
assert( is_undef(n) || is_undef(e) || is_undef(step), "At most 2 of n, e, and step can be given.")
let( step = (n!=undef && e!=undef)? (e-s)/(n-1) : default(step,1) )
is_undef(e)
? assert( is_consistent([s, step]), "Incompatible data.")
[for (i=[0:1:n-1]) s+step*i]
: assert( is_vector([s,step,e]), "Start `s`, step `step` and end `e` must be numbers.")
[for (v=[s:step:e]) v] ;
// nl1 = count(5); // Returns: [0,1,2,3,4]
// nl2 = count(5,3); // Returns: [3,4,5,6,7]
// nl3 = count(4,3,2); // Returns: [3,5,7,9]
function count(n,s=0,step=1) = [for (i=[0:1:n-1]) s+i*step];
// Function: rangex()
// Usage:
// list = rangex(n, <s=>, <e=>);
// list = rangex(n, <s=>, <step=>);
// list = rangex(e=, <step=>);
// list = rangex(s=, e=, <step=>);
// Topics: List Handling
// Description:
// Returns a list, counting up from starting value `s`, by `step` increments, until
// either `n` values are in the list, or it reaches the value just before the end value `e`.
// If both `n` and `e` are given, returns `n` values evenly spread from `s` to the value
// just before `e`, and `step` is ignored.
// Arguments:
// n = Desired number of values in returned list, if given.
// ---
// s = Starting value. Default: 0
// e = Ending value to stop at, if given.
// step = Amount to increment each value. Default: 1
// See Also: repeat(), range()
// Example:
// a = rangex(4); // Returns [0,1,2,3]
// b = rangex(5,e=1); // Returns [0, 0.2, 0.4, 0.6, 0.8]
// c = rangex(n=4, step=2); // Returns [0,2,4,6]
// d = rangex(n=4, step=0.25); // Returns [0, 0.25, 0.5, 0.75]
// e = rangex(n=4, s=3, step=3); // Returns [3,6,9,12]
// f = rangex(n=5, s=0, e=10); // Returns [0, 2, 4, 6, 8]
// g = rangex(e=3); // Returns [0,1,2]
// h = rangex(e=7, step=2); // Returns [0,2,4,6]
// i = rangex(s=3, e=5); // Returns [3,4]
// j = rangex(s=3, e=8, step=2); // Returns [3,5,7]
// k = rangex(s=2, e=8, step=2); // Returns [2,4,6]
// l = rangex(s=2, e=8.1, step=2); // Returns [2,4,6,8]
function rangex(n, s=0, e, step) =
assert( is_undef(n) || is_finite(n), "Parameter `n` must be a number.")
assert( is_undef(n) || is_undef(e) || is_undef(step), "At most 2 of n, e, and step can be given.")
let( step = (n!=undef && e!=undef)? (e-s)/n : default(step,1) )
is_undef(e)
? assert( is_consistent([s, step]), "Incompatible data.")
[for (i=[0:1:n-1]) s+step*i]
: assert( is_vector([s,step,e]), "Start `s`, step `step` and end `e` must be numbers.")
let(steps=floor((e-s)/step+0.5))
[for (i=[0:1:steps-1]) s+step*i];
// Section: List Manipulation

View file

@ -437,10 +437,8 @@ function bezier_curvature(curve, u) =
// move_copies(bezier_curve(bez, 8)) sphere(r=1.5, $fn=12);
// trace_bezier(bez, N=len(bez)-1);
function bezier_curve(curve,n,endpoint=true) =
[
each bezier_points(curve, rangex(endpoint?n-1:n,0,1)),
if (endpoint) last(curve)
];
bezier_points(curve, lerpn(0,1,n,endpoint));
// Function: bezier_segment_closest_point()
// Usage:
@ -509,7 +507,7 @@ function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) =
function bezier_segment_length(curve, start_u=0, end_u=1, max_deflect=0.01) =
let(
segs = len(curve) * 2,
uvals = [for (i=[0:1:segs]) lerp(start_u, end_u, i/segs)],
uvals = lerpn(start_u, end_u, segs),
path = bezier_points(curve,uvals),
defl = max([
for (i=idx(path,e=-3)) let(
@ -693,7 +691,7 @@ function bezier_path_length(path, N=3, max_deflect=0.001) =
// Function: bezier_path()
// Usage:
// path = bezier_path(bezier, <splinesteps>, <N>)
// path = bezier_path(bezier, <splinesteps>, <N>, <endpoint>)
// Topics: Bezier Paths
// See Also: bezier_points(), bezier_curve()
// Description:
@ -702,6 +700,7 @@ function bezier_path_length(path, N=3, max_deflect=0.001) =
// bezier = A bezier path to approximate.
// splinesteps = Number of straight lines to split each bezier segment into. default=16
// N = The degree of the bezier curves. Cubic beziers have N=3. Default: 3
// endpoint = If true, include the very last point of the bezier path. Default: true
// Example(2D):
// bez = [
// [0,0], [-5,30],
@ -711,18 +710,19 @@ function bezier_path_length(path, N=3, max_deflect=0.001) =
// ];
// trace_path(bez, size=1, N=3, showpts=true);
// trace_path(bezier_path(bez, N=3), size=3);
function bezier_path(bezier, splinesteps=16, N=3) =
function bezier_path(bezier, splinesteps=16, N=3, endpoint=true) =
assert(is_path(bezier))
assert(is_int(N))
assert(is_int(splinesteps))
assert(len(bezier)%N == 1, str("A degree ",N," bezier path shound have a multiple of ",N," points in it, plus 1."))
assert(len(bezier)%N == 1, str("A degree ",N," bezier path should have a multiple of ",N," points in it, plus 1."))
let(
segs = (len(bezier)-1)/N
) deduplicate([
for (seg = [0:1:segs-1], i = [0:1:splinesteps-1])
bezier_path_point(bezier, seg, i/splinesteps, N=N),
bezier_path_point(bezier, segs-1, 1, N=N)
]);
segs = (len(bezier)-1) / N,
step = 1 / splinesteps
) [
for (seg = [0:1:segs-1])
each bezier_points(select(bezier, seg*N, (seg+1)*N), [0:step:1-step/2]),
if (endpoint) bezier_path_point(bezier, segs-1, 1, N=N)
];
@ -861,19 +861,18 @@ function bezier_close_to_axis(bezier, axis="X", N=3) =
assert(is_int(N))
assert(len(bezier)%N == 1, str("A degree ",N," bezier path shound have a multiple of ",N," points in it, plus 1."))
let(
bezend = len(bezier)-1,
sp = bezier[0],
ep = bezier[bezend]
ep = last(bezier)
) (axis=="X")? concat(
[for (i=[0:1:N-1]) lerp([sp.x,0], sp, i/N)],
bezier,
[for (i=[1:1:N]) lerp(ep, [ep.x,0], i/N)],
[for (i=[1:1:N]) lerp([ep.x,0], [sp.x,0], i/N)]
lerpn([sp.x,0], sp, N, false),
list_head(bezier),
lerpn(ep, [ep.x,0], N, false),
lerpn([ep.x,0], [sp.x,0], N+1)
) : (axis=="Y")? concat(
[for (i=[0:1:N-1]) lerp([0,sp.y], sp, i/N)],
bezier,
[for (i=[1:1:N]) lerp(ep, [0,ep.y], i/N)],
[for (i=[1:1:N]) lerp([0,ep.y], [0,sp.y], i/N)]
lerpn([0,sp.y], sp, N, false),
list_head(bezier),
lerpn(ep, [0,ep.y], N, false),
lerpn([0,ep.y], [0,sp.y], N+1)
) : (
assert(in_list(axis, ["X","Y"]))
);
@ -907,10 +906,10 @@ function bezier_offset(offset, bezier, N=3) =
backbez = reverse([ for (pt = bezier) pt+offset ]),
bezend = len(bezier)-1
) concat(
bezier,
[for (i=[1:1:N-1]) lerp(bezier[bezend], backbez[0], i/N)],
backbez,
[for (i=[1:1:N]) lerp(backbez[bezend], bezier[0], i/N)]
list_head(bezier),
lerpn(bezier[bezend], backbez[0], N, false),
list_head(backbez),
lerpn(backbez[bezend], bezier[0], N+1)
);

View file

@ -158,7 +158,7 @@ function _hull_collinear(points) =
// %polyhedron(points=pts, faces=faces);
function hull3d_faces(points) =
assert(is_path(points,3),"Invalid input to hull3d_faces")
len(points) < 3 ? range(len(points))
len(points) < 3 ? count(len(points))
: let ( // start with a single non-collinear triangle
tri = noncollinear_triple(points, error=false)
)

View file

@ -494,7 +494,7 @@ function rand_int(minval, maxval, N, seed=undef) =
function gaussian_rands(mean, stddev, N=1, seed=undef) =
assert( is_finite(mean+stddev+N) && (is_undef(seed) || is_finite(seed) ), "Input must be finite numbers.")
let(nums = is_undef(seed)? rands(0,1,N*2) : rands(0,1,N*2,seed))
[for (i = range(N)) mean + stddev*sqrt(-2*ln(nums[i*2]))*cos(360*nums[i*2+1])];
[for (i = count(N)) mean + stddev*sqrt(-2*ln(nums[i*2]))*cos(360*nums[i*2+1])];
// Function: log_rands()

View file

@ -1240,18 +1240,15 @@ module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=fals
length = path_length(path,closed);
distances =
is_def(sp)? ( // Start point given
is_def(n) && is_def(spacing)? range(s=sp, step=spacing, n=n) :
is_def(n)? range(s=sp, e=length, n=n) :
range(s=sp, step=spacing, e=length)
)
: is_def(n) && is_undef(spacing)? ( // N alone given
closed ? rangex(s=0, e=length, n=n)
: range(s=0, e=length, n=n)
is_def(n) && is_def(spacing)? count(n,sp,spacing) :
is_def(n)? lerpn(sp, length, n) :
list([sp:spacing:length])
)
: is_def(n) && is_undef(spacing)? lerpn(0,length,n,!closed) // N alone given
: ( // No start point and spacing is given, N maybe given
let(
n = is_def(n)? n : floor(length/spacing)+(closed?0:1),
ptlist = range(s=0,step=spacing,n=n),
ptlist = count(n,0,spacing),
listcenter = mean(ptlist)
) closed?
sort([for(entry=ptlist) posmod(entry-listcenter,length)]) :
@ -1607,7 +1604,7 @@ function resample_path(path, N, spacing, closed=false) =
// path_cut to return the endpoint (which might fail due to rounding)
// Add last point later
N = is_def(N) ? N-(closed?0:1) : round(length/spacing),
distlist = rangex(N,e=length),
distlist = lerpn(0,length,N,false),
cuts = path_cut_points(path, distlist, closed=closed)
)
[ each subindex(cuts,0),

View file

@ -438,7 +438,7 @@ function linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg
path = is_undef(maxseg)? p : [
for (seg=pair(p,true)) each
let(steps=ceil(norm(seg.y-seg.x)/maxseg))
lerp(seg.x, seg.y, [0:1/steps:1-EPSILON])
lerpn(seg.x, seg.y, steps, false)
]
)
rot(twist, p=scale([scale,scale],p=path))
@ -451,7 +451,7 @@ function linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg
path = is_undef(maxseg)? p : [
for (seg=pair(p,true)) each
let(steps=ceil(norm(seg.y-seg.x)/maxseg))
lerp(seg.x, seg.y, [0:1/steps:1-EPSILON])
lerpn(seg.x, seg.y, steps, false)
],
verts = [
for (i=[0:1:slices]) let(

View file

@ -194,7 +194,7 @@ include <structs.scad>
// square = [[0,0],[1,0],[1,1],[0,1]];
// spiral = flatten(repeat(concat(square,reverse(square)),5)); // Squares repeat 10 times, forward and backward
// squareind = [for(i=[0:9]) each [i,i,i,i]]; // Index of the square for each point
// z = range(40)*.2+squareind;
// z = count(40)*.2+squareind;
// path3d = hstack(spiral,z); // 3D spiral
// rounding = squareind/20;
// // Setting k=1 means curvature won't be continuous, but curves are as round as possible
@ -899,7 +899,7 @@ function _make_offset_polyhedron(path,offsets, offset_type, flip_faces, quality,
vertexcount=0, vertices=[], faces=[] )=
offsetind==len(offsets)? (
let(
bottom = range(n=len(path),s=vertexcount),
bottom = count(len(path),vertexcount),
oriented_bottom = !flip_faces? bottom : reverse(bottom)
) [vertices, concat(faces,[oriented_bottom])]
) : (
@ -1956,7 +1956,7 @@ function bezier_patch_degenerate(patch, splinesteps=16, reverse=false) =
bot_degen = last(patch)[0] == last(last(patch)),
left_degen = patch[0][0] == last(patch)[0],
right_degen = last(patch[0]) == last(last(patch)),
samplepts = range(splinesteps+1)/splinesteps
samplepts = count(splinesteps+1)/splinesteps
)
top_degen && bot_degen && left_degen && right_degen ? // fully degenerate case
[repeat([patch[0][0]],4), EMPTY_VNF] :
@ -1990,13 +1990,13 @@ function bezier_patch_degenerate(patch, splinesteps=16, reverse=false) =
// at this point top_degen is true // only top is degenerate
let(
full_degen = patch[1][0] == last(patch[1]),
rowmax = full_degen ? range(splinesteps+1) :
rowmax = full_degen ? count(splinesteps+1) :
[for(j=[0:splinesteps]) j<=splinesteps/2 ? 2*j : splinesteps],
vbb=echo("single degenerate case"),
bpatch = [for(i=[0:1:len(patch[0])-1]) bezier_points(subindex(patch,i), samplepts)],
pts = [
[bpatch[0][0]],
for(j=[1:splinesteps]) bezier_points(subindex(bpatch,j), range(rowmax[j]+1)/rowmax[j])
for(j=[1:splinesteps]) bezier_points(subindex(bpatch,j), count(rowmax[j]+1)/rowmax[j])
],
vnf = vnf_tri_array(pts, reverse=reverse)
) [

View file

@ -1085,7 +1085,7 @@ function _ISO_thread_tolerance(diameter, pitch, internal=false, tolerance=undef)
],
rangepts = [0.99, 1.4, 2.8, 5.6, 11.2, 22.4, 45, 90, 180, 300],
d_ind = floor(lookup(diameter,hstack(rangepts,range(len(rangepts))))),
d_ind = floor(lookup(diameter,hstack(rangepts,count(len(rangepts))))),
avgd = sqrt(rangepts[d_ind]* rangepts[d_ind+1]),
T_d2_6 = 90*pow(P, 0.4)*pow(avgd,0.1),

View file

@ -595,7 +595,7 @@ function subdivide_long_segments(path, maxlen, closed=false) =
[
for (p=pair(path,closed)) let(
steps = ceil(norm(p[1]-p[0])/maxlen)
) each lerp(p[0],p[1],[0:1/steps:1-EPSILON]),
) each lerpn(p[0], p[1], steps, false),
if (!closed) last(path)
];
@ -620,7 +620,7 @@ function slice_profiles(profiles,slices,closed=false) =
let(
count = is_num(slices) ? repeat(slices,len(profiles)-(closed?0:1)) : slices,
slicelist = [for (i=[0:len(profiles)-(closed?1:2)])
each [for(j = [0:count[i]]) lerp(profiles[i],select(profiles,i+1),j/(count[i]+1))]
each lerpn(profiles[i], select(profiles,i+1), count[i]+1, false)
]
)
concat(slicelist, closed?[]:[profiles[len(profiles)-1]]);
@ -894,7 +894,7 @@ function associate_vertices(polygons, split, curpoly=0) =
str("Split ",cursplit," at polygon ",curpoly," has invalid vertices. Must be in [0:",polylen-1,"]"))
len(cursplit)==0 ? associate_vertices(polygons,split,curpoly+1) :
let(
splitindex = sort(concat(range(polylen), cursplit)),
splitindex = sort(concat(count(polylen), cursplit)),
newpoly = [for(i=[0:len(polygons)-1]) i<=curpoly ? select(polygons[i],splitindex) : polygons[i]]
)
associate_vertices(newpoly, split, curpoly+1);

View file

@ -335,7 +335,7 @@ function _str_find_last(str,pattern,sindex) =
(sindex >=0 ? sindex : undef);
function _str_find_all(str,pattern) =
pattern == "" ? range(len(str)) :
pattern == "" ? count(len(str)) :
[for(i=[0:1:len(str)-len(pattern)]) if (_str_cmp(str,i,pattern)) i];

View file

@ -133,36 +133,13 @@ module test_repeat() {
test_repeat();
module test_range() {
assert(range(4) == [0,1,2,3]);
assert(range(n=4, step=2) == [0,2,4,6]);
assert(range(n=4, s=3, step=3) == [3,6,9,12]);
assert(range(e=3) == [0,1,2,3]);
assert(range(e=6, step=2) == [0,2,4,6]);
assert(range(s=3, e=5) == [3,4,5]);
assert(range(s=3, e=8, step=2) == [3,5,7]);
assert(range(s=4, e=8, step=2) == [4,6,8]);
assert(range(e=4, n=3) == [0,2,4]);
assert(range(n=4, s=[3,4], step=[2,3]) == [[3,4], [5,7], [7,10], [9,13]]);
module test_count() {
assert_equal(count(5), [0,1,2,3,4]);
assert_equal(count(5,3), [3,4,5,6,7]);
assert_equal(count(4,3,2), [3,5,7,9]);
assert_equal(count(5,0,0.25), [0, 0.25, 0.5, 0.75, 1.0]);
}
test_range();
module test_rangex() {
assert_equal(rangex(4), [0,1,2,3]);
assert_approx(rangex(5,e=1), [0, 0.2, 0.4, 0.6, 0.8]);
assert_equal(rangex(n=4, step=2), [0,2,4,6]);
assert_approx(rangex(n=4, step=0.25), [0,0.25,0.5,0.75]);
assert_equal(rangex(n=4, s=3, step=3), [3,6,9,12]);
assert_equal(rangex(e=3), [0,1,2]);
assert_equal(rangex(e=6, step=2), [0,2,4]);
assert_equal(rangex(s=3, e=5), [3,4]);
assert_equal(rangex(s=3, e=8, step=2), [3,5,7]);
assert_equal(rangex(s=4, e=8, step=2), [4,6]);
assert_equal(rangex(e=6, n=3), [0,2,4]);
assert_equal(rangex(n=4, s=[3,4], step=[2,3]), [[3,4], [5,7], [7,10], [9,13]]);
}
test_rangex();
test_count();
module test_reverse() {
@ -322,7 +299,7 @@ test_enumerate();
module test_shuffle() {
nums1 = [for (i=range(100)) i];
nums1 = count(100);
nums2 = shuffle(nums1,33);
nums3 = shuffle(nums2,99);
assert(sort(nums2)==nums1);

View file

@ -481,7 +481,7 @@ module test_circle_3points() {
radii = rands(10,100,count,seed_value=390);
angles = rands(0,360,count,seed_value=699);
// 2D tests.
for(i = range(count)) {
for(i = count(count)) {
cp = select(coords,i,i+1);
r = radii[i];
angs = sort(select(angles,i,i+2));
@ -506,7 +506,7 @@ module test_circle_3points() {
assert(approx(res[2], UP));
}
}
for(i = range(count)) {
for(i = count(count)) {
cp = select(coords,i,i+1);
r = radii[i];
angs = sort(select(angles,i,i+2));
@ -532,7 +532,7 @@ module test_circle_3points() {
}
}
// 3D tests.
for(i = range(count)) {
for(i = count(count)) {
cp = select(coords,i,i+2);
r = radii[i];
nrm = unit(select(coords,i+10,i+12));
@ -559,7 +559,7 @@ module test_circle_3points() {
assert(approx(res[2], n));
}
}
for(i = range(count)) {
for(i = count(count)) {
cp = select(coords,i,i+2);
r = radii[i];
nrm = unit(select(coords,i+10,i+12));
@ -987,8 +987,8 @@ module test_pointlist_bounds() {
module test_closest_point() {
ptlist = [for (i=range(100)) rands(-100,100,2,seed_value=8463)];
testpts = [for (i=range(100)) rands(-100,100,2,seed_value=6834)];
ptlist = [for (i=count(100)) rands(-100,100,2,seed_value=8463)];
testpts = [for (i=count(100)) rands(-100,100,2,seed_value=6834)];
for (pt = testpts) {
pidx = closest_point(pt,ptlist);
dists = [for (p=ptlist) norm(pt-p)];
@ -1000,8 +1000,8 @@ module test_closest_point() {
module test_furthest_point() {
ptlist = [for (i=range(100)) rands(-100,100,2,seed_value=8463)];
testpts = [for (i=range(100)) rands(-100,100,2,seed_value=6834)];
ptlist = [for (i=count(100)) rands(-100,100,2,seed_value=8463)];
testpts = [for (i=count(100)) rands(-100,100,2,seed_value=6834)];
for (pt = testpts) {
pidx = furthest_point(pt,ptlist);
dists = [for (p=ptlist) norm(pt-p)];

View file

@ -1133,7 +1133,7 @@ module test_real_roots(){
// Wilkinson polynomial is a nasty test:
assert_approx(
sort(real_roots(poly_mult([[1,-1],[1,-2],[1,-3],[1,-4],[1,-5],[1,-6],[1,-7],[1,-8],[1,-9],[1,-10]]))),
range(n=10,s=1));
count(10,1));
assert_equal(real_roots([3]), []);
assert_equal(real_roots(poly_mult([[1,-2,5],[12,-24,24],[-2, -12, -20],[1,-10,50]])),[]);
assert_equal(real_roots(poly_mult([[1,-2,5],[12,-24,24],[-2, -12, -20],[1,-10,50],[1,0,0]])),[0,0]);