1//////////////////////////////////////////////////////////////////////
2// LibFile: utility.scad
3// Functions for type checking, handling undefs, processing function arguments,
4// and testing.
5// Includes:
6// include <BOSL2/std.scad>
7// FileGroup: Data Management
8// FileSummary: Type checking, dealing with undefs, processing function args
9// FileFootnotes: STD=Included in std.scad
10//////////////////////////////////////////////////////////////////////
11
12
13
14// Section: Type Checking
15
16
17// Function: typeof()
18// Usage:
19// typ = typeof(x);
20// Topics: Type Checking
21// See Also: is_type()
22// Description:
23// Returns a string representing the type of the value. One of "undef", "boolean", "number", "nan", "string", "list", "range", "function" or "invalid".
24// Some malformed "ranges", like '[0:NAN:INF]' and '[0:"a":INF]', may be classified as "undef" or "invalid".
25// Arguments:
26// x = value whose type to check
27// Example:
28// typ = typeof(undef); // Returns: "undef"
29// typ = typeof(true); // Returns: "boolean"
30// typ = typeof(42); // Returns: "number"
31// typ = typeof(NAN); // Returns: "nan"
32// typ = typeof("foo"); // Returns: "string"
33// typ = typeof([3,4,5]); // Returns: "list"
34// typ = typeof([3:1:8]); // Returns: "range"
35// typ = typeof(function (x,y) x+y); // Returns: "function"
36function typeof(x) =
37 is_undef(x)? "undef" :
38 is_bool(x)? "boolean" :
39 is_num(x)? "number" :
40 is_nan(x)? "nan" :
41 is_string(x)? "string" :
42 is_list(x)? "list" :
43 is_range(x) ? "range" :
44 version_num()>20210000 && is_function(x) ? "function" :
45 "invalid";
46
47
48// Function: is_type()
49// Usage:
50// bool = is_type(x, types);
51// Topics: Type Checking
52// See Also: typeof()
53// Description:
54// Returns true if the type of the value `x` is one of those given as strings in the list `types`.
55// Valid types are "undef", "boolean", "number", "nan", "string", "list", "range", or "function".
56// Arguments:
57// x = The value to check the type of.
58// types = A list of types to check
59// Example:
60// is_str_or_list = is_type("foo", ["string","list"]); // Returns: true
61// is_str_or_list2 = is_type([1,2,3], ["string","list"]); // Returns: true
62// is_str_or_list3 = is_type(2, ["string","list"]); // Returns: false
63// is_str = is_type("foo", "string"); // Returns: true
64// is_str2 = is_type([3,4], "string"); // Returns: false
65// is_str3 = is_type(["foo"], "string"); // Returns: false
66// is_str4 = is_type(3, "string"); // Returns: false
67function is_type(x,types) =
68 is_list(types)? in_list(typeof(x),types) :
69 is_string(types)? typeof(x) == types :
70 assert(is_list(types)||is_string(types));
71
72
73// Function: is_def()
74// Usage:
75// bool = is_def(x);
76// Topics: Type Checking
77// See Also: typeof(), is_type(), is_str()
78// Description:
79// Returns true if `x` is not `undef`. False if `x==undef`.
80// Arguments:
81// x = value to check
82// Example:
83// bool = is_def(undef); // Returns: false
84// bool = is_def(false); // Returns: true
85// bool = is_def(42); // Returns: true
86// bool = is_def("foo"); // Returns: true
87function is_def(x) = !is_undef(x);
88
89
90// Function: is_str()
91// Usage:
92// bool = is_str(x);
93// Topics: Type Checking
94// See Also: typeof(), is_type(), is_int(), is_def()
95// Description:
96// Returns true if `x` is a string. A shortcut for `is_string()`.
97// Arguments:
98// x = value to check
99// Example:
100// bool = is_str(undef); // Returns: false
101// bool = is_str(false); // Returns: false
102// bool = is_str(42); // Returns: false
103// bool = is_str("foo"); // Returns: true
104function is_str(x) = is_string(x);
105
106
107// Function: is_int()
108// Alias: is_integer()
109// Usage:
110// bool = is_int(n);
111// bool = is_integer(n);
112// Topics: Type Checking
113// See Also: typeof(), is_type(), is_str(), is_def()
114// Description:
115// Returns true if the given value is an integer (it is a number and it rounds to itself).
116// Arguments:
117// n = value to check
118// Example:
119// bool = is_int(undef); // Returns: false
120// bool = is_int(false); // Returns: false
121// bool = is_int(42); // Returns: true
122// bool = is_int("foo"); // Returns: false
123function is_int(n) = is_finite(n) && n == round(n);
124function is_integer(n) = is_finite(n) && n == round(n);
125
126
127// Function: all_integer()
128// Usage:
129// bool = all_integer(x);
130// Description:
131// If given a number, returns true if the number is a finite integer.
132// If given an empty list, returns false. If given a non-empty list, returns
133// true if every item of the list is an integer. Otherwise, returns false.
134// Arguments:
135// x = The value to check.
136// Example:
137// b = all_integer(true); // Returns: false
138// b = all_integer("foo"); // Returns: false
139// b = all_integer(4); // Returns: true
140// b = all_integer(4.5); // Returns: false
141// b = all_integer([]); // Returns: false
142// b = all_integer([3,4,5]); // Returns: true
143// b = all_integer([3,4.2,5]); // Returns: false
144// b = all_integer([3,[4,7],5]); // Returns: false
145function all_integer(x) =
146 is_num(x)? is_int(x) :
147 is_list(x)? (x != [] && [for (xx=x) if(!is_int(xx)) 1] == []) :
148 false;
149
150
151// Function: is_nan()
152// Usage:
153// bool = is_nan(x);
154// Topics: Type Checking
155// See Also: typeof(), is_type(), is_str(), is_def(), is_int()
156// Description:
157// Returns true if a given value `x` is nan, a floating point value representing "not a number".
158// Arguments:
159// x = value to check
160// Example:
161// bool = is_nan(undef); // Returns: false
162// bool = is_nan(false); // Returns: false
163// bool = is_nan(42); // Returns: false
164// bool = is_nan("foo"); // Returns: false
165// bool = is_nan(NAN); // Returns: true
166function is_nan(x) = (x!=x);
167
168
169// Function: is_finite()
170// Usage:
171// bool = is_finite(x);
172// Topics: Type Checking
173// See Also: typeof(), is_type(), is_str(), is_def(), is_int(), is_nan()
174// Description:
175// Returns true if a given value `x` is a finite number.
176// Arguments:
177// x = value to check
178// Example:
179// bool = is_finite(undef); // Returns: false
180// bool = is_finite(false); // Returns: false
181// bool = is_finite(42); // Returns: true
182// bool = is_finite("foo"); // Returns: false
183// bool = is_finite(NAN); // Returns: false
184// bool = is_finite(INF); // Returns: false
185// bool = is_finite(-INF); // Returns: false
186function is_finite(x) = is_num(x) && !is_nan(0*x);
187
188
189// Function: is_range()
190// Usage:
191// bool = is_range(x);
192// Topics: Type Checking
193// See Also: typeof(), is_type(), is_str(), is_def(), is_int()
194// Description:
195// Returns true if its argument is a range
196// Arguments:
197// x = value to check
198// Example:
199// bool = is_range(undef); // Returns: false
200// bool = is_range(false); // Returns: false
201// bool = is_range(42); // Returns: false
202// bool = is_range([3,4,5]); // Returns: false
203// bool = is_range("foo"); // Returns: false
204// bool = is_range([3:5]); // Returns: true
205function is_range(x) = !is_list(x) && is_finite(x[0]) && is_finite(x[1]) && is_finite(x[2]) ;
206
207
208// Function: valid_range()
209// Usage:
210// bool = valid_range(x);
211// Topics: Type Checking
212// See Also: typeof(), is_type(), is_str(), is_def(), is_int(), is_range()
213// Description:
214// Returns true if its argument is a valid range (deprecated ranges excluded).
215// Arguments:
216// x = value to check
217// Example:
218// bool = is_range(undef); // Returns: false
219// bool = is_range(false); // Returns: false
220// bool = is_range(42); // Returns: false
221// bool = is_range([3,4,5]); // Returns: false
222// bool = is_range("foo"); // Returns: false
223// bool = is_range([3:5]); // Returns: true
224// bool = is_range([3:1]); // Returns: false
225function valid_range(x) =
226 is_range(x)
227 && ( x[1]>0
228 ? x[0]<=x[2]
229 : ( x[1]<0 && x[0]>=x[2] ) );
230
231
232// Function: is_func()
233// Usage:
234// bool = is_func(x);
235// Description:
236// Returns true if OpenSCAD supports function literals, and the given item is one.
237// Arguments:
238// x = The value to check
239// Example:
240// f = function (a) a==2;
241// bool = is_func(f); // Returns: true
242function is_func(x) = version_num()>20210000 && is_function(x);
243
244
245// Function: is_consistent()
246// Usage:
247// bool = is_consistent(list, [pattern]);
248// Topics: Type Checking
249// See Also: typeof(), is_type(), is_str(), is_def(), is_int(), is_range(), is_homogeneous()
250// Description:
251// Tests whether input is a list of entries which all have the same list structure
252// and are filled with finite numerical data. You can optionally specify a required
253// list structure with the pattern argument.
254// It returns `true` for the empty list regardless the value of the `pattern`.
255// Arguments:
256// list = list to check
257// pattern = optional pattern required to match
258// Example:
259// is_consistent([3,4,5]); // Returns true
260// is_consistent([[3,4],[4,5],[6,7]]); // Returns true
261// is_consistent([[3,4,5],[3,4]]); // Returns false
262// is_consistent([[3,[3,4,[5]]], [5,[2,9,[9]]]]); // Returns true
263// is_consistent([[3,[3,4,[5]]], [5,[2,9,9]]]); // Returns false
264// is_consistent([3,4,5], 0); // Returns true
265// is_consistent([3,4,undef], 0); // Returns false
266// is_consistent([[3,4],[4,5]], [1,1]); // Returns true
267// is_consistent([[3,"a"],[4,true]], [1,undef]); // Returns true
268// is_consistent([[3,4], 6, [4,5]], [1,1]); // Returns false
269// is_consistent([[1,[3,4]], [4,[5,6]]], [1,[2,3]]); // Returns true
270// is_consistent([[1,[3,INF]], [4,[5,6]]], [1,[2,3]]); // Returns false
271// is_consistent([], [1,[2,3]]); // Returns true
272function is_consistent(list, pattern) =
273 is_list(list)
274 && (len(list)==0
275 || (let(pattern = is_undef(pattern) ? _list_pattern(list[0]): _list_pattern(pattern) )
276 []==[for(entry=0*list) if (entry != pattern) entry]));
277
278//Internal function
279//Creates a list with the same structure of `list` with each of its elements replaced by 0.
280function _list_pattern(list) =
281 is_list(list)
282 ? [for(entry=list) is_list(entry) ? _list_pattern(entry) : 0]
283 : 0;
284
285
286// Function: same_shape()
287// Usage:
288// bool = same_shape(a,b);
289// Topics: Type Checking
290// See Also: is_homogeneous(), is_consistent()
291// Description:
292// Tests whether the inputs `a` and `b` are both numeric and are the same shaped list.
293// Example:
294// same_shape([3,[4,5]],[7,[3,4]]); // Returns true
295// same_shape([3,4,5], [7,[3,4]]); // Returns false
296function same_shape(a,b) = is_def(b) && _list_pattern(a) == b*0;
297
298
299// Function: is_bool_list()
300// Usage:
301// check = is_bool_list(list,[length])
302// Topics: Type Checking
303// See Also: is_homogeneous(), is_consistent()
304// Description:
305// Tests whether input is a list containing only booleans, and optionally checks its length.
306// Arguments:
307// list = list to test
308// length = if given, list must be this length
309function is_bool_list(list, length) =
310 is_list(list) && (is_undef(length) || len(list)==length) && []==[for(entry=list) if (!is_bool(entry)) 1];
311
312
313// Section: Boolean list testing
314
315// Function: any()
316// Usage:
317// bool = any(l);
318// bool = any(l, func); // Requires OpenSCAD 2021.01 or later.
319// Requirements:
320// Requires OpenSCAD 2021.01 or later to use the `func` argument.
321// Description:
322// Returns true if any item in list `l` evaluates as true.
323// If `func` is given then returns true if the function evaluates as true on any list entry.
324// Items that evaluate as true include nonempty lists, nonempty strings, and nonzero numbers.
325// Arguments:
326// l = The list to test for true items.
327// func = An optional function literal of signature (x), returning bool, to test each list item with.
328// Example:
329// any([0,false,undef]); // Returns false.
330// any([1,false,undef]); // Returns true.
331// any([1,5,true]); // Returns true.
332// any([[0,0], [0,0]]); // Returns true.
333// any([[0,0], [1,0]]); // Returns true.
334function any(l, func) =
335 assert(is_list(l), "The input is not a list." )
336 assert(func==undef || is_func(func))
337 is_func(func)
338 ? _any_func(l, func)
339 : _any_bool(l);
340
341function _any_func(l, func, i=0, out=false) =
342 i >= len(l) || out? out :
343 _any_func(l, func, i=i+1, out=out || func(l[i]));
344
345function _any_bool(l, i=0, out=false) =
346 i >= len(l) || out? out :
347 _any_bool(l, i=i+1, out=out || l[i]);
348
349
350// Function: all()
351// Usage:
352// bool = all(l);
353// bool = all(l, func); // Requires OpenSCAD 2021.01 or later.
354// Requirements:
355// Requires OpenSCAD 2021.01 or later to use the `func` argument.
356// Description:
357// Returns true if all items in list `l` evaluate as true.
358// If `func` is given then returns true if the function evaluates as true on all list etnries.
359// Items that evaluate as true include nonempty lists, nonempty strings, and nonzero numbers.
360// Arguments:
361// l = The list to test for true items.
362// func = An optional function literal of signature (x), returning bool, to test each list item with.
363// Example:
364// test1 = all([0,false,undef]); // Returns false.
365// test2 = all([1,false,undef]); // Returns false.
366// test3 = all([1,5,true]); // Returns true.
367// test4 = all([[0,0], [0,0]]); // Returns true.
368// test5 = all([[0,0], [1,0]]); // Returns true.
369// test6 = all([[1,1], [1,1]]); // Returns true.
370function all(l, func) =
371 assert(is_list(l), "The input is not a list.")
372 assert(func==undef || is_func(func))
373 is_func(func)
374 ? _all_func(l, func)
375 : _all_bool(l);
376
377function _all_func(l, func, i=0, out=true) =
378 i >= len(l) || !out? out :
379 _all_func(l, func, i=i+1, out=out && func(l[i]));
380
381function _all_bool(l, i=0, out=true) =
382 i >= len(l) || !out? out :
383 _all_bool(l, i=i+1, out=out && l[i]);
384
385
386// Function: num_true()
387// Usage:
388// seq = num_true(l);
389// seq = num_true(l, func); // Requires OpenSCAD 2021.01 or later.
390// Requirements:
391// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
392// Description:
393// Returns the number of items in `l` that evaluate as true. If `func` is given then counts
394// list entries where the function evaluates as true.
395// Items that evaluate as true include nonempty lists, nonempty strings, and nonzero numbers.
396// Arguments:
397// l = The list to test for true items.
398// func = An optional function literal of signature (x), returning bool, to test each list item with.
399// Example:
400// num1 = num_true([0,false,undef]); // Returns 0.
401// num2 = num_true([1,false,undef]); // Returns 1.
402// num3 = num_true([1,5,false]); // Returns 2.
403// num4 = num_true([1,5,true]); // Returns 3.
404// num5 = num_true([[0,0], [0,0]]); // Returns 2.
405// num6 = num_true([[], [1,0]]); // Returns 1.
406function num_true(l, func) =
407 assert(is_list(l))
408 assert(func==undef || is_func(func))
409 let(
410 true_list = is_def(func)? [for(entry=l) if (func(entry)) 1]
411 : [for(entry=l) if (entry) 1]
412 )
413 len(true_list);
414
415
416
417// Section: Handling `undef`s.
418
419
420// Function: default()
421// Usage:
422// val = default(val, dflt);
423// Topics: Undef Handling
424// See Also: first_defined(), one_defined(), num_defined()
425// Description:
426// Returns the value given as `v` if it is not `undef`.
427// Otherwise, returns the value of `dflt`.
428// Arguments:
429// v = Value to pass through if not `undef`.
430// dflt = Value to return if `v` *is* `undef`. Default: undef
431function default(v,dflt=undef) = is_undef(v)? dflt : v;
432
433
434// Function: first_defined()
435// Usage:
436// val = first_defined(v, [recursive]);
437// Topics: Undef Handling
438// See Also: default(), one_defined(), num_defined(), any_defined(), all_defined()
439// Description:
440// Returns the first item in the list that is not `undef`.
441// If all items are `undef`, or list is empty, returns `undef`.
442// Arguments:
443// v = The list whose items are being checked.
444// recursive = If true, sublists are checked recursively for defined values. The first sublist that has a defined item is returned. Default: false
445// Example:
446// val = first_defined([undef,7,undef,true]); // Returns: 7
447function first_defined(v,recursive=false,_i=0) =
448 _i<len(v) && (
449 is_undef(v[_i]) || (
450 recursive &&
451 is_list(v[_i]) &&
452 is_undef(first_defined(v[_i],recursive=recursive))
453 )
454 )? first_defined(v,recursive=recursive,_i=_i+1) : v[_i];
455
456
457// Function: one_defined()
458// Usage:
459// val = one_defined(vals, names, [dflt])
460// Topics: Undef Handling
461// See Also: default(), first_defined(), num_defined(), any_defined(), all_defined()
462// Description:
463// Examines the input list `vals` and returns the entry which is not `undef`.
464// If more than one entry is not `undef` then an error is asserted, specifying
465// "Must define exactly one of" followed by the names in the `names` parameter.
466// If `dflt` is given, and all `vals` are `undef`, then the value in `dflt` is returned.
467// If `dflt` is *not* given, and all `vals` are `undef`, then an error is asserted.
468// Arguments:
469// vals = The values to return the first one which is not `undef`.
470// names = A string with comma-separated names for the arguments whose values are passed in `vals`.
471// dflt = If given, the value returned if all `vals` are `undef`.
472// Example:
473// length1 = one_defined([length,L,l], ["length","L","l"]);
474// length2 = one_defined([length,L,l], "length,L,l", dflt=1);
475
476function one_defined(vals, names, dflt=_UNDEF) =
477 let(
478 checkargs = is_list(names)? assert(len(vals) == len(names)) :
479 is_string(names)? let(
480 name_cnt = len([for (c=names) if (c==",") 1]) + 1
481 ) assert(len(vals) == name_cnt) :
482 assert(is_list(names) || is_string(names)) 0,
483 ok = num_defined(vals)==1 || (dflt!=_UNDEF && num_defined(vals)==0)
484 ) ok? default(first_defined(vals), dflt) :
485 let(
486 names = is_string(names) ? str_split(names,",") : names,
487 defd = [for (i=idx(vals)) if (is_def(vals[i])) names[i]],
488 msg = str(
489 "Must define ",
490 dflt==_UNDEF? "exactly" : "at most",
491 " one of ",
492 num_defined(vals) == 0 ? names : defd
493 )
494 ) assert(ok,msg);
495
496
497// Function: num_defined()
498// Usage:
499// cnt = num_defined(v);
500// Topics: Undef Handling
501// See Also: default(), first_defined(), one_defined(), any_defined(), all_defined()
502// Description:
503// Counts how many items in list `v` are not `undef`.
504// Example:
505// cnt = num_defined([3,7,undef,2,undef,undef,1]); // Returns: 4
506function num_defined(v) =
507 len([for(vi=v) if(!is_undef(vi)) 1]);
508
509
510// Function: any_defined()
511// Usage:
512// bool = any_defined(v, [recursive]);
513// Topics: Undef Handling
514// See Also: default(), first_defined(), one_defined(), num_defined(), all_defined()
515// Description:
516// Returns true if any item in the given array is not `undef`.
517// Arguments:
518// v = The list whose items are being checked.
519// recursive = If true, any sublists are evaluated recursively. Default: false
520// Example:
521// bool = any_defined([undef,undef,undef]); // Returns: false
522// bool = any_defined([undef,42,undef]); // Returns: true
523// bool = any_defined([34,42,87]); // Returns: true
524// bool = any_defined([undef,undef,[undef]]); // Returns: true
525// bool = any_defined([undef,undef,[undef]],recursive=true); // Returns: false
526// bool = any_defined([undef,undef,[42]],recursive=true); // Returns: true
527function any_defined(v,recursive=false) =
528 first_defined(v,recursive=recursive) != undef;
529
530
531// Function: all_defined()
532// Usage:
533// bool = all_defined(v, [recursive]);
534// Description:
535// Returns true if all items in the given array are not `undef`.
536// Arguments:
537// v = The list whose items are being checked.
538// recursive = If true, any sublists are evaluated recursively. Default: false
539// Example:
540// bool = all_defined([undef,undef,undef]); // Returns: false
541// bool = all_defined([undef,42,undef]); // Returns: false
542// bool = all_defined([34,42,87]); // Returns: true
543// bool = all_defined([23,34,[undef]]); // Returns: true
544// bool = all_defined([23,34,[undef]],recursive=true); // Returns: false
545// bool = all_defined([23,34,[42]],recursive=true); // Returns: true
546function all_defined(v,recursive=false) =
547 []==[for (x=v) if(is_undef(x)||(recursive && is_list(x) && !all_defined(x,recursive))) 0 ];
548
549
550
551// Section: Undef Safe Arithmetic
552
553// Function: u_add()
554// Usage:
555// x = u_add(a, b);
556// Description:
557// Adds `a` to `b`, returning the result, or undef if either value is `undef`.
558// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
559// Arguments:
560// a = First value.
561// b = Second value.
562function u_add(a,b) = is_undef(a) || is_undef(b)? undef : a + b;
563
564
565// Function: u_sub()
566// Usage:
567// x = u_sub(a, b);
568// Description:
569// Subtracts `b` from `a`, returning the result, or undef if either value is `undef`.
570// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
571// Arguments:
572// a = First value.
573// b = Second value.
574function u_sub(a,b) = is_undef(a) || is_undef(b)? undef : a - b;
575
576
577// Function: u_mul()
578// Usage:
579// x = u_mul(a, b);
580// Description:
581// Multiplies `a` by `b`, returning the result, or undef if either value is `undef`.
582// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
583// Arguments:
584// a = First value.
585// b = Second value.
586function u_mul(a,b) =
587 is_undef(a) || is_undef(b)? undef :
588 is_vector(a) && is_vector(b)? v_mul(a,b) :
589 a * b;
590
591
592// Function: u_div()
593// Usage:
594// x = u_div(a, b);
595// Description:
596// Divides `a` by `b`, returning the result, or undef if either value is `undef`.
597// This emulates the way undefs used to be handled in versions of OpenSCAD before 2020.
598// Arguments:
599// a = First value.
600// b = Second value.
601function u_div(a,b) =
602 is_undef(a) || is_undef(b)? undef :
603 is_vector(a) && is_vector(b)? v_div(a,b) :
604 a / b;
605
606
607
608
609// Section: Processing Arguments to Functions and Modules
610
611
612// Function: get_anchor()
613// Usage:
614// anchr = get_anchor(anchor,center,[uncentered],[dflt]);
615// Topics: Argument Handling
616// See Also: get_radius()
617// Description:
618// Calculated the correct anchor from `anchor` and `center`. In order:
619// - If `center` is not `undef` and `center` evaluates as true, then `CENTER` (`[0,0,0]`) is returned.
620// - Otherwise, if `center` is not `undef` and `center` evaluates as false, then the value of `uncentered` is returned.
621// - Otherwise, if `anchor` is not `undef`, then the value of `anchor` is returned.
622// - Otherwise, the value of `dflt` is returned.
623// This ordering ensures that `center` will override `anchor`.
624// Arguments:
625// anchor = The anchor name or vector.
626// center = If not `undef`, this overrides the value of `anchor`.
627// uncentered = The value to return if `center` is not `undef` and evaluates as false. Default: BOTTOM
628// dflt = The default value to return if both `anchor` and `center` are `undef`. Default: `CENTER`
629// Example:
630// anchr1 = get_anchor(undef, undef, BOTTOM, TOP); // Returns: [0, 0, 1] (TOP)
631// anchr2 = get_anchor(RIGHT, undef, BOTTOM, TOP); // Returns: [1, 0, 0] (RIGHT)
632// anchr3 = get_anchor(undef, false, BOTTOM, TOP); // Returns: [0, 0,-1] (BOTTOM)
633// anchr4 = get_anchor(RIGHT, false, BOTTOM, TOP); // Returns: [0, 0,-1] (BOTTOM)
634// anchr5 = get_anchor(undef, true, BOTTOM, TOP); // Returns: [0, 0, 0] (CENTER)
635// anchr6 = get_anchor(RIGHT, true, BOTTOM, TOP); // Returns: [0, 0, 0] (CENTER)
636function get_anchor(anchor,center,uncentered=BOT,dflt=CENTER) =
637 !is_undef(center)? (center? CENTER : uncentered) :
638 !is_undef(anchor)? anchor :
639 dflt;
640
641
642// Function: get_radius()
643// Usage:
644// r = get_radius([r1=], [r2=], [r=], [d1=], [d2=], [d=], [dflt=]);
645// Topics: Argument Handling
646// See Also: get_anchor()
647// Description:
648// Given various radii and diameters, returns the most specific radius. If a diameter is most
649// specific, returns half its value, giving the radius. If no radii or diameters are defined,
650// returns the value of `dflt`. Value specificity order is `r1`, `r2`, `d1`, `d2`, `r`, `d`,
651// then `dflt`. Only one of `r1`, `r2`, `d1`, or `d2` can be defined at once, or else it errors
652// out, complaining about conflicting radius/diameter values.
653// Arguments:
654// ---
655// r1 = Most specific radius.
656// r2 = Second most specific radius.
657// r = Most general radius.
658// d1 = Most specific diameter.
659// d2 = Second most specific diameter.
660// d = Most general diameter.
661// dflt = Value to return if all other values given are `undef`.
662// Example:
663// r = get_radius(r1=undef, r=undef, dflt=undef); // Returns: undef
664// r = get_radius(r1=undef, r=undef, dflt=1); // Returns: 1
665// r = get_radius(r1=undef, r=6, dflt=1); // Returns: 6
666// r = get_radius(r1=7, r=6, dflt=1); // Returns: 7
667// r = get_radius(r1=undef, r2=8, r=6, dflt=1); // Returns: 8
668// r = get_radius(r1=undef, r2=8, d=6, dflt=1); // Returns: 8
669// r = get_radius(r1=undef, d=6, dflt=1); // Returns: 3
670// r = get_radius(d1=7, d=6, dflt=1); // Returns: 3.5
671// r = get_radius(d1=7, d2=8, d=6, dflt=1); // Returns: 3.5
672// r = get_radius(d1=undef, d2=8, d=6, dflt=1); // Returns: 4
673// r = get_radius(r1=8, d=6, dflt=1); // Returns: 8
674function get_radius(r1, r2, r, d1, d2, d, dflt) =
675 assert(num_defined([r1,d1,r2,d2])<2, "Conflicting or redundant radius/diameter arguments given.")
676 assert(num_defined([r,d])<2, "Conflicting or redundant radius/diameter arguments given.")
677 let(
678 rad = !is_undef(r1) ? r1
679 : !is_undef(d1) ? d1/2
680 : !is_undef(r2) ? r2
681 : !is_undef(d2) ? d2/2
682 : !is_undef(r) ? r
683 : !is_undef(d) ? d/2
684 : dflt
685 )
686 assert(is_undef(dflt) || is_finite(rad) || is_vector(rad), "Invalid radius." )
687 rad;
688
689
690// Function: scalar_vec3()
691// Usage:
692// vec = scalar_vec3(v, [dflt]);
693// Topics: Argument Handling
694// See Also: get_anchor(), get_radius(), force_list()
695// Description:
696// This is expands a scalar or a list with length less than 3 to a length 3 vector in the
697// same way that OpenSCAD expands short vectors in some contexts, e.g. cube(10) or rotate([45,90]).
698// If `v` is a scalar, and `dflt==undef`, returns `[v, v, v]`.
699// If `v` is a scalar, and `dflt!=undef`, returns `[v, dflt, dflt]`.
700// If `v` is a vector and dflt is defined, returns the first 3 items, with any missing values replaced by `dflt`.
701// If `v` is a vector and dflt is undef, returns the first 3 items, with any missing values replaced by 0.
702// If `v` is `undef`, returns `undef`.
703// Arguments:
704// v = Value to return vector from.
705// dflt = Default value to set empty vector parts from.
706// Example:
707// vec = scalar_vec3(undef); // Returns: undef
708// vec = scalar_vec3(10); // Returns: [10,10,10]
709// vec = scalar_vec3(10,1); // Returns: [10,1,1]
710// vec = scalar_vec3([10,10],1); // Returns: [10,10,1]
711// vec = scalar_vec3([10,10]); // Returns: [10,10,0]
712// vec = scalar_vec3([10]); // Returns: [10,0,0]
713function scalar_vec3(v, dflt) =
714 is_undef(v)? undef :
715 is_list(v)? [for (i=[0:2]) default(v[i], default(dflt, 0))] :
716 !is_undef(dflt)? [v,dflt,dflt] : [v,v,v];
717
718
719// Function: segs()
720// Usage:
721// sides = segs(r);
722// Topics: Geometry
723// Description:
724// Calculate the standard number of sides OpenSCAD would give a circle based on `$fn`, `$fa`, and `$fs`.
725// Arguments:
726// r = Radius of circle to get the number of segments for.
727// Example:
728// $fn=12; sides=segs(10); // Returns: 12
729// $fa=2; $fs=3, sides=segs(10); // Returns: 21
730function segs(r) =
731 $fn>0? ($fn>3? $fn : 3) :
732 let( r = is_finite(r)? r : 0 )
733 ceil(max(5, min(360/$fa, abs(r)*2*PI/$fs)));
734
735
736// Module: no_children()
737// Usage:
738// no_children($children);
739// Topics: Error Checking
740// See Also: no_function(), no_module(), req_children()
741// Description:
742// Assert that the calling module does not support children. Prints an error message to this effect and fails if children are present,
743// as indicated by its argument.
744// Arguments:
745// $children = number of children the module has.
746// Example:
747// module foo() {
748// no_children($children);
749// }
750module no_children(count) {
751 assert($children==0, "Module no_children() does not support child modules");
752 if ($parent_modules>0) {
753 assert(count==0, str("Module ",parent_module(1),"() does not support child modules"));
754 }
755}
756
757
758// Module: req_children()
759// Usage:
760// req_children($children);
761// Topics: Error Checking
762// See Also: no_function(), no_module()
763// Description:
764// Assert that the calling module requires children. Prints an error message and fails if no
765// children are present as indicated by its argument.
766// Arguments:
767// $children = number of children the module has.
768// Example:
769// module foo() {
770// req_children($children);
771// }
772module req_children(count) {
773 assert($children==0, "Module no_children() does not support child modules");
774 if ($parent_modules>0) {
775 assert(count>0, str("Module ",parent_module(1),"() requires children"));
776 }
777}
778
779
780// Function: no_function()
781// Usage:
782// dummy = no_function(name)
783// Topics: Error Checking
784// See Also: no_children(), no_module()
785// Description:
786// Asserts that the function, "name", only exists as a module.
787// Example:
788// x = no_function("foo");
789function no_function(name) =
790 assert(false,str("You called ",name,"() as a function, but it is available only as a module"));
791
792
793// Module: no_module()
794// Usage:
795// no_module();
796// Topics: Error Checking
797// See Also: no_children(), no_function()
798// Description:
799// Asserts that the called module exists only as a function.
800// Example:
801// module foo() { no_module(); }
802module no_module() {
803 assert(false, str("You called ",parent_module(1),"() as a module but it is available only as a function"));
804}
805
806
807// Module: deprecate()
808// Usage:
809// deprecate(new_name);
810// Description:
811// Display info that the current module is deprecated and you should switch to a new name
812// Arguments:
813// new_name = name of the new module that replaces the old one
814module deprecate(new_name)
815{
816 echo(str("***** Module ",parent_module(1),"() has been replaced by ",new_name,"() and will be removed in a future version *****"));
817}
818
819
820// Section: Testing Helpers
821
822
823function _valstr(x) =
824 is_string(x)? str("\"",str_replace_char(x, "\"", "\\\""),"\"") :
825 is_list(x)? str("[",str_join([for (xx=x) _valstr(xx)],","),"]") :
826 is_num(x) && x==floor(x)? format_int(x) :
827 is_finite(x)? format_float(x,12) : x;
828
829
830// Module: assert_approx()
831// Usage:
832// assert_approx(got, expected, [info]);
833// Topics: Error Checking, Debugging
834// See Also: no_children(), no_function(), no_module(), assert_equal()
835// Description:
836// Tests if the value gotten is what was expected. If not, then
837// the expected and received values are printed to the console and
838// an assertion is thrown to stop execution.
839// Arguments:
840// got = The value actually received.
841// expected = The value that was expected.
842// info = Extra info to print out to make the error clearer.
843// Example:
844// assert_approx(1/3, 0.333333333333333, str("number=",1,", denom=",3));
845module assert_approx(got, expected, info) {
846 no_children($children);
847 if (!approx(got, expected)) {
848 echo();
849 echo(str("EXPECT: ", _valstr(expected)));
850 echo(str("GOT : ", _valstr(got)));
851 if (same_shape(got, expected)) {
852 echo(str("DELTA : ", _valstr(got - expected)));
853 }
854 if (is_def(info)) {
855 echo(str("INFO : ", _valstr(info)));
856 }
857 assert(approx(got, expected));
858 }
859}
860
861
862// Module: assert_equal()
863// Usage:
864// assert_equal(got, expected, [info]);
865// Topics: Error Checking, Debugging
866// See Also: no_children(), no_function(), no_module(), assert_approx()
867// Description:
868// Tests if the value gotten is what was expected. If not, then the expected and received values
869// are printed to the console and an assertion is thrown to stop execution.
870// Arguments:
871// got = The value actually received.
872// expected = The value that was expected.
873// info = Extra info to print out to make the error clearer.
874// Example:
875// assert_approx(3*9, 27, str("a=",3,", b=",9));
876module assert_equal(got, expected, info) {
877 no_children($children);
878 if (got != expected || (is_nan(got) && is_nan(expected))) {
879 echo();
880 echo(str("EXPECT: ", _valstr(expected)));
881 echo(str("GOT : ", _valstr(got)));
882 if (same_shape(got, expected)) {
883 echo(str("DELTA : ", _valstr(got - expected)));
884 }
885 if (is_def(info)) {
886 echo(str("INFO : ", _valstr(info)));
887 }
888 assert(got == expected);
889 }
890}
891
892
893// Module: shape_compare()
894// Usage:
895// shape_compare([eps]) {TEST_SHAPE; EXPECTED_SHAPE;}
896// Topics: Error Checking, Debugging, Testing
897// See Also: assert_approx(), assert_equal()
898// Description:
899// Compares two child shapes, returning empty geometry if they are very nearly the same shape and size.
900// Returns the differential geometry if they are not quite the same shape and size.
901// Arguments:
902// eps = The surface of the two shapes must be within this size of each other. Default: 1/1024
903// Example:
904// $fn=36;
905// shape_compare() {
906// sphere(d=100);
907// rotate_extrude() right_half(planar=true) circle(d=100);
908// }
909module shape_compare(eps=1/1024) {
910 assert($children==2,"Must give exactly two children");
911 union() {
912 difference() {
913 children(0);
914 if (eps==0) {
915 children(1);
916 } else {
917 minkowski() {
918 children(1);
919 spheroid(r=eps, style="octa");
920 }
921 }
922 }
923 difference() {
924 children(1);
925 if (eps==0) {
926 children(0);
927 } else {
928 minkowski() {
929 children(0);
930 spheroid(r=eps, style="octa");
931 }
932 }
933 }
934 }
935}
936
937
938// Section: Looping Helpers
939// You can use a list comprehension with a C-style for loop to iteratively make a calculation.
940// .
941// The syntax is: `[for (INIT; CONDITION; NEXT) RETVAL]` where:
942// - INIT is zero or more `let()` style assignments that are evaluated exactly one time, before the first loop.
943// - CONDITION is an expression evaluated at the start of each loop. If true, continues with the loop.
944// - RETVAL is an expression that returns a list item for each loop.
945// - NEXT is one or more `let()` style assignments that is evaluated at the end of each loop.
946// .
947// Since the INIT phase is only run once, and the CONDITION and RETVAL expressions cannot update
948// variables, that means that only the NEXT phase can be used for iterative calculations.
949// Unfortunately, the NEXT phase runs *after* the RETVAL expression, which means that you need
950// to run the loop one extra time to return the final value. This tends to make the loop code
951// look rather ugly. The `looping()`, `loop_while()` and `loop_done()` functions
952// can make this somewhat more legible.
953// .
954// ```openscad
955// function flat_sum(l) = [
956// for (
957// i = 0,
958// total = 0,
959// state = 0;
960//
961// looping(state);
962//
963// state = loop_while(state, i < len(l)),
964// total = total +
965// loop_done(state) ? 0 :
966// let( x = l[i] )
967// is_list(x) ? flat_sum(x) : x,
968// i = i + 1
969// ) if (loop_done(state)) total;
970// ].x;
971// ```
972
973
974// Function: looping()
975// Usage:
976// bool = looping(state);
977// Topics: Iteration
978// See Also: loop_while(), loop_done()
979// Description:
980// Returns true if the `state` value indicates the current loop should continue. This is useful
981// when using C-style for loops to iteratively calculate a value. Used with `loop_while()` and
982// `loop_done()`. See [Looping Helpers](section-looping-helpers) for an example.
983// Arguments:
984// state = The loop state value.
985function looping(state) = state < 2;
986
987
988// Function: loop_while()
989// Usage:
990// state = loop_while(state, continue);
991// Topics: Iteration
992// See Also: looping(), loop_done()
993// Description:
994// Given the current `state`, and a boolean `continue` that indicates if the loop should still be
995// continuing, returns the updated state value for the the next loop. This is useful when using
996// C-style for loops to iteratively calculate a value. Used with `looping()` and `loop_done()`.
997// See [Looping Helpers](section-looping-helpers) for an example.
998// Arguments:
999// state = The loop state value.
1000// continue = A boolean value indicating whether the current loop should progress.
1001function loop_while(state, continue) =
1002 state > 0 ? 2 :
1003 continue ? 0 : 1;
1004
1005
1006// Function: loop_done()
1007// Usage:
1008// bool = loop_done(state);
1009// Topics: Iteration
1010// See Also: looping(), loop_while()
1011// Description:
1012// Returns true if the `state` value indicates the loop is finishing. This is useful when using
1013// C-style for loops to iteratively calculate a value. Used with `looping()` and `loop_while()`.
1014// See [Looping Helpers](#5-looping-helpers) for an example.
1015// Arguments:
1016// state = The loop state value.
1017function loop_done(state) = state > 0;
1018
1019
1020// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap