Merge pull request #814 from adrianVmariano/master

color functions to color.scad
This commit is contained in:
Revar Desmera 2022-03-27 20:28:01 -07:00 committed by GitHub
commit 5cb91ac860
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1597 additions and 1536 deletions

View file

@ -819,62 +819,6 @@ module hulling(a)
}
// Module: recolor()
// Usage:
// recolor([c]) {...}
// Topics: Attachments
// See Also: color_this(), tags(), hide(), show(), diff(), intersect()
// Description:
// Sets the color for children and all their descendants. This only works with attachables and you cannot
// have any color() modules above it in any parents, only other recolor() or color_this() modules.
// This works by setting the special `$color` variable.
// For a more step-by-step explanation of attachments, see the [[Attachments Tutorial|Tutorial-Attachments]].
// Arguments:
// c = Color name or RGBA vector. Default: The default color in your color scheme.
// Example:
// cuboid([10,10,5])
// recolor("green")attach(TOP,BOT) cuboid([9,9,4.5])
// attach(TOP,BOT) cuboid([8,8,4])
// recolor("purple") attach(TOP,BOT) cuboid([7,7,3.5])
// attach(TOP,BOT) cuboid([6,6,3])
// recolor("cyan")attach(TOP,BOT) cuboid([5,5,2.5])
// attach(TOP,BOT) cuboid([4,4,2]);
module recolor(c="default")
{
$color=c;
children();
}
// Module: color_this()
// Usage:
// color_this([c]) {...}
// Topics: Attachments
// See Also: tags(), recolor()
// Description:
// Sets the color for children at one level, reverting to the previous color for further descendants.
// This works only with attachables and you cannot have any color() modules above it in any parents,
// only recolor() or other color_this() modules. This works using the `$color` and `$save_color` variables.
// .
// For a more step-by-step explanation of attachments, see the [[Attachments Tutorial|Tutorial-Attachments]].
// Arguments:
// c = Color name or RGBA vector. Default: the default color in your color scheme
// Example:
// cuboid([10,10,5])
// color_this("green")attach(TOP,BOT) cuboid([9,9,4.5])
// attach(TOP,BOT) cuboid([8,8,4])
// color_this("purple") attach(TOP,BOT) cuboid([7,7,3.5])
// attach(TOP,BOT) cuboid([6,6,3])
// color_this("cyan")attach(TOP,BOT) cuboid([5,5,2.5])
// attach(TOP,BOT) cuboid([4,4,2]);
module color_this(c="default")
{
$save_color=default($color,"default");
$color=c;
children();
}
// Module: hide()
// Usage:
// hide(tags) {...}
@ -1262,6 +1206,8 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) {
// `$parent_orient` is set to the parent object's `orient` value.
// `$parent_geom` is set to the parent object's `geom` value.
// `$parent_size` is set to the parent object's cubical `[X,Y,Z]` volume size.
// `$color` is used to set the color of the object
// `$save_color` is used to revert color to the parent's color
//
// Example(NORENDER): Cubical Shape
// attachable(anchor, spin, orient, size=size) {

View file

@ -11,15 +11,69 @@
use <builtins.scad>
// Section: Coloring Objects
// Module: recolor()
// Usage:
// recolor([c]) {...}
// Topics: Attachments
// See Also: color_this()
// Description:
// Sets the color for attachable children and all their descendants. This only works with attachables and you cannot
// have any color() modules above it in any parents, only other recolor() or color_this() modules.
// This works by setting the special `$color` variable, which attachable objects make use of to set the color.
// Arguments:
// c = Color name or RGBA vector. Default: The default color in your color scheme.
// Example:
// cuboid([10,10,5])
// recolor("green")attach(TOP,BOT) cuboid([9,9,4.5])
// attach(TOP,BOT) cuboid([8,8,4])
// recolor("purple") attach(TOP,BOT) cuboid([7,7,3.5])
// attach(TOP,BOT) cuboid([6,6,3])
// recolor("cyan")attach(TOP,BOT) cuboid([5,5,2.5])
// attach(TOP,BOT) cuboid([4,4,2]);
module recolor(c="default")
{
$color=c;
children();
}
// Module: color_this()
// Usage:
// color_this([c]) {...}
// Topics: Attachments
// See Also: recolor()
// Description:
// Sets the color for children at one level, reverting to the previous color for further descendants.
// This works only with attachables and you cannot have any color() modules above it in any parents,
// only recolor() or other color_this() modules. This works using the `$color` and `$save_color` variables,
// which attachable objects make use of to set the color.
// Arguments:
// c = Color name or RGBA vector. Default: the default color in your color scheme
// Example:
// cuboid([10,10,5])
// color_this("green")attach(TOP,BOT) cuboid([9,9,4.5])
// attach(TOP,BOT) cuboid([8,8,4])
// color_this("purple") attach(TOP,BOT) cuboid([7,7,3.5])
// attach(TOP,BOT) cuboid([6,6,3])
// color_this("cyan")attach(TOP,BOT) cuboid([5,5,2.5])
// attach(TOP,BOT) cuboid([4,4,2]);
module color_this(c="default")
{
$save_color=default($color,"default");
$color=c;
children();
}
// Module: rainbow()
// Usage:
// rainbow(list) ...
// Description:
// Iterates the list, displaying children in different colors for each list item.
// This is useful for debugging lists of paths and such.
// Iterates the list, displaying children in different colors for each list item. The color
// is set using the color() module, so this module is not compatible with {{recolor()}} or
// {{color_this()}}. This is useful for debugging regions or lists of paths.
// Arguments:
// list = The list of items to iterate through.
// stride = Consecutive colors stride around the color wheel divided into this many parts.
@ -44,62 +98,66 @@ module rainbow(list, stride=1, maxhues, shuffle=false, seed)
hues = shuffle ? shuffle(huelist, seed=seed) : huelist;
for($idx=idx(list)) {
$item = list[$idx];
HSV(h=hues[$idx]) children();
hsv(h=hues[$idx]) children();
}
}
// Section: Colorspace Conversion
// Function&Module: HSL()
// Function&Module: hsl()
// Usage:
// HSL(h,[s],[l],[a]) ...
// rgb = HSL(h,[s],[l]);
// hsl(h,[s],[l],[a]) ...
// rgb = hsl(h,[s],[l],[a]);
// Description:
// When called as a function, returns the [R,G,B] color for the given hue `h`, saturation `s`, and lightness `l` from the HSL colorspace.
// When called as a module, sets the color to the given hue `h`, saturation `s`, and lightness `l` from the HSL colorspace.
// When called as a function, returns the [R,G,B] color for the given hue `h`, saturation `s`, and lightness `l` from the HSL colorspace. If you supply
// the `a` value then you'll get a length 4 list [R,G,B,A].
// When called as a module, sets the color using the color() module to the given hue `h`, saturation `s`, and lightness `l` from the HSL colorspace.
// Arguments:
// h = The hue, given as a value between 0 and 360. 0=red, 60=yellow, 120=green, 180=cyan, 240=blue, 300=magenta.
// s = The saturation, given as a value between 0 and 1. 0 = grayscale, 1 = vivid colors. Default: 1
// l = The lightness, between 0 and 1. 0 = black, 0.5 = bright colors, 1 = white. Default: 0.5
// a = When called as a module, specifies the alpha channel as a value between 0 and 1. 0 = fully transparent, 1=opaque. Default: 1
// a = Specifies the alpha channel as a value between 0 and 1. 0 = fully transparent, 1=opaque. Default: 1
// Example:
// HSL(h=120,s=1,l=0.5) sphere(d=60);
// hsl(h=120,s=1,l=0.5) sphere(d=60);
// Example:
// rgb = HSL(h=270,s=0.75,l=0.6);
// rgb = hsl(h=270,s=0.75,l=0.6);
// color(rgb) cube(60, center=true);
function HSL(h,s=1,l=0.5) =
function hsl(h,s=1,l=0.5,a) =
let(
h=posmod(h,360)
) [
for (n=[0,8,4]) let(
k=(n+h/30)%12
) l - s*min(l,1-l)*max(min(k-3,9-k,1),-1)
for (n=[0,8,4])
let(k=(n+h/30)%12)
l - s*min(l,1-l)*max(min(k-3,9-k,1),-1),
if (is_def(a)) a
];
module HSL(h,s=1,l=0.5,a=1) color(HSL(h,s,l),a) children();
module hsl(h,s=1,l=0.5,a=1) color(hsl(h,s,l),a) children();
// Function&Module: HSV()
// Function&Module: hsv()
// Usage:
// HSV(h,[s],[v],[a]) ...
// rgb = HSV(h,[s],[v]);
// hsv(h,[s],[v],[a]) ...
// rgb = hsv(h,[s],[v],[a]);
// Description:
// When called as a function, returns the [R,G,B] color for the given hue `h`, saturation `s`, and value `v` from the HSV colorspace.
// When called as a module, sets the color to the given hue `h`, saturation `s`, and value `v` from the HSV colorspace.
// When called as a function, returns the [R,G,B] color for the given hue `h`, saturation `s`, and value `v` from the HSV colorspace. If you supply
// the `a` value then you'll get a length 4 list [R,G,B,A].
// When called as a module, sets the color using the color() module to the given hue `h`, saturation `s`, and value `v` from the HSV colorspace.
// Arguments:
// h = The hue, given as a value between 0 and 360. 0=red, 60=yellow, 120=green, 180=cyan, 240=blue, 300=magenta.
// s = The saturation, given as a value between 0 and 1. 0 = grayscale, 1 = vivid colors. Default: 1
// v = The value, between 0 and 1. 0 = darkest black, 1 = bright. Default: 1
// a = When called as a module, specifies the alpha channel as a value between 0 and 1. 0 = fully transparent, 1=opaque. Default: 1
// a = Specifies the alpha channel as a value between 0 and 1. 0 = fully transparent, 1=opaque. Default: 1
// Example:
// HSV(h=120,s=1,v=1) sphere(d=60);
// hsv(h=120,s=1,v=1) sphere(d=60);
// Example:
// rgb = HSV(h=270,s=0.75,v=0.9);
// rgb = hsv(h=270,s=0.75,v=0.9);
// color(rgb) cube(60, center=true);
function HSV(h,s=1,v=1) =
function hsv(h,s=1,v=1,a) =
assert(s>=0 && s<=1)
assert(v>=0 && v<=1)
assert(is_undef(a) || a>=0 && a<=1)
let(
h = posmod(h,360),
c = v * s,
@ -114,9 +172,10 @@ function HSV(h,s=1,v=1) =
: [0,0,0],
m=v-c
)
rgbprime+[m,m,m];
is_def(a) ? point4d(add_scalar(rgbprime,m),a)
: add_scalar(rgbprime,m);
module HSV(h,s=1,v=1,a=1) color(HSV(h,s,v),a) children();
module hsv(h,s=1,v=1,a=1) color(hsv(h,s,v),a) children();

1534
gears.scad

File diff suppressed because it is too large Load diff

View file

@ -377,29 +377,6 @@ function repeat(val, n, i=0) =
[for (j=[1:1:n[i]]) repeat(val, n, i+1)];
// Function: count()
// Usage:
// list = count(n, [s], [step], [reverse]);
// Description:
// Creates a list of `n` numbers, starting at `s`, incrementing by `step` each time.
// You can also pass a list for n and then the length of the input list is used.
// Arguments:
// n = The length of the list of numbers to create, or a list to match the length of
// s = The starting value of the list of numbers.
// step = The amount to increment successive numbers in the list.
// reverse = Reverse the list. Default: false.
// See Also: idx()
// Example:
// 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]
// nl4 = count(5,reverse=true); // Returns: [4,3,2,1,0]
// nl5 = count(5,3,reverse=true); // Returns: [7,6,5,4,3]
function count(n,s=0,step=1,reverse=false) = let(n=is_list(n) ? len(n) : n)
reverse? [for (i=[n-1:-1:0]) s+i*step]
: [for (i=[0:1:n-1]) s+i*step];
// Function: list_bset()
// Usage:
@ -967,7 +944,7 @@ function permutations(l,n=2) =
// Section: Changing list structure
// Section: Changing List Structure
// Function: list_to_matrix()

864
math.scad
View file

