2019-05-16 04:07:27 +00:00
//////////////////////////////////////////////////////////////////////
2021-09-17 01:50:12 +00:00
// LibFile: utility.scad
2022-01-15 03:33:21 +00:00
// Functions for type checking, handling undefs, processing function arguments,
// and testing.
2021-01-05 09:20:01 +00:00
// Includes:
// include <BOSL2/std.scad>
2021-12-13 23:48:30 +00:00
// FileGroup: Data Management
2022-01-15 03:33:21 +00:00
// FileSummary: Type checking, dealing with undefs, processing function args
2021-12-13 23:48:30 +00:00
// FileFootnotes: STD=Included in std.scad
2019-05-16 04:07:27 +00:00
//////////////////////////////////////////////////////////////////////
2021-04-14 20:32:47 +00:00
2022-01-15 03:33:21 +00:00
// Section: Type Checking
2019-10-31 02:52:53 +00:00
// Function: typeof()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns a string representing the type of the value.
2021-03-02 06:44:00 +00:00
// Topics: Type Checking
// See Also: is_type()
2023-04-19 21:26:31 +00:00
// Usage:
// typ = typeof(x);
2019-10-31 02:52:53 +00:00
// Description:
2021-01-29 00:59:46 +00:00
// Returns a string representing the type of the value. One of "undef", "boolean", "number", "nan", "string", "list", "range", "function" or "invalid".
2020-07-24 21:54:34 +00:00
// Some malformed "ranges", like '[0:NAN:INF]' and '[0:"a":INF]', may be classified as "undef" or "invalid".
2022-04-10 13:45:33 +00:00
// Arguments:
// x = value whose type to check
2021-09-16 23:33:55 +00:00
// Example:
2021-01-29 00:59:46 +00:00
// typ = typeof(undef); // Returns: "undef"
// typ = typeof(true); // Returns: "boolean"
// typ = typeof(42); // Returns: "number"
// typ = typeof(NAN); // Returns: "nan"
// typ = typeof("foo"); // Returns: "string"
// typ = typeof([3,4,5]); // Returns: "list"
// typ = typeof([3:1:8]); // Returns: "range"
// typ = typeof(function (x,y) x+y); // Returns: "function"
2019-10-31 02:52:53 +00:00
function typeof ( x ) =
2020-05-30 02:04:34 +00:00
is_undef ( x ) ? "undef" :
is_bool ( x ) ? "boolean" :
is_num ( x ) ? "number" :
2020-06-21 02:51:58 +00:00
is_nan ( x ) ? "nan" :
2020-05-30 02:04:34 +00:00
is_string ( x ) ? "string" :
is_list ( x ) ? "list" :
2020-07-24 21:54:34 +00:00
is_range ( x ) ? "range" :
2021-01-29 00:59:46 +00:00
version_num ( ) > 20210000 && is_function ( x ) ? "function" :
2020-07-24 21:54:34 +00:00
"invalid" ;
2019-10-31 02:52:53 +00:00
2020-07-28 20:51:45 +00:00
2019-10-31 02:52:53 +00:00
// Function: is_type()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the type of 'x' is one of those in the list `types`.
2021-03-02 06:44:00 +00:00
// Topics: Type Checking
// See Also: typeof()
2023-04-19 21:26:31 +00:00
// Usage:
// bool = is_type(x, types);
2019-10-31 02:52:53 +00:00
// Description:
// Returns true if the type of the value `x` is one of those given as strings in the list `types`.
2021-01-29 00:59:46 +00:00
// Valid types are "undef", "boolean", "number", "nan", "string", "list", "range", or "function".
2019-10-31 02:52:53 +00:00
// Arguments:
// x = The value to check the type of.
// types = A list of types to check
// Example:
// is_str_or_list = is_type("foo", ["string","list"]); // Returns: true
// is_str_or_list2 = is_type([1,2,3], ["string","list"]); // Returns: true
// is_str_or_list3 = is_type(2, ["string","list"]); // Returns: false
// is_str = is_type("foo", "string"); // Returns: true
// is_str2 = is_type([3,4], "string"); // Returns: false
// is_str3 = is_type(["foo"], "string"); // Returns: false
// is_str4 = is_type(3, "string"); // Returns: false
function is_type ( x , types ) =
2020-05-30 02:04:34 +00:00
is_list ( types ) ? in_list ( typeof ( x ) , types ) :
is_string ( types ) ? typeof ( x ) = = types :
assert ( is_list ( types ) || is_string ( types ) ) ;
2019-05-16 04:07:27 +00:00
// Function: is_def()
2023-05-30 04:48:48 +00:00
// Synopsis: Returns true if `x` is not `undef`.
2021-03-02 06:44:00 +00:00
// Topics: Type Checking
// See Also: typeof(), is_type(), is_str()
2023-05-30 04:48:48 +00:00
// Usage:
// bool = is_def(x);
2019-05-16 04:07:27 +00:00
// Description:
2019-10-31 02:52:53 +00:00
// Returns true if `x` is not `undef`. False if `x==undef`.
2022-04-10 13:45:33 +00:00
// Arguments:
// x = value to check
2021-01-29 00:59:46 +00:00
// Example:
// bool = is_def(undef); // Returns: false
// bool = is_def(false); // Returns: true
// bool = is_def(42); // Returns: true
// bool = is_def("foo"); // Returns: true
2019-10-31 02:52:53 +00:00
function is_def ( x ) = ! is_undef ( x ) ;
// Function: is_str()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument is a string.
// Topics: Type Checking
// See Also: typeof(), is_type(), is_int(), is_def(), is_int()
2019-10-31 02:52:53 +00:00
// Usage:
2021-01-29 00:59:46 +00:00
// bool = is_str(x);
2019-10-31 02:52:53 +00:00
// Description:
// Returns true if `x` is a string. A shortcut for `is_string()`.
2022-04-10 13:45:33 +00:00
// Arguments:
// x = value to check
2021-01-29 00:59:46 +00:00
// Example:
// bool = is_str(undef); // Returns: false
// bool = is_str(false); // Returns: false
// bool = is_str(42); // Returns: false
// bool = is_str("foo"); // Returns: true
2019-10-31 02:52:53 +00:00
function is_str ( x ) = is_string ( x ) ;
2020-01-09 04:43:19 +00:00
// Function: is_int()
2021-03-02 06:44:00 +00:00
// Alias: is_integer()
2023-05-30 04:48:48 +00:00
// Synopsis: Returns true if the argument is an integer.
2023-04-19 21:26:31 +00:00
// Topics: Type Checking
// See Also: typeof(), is_type(), is_str(), is_def()
2020-01-09 04:43:19 +00:00
// Usage:
2021-01-29 00:59:46 +00:00
// bool = is_int(n);
// bool = is_integer(n);
2020-01-09 04:43:19 +00:00
// Description:
// Returns true if the given value is an integer (it is a number and it rounds to itself).
2022-04-10 13:45:33 +00:00
// Arguments:
// n = value to check
2021-01-29 00:59:46 +00:00
// Example:
// bool = is_int(undef); // Returns: false
// bool = is_int(false); // Returns: false
// bool = is_int(42); // Returns: true
// bool = is_int("foo"); // Returns: false
2020-07-24 21:54:34 +00:00
function is_int ( n ) = is_finite ( n ) && n = = round ( n ) ;
function is_integer ( n ) = is_finite ( n ) && n = = round ( n ) ;
2020-01-09 04:43:19 +00:00
2022-03-28 03:06:42 +00:00
// Function: all_integer()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if all of the numbers in the argument are integers.
// Topics: Type Checking
// See also: is_int(), typeof(), is_type()
2022-03-28 03:06:42 +00:00
// 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 ;
2020-02-07 06:51:16 +00:00
// Function: is_nan()
2023-04-19 21:26:31 +00:00
// Synopsis: Return true if the argument is "not a number".
// Topics: Type Checking
// See Also: typeof(), is_type(), is_str(), is_def(), is_int(), is_finite()
2020-02-07 06:51:16 +00:00
// Usage:
2021-01-29 00:59:46 +00:00
// bool = is_nan(x);
2020-02-07 06:51:16 +00:00
// Description:
// Returns true if a given value `x` is nan, a floating point value representing "not a number".
2022-04-10 13:45:33 +00:00
// Arguments:
// x = value to check
2021-01-29 00:59:46 +00:00
// Example:
// bool = is_nan(undef); // Returns: false
// bool = is_nan(false); // Returns: false
// bool = is_nan(42); // Returns: false
// bool = is_nan("foo"); // Returns: false
// bool = is_nan(NAN); // Returns: true
2020-02-07 09:58:45 +00:00
function is_nan ( x ) = ( x ! = x ) ;
2021-03-28 16:09:07 +00:00
2020-02-07 06:51:16 +00:00
2020-07-07 01:28:35 +00:00
// Function: is_finite()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument is a finite number.
2021-03-02 06:44:00 +00:00
// Topics: Type Checking
// See Also: typeof(), is_type(), is_str(), is_def(), is_int(), is_nan()
2023-04-19 21:26:31 +00:00
// Usage:
// bool = is_finite(x);
2020-07-07 01:28:35 +00:00
// Description:
// Returns true if a given value `x` is a finite number.
2022-04-10 13:45:33 +00:00
// Arguments:
// x = value to check
2021-01-29 00:59:46 +00:00
// Example:
// bool = is_finite(undef); // Returns: false
// bool = is_finite(false); // Returns: false
// bool = is_finite(42); // Returns: true
// bool = is_finite("foo"); // Returns: false
// bool = is_finite(NAN); // Returns: false
// bool = is_finite(INF); // Returns: false
// bool = is_finite(-INF); // Returns: false
2020-10-04 02:50:29 +00:00
function is_finite ( x ) = is_num ( x ) && ! is_nan ( 0 * x ) ;
2020-07-07 01:28:35 +00:00
2020-03-21 13:18:22 +00:00
// Function: is_range()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument is a range.
2021-03-02 06:44:00 +00:00
// Topics: Type Checking
// See Also: typeof(), is_type(), is_str(), is_def(), is_int()
2023-05-30 04:48:48 +00:00
// Usage:
// bool = is_range(x);
2020-03-21 13:18:22 +00:00
// Description:
// Returns true if its argument is a range
2022-04-10 13:45:33 +00:00
// Arguments:
// x = value to check
2021-01-29 00:59:46 +00:00
// Example:
// bool = is_range(undef); // Returns: false
// bool = is_range(false); // Returns: false
// bool = is_range(42); // Returns: false
// bool = is_range([3,4,5]); // Returns: false
// bool = is_range("foo"); // Returns: false
// bool = is_range([3:5]); // Returns: true
2020-10-04 02:50:29 +00:00
function is_range ( x ) = ! is_list ( x ) && is_finite ( x [ 0 ] ) && is_finite ( x [ 1 ] ) && is_finite ( x [ 2 ] ) ;
2020-03-21 13:18:22 +00:00
2020-06-21 02:51:58 +00:00
2020-07-29 21:41:02 +00:00
// Function: valid_range()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument is a valid range.
2021-03-02 06:44:00 +00:00
// Topics: Type Checking
// See Also: typeof(), is_type(), is_str(), is_def(), is_int(), is_range()
2023-04-19 21:26:31 +00:00
// Usage:
// bool = valid_range(x);
2020-07-29 21:41:02 +00:00
// Description:
// Returns true if its argument is a valid range (deprecated ranges excluded).
2022-04-10 13:45:33 +00:00
// Arguments:
// x = value to check
2021-01-29 00:59:46 +00:00
// Example:
// bool = is_range(undef); // Returns: false
// bool = is_range(false); // Returns: false
// bool = is_range(42); // Returns: false
// bool = is_range([3,4,5]); // Returns: false
// bool = is_range("foo"); // Returns: false
// bool = is_range([3:5]); // Returns: true
// bool = is_range([3:1]); // Returns: false
2020-07-29 21:41:02 +00:00
function valid_range ( x ) =
is_range ( x )
&& ( x [ 1 ] > 0
? x [ 0 ] < = x [ 2 ]
: ( x [ 1 ] < 0 && x [ 0 ] >= x [ 2 ] ) ) ;
2021-04-06 23:57:26 +00:00
// Function: is_func()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument is a function literal.
// Topics: Type Checking, Function Literals
// See also: is_type(), typeof()
2021-04-06 23:57:26 +00:00
// Usage:
// bool = is_func(x);
// Description:
// Returns true if OpenSCAD supports function literals, and the given item is one.
// Arguments:
2022-04-10 13:45:33 +00:00
// x = The value to check
2021-04-06 23:57:26 +00:00
// Example:
// f = function (a) a==2;
// bool = is_func(f); // Returns: true
function is_func ( x ) = version_num ( ) > 20210000 && is_function ( x ) ;
2020-03-03 02:39:57 +00:00
// Function: is_consistent()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument is a list with consistent structure and finite numerical data.
// Topics: Type Checking, Testing
// See Also: typeof(), is_type(), is_str(), is_def(), is_int(), is_range(), is_homogeneous()
2020-03-03 02:39:57 +00:00
// Usage:
2021-06-27 03:59:33 +00:00
// bool = is_consistent(list, [pattern]);
2020-03-03 02:39:57 +00:00
// Description:
// Tests whether input is a list of entries which all have the same list structure
2021-03-14 16:08:20 +00:00
// and are filled with finite numerical data. You can optionally specify a required
2021-04-11 11:34:59 +00:00
// list structure with the pattern argument.
// It returns `true` for the empty list regardless the value of the `pattern`.
2021-03-14 16:08:20 +00:00
// Arguments:
// list = list to check
// pattern = optional pattern required to match
2020-03-03 02:39:57 +00:00
// Example:
// is_consistent([3,4,5]); // Returns true
// is_consistent([[3,4],[4,5],[6,7]]); // Returns true
// is_consistent([[3,4,5],[3,4]]); // Returns false
// is_consistent([[3,[3,4,[5]]], [5,[2,9,[9]]]]); // Returns true
// is_consistent([[3,[3,4,[5]]], [5,[2,9,9]]]); // Returns false
2021-03-14 16:08:20 +00:00
// is_consistent([3,4,5], 0); // Returns true
// is_consistent([3,4,undef], 0); // Returns false
// is_consistent([[3,4],[4,5]], [1,1]); // Returns true
// is_consistent([[3,"a"],[4,true]], [1,undef]); // Returns true
// is_consistent([[3,4], 6, [4,5]], [1,1]); // Returns false
// is_consistent([[1,[3,4]], [4,[5,6]]], [1,[2,3]]); // Returns true
// is_consistent([[1,[3,INF]], [4,[5,6]]], [1,[2,3]]); // Returns false
// is_consistent([], [1,[2,3]]); // Returns true
function is_consistent ( list , pattern ) =
is_list ( list )
&& ( len ( list ) = = 0
|| ( let ( pattern = is_undef ( pattern ) ? _list_pattern ( list [ 0 ] ) : _list_pattern ( pattern ) )
2021-03-28 16:09:07 +00:00
[ ] = = [ for ( entry = 0 * list ) if ( entry ! = pattern ) entry ] ) ) ;
2020-07-29 20:50:22 +00:00
2020-07-29 21:41:02 +00:00
//Internal function
2021-03-14 16:08:20 +00:00
//Creates a list with the same structure of `list` with each of its elements replaced by 0.
2020-07-29 21:41:02 +00:00
function _list_pattern ( list ) =
is_list ( list )
? [ for ( entry = list ) is_list ( entry ) ? _list_pattern ( entry ) : 0 ]
: 0 ;
2020-07-29 20:50:22 +00:00
2020-07-28 18:02:35 +00:00
2020-03-17 11:11:25 +00:00
// Function: same_shape()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument lists are numeric and of the same shape.
// Topics: Type Checking, Testing
// See Also: is_homogeneous(), is_consistent()
2020-03-17 11:11:25 +00:00
// Usage:
2021-01-29 00:59:46 +00:00
// bool = same_shape(a,b);
2020-03-17 11:11:25 +00:00
// Description:
// Tests whether the inputs `a` and `b` are both numeric and are the same shaped list.
// Example:
// same_shape([3,[4,5]],[7,[3,4]]); // Returns true
// same_shape([3,4,5], [7,[3,4]]); // Returns false
2021-03-05 21:35:41 +00:00
function same_shape ( a , b ) = is_def ( b ) && _list_pattern ( a ) = = b * 0 ;
2020-03-17 11:11:25 +00:00
2021-02-02 22:49:44 +00:00
// Function: is_bool_list()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument list contains only booleans.
// Topics: Boolean Testing
// See Also: is_homogeneous(), is_consistent()
2021-02-02 22:49:44 +00:00
// Usage:
2021-06-27 03:59:33 +00:00
// check = is_bool_list(list,[length])
2021-02-02 22:49:44 +00:00
// Description:
// Tests whether input is a list containing only booleans, and optionally checks its length.
// Arguments:
// list = list to test
// length = if given, list must be this length
function is_bool_list ( list , length ) =
is_list ( list ) && ( is_undef ( length ) || len ( list ) = = length ) && [ ] = = [ for ( entry = list ) if ( ! is_bool ( entry ) ) 1 ] ;
2022-05-27 14:21:33 +00:00
// Section: Boolean list testing
// Function: any()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if any item in the argument list is true.
// Topics: Type Checking
// See Also: all(), num_true()
2022-05-27 14:21:33 +00:00
// 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.
// If `func` is given then returns true if the function evaluates as true on any list entry.
// Items that evaluate as true include nonempty lists, nonempty strings, and nonzero numbers.
// 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()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if all items in the argument list are true.
// Topics: Type Checking
// See Also: any(), num_true()
2022-05-27 14:21:33 +00:00
// 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 then returns true if the function evaluates as true on all list etnries.
// Items that evaluate as true include nonempty lists, nonempty strings, and nonzero numbers.
// 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: num_true()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the number of true entries in the arguemnt list.
// Topics: Boolean Testing
// See Also: any(), all()
2022-05-27 14:21:33 +00:00
// Usage:
// seq = num_true(l);
// seq = num_true(l, func); // 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 `func` is given then counts
// list entries where the function evaluates as true.
// Items that evaluate as true include nonempty lists, nonempty strings, and nonzero numbers.
// 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:
// num1 = num_true([0,false,undef]); // Returns 0.
// num2 = num_true([1,false,undef]); // Returns 1.
// num3 = num_true([1,5,false]); // Returns 2.
// num4 = num_true([1,5,true]); // Returns 3.
// num5 = num_true([[0,0], [0,0]]); // Returns 2.
// num6 = num_true([[], [1,0]]); // Returns 1.
function num_true ( l , func ) =
assert ( is_list ( l ) )
assert ( func = = undef || is_func ( func ) )
let (
true_list = is_def ( func ) ? [ for ( entry = l ) if ( func ( entry ) ) 1 ]
: [ for ( entry = l ) if ( entry ) 1 ]
)
len ( true_list ) ;
2019-10-31 02:52:53 +00:00
// Section: Handling `undef`s.
2019-05-16 04:07:27 +00:00
// Function: default()
2023-05-01 03:16:43 +00:00
// Synopsis: Returns a default value if the argument is 'undef', else returns the argument.
2021-03-02 06:44:00 +00:00
// Topics: Undef Handling
// See Also: first_defined(), one_defined(), num_defined()
2023-04-19 21:26:31 +00:00
// Usage:
// val = default(val, dflt);
2019-05-16 04:07:27 +00:00
// Description:
2021-03-29 10:33:37 +00:00
// Returns the value given as `v` if it is not `undef`.
// Otherwise, returns the value of `dflt`.
2019-05-16 04:07:27 +00:00
// Arguments:
// v = Value to pass through if not `undef`.
2022-04-10 13:45:33 +00:00
// dflt = Value to return if `v` *is* `undef`. Default: undef
2019-05-16 04:07:27 +00:00
function default ( v , dflt = undef ) = is_undef ( v ) ? dflt : v ;
// Function: first_defined()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the first value in the argument list that is not 'undef'.
2021-03-02 06:44:00 +00:00
// Topics: Undef Handling
// See Also: default(), one_defined(), num_defined(), any_defined(), all_defined()
2023-04-19 21:26:31 +00:00
// Usage:
// val = first_defined(v, [recursive]);
2019-05-16 04:07:27 +00:00
// Description:
// Returns the first item in the list that is not `undef`.
// If all items are `undef`, or list is empty, returns `undef`.
2019-07-11 04:52:47 +00:00
// Arguments:
// v = The list whose items are being checked.
2022-04-10 13:45:33 +00:00
// recursive = If true, sublists are checked recursively for defined values. The first sublist that has a defined item is returned. Default: false
2021-09-16 23:33:55 +00:00
// Example:
2021-04-11 11:34:59 +00:00
// val = first_defined([undef,7,undef,true]); // Returns: 7
2019-07-11 04:52:47 +00:00
function first_defined ( v , recursive = false , _i = 0 ) =
2020-05-30 02:04:34 +00:00
_i < len ( v ) && (
is_undef ( v [ _i ] ) || (
recursive &&
is_list ( v [ _i ] ) &&
is_undef ( first_defined ( v [ _i ] , recursive = recursive ) )
)
) ? first_defined ( v , recursive = recursive , _i = _i + 1 ) : v [ _i ] ;
2020-08-20 21:42:24 +00:00
2019-05-16 04:07:27 +00:00
2020-03-21 13:18:22 +00:00
// Function: one_defined()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the defined value in the argument list if only a single value is defined.
2021-03-02 06:44:00 +00:00
// Topics: Undef Handling
// See Also: default(), first_defined(), num_defined(), any_defined(), all_defined()
2023-04-19 21:26:31 +00:00
// Usage:
// val = one_defined(vals, names, [dflt])
2020-03-21 13:18:22 +00:00
// Description:
2021-01-29 00:59:46 +00:00
// Examines the input list `vals` and returns the entry which is not `undef`.
// If more than one entry is not `undef` then an error is asserted, specifying
// "Must define exactly one of" followed by the names in the `names` parameter.
// If `dflt` is given, and all `vals` are `undef`, then the value in `dflt` is returned.
// If `dflt` is *not* given, and all `vals` are `undef`, then an error is asserted.
// Arguments:
// vals = The values to return the first one which is not `undef`.
// names = A string with comma-separated names for the arguments whose values are passed in `vals`.
// dflt = If given, the value returned if all `vals` are `undef`.
2021-09-16 23:33:55 +00:00
// Example:
// length1 = one_defined([length,L,l], ["length","L","l"]);
// length2 = one_defined([length,L,l], "length,L,l", dflt=1);
2021-04-14 20:32:47 +00:00
function one_defined ( vals , names , dflt = _UNDEF ) =
2021-01-29 00:59:46 +00:00
let (
checkargs = is_list ( names ) ? assert ( len ( vals ) = = len ( names ) ) :
is_string ( names ) ? let (
name_cnt = len ( [ for ( c = names ) if ( c = = "," ) 1 ] ) + 1
) assert ( len ( vals ) = = name_cnt ) :
assert ( is_list ( names ) || is_string ( names ) ) 0 ,
2021-01-29 02:54:34 +00:00
ok = num_defined ( vals ) = = 1 || ( dflt ! = _UNDEF && num_defined ( vals ) = = 0 )
2021-01-29 00:59:46 +00:00
) ok ? default ( first_defined ( vals ) , dflt ) :
let (
names = is_string ( names ) ? str_split ( names , "," ) : names ,
defd = [ for ( i = idx ( vals ) ) if ( is_def ( vals [ i ] ) ) names [ i ] ] ,
msg = str (
"Must define " ,
2021-01-29 02:54:34 +00:00
dflt = = _UNDEF ? "exactly" : "at most" ,
2021-01-29 00:59:46 +00:00
" one of " ,
num_defined ( vals ) = = 0 ? names : defd
)
) assert ( ok , msg ) ;
2020-03-21 13:18:22 +00:00
2019-05-16 04:07:27 +00:00
// Function: num_defined()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the number of defined values in the the argument list.
2021-03-02 06:44:00 +00:00
// Topics: Undef Handling
// See Also: default(), first_defined(), one_defined(), any_defined(), all_defined()
2023-04-19 21:26:31 +00:00
// Usage:
// cnt = num_defined(v);
2021-01-29 00:59:46 +00:00
// Description:
// Counts how many items in list `v` are not `undef`.
// Example:
// cnt = num_defined([3,7,undef,2,undef,undef,1]); // Returns: 4
function num_defined ( v ) =
len ( [ for ( vi = v ) if ( ! is_undef ( vi ) ) 1 ] ) ;
2019-05-16 04:07:27 +00:00
// Function: any_defined()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if any item in the argument list is not `undef`.
2021-03-02 06:44:00 +00:00
// Topics: Undef Handling
// See Also: default(), first_defined(), one_defined(), num_defined(), all_defined()
2023-04-19 21:26:31 +00:00
// Usage:
// bool = any_defined(v, [recursive]);
2019-05-16 04:07:27 +00:00
// Description:
// Returns true if any item in the given array is not `undef`.
2019-07-11 04:52:47 +00:00
// Arguments:
// v = The list whose items are being checked.
2022-04-10 13:45:33 +00:00
// recursive = If true, any sublists are evaluated recursively. Default: false
2021-01-29 00:59:46 +00:00
// Example:
// bool = any_defined([undef,undef,undef]); // Returns: false
// bool = any_defined([undef,42,undef]); // Returns: true
// bool = any_defined([34,42,87]); // Returns: true
// bool = any_defined([undef,undef,[undef]]); // Returns: true
// bool = any_defined([undef,undef,[undef]],recursive=true); // Returns: false
// bool = any_defined([undef,undef,[42]],recursive=true); // Returns: true
function any_defined ( v , recursive = false ) =
first_defined ( v , recursive = recursive ) ! = undef ;
2019-05-16 04:07:27 +00:00
// Function: all_defined()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if all items in the given array are defined.
// Topics: Undef Handling
// See Also: default(), first_defined(), one_defined(), num_defined(), all_defined()
2021-01-29 00:59:46 +00:00
// Usage:
2021-06-27 03:59:33 +00:00
// bool = all_defined(v, [recursive]);
2019-05-16 04:07:27 +00:00
// Description:
// Returns true if all items in the given array are not `undef`.
2019-07-11 04:52:47 +00:00
// Arguments:
// v = The list whose items are being checked.
2022-04-10 13:45:33 +00:00
// recursive = If true, any sublists are evaluated recursively. Default: false
2021-01-29 00:59:46 +00:00
// Example:
// bool = all_defined([undef,undef,undef]); // Returns: false
// bool = all_defined([undef,42,undef]); // Returns: false
// bool = all_defined([34,42,87]); // Returns: true
// bool = all_defined([23,34,[undef]]); // Returns: true
// bool = all_defined([23,34,[undef]],recursive=true); // Returns: false
// bool = all_defined([23,34,[42]],recursive=true); // Returns: true
2020-08-20 21:42:24 +00:00
function all_defined ( v , recursive = false ) =
[ ] = = [ for ( x = v ) if ( is_undef ( x ) || ( recursive && is_list ( x ) && ! all_defined ( x , recursive ) ) ) 0 ] ;
2019-05-16 04:07:27 +00:00
2020-03-21 13:18:22 +00:00
2022-03-28 03:06:42 +00:00
// Section: Undef Safe Arithmetic
// Function: u_add()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the sum of 2 numbers if both are defined, otherwise returns undef.
// Topics: Undef Handling
// See Also: u_sub(), u_mul(), u_div()
2022-03-28 03:06:42 +00:00
// 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()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the difference of 2 numbers if both are defined, otherwise returns undef.
// Topics: Undef Handling
// See Also: u_add(), u_mul(), u_div()
2022-03-28 03:06:42 +00:00
// 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()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the product of 2 numbers if both are defined, otherwise returns undef.
// Topics: Undef Handling
// See Also: u_add(), u_sub(), u_div()
2022-03-28 03:06:42 +00:00
// 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()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the quotient of 2 numbers if both are defined, otherwise returns undef.
// Topics: Undef Handling
// See Also: u_add(), u_sub(), u_mul()
2022-03-28 03:06:42 +00:00
// 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 ;
2022-01-15 03:33:21 +00:00
// Section: Processing Arguments to Functions and Modules
2019-05-16 04:07:27 +00:00
2020-02-29 05:34:58 +00:00
// Function: get_anchor()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the correct anchor from `anchor` and `center`.
2021-03-02 06:44:00 +00:00
// Topics: Argument Handling
2021-04-19 20:58:17 +00:00
// See Also: get_radius()
2023-04-19 21:26:31 +00:00
// Usage:
// anchr = get_anchor(anchor,center,[uncentered],[dflt]);
2020-02-29 05:34:58 +00:00
// Description:
// Calculated the correct anchor from `anchor` and `center`. In order:
// - If `center` is not `undef` and `center` evaluates as true, then `CENTER` (`[0,0,0]`) is returned.
// - Otherwise, if `center` is not `undef` and `center` evaluates as false, then the value of `uncentered` is returned.
// - Otherwise, if `anchor` is not `undef`, then the value of `anchor` is returned.
// - Otherwise, the value of `dflt` is returned.
2023-11-05 15:05:54 +00:00
// .
2020-02-29 05:34:58 +00:00
// This ordering ensures that `center` will override `anchor`.
// Arguments:
// anchor = The anchor name or vector.
// center = If not `undef`, this overrides the value of `anchor`.
2021-11-12 03:55:03 +00:00
// uncentered = The value to return if `center` is not `undef` and evaluates as false. Default: BOTTOM
2020-02-29 05:34:58 +00:00
// dflt = The default value to return if both `anchor` and `center` are `undef`. Default: `CENTER`
2021-09-16 23:33:55 +00:00
// Example:
// anchr1 = get_anchor(undef, undef, BOTTOM, TOP); // Returns: [0, 0, 1] (TOP)
// anchr2 = get_anchor(RIGHT, undef, BOTTOM, TOP); // Returns: [1, 0, 0] (RIGHT)
// anchr3 = get_anchor(undef, false, BOTTOM, TOP); // Returns: [0, 0,-1] (BOTTOM)
// anchr4 = get_anchor(RIGHT, false, BOTTOM, TOP); // Returns: [0, 0,-1] (BOTTOM)
// anchr5 = get_anchor(undef, true, BOTTOM, TOP); // Returns: [0, 0, 0] (CENTER)
// anchr6 = get_anchor(RIGHT, true, BOTTOM, TOP); // Returns: [0, 0, 0] (CENTER)
2020-02-29 05:34:58 +00:00
function get_anchor ( anchor , center , uncentered = BOT , dflt = CENTER ) =
2020-05-30 02:04:34 +00:00
! is_undef ( center ) ? ( center ? CENTER : uncentered ) :
! is_undef ( anchor ) ? anchor :
dflt ;
2020-02-29 05:34:58 +00:00
2019-05-16 04:07:27 +00:00
// Function: get_radius()
2023-04-19 21:26:31 +00:00
// Synopsis: Given various radii and diameters, returns the most specific radius.
2021-03-02 06:44:00 +00:00
// Topics: Argument Handling
2021-04-19 20:58:17 +00:00
// See Also: get_anchor()
2023-04-19 21:26:31 +00:00
// Usage:
// r = get_radius([r1=], [r2=], [r=], [d1=], [d2=], [d=], [dflt=]);
2021-01-29 00:59:46 +00:00
// Description:
// Given various radii and diameters, returns the most specific radius. If a diameter is most
// specific, returns half its value, giving the radius. If no radii or diameters are defined,
// returns the value of `dflt`. Value specificity order is `r1`, `r2`, `d1`, `d2`, `r`, `d`,
// then `dflt`. Only one of `r1`, `r2`, `d1`, or `d2` can be defined at once, or else it errors
2022-03-08 00:43:12 +00:00
// out, complaining about conflicting radius/diameter values.
2019-05-16 04:07:27 +00:00
// Arguments:
2021-01-29 00:59:46 +00:00
// ---
2019-05-16 04:07:27 +00:00
// r1 = Most specific radius.
2019-08-03 09:18:40 +00:00
// r2 = Second most specific radius.
2019-05-16 04:07:27 +00:00
// r = Most general radius.
2021-01-29 00:59:46 +00:00
// d1 = Most specific diameter.
// d2 = Second most specific diameter.
2019-05-16 04:07:27 +00:00
// d = Most general diameter.
// dflt = Value to return if all other values given are `undef`.
2021-09-16 23:33:55 +00:00
// Example:
2021-01-29 00:59:46 +00:00
// r = get_radius(r1=undef, r=undef, dflt=undef); // Returns: undef
// r = get_radius(r1=undef, r=undef, dflt=1); // Returns: 1
// r = get_radius(r1=undef, r=6, dflt=1); // Returns: 6
// r = get_radius(r1=7, r=6, dflt=1); // Returns: 7
// r = get_radius(r1=undef, r2=8, r=6, dflt=1); // Returns: 8
// r = get_radius(r1=undef, r2=8, d=6, dflt=1); // Returns: 8
// r = get_radius(r1=undef, d=6, dflt=1); // Returns: 3
// r = get_radius(d1=7, d=6, dflt=1); // Returns: 3.5
// r = get_radius(d1=7, d2=8, d=6, dflt=1); // Returns: 3.5
// r = get_radius(d1=undef, d2=8, d=6, dflt=1); // Returns: 4
// r = get_radius(r1=8, d=6, dflt=1); // Returns: 8
2020-12-13 04:28:33 +00:00
function get_radius ( r1 , r2 , r , d1 , d2 , d , dflt ) =
2020-08-20 21:42:24 +00:00
assert ( num_defined ( [ r1 , d1 , r2 , d2 ] ) < 2 , "Conflicting or redundant radius/diameter arguments given." )
2021-04-21 01:52:45 +00:00
assert ( num_defined ( [ r , d ] ) < 2 , "Conflicting or redundant radius/diameter arguments given." )
let (
rad = ! is_undef ( r1 ) ? r1
: ! is_undef ( d1 ) ? d1 / 2
: ! is_undef ( r2 ) ? r2
: ! is_undef ( d2 ) ? d2 / 2
: ! is_undef ( r ) ? r
: ! is_undef ( d ) ? d / 2
: dflt
)
assert ( is_undef ( dflt ) || is_finite ( rad ) || is_vector ( rad ) , "Invalid radius." )
rad ;
2020-08-20 21:42:24 +00:00
2019-05-16 04:07:27 +00:00
// Function: scalar_vec3()
2023-04-19 21:26:31 +00:00
// Synopsis: Expands a scalar or a list with length less than 3 to a length 3 vector.
2021-03-02 06:44:00 +00:00
// Topics: Argument Handling
// See Also: get_anchor(), get_radius(), force_list()
2023-04-19 21:26:31 +00:00
// Usage:
// vec = scalar_vec3(v, [dflt]);
2019-05-16 04:07:27 +00:00
// Description:
2021-09-17 01:50:12 +00:00
// This is expands a scalar or a list with length less than 3 to a length 3 vector in the
// same way that OpenSCAD expands short vectors in some contexts, e.g. cube(10) or rotate([45,90]).
2019-05-16 04:07:27 +00:00
// If `v` is a scalar, and `dflt==undef`, returns `[v, v, v]`.
// If `v` is a scalar, and `dflt!=undef`, returns `[v, dflt, dflt]`.
2022-09-09 01:37:54 +00:00
// If `v` is a vector and dflt is defined, returns the first 3 items, with any missing values replaced by `dflt`.
// If `v` is a vector and dflt is undef, returns the first 3 items, with any missing values replaced by 0.
2019-05-16 04:07:27 +00:00
// If `v` is `undef`, returns `undef`.
// Arguments:
// v = Value to return vector from.
// dflt = Default value to set empty vector parts from.
2021-09-16 23:33:55 +00:00
// Example:
2021-01-29 00:59:46 +00:00
// vec = scalar_vec3(undef); // Returns: undef
// vec = scalar_vec3(10); // Returns: [10,10,10]
// vec = scalar_vec3(10,1); // Returns: [10,1,1]
// vec = scalar_vec3([10,10],1); // Returns: [10,10,1]
2022-09-09 01:37:54 +00:00
// vec = scalar_vec3([10,10]); // Returns: [10,10,0]
// vec = scalar_vec3([10]); // Returns: [10,0,0]
2021-01-29 00:59:46 +00:00
function scalar_vec3 ( v , dflt ) =
2020-05-30 02:04:34 +00:00
is_undef ( v ) ? undef :
is_list ( v ) ? [ for ( i = [ 0 : 2 ] ) default ( v [ i ] , default ( dflt , 0 ) ) ] :
! is_undef ( dflt ) ? [ v , dflt , dflt ] : [ v , v , v ] ;
2019-05-16 04:07:27 +00:00
2020-01-09 04:43:19 +00:00
// Function: segs()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns the number of sides for a circle given `$fn`, `$fa`, and `$fs`.
// Topics: Geometry
2023-05-30 04:48:48 +00:00
// See Also: circle(), cyl()
2020-01-09 04:43:19 +00:00
// Usage:
// sides = segs(r);
// Description:
// Calculate the standard number of sides OpenSCAD would give a circle based on `$fn`, `$fa`, and `$fs`.
// Arguments:
// r = Radius of circle to get the number of segments for.
2021-09-16 23:33:55 +00:00
// Example:
2021-01-29 00:59:46 +00:00
// $fn=12; sides=segs(10); // Returns: 12
2023-12-13 22:00:11 +00:00
// $fa=2; $fs=3; sides=segs(10); // Returns: 21
2020-07-24 21:54:34 +00:00
function segs ( r ) =
2020-05-30 02:04:34 +00:00
$fn > 0 ? ( $fn > 3 ? $fn : 3 ) :
2021-01-29 00:59:46 +00:00
let ( r = is_finite ( r ) ? r : 0 )
ceil ( max ( 5 , min ( 360 / $fa , abs ( r ) * 2 * PI / $fs ) ) ) ;
2020-01-09 04:43:19 +00:00
2020-06-21 02:51:58 +00:00
2020-08-01 19:54:58 +00:00
// Module: no_children()
2023-04-19 21:26:31 +00:00
// Synopsis: Assert that the calling module does not support children.
2021-04-11 11:34:59 +00:00
// Topics: Error Checking
2022-03-30 23:44:46 +00:00
// See Also: no_function(), no_module(), req_children()
2023-04-19 21:26:31 +00:00
// Usage:
// no_children($children);
2020-08-01 19:54:58 +00:00
// Description:
2021-04-07 02:18:57 +00:00
// Assert that the calling module does not support children. Prints an error message to this effect and fails if children are present,
// as indicated by its argument.
2020-08-01 19:54:58 +00:00
// Arguments:
// $children = number of children the module has.
2021-01-29 00:59:46 +00:00
// Example:
2021-01-29 01:13:35 +00:00
// module foo() {
// no_children($children);
// }
2020-08-01 19:54:58 +00:00
module no_children ( count ) {
2020-08-27 23:25:41 +00:00
assert ( $children = = 0 , "Module no_children() does not support child modules" ) ;
2021-01-29 01:26:46 +00:00
if ( $ parent_modules > 0 ) {
assert ( count = = 0 , str ( "Module " , parent_module ( 1 ) , "() does not support child modules" ) ) ;
}
2020-08-01 19:54:58 +00:00
}
2021-01-29 00:59:46 +00:00
2022-03-30 23:44:46 +00:00
// Module: req_children()
2023-05-01 03:16:43 +00:00
// Synopsis: Assert that the calling module requires children.
2022-03-30 23:44:46 +00:00
// Topics: Error Checking
// See Also: no_function(), no_module()
2023-04-19 21:26:31 +00:00
// Usage:
// req_children($children);
2022-03-30 23:44:46 +00:00
// Description:
// Assert that the calling module requires children. Prints an error message and fails if no
// children are present as indicated by its argument.
// Arguments:
// $children = number of children the module has.
// Example:
// module foo() {
// req_children($children);
// }
module req_children ( count ) {
assert ( $children = = 0 , "Module no_children() does not support child modules" ) ;
if ( $ parent_modules > 0 ) {
assert ( count > 0 , str ( "Module " , parent_module ( 1 ) , "() requires children" ) ) ;
}
}
2021-01-03 04:28:11 +00:00
// Function: no_function()
2023-04-19 21:26:31 +00:00
// Synopsis: Assert that the argument exists only as a module and not as a function.
2021-03-02 06:44:00 +00:00
// Topics: Error Checking
// See Also: no_children(), no_module()
2023-05-30 04:48:48 +00:00
// Usage:
// dummy = no_function(name)
2021-01-03 04:28:11 +00:00
// Description:
// Asserts that the function, "name", only exists as a module.
// Example:
2021-01-29 00:59:46 +00:00
// x = no_function("foo");
2021-01-03 04:28:11 +00:00
function no_function ( name ) =
assert ( false , str ( "You called " , name , "() as a function, but it is available only as a module" ) ) ;
// Module: no_module()
2023-04-19 21:26:31 +00:00
// Synopsis: Assert that the argument exists only as a function and not as a module.
2021-03-02 06:44:00 +00:00
// Topics: Error Checking
// See Also: no_children(), no_function()
2023-04-19 21:26:31 +00:00
// Usage:
// no_module();
2021-01-03 04:28:11 +00:00
// Description:
// Asserts that the called module exists only as a function.
2021-01-29 00:59:46 +00:00
// Example:
2021-09-06 23:46:18 +00:00
// module foo() { no_module(); }
2021-01-03 04:28:11 +00:00
module no_module ( ) {
assert ( false , str ( "You called " , parent_module ( 1 ) , "() as a module but it is available only as a function" ) ) ;
}
2020-08-01 19:54:58 +00:00
2022-10-14 22:32:04 +00:00
// Module: deprecate()
2023-04-19 21:26:31 +00:00
// Synopsis: Display a console note that a module is deprecated and suggest a replacement.
2023-05-30 04:48:48 +00:00
// Topics: Error Checking
// See Also: no_function(), no_module()
2022-10-14 22:32:04 +00:00
// Usage:
// deprecate(new_name);
// Description:
// Display info that the current module is deprecated and you should switch to a new name
// Arguments:
// new_name = name of the new module that replaces the old one
module deprecate ( new_name )
{
echo ( str ( "***** Module " , parent_module ( 1 ) , "() has been replaced by " , new_name , "() and will be removed in a future version *****" ) ) ;
}
2021-03-29 10:33:37 +00:00
2020-06-21 02:51:58 +00:00
// Section: Testing Helpers
function _valstr ( x ) =
2021-07-01 02:56:17 +00:00
is_string ( x ) ? str ( "\"" , str_replace_char ( x , "\"" , "\\\"" ) , "\"" ) :
2020-06-21 02:51:58 +00:00
is_list ( x ) ? str ( "[" , str_join ( [ for ( xx = x ) _valstr ( xx ) ] , "," ) , "]" ) :
2022-01-11 00:46:51 +00:00
is_num ( x ) && x = = floor ( x ) ? format_int ( x ) :
is_finite ( x ) ? format_float ( x , 12 ) : x ;
2020-06-21 02:51:58 +00:00
// Module: assert_approx()
2023-04-20 00:34:33 +00:00
// Synopsis: Assert that a value is approximately what was expected.
2021-03-02 06:44:00 +00:00
// Topics: Error Checking, Debugging
// See Also: no_children(), no_function(), no_module(), assert_equal()
2023-04-19 21:26:31 +00:00
// Usage:
// assert_approx(got, expected, [info]);
2020-06-21 02:51:58 +00:00
// Description:
2023-04-20 00:34:33 +00:00
// Tests if the value gotten is what was expected, plus or minus 1e-9. If not, then
2020-06-21 02:51:58 +00:00
// the expected and received values are printed to the console and
// an assertion is thrown to stop execution.
2023-04-20 00:34:33 +00:00
// Returns false if both 'got' and 'expected' are 'nan'.
2020-06-21 02:51:58 +00:00
// Arguments:
// got = The value actually received.
// expected = The value that was expected.
// info = Extra info to print out to make the error clearer.
2021-01-29 00:59:46 +00:00
// Example:
2022-04-10 13:45:33 +00:00
// assert_approx(1/3, 0.333333333333333, str("number=",1,", denom=",3));
2020-06-21 02:51:58 +00:00
module assert_approx ( got , expected , info ) {
2020-08-27 23:25:41 +00:00
no_children ( $children ) ;
2020-06-21 02:51:58 +00:00
if ( ! approx ( got , expected ) ) {
echo ( ) ;
echo ( str ( "EXPECT: " , _valstr ( expected ) ) ) ;
echo ( str ( "GOT : " , _valstr ( got ) ) ) ;
if ( same_shape ( got , expected ) ) {
echo ( str ( "DELTA : " , _valstr ( got - expected ) ) ) ;
}
if ( is_def ( info ) ) {
echo ( str ( "INFO : " , _valstr ( info ) ) ) ;
}
assert ( approx ( got , expected ) ) ;
}
}
// Module: assert_equal()
2023-04-19 21:26:31 +00:00
// Synopsis: Assert that a value is expected.
// See Also: no_children(), no_function(), no_module(), assert_approx()
// Topics: Error Checking, Debugging
2020-06-21 02:51:58 +00:00
// Usage:
2021-06-27 03:59:33 +00:00
// assert_equal(got, expected, [info]);
2020-06-21 02:51:58 +00:00
// Description:
2021-01-29 00:59:46 +00:00
// Tests if the value gotten is what was expected. If not, then the expected and received values
// are printed to the console and an assertion is thrown to stop execution.
2023-04-20 00:34:33 +00:00
// Returns true if both 'got' and 'expected' are 'nan'.
2020-06-21 02:51:58 +00:00
// Arguments:
// got = The value actually received.
// expected = The value that was expected.
// info = Extra info to print out to make the error clearer.
2021-01-29 00:59:46 +00:00
// Example:
// assert_approx(3*9, 27, str("a=",3,", b=",9));
2020-06-21 02:51:58 +00:00
module assert_equal ( got , expected , info ) {
2020-08-27 23:25:41 +00:00
no_children ( $children ) ;
2020-06-21 02:51:58 +00:00
if ( got ! = expected || ( is_nan ( got ) && is_nan ( expected ) ) ) {
echo ( ) ;
echo ( str ( "EXPECT: " , _valstr ( expected ) ) ) ;
echo ( str ( "GOT : " , _valstr ( got ) ) ) ;
if ( same_shape ( got , expected ) ) {
echo ( str ( "DELTA : " , _valstr ( got - expected ) ) ) ;
}
if ( is_def ( info ) ) {
echo ( str ( "INFO : " , _valstr ( info ) ) ) ;
}
assert ( got = = expected ) ;
}
}
2020-07-21 23:54:59 +00:00
// Module: shape_compare()
2023-04-19 21:26:31 +00:00
// Synopsis: Compares two child shapes.
// SynTags: Geom
2021-03-02 06:44:00 +00:00
// Topics: Error Checking, Debugging, Testing
// See Also: assert_approx(), assert_equal()
2023-04-19 21:26:31 +00:00
// Usage:
// shape_compare([eps]) {TEST_SHAPE; EXPECTED_SHAPE;}
2020-07-21 23:54:59 +00:00
// Description:
// Compares two child shapes, returning empty geometry if they are very nearly the same shape and size.
2021-01-29 00:59:46 +00:00
// Returns the differential geometry if they are not quite the same shape and size.
2020-07-21 23:54:59 +00:00
// Arguments:
// eps = The surface of the two shapes must be within this size of each other. Default: 1/1024
2021-01-29 00:59:46 +00:00
// Example:
// $fn=36;
// shape_compare() {
// sphere(d=100);
// rotate_extrude() right_half(planar=true) circle(d=100);
// }
2020-07-21 23:54:59 +00:00
module shape_compare ( eps = 1 / 1024 ) {
2022-04-10 13:45:33 +00:00
assert ( $children = = 2 , "Must give exactly two children" ) ;
2020-07-21 23:54:59 +00:00
union ( ) {
difference ( ) {
children ( 0 ) ;
if ( eps = = 0 ) {
children ( 1 ) ;
} else {
minkowski ( ) {
children ( 1 ) ;
2021-01-29 00:59:46 +00:00
spheroid ( r = eps , style = "octa" ) ;
2020-07-21 23:54:59 +00:00
}
}
}
difference ( ) {
children ( 1 ) ;
if ( eps = = 0 ) {
children ( 0 ) ;
} else {
minkowski ( ) {
children ( 0 ) ;
2021-01-29 00:59:46 +00:00
spheroid ( r = eps , style = "octa" ) ;
2020-07-21 23:54:59 +00:00
}
}
}
}
}
2023-03-03 03:54:39 +00:00
// Section: C-Style For Loop Helpers
2020-10-06 08:41:39 +00:00
// You can use a list comprehension with a C-style for loop to iteratively make a calculation.
2020-10-06 20:48:14 +00:00
// .
2020-10-06 08:41:39 +00:00
// The syntax is: `[for (INIT; CONDITION; NEXT) RETVAL]` where:
// - INIT is zero or more `let()` style assignments that are evaluated exactly one time, before the first loop.
// - CONDITION is an expression evaluated at the start of each loop. If true, continues with the loop.
2021-04-07 02:18:57 +00:00
// - RETVAL is an expression that returns a list item for each loop.
// - NEXT is one or more `let()` style assignments that is evaluated at the end of each loop.
2020-10-06 20:48:14 +00:00
// .
2020-10-06 08:41:39 +00:00
// Since the INIT phase is only run once, and the CONDITION and RETVAL expressions cannot update
// variables, that means that only the NEXT phase can be used for iterative calculations.
// Unfortunately, the NEXT phase runs *after* the RETVAL expression, which means that you need
// to run the loop one extra time to return the final value. This tends to make the loop code
2020-10-07 01:48:39 +00:00
// look rather ugly. The `looping()`, `loop_while()` and `loop_done()` functions
2020-10-06 08:41:39 +00:00
// can make this somewhat more legible.
2021-01-29 00:59:46 +00:00
// .
2020-10-06 20:48:14 +00:00
// ```openscad
2020-10-06 08:41:39 +00:00
// function flat_sum(l) = [
// for (
// i = 0,
// total = 0,
// state = 0;
//
// looping(state);
//
2020-10-07 01:48:39 +00:00
// state = loop_while(state, i < len(l)),
2020-10-06 08:41:39 +00:00
// total = total +
2020-10-07 01:48:39 +00:00
// loop_done(state) ? 0 :
2020-10-06 08:41:39 +00:00
// let( x = l[i] )
// is_list(x) ? flat_sum(x) : x,
// i = i + 1
// ) if (loop_done(state)) total;
// ].x;
2020-10-06 20:48:14 +00:00
// ```
2020-10-06 08:41:39 +00:00
// Function: looping()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument indicates the current C-style loop should continue.
2021-03-02 06:44:00 +00:00
// Topics: Iteration
// See Also: loop_while(), loop_done()
2023-04-19 21:26:31 +00:00
// Usage:
// bool = looping(state);
2020-10-06 08:41:39 +00:00
// Description:
2021-01-29 00:59:46 +00:00
// Returns true if the `state` value indicates the current loop should continue. This is useful
// when using C-style for loops to iteratively calculate a value. Used with `loop_while()` and
2021-03-02 06:44:00 +00:00
// `loop_done()`. See [Looping Helpers](section-looping-helpers) for an example.
2020-10-06 08:41:39 +00:00
// Arguments:
// state = The loop state value.
2020-10-07 01:48:39 +00:00
function looping ( state ) = state < 2 ;
2020-10-06 08:41:39 +00:00
2020-10-07 01:48:39 +00:00
// Function: loop_while()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if both arguments indicate the current C-style loop should continue.
2021-03-02 06:44:00 +00:00
// Topics: Iteration
// See Also: looping(), loop_done()
2023-04-19 21:26:31 +00:00
// Usage:
// state = loop_while(state, continue);
2020-10-06 08:41:39 +00:00
// Description:
// Given the current `state`, and a boolean `continue` that indicates if the loop should still be
2021-01-29 00:59:46 +00:00
// continuing, returns the updated state value for the the next loop. This is useful when using
// C-style for loops to iteratively calculate a value. Used with `looping()` and `loop_done()`.
2021-03-02 06:44:00 +00:00
// See [Looping Helpers](section-looping-helpers) for an example.
2020-10-06 08:41:39 +00:00
// Arguments:
// state = The loop state value.
// continue = A boolean value indicating whether the current loop should progress.
2020-10-07 01:48:39 +00:00
function loop_while ( state , continue ) =
state > 0 ? 2 :
continue ? 0 : 1 ;
2020-10-06 08:41:39 +00:00
// Function: loop_done()
2023-04-19 21:26:31 +00:00
// Synopsis: Returns true if the argument indicates the current C-style loop is finishing.
2021-03-02 06:44:00 +00:00
// Topics: Iteration
// See Also: looping(), loop_while()
2023-04-19 21:26:31 +00:00
// Usage:
// bool = loop_done(state);
2020-10-06 08:41:39 +00:00
// Description:
2021-01-29 00:59:46 +00:00
// Returns true if the `state` value indicates the loop is finishing. This is useful when using
// C-style for loops to iteratively calculate a value. Used with `looping()` and `loop_while()`.
// See [Looping Helpers](#5-looping-helpers) for an example.
2020-10-06 08:41:39 +00:00
// Arguments:
// state = The loop state value.
2020-10-07 01:48:39 +00:00
function loop_done ( state ) = state > 0 ;
2020-10-04 02:50:29 +00:00
2020-06-21 02:51:58 +00:00
2020-05-30 02:04:34 +00:00
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap