From 1d324128e4cbc6f1a6496147cacb340726e70c42 Mon Sep 17 00:00:00 2001 From: Jerome Plut Date: Thu, 3 Dec 2020 21:21:05 +0100 Subject: [PATCH] Added more flexibility to get_named_args(), and all the left_half(), etc. as functions --- arrays.scad | 12 +++++++++ common.scad | 73 +++++++++++++++++++++++++++++++++++---------------- mutators.scad | 63 +++++++++++++++++++++++++++++++++----------- vnf.scad | 4 +-- 4 files changed, 112 insertions(+), 40 deletions(-) diff --git a/arrays.scad b/arrays.scad index e23ef7b..967109b 100644 --- a/arrays.scad +++ b/arrays.scad @@ -89,6 +89,18 @@ function select(list, start, end=undef) = : concat([for (i = [s:1:l-1]) list[i]], [for (i = [0:1:e]) list[i]]) ; +// Function: last() +// Description: +// Returns the last element of a list, or undef if empty. +// Usage: +// last(list) +// Arguments: +// list = The list to get the last element of. +// Example: +// l = [3,4,5,6,7,8,9]; +// last(l); // Returns 9. +function last(list) = list[len(list)-1]; + // Function: slice() // Description: // Returns a slice of a list. The first item is index 0. diff --git a/common.scad b/common.scad index 24b181a..a0abd81 100644 --- a/common.scad +++ b/common.scad @@ -312,27 +312,41 @@ function get_height(h=undef,l=undef,height=undef,dflt=undef) = assert(num_defined([h,l,height])<=1,"You must specify only one of `l`, `h`, and `height`") first_defined([h,l,height,dflt]); -// Function: get_named_args(anonymous, named, _undef) +// Function: get_named_args(positional, named, _undef) // Usage: -// function f(anon1=_undef, anon2=_undef,..., +// function f(pos1=_undef, pos2=_undef,..., // named1=_undef, named2=_undef, ...) = -// let(args = get_named_args([anon1, anon2, ...], -// [[named1, default1], [named2, default2], ...])) +// let(args = get_named_args([pos1, pos2, ...], +// [[named1, default1], [named2, default2], ...]), +// named1=args[0], named2=args[1], ...) // ... // Description: -// given a set of anonymous and named arguments, returns the values of -// named arguments, in order. -// - All named arguments which were provided by the user take the -// value provided. +// Given the values of some positional and named arguments, +// returns a list of the values assigned to named arguments, +// in the following way: +// - All named arguments which were explicitly assigned in the +// function call take the value provided. // - All named arguments which were not provided by the user are -// affected from anonymous arguments, in order. +// affected from positional arguments; the priority order in which +// these are assigned is given by the `priority` argument, while the +// positional assignation is done in the order of the named arguments. // - Any remaining named arguments take the provided default values. // Arguments: -// anonymous = the list of values of anonymous arguments. -// named = the list of [passed-value, default value] of named arguments. -// _undef = the default value used by the calling function for all -// arguments (this is *not* undef, or any value that the user might -// purposely want to use as an argument value). +// positional = the list of values of positional arguments. +// named = the list of named arguments; each entry of the list has the +// form [passed-value, default-value, priority], where +// passed-value is the value that was passed at function call; +// default-value is the value that will be used if nothing is read +// from either named or positional arguments; +// priority is the priority assigned to this argument. +// _undef = the default value used by the calling function for all arguments (default is some random string that you will never use). (this is *not* undef, or any value that the user might purposely want to use as an argument value). +// +// If only k positional arguments are used, then the k named values +// with lowest 'priority' value (among the unassigned ones) will get them. +// The arguments will be assigned in the order of the named values. +// By default these two orders coincide. +// +// // Examples: // function f(arg1=_undef, arg2=_undef, arg3=_undef, // named1=_undef, named2=_undef, named3=_undef) = @@ -349,16 +363,29 @@ function get_height(h=undef,l=undef,height=undef,dflt=undef) = // result of `dd if=/dev/random bs=32 count=1 |base64` : _undef="LRG+HX7dy89RyHvDlAKvb9Y04OTuaikpx205CTh8BSI"; -function get_named_args(anonymous, named,_undef=_undef) = - /* u: set of undefined indices in named arguments */ - let(from_anon = [for(p=enumerate(named)) if(p[1][0]==_undef) p[0]], - n = len(anonymous)) - echo("from_anon:", from_anon) +/* Note: however tempting it might be, it is *not* possible to accept + * named argument as a list [named1, named2, ...] (without default + * values), because the values [named1, named2...] themselves might be + * lists, and we will not be able to distinguish the two cases. */ +function get_named_args(positional, named,_undef=_undef) = + let(deft = [for(p=named) p[1]], // default is undef + // indices of the values to fetch from positional args: + unknown = [for(x=enumerate(named)) if(x[1][0]==_undef) x[0]], + // number of values given to positional arguments: + n_positional = count_true([for(p=positional) p!=_undef])) + assert(n_positional <= len(unknown), + str("too many positional arguments (", n_positional, " given, ", + len(unknown), " required)")) + let( + // those elements which have no priority assigned go last (prio=+∞): + prio = sortidx([for(u=unknown) default(named[u][2], 1/0)]), + // list of indices of values assigned from positional arguments: + assigned = sort([for(i=[0:1:n_positional-1]) prio[i]])) [ for(e = enumerate(named)) - // if the value is defined, return it: - e[1][0] != _undef ? e[1][0] : - let(k = anonymous[search(e[0], from_anon)[0]]) - k != _undef ? k : e[1][1] ]; + let(idx=e[0], val=e[1][0], ass=search(idx, assigned)) + val != _undef ? val : + ass != [] ? positional[ass[0]] : + deft[idx] ]; // Function: scalar_vec3() // Usage: // scalar_vec3(v, ); diff --git a/mutators.scad b/mutators.scad index 8c4bc96..46b05e8 100644 --- a/mutators.scad +++ b/mutators.scad @@ -121,16 +121,9 @@ module half_of(v=UP, cp, s=1000, planar=false) function half_of(_arg1=_undef, _arg2=_undef, _arg3=_undef, _arg4=_undef, v=_undef, cp=_undef, p=_undef, s=_undef) = let(args=get_named_args([_arg1, _arg2, _arg3, _arg4], - [[v], [cp, 0], [p], [s, 1e4]]), + [[v,undef,0], [cp,0,2], [p,undef,1], [s,1e4,3]]), v=args[0], cp0=args[1], p=args[2], s=args[3], cp = is_num(cp0) ? cp0*unit(v) : cp0) - echo("_undef=", _undef) - echo("v=", v) - echo("cp=", cp) - echo("p=", p) - echo("vnf?", is_vnf(p)) - echo("region?", is_region(p)) - echo("s=", s) assert(is_vector(v,2)||is_vector(v,3), "must provide a half-plane or half-space") let(d=len(v)) @@ -164,11 +157,15 @@ function half_of(_arg1=_undef, _arg2=_undef, _arg3=_undef, _arg4=_undef, : assert(false, "must pass either a point, a path, a region, or a VNF"); -// Module: left_half() +// Function&Module: left_half() // -// Usage: +// Usage: as module // left_half([s], [x]) ... // left_half(planar=true, [s], [x]) ... +// Usage: as function +// left_half([s], [x], path) +// left_half([s], [x], region) +// left_half([s], [x], vnf) // // Description: // Slices an object at a vertical Y-Z cut plane, and masks away everything that is right of it. @@ -197,10 +194,16 @@ module left_half(s=1000, x=0, planar=false) } } } +function left_half(_arg1=_undef, _arg2=_undef, _arg3=_undef, + x=_undef, p=_undef, s=_undef) = + let(args=get_named_args([_arg1, _arg2, _arg3], + [[x, 0,1], [p,undef,0], [s, 1e4,2]]), + x=args[0], p=args[1], s=args[2]) + half_of(v=[1,0,0], cp=x, p=p); -// Module: right_half() +// Function&Module: right_half() // // Usage: // right_half([s], [x]) ... @@ -233,10 +236,16 @@ module right_half(s=1000, x=0, planar=false) } } } +function right_half(_arg1=_undef, _arg2=_undef, _arg3=_undef, + x=_undef, p=_undef, s=_undef) = + let(args=get_named_args([_arg1, _arg2, _arg3], + [[x, 0,1], [p,undef,0], [s, 1e4,2]]), + x=args[0], p=args[1], s=args[2]) + half_of(v=[-1,0,0], cp=x, p=p); -// Module: front_half() +// Function&Module: front_half() // // Usage: // front_half([s], [y]) ... @@ -269,10 +278,16 @@ module front_half(s=1000, y=0, planar=false) } } } +function front_half(_arg1=_undef, _arg2=_undef, _arg3=_undef, + x=_undef, p=_undef, s=_undef) = + let(args=get_named_args([_arg1, _arg2, _arg3], + [[x, 0,1], [p,undef,0], [s, 1e4,2]]), + x=args[0], p=args[1], s=args[2]) + half_of(v=[0,1,0], cp=x, p=p); -// Module: back_half() +// Function&Module: back_half() // // Usage: // back_half([s], [y]) ... @@ -305,10 +320,16 @@ module back_half(s=1000, y=0, planar=false) } } } +function back_half(_arg1=_undef, _arg2=_undef, _arg3=_undef, + x=_undef, p=_undef, s=_undef) = + let(args=get_named_args([_arg1, _arg2, _arg3], + [[x, 0,1], [p,undef,0], [s, 1e4,2]]), + x=args[0], p=args[1], s=args[2]) + half_of(v=[0,-1,0], cp=x, p=p); -// Module: bottom_half() +// Function&Module: bottom_half() // // Usage: // bottom_half([s], [z]) ... @@ -333,10 +354,16 @@ module bottom_half(s=1000, z=0) } } } +function right_half(_arg1=_undef, _arg2=_undef, _arg3=_undef, + x=_undef, p=_undef, s=_undef) = + let(args=get_named_args([_arg1, _arg2, _arg3], + [[x, 0,1], [p,undef,0], [s, 1e4,2]]), + x=args[0], p=args[1], s=args[2]) + half_of(v=[0,0,-1], cp=x, p=p); -// Module: top_half() +// Function&Module: top_half() // // Usage: // top_half([s], [z]) ... @@ -361,6 +388,12 @@ module top_half(s=1000, z=0) } } } +function right_half(_arg1=_undef, _arg2=_undef, _arg3=_undef, + x=_undef, p=_undef, s=_undef) = + let(args=get_named_args([_arg1, _arg2, _arg3], + [[x, 0,1], [p,undef,0], [s, 1e4,2]]), + x=args[0], p=args[1], s=args[2]) + half_of(v=[0,0,1], cp=x, p=p); diff --git a/vnf.scad b/vnf.scad index 8b40841..95396f5 100644 --- a/vnf.scad +++ b/vnf.scad @@ -924,7 +924,7 @@ function _vnf_halfspace_paths(edges, i=0, paths=[]) = */ i >= len(edges) ? paths : // termination condition let(e = edges[i], - s = [for(x=enumerate(paths)) if(x[1][len(x[1])-1] == e[0]) x[0]]) + s = [for(x=enumerate(paths)) if(last(x[1]) == e[0]) x[0]]) _vnf_halfspace_paths(edges, i+1, // if cannot attach to previous path: create a new one s == [] ? concat(paths, [e]) : @@ -947,7 +947,7 @@ function vnf_halfspace(_arg1=_undef, _arg2=_undef, newedges=[for(x=tmp2) each x[1]], // generate new faces paths=_vnf_halfspace_paths(newedges), - loops=[for(p=paths) if(p[0] == p[len(p)-1]) p]) + loops=[for(p=paths) if(p[0] == last(p)) p]) [coords, concat(newfaces, loops)]; //