@ -1,10 +1,13 @@
//////////////////////////////////////////////////////////////////////
// LibFile: math.scad
// Math helper functions.
// Assorted math functions, including linear interpolation, list operations (sums, mean, products),
// convolution, quantization, log2, hyperbolic trig functions, random numbers, derivatives,
// polynomials, and root finding.
// Includes:
// include <BOSL2/std.scad>
// FileGroup: Math
// FileSummary: General miscellaneous math function.
// FileSummary: Math on lists, special functions, quantization, random numbers, calculus, root finding
//
// FileFootnotes: STD=Included in std.scad
//////////////////////////////////////////////////////////////////////
@ -28,7 +31,97 @@ NAN = acos(2);
// Section: Simple math
// Section: Interpolation and Counting
// Function: count()
// Usage:
// list = count(n, [s], [step], [reverse]);
// Description:
// Creates a list of `n` numbers, starting at `s`, incrementing by `step` each time.
// You can also pass a list for n and then the length of the input list is used.
// Arguments:
// n = The length of the list of numbers to create, or a list to match the length of
// s = The starting value of the list of numbers.
// step = The amount to increment successive numbers in the list.
// reverse = Reverse the list. Default: false.
// See Also: idx()
// Example:
// 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]
// nl4 = count(5,reverse=true); // Returns: [4,3,2,1,0]
// nl5 = count(5,3,reverse=true); // Returns: [7,6,5,4,3]
function count(n,s=0,step=1,reverse=false) = let(n=is_list(n) ? len(n) : n)
reverse? [for (i=[n-1:-1:0]) s+i*step]
: [for (i=[0:1:n-1]) s+i*step];
// Function: lerp()
// Usage:
// x = lerp(a, b, u);
// l = lerp(a, b, LIST);
// Description:
// Interpolate between two values or vectors.
// If `u` is given as a number, returns the single interpolated value.
// If `u` is 0.0, then the value of `a` is returned.
// If `u` is 1.0, then the value of `b` is returned.
// If `u` is a range, or list of numbers, returns a list of interpolated values.
// It is valid to use a `u` value outside the range 0 to 1. The result will be an extrapolation
// along the slope formed by `a` and `b`.
// Arguments:
// a = First value or vector.
// b = Second value or vector.
// u = The proportion from `a` to `b` to calculate. Standard range is 0.0 to 1.0, inclusive. If given as a list or range of values, returns a list of results.
// Example:
// x = lerp(0,20,0.3); // Returns: 6
// x = lerp(0,20,0.8); // Returns: 16
// x = lerp(0,20,-0.1); // Returns: -2
// x = lerp(0,20,1.1); // Returns: 22
// p = lerp([0,0],[20,10],0.25); // Returns [5,2.5]
// l = lerp(0,20,[0.4,0.6]); // Returns: [8,12]
// l = lerp(0,20,[0.25:0.25:0.75]); // Returns: [5,10,15]
// Example(2D):
// p1 = [-50,-20]; p2 = [50,30];
// stroke([p1,p2]);
// pts = lerp(p1, p2, [0:1/8:1]);
// // Points colored in ROYGBIV order.
// rainbow(pts) translate($item) circle(d=3,$fn=8);
function lerp(a,b,u) =
assert(same_shape(a,b), "Bad or inconsistent inputs to lerp")
is_finite(u)? (1-u)*a + u*b :
assert(is_finite(u) || is_vector(u) || valid_range(u), "Input u to lerp must be a number, vector, or valid range.")
[for (v = u) (1-v)*a + v*b ];
// Function: lerpn()
// Usage:
// x = lerpn(a, b, n);
// x = lerpn(a, b, n, [endpoint]);
// Description:
// Returns exactly `n` values, linearly interpolated between `a` and `b`.
// If `endpoint` is true, then the last value will exactly equal `b`.
// If `endpoint` is false, then the last value will about `a+(b-a)*(1-1/n)`.
// Arguments:
// a = First value or vector.
// b = Second value or vector.
// n = The number of values to return.
// endpoint = If true, the last value will be exactly `b`. If false, the last value will be one step less.
// Example:
// l = lerpn(-4,4,9); // Returns: [-4,-3,-2,-1,0,1,2,3,4]
// l = lerpn(-4,4,8,false); // Returns: [-4,-3,-2,-1,0,1,2,3]
// l = lerpn(0,1,6); // Returns: [0, 0.2, 0.4, 0.6, 0.8, 1]
// l = lerpn(0,1,5,false); // Returns: [0, 0.2, 0.4, 0.6, 0.8]
function lerpn(a,b,n,endpoint=true) =
assert(same_shape(a,b), "Bad or inconsistent inputs to lerpn")
assert(is_int(n))
assert(is_bool(endpoint))
let( d = n - (endpoint? 1 : 0) )
[for (i=[0:1:n-1]) let(u=i/d) (1-u)*a + u*b];
// Section: Miscellaneous Functions
// Function: sqr()
// Usage:
@ -139,124 +232,45 @@ function binomial_coefficient(n,k) =
b[len(b)-1];
// Function: lerp()
// Function: gcd()
// Usage:
// x = lerp(a, b, u);
// l = lerp(a, b, LIST);
// x = gcd(a,b)
// Description:
// Interpolate between two values or vectors.
// If `u` is given as a number, returns the single interpolated value.
// If `u` is 0.0, then the value of `a` is returned.
// If `u` is 1.0, then the value of `b` is returned.
// If `u` is a range, or list of numbers, returns a list of interpolated values.
// It is valid to use a `u` value outside the range 0 to 1. The result will be an extrapolation
// along the slope formed by `a` and `b`.
// Arguments:
// a = First value or vector.
// b = Second value or vector.
// u = The proportion from `a` to `b` to calculate. Standard range is 0.0 to 1.0, inclusive. If given as a list or range of values, returns a list of results.
// Example:
// x = lerp(0,20,0.3); // Returns: 6
// x = lerp(0,20,0.8); // Returns: 16
// x = lerp(0,20,-0.1); // Returns: -2
// x = lerp(0,20,1.1); // Returns: 22
// p = lerp([0,0],[20,10],0.25); // Returns [5,2.5]
// l = lerp(0,20,[0.4,0.6]); // Returns: [8,12]
// l = lerp(0,20,[0.25:0.25:0.75]); // Returns: [5,10,15]
// Example(2D):
// p1 = [-50,-20]; p2 = [50,30];
// stroke([p1,p2]);
// pts = lerp(p1, p2, [0:1/8:1]);
// // Points colored in ROYGBIV order.
// rainbow(pts) translate($item) circle(d=3,$fn=8);
function lerp(a,b,u) =
assert(same_shape(a,b), "Bad or inconsistent inputs to lerp")
is_finite(u)? (1-u)*a + u*b :
assert(is_finite(u) || is_vector(u) || valid_range(u), "Input u to lerp must be a number, vector, or valid range.")
[for (v = u) (1-v)*a + v*b ];
// Computes the Greatest Common Divisor/Factor of `a` and `b`.
function gcd(a,b) =
assert(is_int(a) && is_int(b),"Arguments to gcd must be integers")
b==0 ? abs(a) : gcd(b,a % b);
// Function: lerpn()
// Computes lcm for two integers
function _lcm(a,b) =
assert(is_int(a) && is_int(b), "Invalid non-integer parameters to lcm")
assert(a!=0 && b!=0, "Arguments to lcm should not be zero")
abs(a*b) / gcd(a,b);
// Computes lcm for a list of values
function _lcmlist(a) =
len(a)==1 ? a[0] :
_lcmlist(concat(lcm(a[0],a[1]),list_tail(a,2)));
// Function: lcm()
// Usage:
// x = lerpn(a, b, n);
// x = lerpn(a, b, n, [endpoint]);
// div = lcm(a, b);
// divs = lcm(list);
// Description:
// Returns exactly `n` values, linearly interpolated between `a` and `b`.
// If `endpoint` is true, then the last value will exactly equal `b`.
// If `endpoint` is false, then the last value will about `a+(b-a)*(1-1/n)`.
// Arguments:
// a = First value or vector.
// b = Second value or vector.
// n = The number of values to return.
// endpoint = If true, the last value will be exactly `b`. If false, the last value will be one step less.
// Example:
// l = lerpn(-4,4,9); // Returns: [-4,-3,-2,-1,0,1,2,3,4]
// l = lerpn(-4,4,8,false); // Returns: [-4,-3,-2,-1,0,1,2,3]
// l = lerpn(0,1,6); // Returns: [0, 0.2, 0.4, 0.6, 0.8, 1]
// l = lerpn(0,1,5,false); // Returns: [0, 0.2, 0.4, 0.6, 0.8]
function lerpn(a,b,n,endpoint=true) =
assert(same_shape(a,b), "Bad or inconsistent inputs to lerpn")
assert(is_int(n))
assert(is_bool(endpoint))
let( d = n - (endpoint? 1 : 0) )
[for (i=[0:1:n-1]) let(u=i/d) (1-u)*a + u*b];
// Computes the Least Common Multiple of the two arguments or a list of arguments. Inputs should
// be non-zero 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);
// Section: Undef Safe Math
// Function: u_add()
// Usage:
// x = u_add(a, b);
// Description:
// Adds `a` to `b`, returning the result, or undef if either value is `undef`.
// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
// Arguments:
// a = First value.
// b = Second value.
function u_add(a,b) = is_undef(a) || is_undef(b)? undef : a + b;
// Function: u_sub()
// Usage:
// x = u_sub(a, b);
// Description:
// Subtracts `b` from `a`, returning the result, or undef if either value is `undef`.
// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
// Arguments:
// a = First value.
// b = Second value.
function u_sub(a,b) = is_undef(a) || is_undef(b)? undef : a - b;
// Function: u_mul()
// Usage:
// x = u_mul(a, b);
// Description:
// Multiplies `a` by `b`, returning the result, or undef if either value is `undef`.
// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
// Arguments:
// a = First value.
// b = Second value.
function u_mul(a,b) =
is_undef(a) || is_undef(b)? undef :
is_vector(a) && is_vector(b)? v_mul(a,b) :
a * b;
// Function: u_div()
// Usage:
// x = u_div(a, b);
// Description:
// Divides `a` by `b`, returning the result, or undef if either value is `undef`.
// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
// Arguments:
// a = First value.
// b = Second value.
function u_div(a,b) =
is_undef(a) || is_undef(b)? undef :
is_vector(a) && is_vector(b)? v_div(a,b) :
a / b;
// Section: Hyperbolic Trigonometry
@ -488,6 +502,228 @@ function modang(x) =
let(xx = posmod(x,360)) xx<180? xx : xx-360;
// Section: Operations on Lists (Sums, Mean, Products)
// Function: sum()
// Usage:
// x = sum(v, [dflt]);
// Description:
// Returns the sum of all entries in the given consistent list.
// If passed an array of vectors, returns the sum the vectors.
// If passed an array of matrices, returns the sum of the matrices.
// If passed an empty list, the value of `dflt` will be returned.
// Arguments:
// v = The list to get the sum of.
// dflt = The default value to return if `v` is an empty list. Default: 0
// Example:
// sum([1,2,3]); // returns 6.
// sum([[1,2,3], [3,4,5], [5,6,7]]); // returns [9, 12, 15]
function sum(v, dflt=0) =
v==[]? dflt :
assert(is_consistent(v), "Input to sum is non-numeric or inconsistent")
is_finite(v[0]) || is_vector(v[0]) ? [for(i=v) 1]*v :
_sum(v,v[0]*0);
function _sum(v,_total,_i=0) = _i>=len(v) ? _total : _sum(v,_total+v[_i], _i+1);
// Function: mean()
// Usage:
// x = mean(v);
// Description:
// Returns the arithmetic mean/average of all entries in the given array.
// If passed a list of vectors, returns a vector of the mean of each part.
// Arguments:
// v = The list of values to get the mean of.
// Example:
// mean([2,3,4]); // returns 3.
// mean([[1,2,3], [3,4,5], [5,6,7]]); // returns [3, 4, 5]
function mean(v) =
assert(is_list(v) && len(v)>0, "Invalid list.")
sum(v)/len(v);
// Function: median()
// Usage:
// middle = median(v)
// Description:
// Returns the median of the given vector.
function median(v) =
assert(is_vector(v), "Input to median must be a vector")
len(v)%2 ? max( list_smallest(v, ceil(len(v)/2)) ) :
let( lowest = list_smallest(v, len(v)/2 + 1),
max = max(lowest),
imax = search(max,lowest,1),
max2 = max([for(i=idx(lowest)) if(i!=imax[0]) lowest[i] ])
)
(max+max2)/2;
// Function: deltas()
// Usage:
// delts = deltas(v);
// Description:
// Returns a list with the deltas of adjacent entries in the given list, optionally wrapping back to the front.
// The list should be a consistent list of numeric components (numbers, vectors, matrix, etc).
// Given [a,b,c,d], returns [b-a,c-b,d-c].
//
// Arguments:
// v = The list to get the deltas of.
// wrap = If true, wrap back to the start from the end. ie: return the difference between the last and first items as the last delta. Default: false
// Example:
// deltas([2,5,9,17]); // returns [3,4,8].
// deltas([[1,2,3], [3,6,8], [4,8,11]]); // returns [[2,4,5], [1,2,3]]
function deltas(v, wrap=false) =
assert( is_consistent(v) && len(v)>1 , "Inconsistent list or with length<=1.")
[for (p=pair(v,wrap)) p[1]-p[0]] ;
// Function: cumsum()
// Usage:
// sums = cumsum(v);
// Description:
// Returns a list where each item is the cumulative sum of all items up to and including the corresponding entry in the input list.
// If passed an array of vectors, returns a list of cumulative vectors sums.
// Arguments:
// v = The list to get the sum of.
// Example:
// cumsum([1,1,1]); // returns [1,2,3]
// cumsum([2,2,2]); // returns [2,4,6]
// cumsum([1,2,3]); // returns [1,3,6]
// cumsum([[1,2,3], [3,4,5], [5,6,7]]); // returns [[1,2,3], [4,6,8], [9,12,15]]
function cumsum(v) =
assert(is_consistent(v), "The input is not consistent." )
len(v)<=1 ? v :
_cumsum(v,_i=1,_acc=[v[0]]);
function _cumsum(v,_i=0,_acc=[]) =
_i>=len(v) ? _acc :
_cumsum( v, _i+1, [ each _acc, _acc[len(_acc)-1] + v[_i] ] );
// Function: product()
// Usage:
// x = product(v);
// Description:
// Returns the product of all entries in the given list.
// If passed a list of vectors of same dimension, returns a vector of products of each part.
// If passed a list of square matrices, returns the resulting product matrix.
// Arguments:
// v = The list to get the product of.
// Example:
// product([2,3,4]); // returns 24.
// product([[1,2,3], [3,4,5], [5,6,7]]); // returns [15, 48, 105]
function product(v) =
assert( is_vector(v) || is_matrix(v) || ( is_matrix(v[0],square=true) && is_consistent(v)),
"Invalid input.")
_product(v, 1, v[0]);
function _product(v, i=0, _tot) =
i>=len(v) ? _tot :
_product( v,
i+1,
( is_vector(v[i])? v_mul(_tot,v[i]) : _tot*v[i] ) );
// Function: cumprod()
// Description:
// Returns a list where each item is the cumulative product of all items up to and including the corresponding entry in the input list.
// If passed an array of vectors, returns a list of elementwise vector products. If passed a list of square matrices returns matrix
// products multiplying on the left, so a list `[A,B,C]` will produce the output `[A,BA,CBA]`.
// Arguments:
// list = The list to get the product of.
// Example:
// cumprod([1,3,5]); // returns [1,3,15]
// cumprod([2,2,2]); // returns [2,4,8]
// cumprod([[1,2,3], [3,4,5], [5,6,7]])); // returns [[1, 2, 3], [3, 8, 15], [15, 48, 105]]
function cumprod(list) =
is_vector(list) ? _cumprod(list) :
assert(is_consistent(list), "Input must be a consistent list of scalars, vectors or square matrices")
is_matrix(list[0]) ? assert(len(list[0])==len(list[0][0]), "Matrices must be square") _cumprod(list)
: _cumprod_vec(list);
function _cumprod(v,_i=0,_acc=[]) =
_i==len(v) ? _acc :
_cumprod(
v, _i+1,
concat(
_acc,
[_i==0 ? v[_i] : v[_i]*_acc[len(_acc)-1]]
)
);
function _cumprod_vec(v,_i=0,_acc=[]) =
_i==len(v) ? _acc :
_cumprod_vec(
v, _i+1,
concat(
_acc,
[_i==0 ? v[_i] : v_mul(_acc[len(_acc)-1],v[_i])]
)
);
// Function: convolve()
// Usage:
// x = convolve(p,q);
// Description:
// Given two vectors, or one vector and a path or
// two paths of the same dimension, finds the convolution of them.
// If both parameter are vectors, returns the vector convolution.
// If one parameter is a vector and the other a path,
// convolves using products by scalars and returns a path.
// If both parameters are paths, convolve using scalar products
// and returns a vector.
// The returned vector or path has length len(p)+len(q)-1.
// Arguments:
// p = The first vector or path.
// q = The second vector or path.
// Example:
// a = convolve([1,1],[1,2,1]); // Returns: [1,3,3,1]
// b = convolve([1,2,3],[1,2,1])); // Returns: [1,4,8,8,3]
// c = convolve([[1,1],[2,2],[3,1]],[1,2,1])); // Returns: [[1,1],[4,4],[8,6],[8,4],[3,1]]
// d = convolve([[1,1],[2,2],[3,1]],[[1,2],[2,1]])); // Returns: [3,9,11,7]
function convolve(p,q) =
p==[] || q==[] ? [] :
assert( (is_vector(p) || is_matrix(p))
&& ( is_vector(q) || (is_matrix(q) && ( !is_vector(p[0]) || (len(p[0])==len(q[0])) ) ) ) ,
"The inputs should be vectors or paths all of the same dimension.")
let( n = len(p),
m = len(q))
[for(i=[0:n+m-2], k1 = max(0,i-n+1), k2 = min(i,m-1) )
sum([for(j=[k1:k2]) p[i-j]*q[j] ])
];
// Function: sum_of_sines()
// Usage:
// sum_of_sines(a,sines)
// Description:
// Gives the sum of a series of sines, at a given angle.
// Arguments:
// a = Angle to get the value for.
// sines = List of [amplitude, frequency, offset] items, where the frequency is the number of times the cycle repeats around the circle.
// Example:
// v = sum_of_sines(30, [[10,3,0], [5,5.5,60]]);
function sum_of_sines(a, sines) =
assert( is_finite(a) && is_matrix(sines,undef,3), "Invalid input.")
sum([ for (s = sines)
let(
ss=point3d(s),
v=ss[0]*sin(a*ss[1]+ss[2])
) v
]);
// Section: Random Number Generation
// Function: rand_int()
@ -635,407 +871,6 @@ function random_polygon(n=3,size=1, seed) =
// Section: GCD/GCF, LCM
// Function: gcd()
// Usage:
// x = gcd(a,b)
// Description:
// Computes the Greatest Common Divisor/Factor of `a` and `b`.
function gcd(a,b) =
assert(is_int(a) && is_int(b),"Arguments to gcd must be integers")
b==0 ? abs(a) : gcd(b,a % b);
// Computes lcm for two integers
function _lcm(a,b) =
assert(is_int(a) && is_int(b), "Invalid non-integer parameters to lcm")
assert(a!=0 && b!=0, "Arguments to lcm should not be zero")
abs(a*b) / gcd(a,b);
// Computes lcm for a list of values
function _lcmlist(a) =
len(a)==1 ? a[0] :
_lcmlist(concat(lcm(a[0],a[1]),list_tail(a,2)));
// Function: lcm()
// Usage:
// div = lcm(a, b);
// divs = lcm(list);
// Description:
// Computes the Least Common Multiple of the two arguments or a list of arguments. Inputs should
// be non-zero 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);
// Section: Sums, Products, Aggregate Functions.
// Function: sum()
// Usage:
// x = sum(v, [dflt]);
// Description:
// Returns the sum of all entries in the given consistent list.
// If passed an array of vectors, returns the sum the vectors.
// If passed an array of matrices, returns the sum of the matrices.
// If passed an empty list, the value of `dflt` will be returned.
// Arguments:
// v = The list to get the sum of.
// dflt = The default value to return if `v` is an empty list. Default: 0
// Example:
// sum([1,2,3]); // returns 6.
// sum([[1,2,3], [3,4,5], [5,6,7]]); // returns [9, 12, 15]
function sum(v, dflt=0) =
v==[]? dflt :
assert(is_consistent(v), "Input to sum is non-numeric or inconsistent")
is_finite(v[0]) || is_vector(v[0]) ? [for(i=v) 1]*v :
_sum(v,v[0]*0);
function _sum(v,_total,_i=0) = _i>=len(v) ? _total : _sum(v,_total+v[_i], _i+1);
// Function: cumsum()
// Usage:
// sums = cumsum(v);
// Description:
// Returns a list where each item is the cumulative sum of all items up to and including the corresponding entry in the input list.
// If passed an array of vectors, returns a list of cumulative vectors sums.
// Arguments:
// v = The list to get the sum of.
// Example:
// cumsum([1,1,1]); // returns [1,2,3]
// cumsum([2,2,2]); // returns [2,4,6]
// cumsum([1,2,3]); // returns [1,3,6]
// cumsum([[1,2,3], [3,4,5], [5,6,7]]); // returns [[1,2,3], [4,6,8], [9,12,15]]
function cumsum(v) =
assert(is_consistent(v), "The input is not consistent." )
len(v)<=1 ? v :
_cumsum(v,_i=1,_acc=[v[0]]);
function _cumsum(v,_i=0,_acc=[]) =
_i>=len(v) ? _acc :
_cumsum( v, _i+1, [ each _acc, _acc[len(_acc)-1] + v[_i] ] );
// Function: sum_of_sines()
// Usage:
// sum_of_sines(a,sines)
// Description:
// Gives the sum of a series of sines, at a given angle.
// Arguments:
// a = Angle to get the value for.
// sines = List of [amplitude, frequency, offset] items, where the frequency is the number of times the cycle repeats around the circle.
// Example:
// v = sum_of_sines(30, [[10,3,0], [5,5.5,60]]);
function sum_of_sines(a, sines) =
assert( is_finite(a) && is_matrix(sines,undef,3), "Invalid input.")
sum([ for (s = sines)
let(
ss=point3d(s),
v=ss[0]*sin(a*ss[1]+ss[2])
) v
]);
// Function: deltas()
// Usage:
// delts = deltas(v);
// Description:
// Returns a list with the deltas of adjacent entries in the given list, optionally wrapping back to the front.
// The list should be a consistent list of numeric components (numbers, vectors, matrix, etc).
// Given [a,b,c,d], returns [b-a,c-b,d-c].
//
// Arguments:
// v = The list to get the deltas of.
// wrap = If true, wrap back to the start from the end. ie: return the difference between the last and first items as the last delta. Default: false
// Example:
// deltas([2,5,9,17]); // returns [3,4,8].
// deltas([[1,2,3], [3,6,8], [4,8,11]]); // returns [[2,4,5], [1,2,3]]
function deltas(v, wrap=false) =
assert( is_consistent(v) && len(v)>1 , "Inconsistent list or with length<=1.")
[for (p=pair(v,wrap)) p[1]-p[0]] ;
// Function: product()
// Usage:
// x = product(v);
// Description:
// Returns the product of all entries in the given list.
// If passed a list of vectors of same dimension, returns a vector of products of each part.
// If passed a list of square matrices, returns the resulting product matrix.
// Arguments:
// v = The list to get the product of.
// Example:
// product([2,3,4]); // returns 24.
// product([[1,2,3], [3,4,5], [5,6,7]]); // returns [15, 48, 105]
function product(v) =
assert( is_vector(v) || is_matrix(v) || ( is_matrix(v[0],square=true) && is_consistent(v)),
"Invalid input.")
_product(v, 1, v[0]);
function _product(v, i=0, _tot) =
i>=len(v) ? _tot :
_product( v,
i+1,
( is_vector(v[i])? v_mul(_tot,v[i]) : _tot*v[i] ) );
// Function: cumprod()
// Description:
// Returns a list where each item is the cumulative product of all items up to and including the corresponding entry in the input list.
// If passed an array of vectors, returns a list of elementwise vector products. If passed a list of square matrices returns matrix
// products multiplying on the left, so a list `[A,B,C]` will produce the output `[A,BA,CBA]`.
// Arguments:
// list = The list to get the product of.
// Example:
// cumprod([1,3,5]); // returns [1,3,15]
// cumprod([2,2,2]); // returns [2,4,8]
// cumprod([[1,2,3], [3,4,5], [5,6,7]])); // returns [[1, 2, 3], [3, 8, 15], [15, 48, 105]]
function cumprod(list) =
is_vector(list) ? _cumprod(list) :
assert(is_consistent(list), "Input must be a consistent list of scalars, vectors or square matrices")
is_matrix(list[0]) ? assert(len(list[0])==len(list[0][0]), "Matrices must be square") _cumprod(list)
: _cumprod_vec(list);
function _cumprod(v,_i=0,_acc=[]) =
_i==len(v) ? _acc :
_cumprod(
v, _i+1,
concat(
_acc,
[_i==0 ? v[_i] : v[_i]*_acc[len(_acc)-1]]
)
);
function _cumprod_vec(v,_i=0,_acc=[]) =
_i==len(v) ? _acc :
_cumprod_vec(
v, _i+1,
concat(
_acc,
[_i==0 ? v[_i] : v_mul(_acc[len(_acc)-1],v[_i])]
)
);
// Function: mean()
// Usage:
// x = mean(v);
// Description:
// Returns the arithmetic mean/average of all entries in the given array.
// If passed a list of vectors, returns a vector of the mean of each part.
// Arguments:
// v = The list of values to get the mean of.
// Example:
// mean([2,3,4]); // returns 3.
// mean([[1,2,3], [3,4,5], [5,6,7]]); // returns [3, 4, 5]
function mean(v) =
assert(is_list(v) && len(v)>0, "Invalid list.")
sum(v)/len(v);
// Function: median()
// Usage:
// middle = median(v)
// Description:
// Returns the median of the given vector.
function median(v) =
assert(is_vector(v), "Input to median must be a vector")
len(v)%2 ? max( list_smallest(v, ceil(len(v)/2)) ) :
let( lowest = list_smallest(v, len(v)/2 + 1),
max = max(lowest),
imax = search(max,lowest,1),
max2 = max([for(i=idx(lowest)) if(i!=imax[0]) lowest[i] ])
)
(max+max2)/2;
// Function: convolve()
// Usage:
// x = convolve(p,q);
// Description:
// Given two vectors, or one vector and a path or
// two paths of the same dimension, finds the convolution of them.
// If both parameter are vectors, returns the vector convolution.
// If one parameter is a vector and the other a path,
// convolves using products by scalars and returns a path.
// If both parameters are paths, convolve using scalar products
// and returns a vector.
// The returned vector or path has length len(p)+len(q)-1.
// Arguments:
// p = The first vector or path.
// q = The second vector or path.
// Example:
// a = convolve([1,1],[1,2,1]); // Returns: [1,3,3,1]
// b = convolve([1,2,3],[1,2,1])); // Returns: [1,4,8,8,3]
// c = convolve([[1,1],[2,2],[3,1]],[1,2,1])); // Returns: [[1,1],[4,4],[8,6],[8,4],[3,1]]
// d = convolve([[1,1],[2,2],[3,1]],[[1,2],[2,1]])); // Returns: [3,9,11,7]
function convolve(p,q) =
p==[] || q==[] ? [] :
assert( (is_vector(p) || is_matrix(p))
&& ( is_vector(q) || (is_matrix(q) && ( !is_vector(p[0]) || (len(p[0])==len(q[0])) ) ) ) ,
"The inputs should be vectors or paths all of the same dimension.")
let( n = len(p),
m = len(q))
[for(i=[0:n+m-2], k1 = max(0,i-n+1), k2 = min(i,m-1) )
sum([for(j=[k1:k2]) p[i-j]*q[j] ])
];
// Function: all_integer()
// Usage:
// bool = all_integer(x);
// Description:
// If given a number, returns true if the number is a finite integer.
// If given an empty list, returns false. If given a non-empty list, returns
// true if every item of the list is an integer. Otherwise, returns false.
// Arguments:
// x = The value to check.
// Example:
// b = all_integer(true); // Returns: false
// b = all_integer("foo"); // Returns: false
// b = all_integer(4); // Returns: true
// b = all_integer(4.5); // Returns: false
// b = all_integer([]); // Returns: false
// b = all_integer([3,4,5]); // Returns: true
// b = all_integer([3,4.2,5]); // Returns: false
// b = all_integer([3,[4,7],5]); // Returns: false
function all_integer(x) =
is_num(x)? is_int(x) :
is_list(x)? (x != [] && [for (xx=x) if(!is_int(xx)) 1] == []) :
false;
// Function: any()
// Usage:
// bool = any(l);
// bool = any(l, func); // Requires OpenSCAD 2021.01 or later.
// Requirements:
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
// Description:
// Returns true if any item in list `l` evaluates as true.
// Arguments:
// l = The list to test for true items.
// func = An optional function literal of signature (x), returning bool, to test each list item with.
// Example:
// any([0,false,undef]); // Returns false.
// any([1,false,undef]); // Returns true.
// any([1,5,true]); // Returns true.
// any([[0,0], [0,0]]); // Returns true.
// any([[0,0], [1,0]]); // Returns true.
function any(l, func) =
assert(is_list(l), "The input is not a list." )
assert(func==undef || is_func(func))
is_func(func)
? _any_func(l, func)
: _any_bool(l);
function _any_func(l, func, i=0, out=false) =
i >= len(l) || out? out :
_any_func(l, func, i=i+1, out=out || func(l[i]));
function _any_bool(l, i=0, out=false) =
i >= len(l) || out? out :
_any_bool(l, i=i+1, out=out || l[i]);
// Function: all()
// Usage:
// bool = all(l);
// bool = all(l, func); // Requires OpenSCAD 2021.01 or later.
// Requirements:
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
// Description:
// Returns true if all items in list `l` evaluate as true. If `func` is given a function liteal
// of signature (x), returning bool, then that function literal is evaluated for each list item.
// Arguments:
// l = The list to test for true items.
// func = An optional function literal of signature (x), returning bool, to test each list item with.
// Example:
// test1 = all([0,false,undef]); // Returns false.
// test2 = all([1,false,undef]); // Returns false.
// test3 = all([1,5,true]); // Returns true.
// test4 = all([[0,0], [0,0]]); // Returns true.
// test5 = all([[0,0], [1,0]]); // Returns true.
// test6 = all([[1,1], [1,1]]); // Returns true.
function all(l, func) =
assert(is_list(l), "The input is not a list.")
assert(func==undef || is_func(func))
is_func(func)
? _all_func(l, func)
: _all_bool(l);
function _all_func(l, func, i=0, out=true) =
i >= len(l) || !out? out :
_all_func(l, func, i=i+1, out=out && func(l[i]));
function _all_bool(l, i=0, out=true) =
i >= len(l) || !out? out :
_all_bool(l, i=i+1, out=out && l[i]);
// Function: count_true()
// Usage:
// seq = count_true(l, [nmax=]);
// seq = count_true(l, func, [nmax=]); // Requires OpenSCAD 2021.01 or later.
// Requirements:
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
// Description:
// Returns the number of items in `l` that evaluate as true.
// If `l` is a lists of lists, this is applied recursively to each
// sublist. Returns the total count of items that evaluate as true
// in all recursive sublists.
// Arguments:
// l = The list to test for true items.
// func = An optional function literal of signature (x), returning bool, to test each list item with.
// ---
// nmax = Max number of true items to count. Default: `undef` (no limit)
// Example:
// num1 = count_true([0,false,undef]); // Returns 0.
// num2 = count_true([1,false,undef]); // Returns 1.
// num3 = count_true([1,5,false]); // Returns 2.
// num4 = count_true([1,5,true]); // Returns 3.
// num5 = count_true([[0,0], [0,0]]); // Returns 2.
// num6 = count_true([[0,0], [1,0]]); // Returns 2.
// num7 = count_true([[1,1], [1,1]]); // Returns 2.
// num8 = count_true([[1,1], [1,1]], nmax=1); // Returns 1.
function count_true(l, func, nmax) =
assert(is_list(l))
assert(func==undef || is_func(func))
is_func(func)
? _count_true_func(l, func, nmax)
: _count_true_bool(l, nmax);
function _count_true_func(l, func, nmax, i=0, out=0) =
i >= len(l) || (nmax!=undef && out>=nmax) ? out :
_count_true_func(
l, func, nmax, i = i + 1,
out = out + (func(l[i])? 1:0)
);
function _count_true_bool(l, nmax, i=0, out=0) =
i >= len(l) || (nmax!=undef && out>=nmax) ? out :
_count_true_bool(
l, nmax, i = i + 1,
out = out + (l[i]? 1:0)
);
// Section: Calculus
// Function: deriv()
@ -1609,4 +1444,5 @@ function _rootfind(f, xpts, ypts, yrange, tol, i=0) =
: _rootfind(f, xinterval, yinterval, new_yrange, tol, i+1);
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -306,21 +306,25 @@ include <structs.scad>
// $fn=64;
// path = [[0, 0],[10, 0],[20, 20],[30, -10]];
// debug_polygon(path);
// //polygon(round_corners(path,cut = [1,3,1,1], method="circle"));
// //polygon(round_corners(path,cut = [1,3,1,1],
// // method="circle"));
// Example(2D): The list of factors shows that the problem is in the first two rounding values, because the factors are smaller than one. If we multiply the first two parameters by 0.85 then the roundings fit. The verbose option gives us the same fit factors.
// $fn=64;
// path = [[0, 0],[10, 0],[20, 20],[30, -10]];
// polygon(round_corners(path,cut = [0.85,3*0.85,1,1], method="circle", verbose=true));
// polygon(round_corners(path,cut = [0.85,3*0.85,1,1],
// method="circle", verbose=true));
// Example(2D): From the fit factors we can see that rounding at vertices 2 and 3 could be increased a lot. Applying those factors we get this more rounded shape. The new fit factors show that we can still further increase the rounding parameters if we wish.
// $fn=64;
// path = [[0, 0],[10, 0],[20, 20],[30, -10]];
// polygon(round_corners(path,cut = [0.85,3*0.85,2.13, 10.15], method="circle",verbose=true));
// polygon(round_corners(path,cut = [0.85,3*0.85,2.13, 10.15],
// method="circle",verbose=true));
// Example(2D): Using the `joint` parameter it's easier to understand whether your roundvers will fit. We can guarantee a fairly large roundover on any path by picking each one to use up half the segment distance along the shorter of its two segments:
// $fn=64;
// path = [[0, 0],[10, 0],[20, 20],[30, -10]];
// path_len = path_segment_lengths(path,closed=true);
// halflen = [for(i=idx(path)) min(select(path_len,i-1,i))/2];
// polygon(round_corners(path,joint = halflen, method="circle",verbose=true));
// polygon(round_corners(path,joint = halflen,
// method="circle",verbose=true));
// Example(2D): Chamfering, specifying the chamfer width
// path = star(5, step=2, d=100);
// path2 = round_corners(path, method="chamfer", width=5);
@ -619,16 +623,23 @@ function _rounding_offsets(edgespec,z_dir=1) =
// polygon(smooth_path(square(4),size=0.4,closed=true));
// Example(2D): Turning on uniform tangent calculation also changes the end derivatives:
// color("green")stroke(square(4), width=0.1);
// stroke(smooth_path(square(4),size=0.4,uniform=true), width=0.1);
// stroke(smooth_path(square(4),size=0.4,uniform=true),
// width=0.1);
// Example(2D): Here's a wide rectangle. Using size means all edges bulge the same amount, regardless of their length.
// color("green")
// stroke(square([10,4]), closed=true, width=0.1);
// stroke(smooth_path(square([10,4]),size=1,closed=true),
// width=0.1);
// Example(2D): With relsize the bulge is proportional to the side length.
// color("green")stroke(square([10,4]), closed=true, width=0.1);
// stroke(smooth_path(square([10,4]),size=1,closed=true),width=0.1);
// Example(2D): Here's a wide rectangle. With relsize the bulge is proportional to the side length.
// color("green")stroke(square([10,4]), closed=true, width=0.1);
// stroke(smooth_path(square([10,4]),relsize=0.1,closed=true),width=0.1);
// Example(2D): Here's a wide rectangle. Settting uniform to true biases the tangents to aline more with the line sides
// color("green")stroke(square([10,4]), closed=true, width=0.1);
// stroke(smooth_path(square([10,4]),uniform=true,relsize=0.1,closed=true),width=0.1);
// stroke(smooth_path(square([10,4]),relsize=0.1,closed=true),
// width=0.1);
// Example(2D): Settting uniform to true biases the tangents to aline more with the line sides
// color("green")
// stroke(square([10,4]), closed=true, width=0.1);
// stroke(smooth_path(square([10,4]),uniform=true,
// relsize=0.1,closed=true),
// width=0.1);
// Example(2D): A more interesting shape:
// path = [[0,0], [4,0], [7,14], [-3,12]];
// polygon(smooth_path(path,size=1,closed=true));
@ -636,11 +647,13 @@ function _rounding_offsets(edgespec,z_dir=1) =
// polygon(smooth_path(square(4), size=.25,closed=true));
// Example(2D): Here's the square with a size that's too big to achieve, so you get the maximum possible curve:
// color("green")stroke(square(4), width=0.1,closed=true);
// stroke(smooth_path(square(4), size=4, closed=true),closed=true,width=.1);
// stroke(smooth_path(square(4), size=4, closed=true),
// closed=true,width=.1);
// Example(2D): You can alter the shape of the curve by specifying your own arbitrary tangent values
// polygon(smooth_path(square(4),tangents=1.25*[[-2,-1], [-4,1], [1,2], [6,-1]],size=0.4,closed=true));
// Example(2D): Or you can give a different size for each segment
// polygon(smooth_path(square(4),size = [.4, .05, 1, .3],closed=true));
// polygon(smooth_path(square(4),size = [.4, .05, 1, .3],
// closed=true));
// Example(FlatSpin,VPD=35,VPT=[4.5,4.5,1]): Works on 3d paths as well
// path = [[0,0,0],[3,3,2],[6,0,1],[9,9,0]];
// stroke(smooth_path(path,relsize=.1),width=.3);
@ -716,23 +729,31 @@ function _scalar_to_vector(value,length,varname) =
// Example(2D): Specifying pairs of joint values at a path joint creates an asymmetric curve
// horiz = [[0,0],[10,0]];
// vert = [[0,0],[0,10]];
// stroke(path_join([horiz, vert, -horiz],joint=[[4,1],[1,4]],$fn=16),width=.3);
// stroke(path_join([horiz, vert, -horiz],
// joint=[[4,1],[1,4]],$fn=16),width=.3);
// Example(2D): A closed square
// horiz = [[0,0],[10,0]];
// vert = [[0,0],[0,10]];
// stroke(path_join([horiz, vert, -horiz, -vert],joint=3,k=1,closed=true,$fn=16),closed=true);
// stroke(path_join([horiz, vert, -horiz, -vert],
// joint=3,k=1,closed=true,$fn=16),closed=true);
// Example(2D): Different curve at each corner by changing the joint size
// horiz = [[0,0],[10,0]];
// vert = [[0,0],[0,10]];
// stroke(path_join([horiz, vert, -horiz, -vert],joint=[3,0,1,2],k=1,closed=true,$fn=16),closed=true,width=0.4);
// stroke(path_join([horiz, vert, -horiz, -vert],
// joint=[3,0,1,2],k=1,closed=true,$fn=16),
// closed=true,width=0.4);
// Example(2D): Different curve at each corner by changing the curvature parameter. Note that k=0 still gives a small curve, unlike joint=0 which gives a sharp corner.
// horiz = [[0,0],[10,0]];
// vert = [[0,0],[0,10]];
// stroke(path_join([horiz, vert, -horiz, -vert],joint=3,k=[1,.5,0,.7],closed=true,$fn=16),closed=true,width=0.4);
// stroke(path_join([horiz, vert, -horiz, -vert],joint=3,
// k=[1,.5,0,.7],closed=true,$fn=16),
// closed=true,width=0.4);
// Example(2D): Joint value of 7 is larger than half the square so curves interfere with each other, which breaks symmetry because they are computed sequentially
// horiz = [[0,0],[10,0]];
// vert = [[0,0],[0,10]];
// stroke(path_join([horiz, vert, -horiz, -vert],joint=7,k=.4,closed=true,$fn=16),closed=true);
// stroke(path_join([horiz, vert, -horiz, -vert],joint=7,
// k=.4,closed=true,$fn=16),
// closed=true);
// Example(2D): Unlike round_corners, we can add curves onto curves.
// $fn=64;
// myarc = arc(width=20, thickness=5 );
@ -740,29 +761,41 @@ function _scalar_to_vector(value,length,varname) =
// Example(2D): Here we make a closed shape from two arcs and round the sharp tips
// arc1 = arc(width=20, thickness=4,$fn=75);
// arc2 = reverse(arc(width=20, thickness=2,$fn=75));
// stroke(path_join([arc1,arc2]),width=.3); // Join without rounding
// color("red")stroke(path_join([arc1,arc2], 3,k=1,closed=true), width=.3,closed=true,$fn=12); // Join with rounding
// // Without rounding
// stroke(path_join([arc1,arc2]),width=.3);
// // With rounding
// color("red")stroke(path_join([arc1,arc2], 3,k=1,closed=true),
// width=.3,closed=true,$fn=12);
// Example(2D): Combining arcs with segments
// arc1 = arc(width=20, thickness=4,$fn=75);
// arc2 = reverse(arc(width=20, thickness=2,$fn=75));
// vpath = [[0,0],[0,-5]];
// stroke(path_join([arc1,vpath,arc2,reverse(vpath)]),width=.2);
// color("red")stroke(path_join([arc1,vpath,arc2,reverse(vpath)], [1,2,2,1],k=1,closed=true), width=.2,closed=true,$fn=12);
// color("red")stroke(path_join([arc1,vpath,arc2,reverse(vpath)],
// [1,2,2,1],k=1,closed=true),
// width=.2,closed=true,$fn=12);
// Example(2D): Here relocation is off. We have three segments (in yellow) and add the curves to the segments. Notice that joint zero still produces a curve because it refers to the endpoints of the supplied paths.
// p1 = [[0,0],[2,0]];
// p2 = [[3,1],[1,3]];
// p3 = [[0,3],[-1,1]];
// color("red")stroke(path_join([p1,p2,p3], joint=0, relocate=false,closed=true),width=.3,$fn=12);
// color("red")stroke(
// path_join([p1,p2,p3], joint=0, relocate=false,
// closed=true),
// width=.3,$fn=12);
// for(x=[p1,p2,p3]) stroke(x,width=.3);
// Example(2D): If you specify closed=true when the last path doesn't meet the first one then it is similar to using relocate=false: the function tries to close the path using a curve. In the example below, this results in a long curve to the left, when given the unclosed three segments as input. Note that if the segments are parallel the function fails with an error. The extension of the curves must intersect in a corner for the rounding to be well-defined. To get a normal rounding of the closed shape, you must include a fourth path, the last segment that closes the shape.
// horiz = [[0,0],[10,0]];
// vert = [[0,0],[0,10]];
// h2 = [[0,-3],[10,0]];
// color("red")stroke(path_join([horiz, vert, -h2],closed=true,joint=3,$fn=25),closed=true,width=.5);
// color("red")stroke(
// path_join([horiz, vert, -h2],closed=true,
// joint=3,$fn=25),
// closed=true,width=.5);
// stroke(path_join([horiz, vert, -h2]),width=.3);
// Example(2D): With a single path with closed=true the start and end junction is rounded.
// tri = regular_ngon(n=3, r=7);
// stroke(path_join([tri], joint=3,closed=true,$fn=12),closed=true,width=.5);
// stroke(path_join([tri], joint=3,closed=true,$fn=12),
// closed=true,width=.5);
module path_join(paths,joint=0,k=0.5,relocate=true,closed=false) { no_module();}
function path_join(paths,joint=0,k=0.5,relocate=true,closed=false)=
assert(is_list(paths),"Input paths must be a list of paths")

View file

@ -126,13 +126,80 @@ function phillips_diam(size, depth) =
// Section: Torx Drive
// Function: torx_outer_diam()
// Module: torx_mask()
// Usage:
// diam = torx_outer_diam(size);
// torx_mask(size, l, [center]);
// Description: Creates a torx bit tip.
// Arguments:
// size = Torx size.
// l = Length of bit.
// center = If true, centers bit vertically.
// ---
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Examples:
// torx_mask(size=30, l=10, $fa=1, $fs=1);
module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
anchor = get_anchor(anchor, center, BOT, BOT);
od = torx_diam(size);
attachable(anchor,spin,orient, d=od, l=l) {
linear_extrude(height=l, convexity=4, center=true) {
torx_mask2d(size);
}
children();
}
}
// Module: torx_mask2d()
// Usage:
// torx_mask2d(size);
// Description: Creates a torx bit 2D profile.
// Arguments:
// size = Torx size.
// Example(2D):
// torx_mask2d(size=30, $fa=1, $fs=1);
module torx_mask2d(size) {
od = torx_diam(size);
id = _torx_inner_diam(size);
tip = _torx_tip_radius(size);
rounding = _torx_rounding_radius(size);
base = od - 2*tip;
$fn = quantup(segs(od/2),12);
difference() {
union() {
circle(d=base);
zrot_copies(n=2) {
hull() {
zrot_copies(n=3) {
translate([base/2,0,0]) {
circle(r=tip, $fn=$fn/2);
}
}
}
}
}
zrot_copies(n=6) {
zrot(180/6) {
translate([id/2+rounding,0,0]) {
circle(r=rounding);
}
}
}
}
}
// Function: torx_diam()
// Usage:
// diam = torx_diam(size);
// Description: Get the typical outer diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_outer_diam(size) = lookup(size, [
function torx_diam(size) = lookup(size, [
[ 6, 1.75],
[ 8, 2.40],
[ 10, 2.80],
@ -152,13 +219,13 @@ function torx_outer_diam(size) = lookup(size, [
]);
// Function: torx_inner_diam()
// Usage:
// diam = torx_inner_diam(size);
// Description: Get typical inner diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_inner_diam(size) = lookup(size, [
/// Internal Function: torx_inner_diam()
/// Usage:
/// diam = torx_inner_diam(size);
/// Description: Get typical inner diameter of Torx profile.
/// Arguments:
/// size = Torx size.
function _torx_inner_diam(size) = lookup(size, [
[ 6, 1.27],
[ 8, 1.75],
[ 10, 2.05],
@ -204,13 +271,13 @@ function torx_depth(size) = lookup(size, [
]);
// Function: torx_tip_radius()
// Usage:
// rad = torx_tip_radius(size);
// Description: Gets minor rounding radius of Torx profile.
// Arguments:
// size = Torx size.
function torx_tip_radius(size) = lookup(size, [
/// Internal Function: torx_tip_radius()
/// Usage:
/// rad = torx_tip_radius(size);
/// Description: Gets minor rounding radius of Torx profile.
/// Arguments:
/// size = Torx size.
function _torx_tip_radius(size) = lookup(size, [
[ 6, 0.132],
[ 8, 0.190],
[ 10, 0.229],
@ -230,13 +297,13 @@ function torx_tip_radius(size) = lookup(size, [
]);
// Function: torx_rounding_radius()
// Usage:
// rad = torx_rounding_radius(size);
// Description: Gets major rounding radius of Torx profile.
// Arguments:
// size = Torx size.
function torx_rounding_radius(size) = lookup(size, [
/// Internal Function: torx_rounding_radius()
/// Usage:
/// rad = torx_rounding_radius(size);
/// Description: Gets major rounding radius of Torx profile.
/// Arguments:
/// size = Torx size.
function _torx_rounding_radius(size) = lookup(size, [
[ 6, 0.383],
[ 8, 0.510],
[ 10, 0.598],
@ -257,71 +324,6 @@ function torx_rounding_radius(size) = lookup(size, [
// Module: torx_mask2d()
// Usage:
// torx_mask2d(size);
// Description: Creates a torx bit 2D profile.
// Arguments:
// size = Torx size.
// Example(2D):
// torx_mask2d(size=30, $fa=1, $fs=1);
module torx_mask2d(size) {
od = torx_outer_diam(size);
id = torx_inner_diam(size);
tip = torx_tip_radius(size);
rounding = torx_rounding_radius(size);
base = od - 2*tip;
$fn = quantup(segs(od/2),12);
difference() {
union() {
circle(d=base);
zrot_copies(n=2) {
hull() {
zrot_copies(n=3) {
translate([base/2,0,0]) {
circle(r=tip, $fn=$fn/2);
}
}
}
}
}
zrot_copies(n=6) {
zrot(180/6) {
translate([id/2+rounding,0,0]) {
circle(r=rounding);
}
}
}
}
}
// Module: torx_mask()
// Usage:
// torx_mask(size, l, [center]);
// Description: Creates a torx bit tip.
// Arguments:
// size = Torx size.
// l = Length of bit.
// center = If true, centers bit vertically.
// ---
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Examples:
// torx_mask(size=30, l=10, $fa=1, $fs=1);
module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
anchor = get_anchor(anchor, center, BOT, BOT);
od = torx_outer_diam(size);
attachable(anchor,spin,orient, d=od, l=l) {
linear_extrude(height=l, convexity=4, center=true) {
torx_mask2d(size);
}
children();
}
}
// Section: Robertson/Square Drives

3
tests/README.txt Normal file
View file

@ -0,0 +1,3 @@
This directory contains regression tests scripts to check whether the
library is working correctly. If all the scripts run without
producing any output, then all the tests have passed.

View file

@ -1,7 +1,7 @@
include <../std.scad>
module test_HSL() {
module test_hsl() {
for (h = [0:30:360]) {
for (s = [0:0.2:1]) {
for (l = [0:0.2:1]) {
@ -16,15 +16,15 @@ module test_HSL() {
h<=300? [x,0,c] :
[c,0,x]
);
assert_approx(HSL(h,s,l), rgb, format("h={}, s={}, l={}", [h,s,l]));
assert_approx(hsl(h,s,l), rgb, format("h={}, s={}, l={}", [h,s,l]));
}
}
}
}
test_HSL();
test_hsl();
module test_HSV() {
module test_hsv() {
for (h = [0:30:360]) {
for (s = [0:0.2:1]) {
for (v = [0:0.2:1]) {
@ -39,12 +39,12 @@ module test_HSV() {
h<=300? [x,0,c] :
[c,0,x]
);
assert_approx(HSV(h,s,v), rgb, format("h={}, s={}, v={}", [h,s,v]));
assert_approx(hsv(h,s,v), rgb, format("h={}, s={}, v={}", [h,s,v]));
}
}
}
}
test_HSV();
test_hsv();
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -2,24 +2,24 @@ include <../std.scad>
include <../screw_drive.scad>
module test_torx_outer_diam() {
assert_approx(torx_outer_diam(10), 2.80);
assert_approx(torx_outer_diam(15), 3.35);
assert_approx(torx_outer_diam(20), 3.95);
assert_approx(torx_outer_diam(25), 4.50);
assert_approx(torx_outer_diam(30), 5.60);
assert_approx(torx_outer_diam(40), 6.75);
module test_torx_diam() {
assert_approx(torx_diam(10), 2.80);
assert_approx(torx_diam(15), 3.35);
assert_approx(torx_diam(20), 3.95);
assert_approx(torx_diam(25), 4.50);
assert_approx(torx_diam(30), 5.60);
assert_approx(torx_diam(40), 6.75);
}
test_torx_outer_diam();
test_torx_diam();
module test_torx_inner_diam() {
assert_approx(torx_inner_diam(10), 2.05);
assert_approx(torx_inner_diam(15), 2.40);
assert_approx(torx_inner_diam(20), 2.85);
assert_approx(torx_inner_diam(25), 3.25);
assert_approx(torx_inner_diam(30), 4.05);
assert_approx(torx_inner_diam(40), 4.85);
assert_approx(_torx_inner_diam(10), 2.05);
assert_approx(_torx_inner_diam(15), 2.40);
assert_approx(_torx_inner_diam(20), 2.85);
assert_approx(_torx_inner_diam(25), 3.25);
assert_approx(_torx_inner_diam(30), 4.05);
assert_approx(_torx_inner_diam(40), 4.85);
}
test_torx_inner_diam();
@ -36,23 +36,23 @@ test_torx_depth();
module test_torx_tip_radius() {
assert_approx(torx_tip_radius(10), 0.229);
assert_approx(torx_tip_radius(15), 0.267);
assert_approx(torx_tip_radius(20), 0.305);
assert_approx(torx_tip_radius(25), 0.375);
assert_approx(torx_tip_radius(30), 0.451);
assert_approx(torx_tip_radius(40), 0.546);
assert_approx(_torx_tip_radius(10), 0.229);
assert_approx(_torx_tip_radius(15), 0.267);
assert_approx(_torx_tip_radius(20), 0.305);
assert_approx(_torx_tip_radius(25), 0.375);
assert_approx(_torx_tip_radius(30), 0.451);
assert_approx(_torx_tip_radius(40), 0.546);
}
test_torx_tip_radius();
module test_torx_rounding_radius() {
assert_approx(torx_rounding_radius(10), 0.598);
assert_approx(torx_rounding_radius(15), 0.716);
assert_approx(torx_rounding_radius(20), 0.859);
assert_approx(torx_rounding_radius(25), 0.920);
assert_approx(torx_rounding_radius(30), 1.194);
assert_approx(torx_rounding_radius(40), 1.428);
assert_approx(_torx_rounding_radius(10), 0.598);
assert_approx(_torx_rounding_radius(15), 0.716);
assert_approx(_torx_rounding_radius(20), 0.859);
assert_approx(_torx_rounding_radius(25), 0.920);
assert_approx(_torx_rounding_radius(30), 1.194);
assert_approx(_torx_rounding_radius(40), 1.428);
}
test_torx_rounding_radius();

View file

@ -287,7 +287,7 @@ shell2d(thickness=-5,or=[5,0],ir=[5,0]) star(5,step=2,d=100);
## Color Manipulators
The built-in OpenSCAD `color()` module can let you set the RGB color of an object, but it's often
easier to select colors using other color schemes. You can use the HSL or Hue-Saturation-Lightness
color scheme with the `HSL()` module:
color scheme with the `hsl()` module:
```openscad-3D
include <BOSL2/std.scad>
@ -295,12 +295,12 @@ n = 10; size = 100/n;
for (a=count(n), b=count(n), c=count(n)) {
let( h=360*a/n, s=1-b/(n-1), l=c/(n-1))
translate(size*[a,b,c]) {
HSL(h,s,l) cube(size);
hsl(h,s,l) cube(size);
}
}
```
You can use the HSV or Hue-Saturation-Value color scheme with the `HSV()` module:
You can use the HSV or Hue-Saturation-Value color scheme with the `hsv()` module:
```openscad-3D
include <BOSL2/std.scad>
@ -308,7 +308,7 @@ n = 10; size = 100/n;
for (a=count(n), b=count(n), c=count(n)) {
let( h=360*a/n, s=1-b/(n-1), v=c/(n-1))
translate(size*[a,b,c]) {
HSV(h,s,v) cube(size);
hsv(h,s,v) cube(size);
}
}
```

View file

@ -116,6 +116,30 @@ function is_int(n) = is_finite(n) && n == round(n);
function is_integer(n) = is_finite(n) && n == round(n);
// Function: all_integer()
// Usage:
// bool = all_integer(x);
// Description:
// If given a number, returns true if the number is a finite integer.
// If given an empty list, returns false. If given a non-empty list, returns
// true if every item of the list is an integer. Otherwise, returns false.
// Arguments:
// x = The value to check.
// Example:
// b = all_integer(true); // Returns: false
// b = all_integer("foo"); // Returns: false
// b = all_integer(4); // Returns: true
// b = all_integer(4.5); // Returns: false
// b = all_integer([]); // Returns: false
// b = all_integer([3,4,5]); // Returns: true
// b = all_integer([3,4.2,5]); // Returns: false
// b = all_integer([3,[4,7],5]); // Returns: false
function all_integer(x) =
is_num(x)? is_int(x) :
is_list(x)? (x != [] && [for (xx=x) if(!is_int(xx)) 1] == []) :
false;
// Function: is_nan()
// Usage:
// bool = is_nan(x);
@ -404,6 +428,181 @@ function all_defined(v,recursive=false) =
// Section: Undef Safe Arithmetic
// Function: u_add()
// Usage:
// x = u_add(a, b);
// Description:
// Adds `a` to `b`, returning the result, or undef if either value is `undef`.
// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
// Arguments:
// a = First value.
// b = Second value.
function u_add(a,b) = is_undef(a) || is_undef(b)? undef : a + b;
// Function: u_sub()
// Usage:
// x = u_sub(a, b);
// Description:
// Subtracts `b` from `a`, returning the result, or undef if either value is `undef`.
// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
// Arguments:
// a = First value.
// b = Second value.
function u_sub(a,b) = is_undef(a) || is_undef(b)? undef : a - b;
// Function: u_mul()
// Usage:
// x = u_mul(a, b);
// Description:
// Multiplies `a` by `b`, returning the result, or undef if either value is `undef`.
// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
// Arguments:
// a = First value.
// b = Second value.
function u_mul(a,b) =
is_undef(a) || is_undef(b)? undef :
is_vector(a) && is_vector(b)? v_mul(a,b) :
a * b;
// Function: u_div()
// Usage:
// x = u_div(a, b);
// Description:
// Divides `a` by `b`, returning the result, or undef if either value is `undef`.
// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
// Arguments:
// a = First value.
// b = Second value.
function u_div(a,b) =
is_undef(a) || is_undef(b)? undef :
is_vector(a) && is_vector(b)? v_div(a,b) :
a / b;
// Section: Boolean list testing
// Function: any()
// Usage:
// bool = any(l);
// bool = any(l, func); // Requires OpenSCAD 2021.01 or later.
// Requirements:
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
// Description:
// Returns true if any item in list `l` evaluates as true.
// Arguments:
// l = The list to test for true items.
// func = An optional function literal of signature (x), returning bool, to test each list item with.
// Example:
// any([0,false,undef]); // Returns false.
// any([1,false,undef]); // Returns true.
// any([1,5,true]); // Returns true.
// any([[0,0], [0,0]]); // Returns true.
// any([[0,0], [1,0]]); // Returns true.
function any(l, func) =
assert(is_list(l), "The input is not a list." )
assert(func==undef || is_func(func))
is_func(func)
? _any_func(l, func)
: _any_bool(l);
function _any_func(l, func, i=0, out=false) =
i >= len(l) || out? out :
_any_func(l, func, i=i+1, out=out || func(l[i]));
function _any_bool(l, i=0, out=false) =
i >= len(l) || out? out :
_any_bool(l, i=i+1, out=out || l[i]);
// Function: all()
// Usage:
// bool = all(l);
// bool = all(l, func); // Requires OpenSCAD 2021.01 or later.
// Requirements:
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
// Description:
// Returns true if all items in list `l` evaluate as true. If `func` is given a function liteal
// of signature (x), returning bool, then that function literal is evaluated for each list item.
// Arguments:
// l = The list to test for true items.
// func = An optional function literal of signature (x), returning bool, to test each list item with.
// Example:
// test1 = all([0,false,undef]); // Returns false.
// test2 = all([1,false,undef]); // Returns false.
// test3 = all([1,5,true]); // Returns true.
// test4 = all([[0,0], [0,0]]); // Returns true.
// test5 = all([[0,0], [1,0]]); // Returns true.
// test6 = all([[1,1], [1,1]]); // Returns true.
function all(l, func) =
assert(is_list(l), "The input is not a list.")
assert(func==undef || is_func(func))
is_func(func)
? _all_func(l, func)
: _all_bool(l);
function _all_func(l, func, i=0, out=true) =
i >= len(l) || !out? out :
_all_func(l, func, i=i+1, out=out && func(l[i]));
function _all_bool(l, i=0, out=true) =
i >= len(l) || !out? out :
_all_bool(l, i=i+1, out=out && l[i]);
// Function: count_true()
// Usage:
// seq = count_true(l, [nmax=]);
// seq = count_true(l, func, [nmax=]); // Requires OpenSCAD 2021.01 or later.
// Requirements:
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
// Description:
// Returns the number of items in `l` that evaluate as true.
// If `l` is a lists of lists, this is applied recursively to each
// sublist. Returns the total count of items that evaluate as true
// in all recursive sublists.
// Arguments:
// l = The list to test for true items.
// func = An optional function literal of signature (x), returning bool, to test each list item with.
// ---
// nmax = Max number of true items to count. Default: `undef` (no limit)
// Example:
// num1 = count_true([0,false,undef]); // Returns 0.
// num2 = count_true([1,false,undef]); // Returns 1.
// num3 = count_true([1,5,false]); // Returns 2.
// num4 = count_true([1,5,true]); // Returns 3.
// num5 = count_true([[0,0], [0,0]]); // Returns 2.
// num6 = count_true([[0,0], [1,0]]); // Returns 2.
// num7 = count_true([[1,1], [1,1]]); // Returns 2.
// num8 = count_true([[1,1], [1,1]], nmax=1); // Returns 1.
function count_true(l, func, nmax) =
assert(is_list(l))
assert(func==undef || is_func(func))
is_func(func)
? _count_true_func(l, func, nmax)
: _count_true_bool(l, nmax);
function _count_true_func(l, func, nmax, i=0, out=0) =
i >= len(l) || (nmax!=undef && out>=nmax) ? out :
_count_true_func(
l, func, nmax, i = i + 1,
out = out + (func(l[i])? 1:0)
);
function _count_true_bool(l, nmax, i=0, out=0) =
i >= len(l) || (nmax!=undef && out>=nmax) ? out :
_count_true_bool(
l, nmax, i = i + 1,
out = out + (l[i]? 1:0)
);
// Section: Processing Arguments to Functions and Modules