diff --git a/arrays.scad b/arrays.scad index 342842b..52085b2 100644 --- a/arrays.scad +++ b/arrays.scad @@ -208,6 +208,46 @@ function deduplicate(list, closed=false, eps=EPSILON) = [for (i=[0:1:l-1]) if (i==end || list[i] != list[(i+1)%l]) list[i]]; +// Function: repeat_entries() +// Usage: +// newlist = repeat_entries(list, N) +// Description: +// Takes a list as input and duplicates some of its entries to produce a list +// with length `N`. If the requested `N` is not a multiple of the list length then +// the entries will be duplicated as uniformly as possible. You can also set `N` to a vector, +// in which case len(N) must equal len(list) and the output repeats the ith entry N[i] times. +// In either case, the result will be a list of length `N`. The `exact` option requires +// that the final length is exactly as requested. If you set it to `false` then the +// algorithm will favor uniformity and the output list may have a different number of +// entries due to rounding. +// +// When applied to a path the output path is the same geometrical shape but has some vertices +// repeated. This can be useful when you need to align paths with a different number of points. +// (See also subdivide_path for a different way to do that.) +// Arguments: +// list = list whose entries will be repeated +// N = scalar total number of points desired or vector requesting N[i] copies of vertex i. +// exact = if true return exactly the requested number of points, possibly sacrificing uniformity. If false, return uniform points that may not match the number of points requested. Default: True +// Examples: +// list = [0,1,2,3]/ +// echo(repeat_entries(list, 6)); // Ouputs [0,0,1,2,2,3] +// echo(repeat_entries(list, 6, exact=false)); // Ouputs [0,0,1,1,2,2,3,3] +// echo(repeat_entries(list, [1,1,2,1], exact=false)); // Ouputs [0,1,2,2,3] +function repeat_entries(list, N, exact = true) = + assert(is_list(list)) + assert((is_num(N) && N>0) || is_vector(N),"Parameter N to repeat_entries must be postive number or vector") + let( + length = len(list), + reps_guess = is_list(N) ? + assert(len(N)==len(list), "Vector parameter N to repeat_entries has the wrong length") + N + : replist(N/length,length), + reps = exact ? _sum_preserving_round(reps_guess) + : [for (val=reps_guess) round(val)] + ) + [for(i=[0:length-1]) each replist(list[i],reps[i])]; + + // Function: list_set() // Usage: // list_set(list, indices, values, [dflt], [minlen]) diff --git a/math.scad b/math.scad index 2b97b30..caa8159 100644 --- a/math.scad +++ b/math.scad @@ -648,5 +648,50 @@ function count_true(l, nmax=undef, i=0, cnt=0) = ) ); +// If argument is a list return it. Otherwise return a singleton list containing the argument. +function _force_list(x) = is_list(x) ? x : [x]; + +// Function: gcd() +// Usage: +// gcd(a,b) +// Description: +// Computes the greatest common divisor of `a` and `b`. +function gcd(a,b) = + assert(is_integer(a) && is_integer(b),"Arguments to gcd must be integers") + b==0 ? abs(a) : gcd(b,a % b); + +// Computes lcm for two scalars +function _lcm(a,b) = + let( + parmok = is_integer(a) && is_integer(b), + dummy=assert(parmok,"Invalid non-integer parameters to lcm") + assert(a!=0 && b!=0, "Arguments to lcm must be nonzero") + ) + abs(a*b) / gcd(a,b); + +// Computes lcm for a list of values +function _lcmlist(a) = + len(a)==1 ? a[0] : _lcmlist(concat(slice(a,0,len(a)-2),[lcm(a[len(a)-2],a[len(a)-1])])); + +// Function: lcm() +// Usage: +// lcm(a,b) +// lcm(list) +// Description: Computes the least common multiple of the two arguments or a list of arguments. Inputs should be +// nonzero integers. The output is always a positive integer. It is an error to pass zero as an argument. +function lcm(a,b=[]) = + !is_list(a) && !is_list(b) ? _lcm(a,b) : + let( + arglist = concat(_force_list(a),_force_list(b)) + ) + assert(len(arglist)>0,"invalid call to lcm with empty list(s)") + _lcmlist(arglist); + +// Function: is_integer() +// Usage: +// is_integer(n) +// Description: returns true if the given value is an integer (it is a number and it rounds to itself). +function is_integer(n) = is_num(n) && n == round(n); + // vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/shapes2d.scad b/shapes2d.scad index 580b887..d98613a 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -645,6 +645,9 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // for(i=[-1.5:3:1.5]) right(i*1.5) supershape(m1=2,m2=10,n1=i,n2=1); // for(i=[1:3],j=[-1,1]) translate([3.5*i,1.5*j])supershape(m1=4,m2=6,n1=i*j,n2=1); // for(i=[1:3]) right(2.5*i)supershape(step=.5,m1=88, m2=64, n1=-i*i,n2=1,r=1); +// Examples: +// linear_extrude(height=0.3, scale=0) supershape(step=1, m1=6, n1=0.4, n2=0, n3=6); +// linear_extrude(height=5, scale=0) supershape(step=1, b=3, m1=6, n1=3.8, n2=16, n3=10); function supershape(step=0.5,m1=4,m2=undef,n1=1,n2=undef,n3=undef,a=1,b=undef,r=undef,d=undef,anchor=CENTER, spin=0) = let( r = get_radius(r=r, d=d, dflt=undef),