This library is a collection of OpenSCAD functions for use with OpenSCAD function-literals.
It is meant to provide algorithms and tools to help build efficient scripts using functional programming techniques in OpenSCAD.
This was started as somewhat of an experiment or coding exercise to see how well various general algorithms could be implemented and built upon in the OpenSCAD language. The C++ standard algorithms library was chosen as a model for most of funcutils API. An attempt was made to support as much of the C++ API as possible, although due to language differences some of these functions may be decidedly less useful or practical within OpenSCAD.
In addition to std_algorithm.scad, various other utilities have been provided which the authors felt were particularly useful for OpenSCAD.
ℹ️ - Prior to release 2021.01, function-literals were available as an experimental feature, accessible from Development Snapshots and Nightly Builds.
- Such builds must be newer than Oct. 23 2019 when function-literals was merged into OpenSCAD master.
- If build is from sources between 2019.10.23 and 2021.01 Release, then the feature must be enabled within OpenSCAD by checking the option under Preferences -> Features -> function-literals.
- If built from sources from the above date range, then experimental features must be enabled in the build config e.g.:
cmake -DEXPERIMENTAL=ON [...](pre-built Development Snapshots and Nightlies from OpenSCAD all have this configuration enabled)
In order to pass functions as values, function literals are declared slightly differently than "traditional" OpenSCAD functions, so that they exist as an assignment in OpenSCAD's variable namespace.
- function literal:
foo = function(x) x + 1337; - vs traditional function:
function foo(x) = x + 1337;
- function literal declarations are variable assignments, and
use <filename>does not evaluate assignments. - Therefore
include <filename>should be used instead, for files from this library.
Currently OpenSCAD's builtin functions are not available directly as literals (this might change in future), but can be wrapped in anonymous functions for passing into higher-order functions.
Example:
echo(filter([1,[],2,3,4,5,undef,"no"], function(x) is_num(x))); // result: [1,2,3,4,5]
- funcutils.scad
- Main file to include all other files in this library
- func.scad
- Minimal collection of common higher-order functions:
map,filter,fold
- Minimal collection of common higher-order functions:
- ops.scad
- Elementary function literals for comparison, arithmetic and boolean value operations.
- std_algorithm.scad
- Functions modeled after C++ Algorithms standard library, mostly operating on ranges of sequences (lists/vectors or strings)
- list.scad
- Various list-based functions which are not directly modeled after C++ STL algorithms.
- math.scad
- Mathematical functions which are not directly modeled after C++ STL algorithms.
- string.scad
- String specific functions
- types.scad
- Extra type checking functions for which no OpenSCAD buitin exists.
include <...> their own dependencies.
To avoid duplicate definitions, do not manually include any more-deeply-nested dependencies than the outermost one(s) you choose.
So, if including top-level functils.scad, then do not include any others.
Top level file, existing only to include all others. No function definitions in itself.
Elementary Operators
eq (x, y)- Equal
ne (x, y)- Not Equal
lt (x, y)- Less Than
gt (x, y)- Greater Than
le (x, y)- Less than or Equal
ge (x, y)- Greater than or Equal
ident (x)- Identity function (returns x unchanged)
neg (x)- Negation
add (x, y)- Addition
sub (x, y)- Subtraction
mul (x, y)- Multiplication
div (x, y)- Division
rem (x, y)- Remainder (
x % y)
- Remainder (
mod (x, y)- Modulo operator. Unlike remainder, always returns non-negative value in range:
[0,y)
- Modulo operator. Unlike remainder, always returns non-negative value in range:
not (x)- NOT
and (x, y)- AND
or (x, y)- OR
xor (x, y)- XOR
nand (x, y)- NAND
nor (x, y)- NOR
xnor (x, y)- XNOR
Various list-based functions which are not directly modeled after C++ STL algorithms.
clamp_range (v, begin, step, end)- Return a range with
begin(inclusive) andend(exclusive) clamped between0andlen(v). - Allows negative values to wrap but only once (
-1becomes index of last element, and values less than-len(v)clamp to0). - mainly for use by slice function
- Return a range with
def (x, default)- return
x, ordefaultifxundefined
- return
get (v, i)- get element from
vwith wrapping, using index mod(i,len(v))
- get element from
insert (v, x, i)- insert element
xintovat indexi
- insert element
insertv (v, i, xs)- insert vector of elements
xsintovat indexi
- insert vector of elements
insert_sorted (v, x, cmp=lt)- insert
xinto sorted vectorv
- insert
insertv_sorted (v, xs, cmp=lt)- insert vector
xsinto sorted vectorv xsdoes not need to be sorted
- insert vector
echo(insertv_sorted([1,2,3,5,5,8,12],[6,4,7,-8,4,3,5,-6,5,3,2,1,10,0,-1]));
echo(insertv_sorted([],[for (x=rands(0,10000,10000)) floor(x)]));
replace_range (v, range, xs)- replace elements of
vin range with elements from vectorxs rangeis vec2 containing[begin,end)indices.xsdoes not need to be same length as range
- replace elements of
rotl (v, l=1)- Rotate elements in vector
v, bylpositions to the left.
- Rotate elements in vector
rotr (v, l=1)- Rotate elements in vector
v, bylpositions to the right.
- Rotate elements in vector
sublist (s, b=0, e)- Extract sub-list given
begin(inclusive) andend(exclusive). Ifendnot specified, go to end of list
- Extract sub-list given
slice (v, begin, step, end, range)- slice vector
v, similar to Python's builtin slice function or extended indexing syntax. - all params after
vare optional rangeparam overrides others- unnamed range may be optionally positioned in place of
begin
- slice vector
Functions modeled after C++ STL Algorithms (incomplete, see std algorithm status for details)
ℹ️ Where C++ uses first and last iterators for most of the following functions, OpenSCAD uses indices of the same name. This results in some differences between function signatures including:
v(for "vector", but may also be a string) is always the first parameter for such OpenSCAD functionsfirst=0is default when omittedlast=len(v)is default when omitted- default parameter values cannot depend on previous parameters being initialized in order, so documented function signatures don't explicitely show
last=len(v), but it is set within the function body via end (v, last)
- default parameter values cannot depend on previous parameters being initialized in order, so documented function signatures don't explicitely show
- In some cases, the OpenSCAD version of functions have parameters in a different order than C++
- For example C++'s
std::find(first, last, value);isfind(v, value, first, last)in OpenSCAD.valuecomes beforefirstandlastsince they can be reasonably defaulted, whenvaluecan't.
- For example C++'s
end (v,last)(ref: std::end, loosely based)- return
last, OR length ofviflastnot defined.
- return
find_if (v, first=0, last, p)(ref: std::find_if)- return index of first element in range
[first,last)which matches predicatep, orlastif none found. find_if_not (v, first=0, last, p)(ref: std::find_if_not)- return index of first element in range
[first,last)which does not match predicatep, orlastif none found. find (v, value, first=0, last, cmp=eq)(ref: std::find)- return index of first element in range
[first,last)equivalent tovalue, orlastif none found.
- return index of first element in range
v=[6,4,7,-8,4,3,5,-6,5,3,2,1,10,0,-1];
echo(find(v,-8));
echo(find(v,-1));
echo(find(v,undef));
echo(find([0],0));
echo(find([0],1));
echo(find([],undef));
all_of (v, first=0, last, p)(ref: std::all_of)- Checks if unary predicate
preturnstruefor all elements in the range[first, last).
- Checks if unary predicate
any_of (v, first=0, last, p)(ref: std::any_of)- Checks if unary predicate
preturnstruefor at least one element in the range[first, last).
- Checks if unary predicate
none_of (v,first=0,last,p)(ref: std::none_of)- Checks if unary predicate
preturnstruefor no elements in the range[first, last).
- Checks if unary predicate
for(v=[ [], [0,1,2,3], [0,1,2,3,0/0], [[],false,true,"ok"] ]) let(p=function(x)is_num(x)) {
if (all_of(v,p=p)) echo(str("all_of(",v,",is_num) = true"));
if (any_of(v,p=p)) echo(str("any_of(",v,",is_num) = true"));
if (none_of(v,p=p)) echo(str("none_of(",v,",is_num) = true"));
}
contains (v, value, first, last, cmp=eq)- No direct analogue in C++ STL, but a variation of
std::findreturning bool, or loosely based on std::map::contains - return
trueif any element in range[first,last)is equivalent tovalue
- No direct analogue in C++ STL, but a variation of
echo(contains([6,4,7,-8,4,3,5,-6,5,3,2,1,10,0,-1],3.14));
echo(contains([6,4,7,-8,4,3,5,-6,5,3,2,1,10,0,-1],-1));
count (v, value, first=0, last, cmp=eq)ref: std::count- return the number of elements in the range
[first, last)equal tovalue
- return the number of elements in the range
count_if (v, p, first=0, last)ref: std::count_if- return the number of elements in the range
[first, last)for which predicatepreturnstrue
- return the number of elements in the range
mismatch (v1, first1=0, last1, v2, first2=0, last2, cmp=eq)ref: std::mismatch- return pair of indices
[i1,i2]to the first two non-equal elements. - if no mismatches are found when the comparison reaches
last1orlast2, whichever happens first, the pair holds the end index and the corresponding index from the other range.
- return pair of indices
echo(count("this is a test","t"));
echo(count_if([1,2,3,4,5,6,7,8,9,10],p=function(x)x>3));
echo(mismatch(v1="oh! hi, how are you?",first1=4,v2="hi, how's it going?"));// [11, 7]
echo(mismatch(v1="Hello World",v2="Hello World"));// [11, 11]
echo(mismatch(v1="Hello!",v2="Hello")); // [5, 5]
echo(mismatch(v1="l",v2="Hello",first2=3)); // [1, 4]
remove(v,first=0,last)ref: std::remove- remove range of elements [first,last) from list
remove_if(v,first=0,last,p)ref: std::remove_if- remove elements from range which match predicate
echo(remove([0,1,2,3,4,5,6,7,8,9],3,7));
echo(remove([0,1,2,3,4,5,6,7,8,9]));
echo(remove_if([0,1,2,3,4,5,6,7,8,9],p=function(x)x%3==0));
replace (v, old_value, new_value, first=0, last, cmp=eq)ref: std::replace- Replaces all elements that are equivalent to
old_valuewithnew_value, in the range[first, last).
- Replaces all elements that are equivalent to
replace_if (v, new_value, first=0, last, p)ref: std::replace_if- Replaces all elements for which predicate
preturnstrue, withnew_value, in the range[first, last).
- Replaces all elements for which predicate
echo(replace([0,1,2,3,1,4,5,6,1,7,8,1,9],1,[]));
echo(replace_if([0,1,2,3,1,4,5,6,1,7,8,1,9],0,p=function(x)x<5));
reverse (v, first=0, last)ref: std::reverse- Reverse a range of elements
echo(reverse([0,1,2,3,4,5,6,7,8,9]));
echo(reverse([0,1,2,3,4,5,6,7,8,9],3,7));
std_rotate (v, first=0, first_n, last)ref: std::rotate- Reorders elements in the range [first,last) such that sub-ranges:
- [first_n, last) moves to the beginning of the range, and
- [first, first_n) moves to end of the range.
- Reorders elements in the range [first,last) such that sub-ranges:
// move elements [3,7) to position 12: https://www.youtube.com/watch?v=W2tWOdzgXHA&feature=youtu.be&t=630
echo(std_rotate([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],3,7,12));
// move elements [8,12) to position 3: https://www.youtube.com/watch?v=W2tWOdzgXHA&feature=youtu.be&t=645
echo(std_rotate([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],3,8,12));
unique (v, first=0, last, cmp=eq)ref: std::unique- Removes all but the first element from every group of consecutive equivalent elements in range
[first,last). vshould be pre-sorted if uniqueness across entire vector is desired.
- Removes all but the first element from every group of consecutive equivalent elements in range
echo(unique(insertv_sorted([],[1,2,3,1,2,3,3,4,5,4,5,6,7])));
stable_partition (v, first=0, last, p)ref: std::stable_partition- Reorders the elements in the range
[first, last)in such a way that all elements for which the predicatepreturnstrueprecede the elements for which predicatepreturnsfalse. - Relative order of the elements is preserved.
- Reorders the elements in the range
is_even = function(x)x%2==0;
echo(stable_partition([0,1,2,3,4,5,6,7,8,9],p=is_even));
- None yet implemented
upper_bound (v, value, first=0, last=undef, cmp=lt)ref: std::upper_bound- return index of first element in range
[first,last)which is greater thanvalue.
- return index of first element in range
lower_bound (v, value, first=0, last=undef, cmp=lt)ref: std::lower_bound- return index of first element in range
[first,last)which is not less thanvalue.
- return index of first element in range
binary_search (v, value, first=0, last=undef, cmp=lt)ref: std::binary_search- return
trueif range[first,last)containsvalue.
- return
v = [1, 1, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6];
echo(sublist(v,lower_bound(v,4),upper_bound(v,4)));
echo(binary_search([1,2,3,5,5,8,12],3));
max (a, b, cmp=lt)ref: std::max- return the greater of the two values. if equivalent, return
a - overrides
maxbuiltin function, but with customizable comparator
- return the greater of the two values. if equivalent, return
max_element (v, first=0, last, cmp=lt)ref: std::max_element- return index of maximum value in range
[first,last), orlastif empty range
- return index of maximum value in range
min (a, b, cmp=lt)ref: std::min- return the lesser of the two values. if equivalent, return
a - overrides
minbuiltin function, but with customizable comparator
- return the lesser of the two values. if equivalent, return
min_element (v, first=0, last, cmp=lt)ref: std::min_element- return index of minimum value in range
[first,last), orlastif empty range
- return index of minimum value in range
minmax (a, b, cmp=lt)ref: std::minmax- return
[a,b]whena <= bor[b,a]whena > b
- return
minmax_element (v, first=0, last, cmp=lt)ref: std::minmax_element- return pair of indices
[i,j]wherev[i]is minimum element andv[j]is maximum in range[first,last), OR[last,last]if empty range - if multiple elements are equivalent to the minimum, return first such index for
i - if multiple elements are equivalent to the maximum, return last such index for
j
- return pair of indices
clamp(x,lo,hi,cmp=lt)ref std::clamp- return
loifxis less thanlo, or tohiifhiis less thanx, otherwisex
- return
// Example compare by index
echo(max([100,1],[-100,2],function(a,b) a[0]<b[0])); // [100,1]
echo(max([100,1],[-100,2],function(a,b) a[1]<b[1])); // [-100,2]
echo(min([100,1],[-100,2],function(a,b) a[0]<b[0])); // [-100,2]
echo(min([100,1],[-100,2],function(a,b) a[1]<b[1])); // [100,1]
// return first when "equivalent"
echo(max([1,2,3],[3,2,1],function(a,b) a[1]<b[1])); // [1,2,3]
echo(min([1,2,3],[3,2,1],function(a,b) a[1]<b[1])); // [1,2,3]
v= [0,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1,0,9];
echo(min_element(v)); // 0
echo(min_element(v,10)); // 18
echo(max_element(v)); // 9
echo(max_element(v,10)); // 19
lencmp = function (a,b) len(a) < len(b);
echo(minmax("World!","Hello",lencmp)); // ["Hello", "World!"]
echo(minmax("Hello","World!",lencmp)); // ["Hello", "World!"]
// [a,b] stay in-order when equivalent
echo(minmax("Hello","World",lencmp)); // ["Hello", "World"]
echo(minmax("world","Hello",lencmp)); // ["World", "Hello"]
s = "The quick brown fox jumps over the lazy dog";
x = minmax_element(s);
echo(x,s[x[0]],s[x[1]]); // [3, 37], " ", "z"
accumulate (v, first=0, last=undef, init=0, op=add)ref: std::accumulate- same as "fold" but with optional params for first, last, op (default addition)
echo(accumulate([1,2,3,4,5]));
echo(accumulate([1,2,3,4,5],init=1,op=function(x,y)x*y));
String specific functions
Strings may be iterated over character by character, and many of the same functions/algorithsm that work on vectors will also "work" on strings. However the value returned is typically a vector of single-character strings, when what is expected is a single string. This is why string-specific functions have their own place here.
substr (s, b, e)- extract substring given begin(inclusive) and end(exclusive)
- if end not specified, go to end of string
join (strs, delimiter="")- converts a vector of values into a single string, with optional delimiter between elements
- uses efficient binary tree based join, where depth of recursion is
log_2(len(strs)).
fixed (x, w, p, sp="0")- format number
xas string with fixed widthwand precisionp.
- format number
Extra type checking functions for which no OpenSCAD buitin exists.
is_range (x)- return true only if x is a range object
is_nan (x)- return true only if x is the nan value
has_nan (x)has_function (v)- return true only if x is a list containing a function
for (
x=[undef,1/0,-1/0,0/0,[],[1,2,3],[1,0/0,2],[1,function(x)x,2],[1,function(x)x,0/0,2],
"hi!","",function(x)x,true,false,0,1,-1,[0:-10],[0:1:10],[0:0:0],[0:1:2]]
) {
if (is_nan(x)) echo(str("is_nan(",x,") = true"));
if (has_nan(x)) echo(str("has_nan(",x,") = true"));
if (has_function(x)) echo(str("has_function(",x,") = true"));
if (is_range(x)) echo(str("is_range(",x,") = true"));
}