discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

questions about best practices with variable scope and how best to access predefined variables

JD
John David
Sun, Mar 10, 2024 9:57 AM

I am working on rewriting/generalizing a little project which builds French
cleat toolholders, and have a number of questions.  I have a couple of
variants of this using simple cubes, triangles, cylinders, but I am trying
to understand more advanced features of OpenSCAD.

For people that care about such things, French cleats normally come with a
45 or 30-degree angle, but can be set to any desired angle.  The choice of
materials are often 3/4" plywood, 3/4" lumber (ie 1x*), 1/4" plywood...
Since the angle, thickness, width can all be defined up front, I would like
to set up some reasonable defaults, but allow the user to override them.

Q1: is there are way to access a nested (sub)module from outside its parent
scope?

I was hoping I could find a way to do something like:

module French_Cleat() {
module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
{fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

and then be able to call it:

French_Cleat.define_solid_params(angle=30);
cp = French_Cleat.cleat_profile();

That did not work at all...

Q2: it looks like functions are compiled when I include/use a file, and not
when used.  Is there a way around this.

Using the above code, I called:
define_solid_params(angle=30);
cp30 = cleat_profile();

and OpenSCAD returned a profile which used the default 45, and not the
overwritten angle=30, because it appears that any global variables are set
at compile time.  Is there a way to work around that?

Sorry for the very basic questions, but I am still learning the syntax and
semantics.

EBo --

I am working on rewriting/generalizing a little project which builds French cleat toolholders, and have a number of questions. I have a couple of variants of this using simple cubes, triangles, cylinders, but I am trying to understand more advanced features of OpenSCAD. For people that care about such things, French cleats normally come with a 45 or 30-degree angle, but can be set to any desired angle. The choice of materials are often 3/4" plywood, 3/4" lumber (ie 1x*), 1/4" plywood... Since the angle, thickness, width can all be defined up front, I would like to set up some reasonable defaults, but allow the user to override them. Q1: is there are way to access a nested (sub)module from outside its parent scope? I was hoping I could find a way to do something like: module French_Cleat() { module define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) {fc_angle=angle; ...} function cleat_profile () =....; // uses fc_angle } and then be able to call it: French_Cleat.define_solid_params(angle=30); cp = French_Cleat.cleat_profile(); That did not work at all... Q2: it looks like functions are compiled when I include/use a file, and not when used. Is there a way around this. Using the above code, I called: define_solid_params(angle=30); cp30 = cleat_profile(); and OpenSCAD returned a profile which used the default 45, and not the overwritten angle=30, because it appears that any global variables are set at compile time. Is there a way to work around that? Sorry for the very basic questions, but I am still learning the syntax and semantics. EBo --
J
jon
Sun, Mar 10, 2024 12:00 PM

Others may have better answers, but I would have define_solid_params()
set global variables like sp_thickness, sp_width, etc.

Jon

On 3/10/2024 5:57 AM, John David via Discuss wrote:

I am working on rewriting/generalizing a little project which builds
French cleat toolholders, and have a number of questions.  I have a
couple of variants of this using simple cubes, triangles, cylinders,
but I am trying to understand more advanced features of OpenSCAD.

For people that care about such things, French cleats normally come
with a 45 or 30-degree angle, but can be set to any desired angle. 
The choice of materials are often 3/4" plywood, 3/4" lumber (ie 1x*),
1/4" plywood...  Since the angle, thickness, width can all be defined
up front, I would like to set up some reasonable defaults, but allow
the user to override them.

Q1: is there are way to access a nested (sub)module from outside its
parent scope?

I was hoping I could find a way to do something like:

module French_Cleat() {
    module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
    {fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

and then be able to call it:

French_Cleat.define_solid_params(angle=30);
cp = French_Cleat.cleat_profile();

That did not work at all...

Q2: it looks like functions are compiled when I include/use a file,
and not when used.  Is there a way around this.

Using the above code, I called:
    define_solid_params(angle=30);
    cp30 = cleat_profile();

and OpenSCAD returned a profile which used the default 45, and not the
overwritten angle=30, because it appears that any global variables are
set at compile time.  Is there a way to work around that?

Sorry for the very basic questions, but I am still learning the syntax
and semantics.

  EBo --


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

--
This email has been checked for viruses by AVG antivirus software.
www.avg.com

Others may have better answers, but I would have define_solid_params() set global variables like sp_thickness, sp_width, etc. Jon On 3/10/2024 5:57 AM, John David via Discuss wrote: > I am working on rewriting/generalizing a little project which builds > French cleat toolholders, and have a number of questions.  I have a > couple of variants of this using simple cubes, triangles, cylinders, > but I am trying to understand more advanced features of OpenSCAD. > > For people that care about such things, French cleats normally come > with a 45 or 30-degree angle, but can be set to any desired angle.  > The choice of materials are often 3/4" plywood, 3/4" lumber (ie 1x*), > 1/4" plywood...  Since the angle, thickness, width can all be defined > up front, I would like to set up some reasonable defaults, but allow > the user to override them. > > Q1: is there are way to access a nested (sub)module from outside its > parent scope? > > I was hoping I could find a way to do something like: > > module French_Cleat() { >     module > define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) >     {fc_angle=angle; ...} > function cleat_profile () =....; // uses fc_angle > } > > and then be able to call it: > > French_Cleat.define_solid_params(angle=30); > cp = French_Cleat.cleat_profile(); > > That did not work at all... > > Q2: it looks like functions are compiled when I include/use a file, > and not when used.  Is there a way around this. > > Using the above code, I called: >     define_solid_params(angle=30); >     cp30 = cleat_profile(); > > and OpenSCAD returned a profile which used the default 45, and not the > overwritten angle=30, because it appears that any global variables are > set at compile time.  Is there a way to work around that? > > Sorry for the very basic questions, but I am still learning the syntax > and semantics. > >   EBo -- > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org -- This email has been checked for viruses by AVG antivirus software. www.avg.com
RW
Raymond West
Sun, Mar 10, 2024 12:49 PM

Yes. Put the user adjustable variables at the front, comment them for
use with the customizer
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer

On 10/03/2024 12:00, jon via Discuss wrote:

Others may have better answers, but I would have define_solid_params()
set global variables like sp_thickness, sp_width, etc.

Jon

On 3/10/2024 5:57 AM, John David via Discuss wrote:

I am working on rewriting/generalizing a little project which builds
French cleat toolholders, and have a number of questions.  I have a
couple of variants of this using simple cubes, triangles, cylinders,
but I am trying to understand more advanced features of OpenSCAD.

For people that care about such things, French cleats normally come
with a 45 or 30-degree angle, but can be set to any desired angle. 
The choice of materials are often 3/4" plywood, 3/4" lumber (ie 1x*),
1/4" plywood...  Since the angle, thickness, width can all be defined
up front, I would like to set up some reasonable defaults, but allow
the user to override them.

Q1: is there are way to access a nested (sub)module from outside its
parent scope?

I was hoping I could find a way to do something like:

module French_Cleat() {
    module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
    {fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

and then be able to call it:

French_Cleat.define_solid_params(angle=30);
cp = French_Cleat.cleat_profile();

That did not work at all...

Q2: it looks like functions are compiled when I include/use a file,
and not when used.  Is there a way around this.

Using the above code, I called:
    define_solid_params(angle=30);
    cp30 = cleat_profile();

and OpenSCAD returned a profile which used the default 45, and not
the overwritten angle=30, because it appears that any global
variables are set at compile time.  Is there a way to work around that?

Sorry for the very basic questions, but I am still learning the
syntax and semantics.

  EBo --


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Yes. Put the user adjustable variables at the front, comment them for use with the customizer https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer On 10/03/2024 12:00, jon via Discuss wrote: > Others may have better answers, but I would have define_solid_params() > set global variables like sp_thickness, sp_width, etc. > > Jon > > On 3/10/2024 5:57 AM, John David via Discuss wrote: >> I am working on rewriting/generalizing a little project which builds >> French cleat toolholders, and have a number of questions.  I have a >> couple of variants of this using simple cubes, triangles, cylinders, >> but I am trying to understand more advanced features of OpenSCAD. >> >> For people that care about such things, French cleats normally come >> with a 45 or 30-degree angle, but can be set to any desired angle.  >> The choice of materials are often 3/4" plywood, 3/4" lumber (ie 1x*), >> 1/4" plywood...  Since the angle, thickness, width can all be defined >> up front, I would like to set up some reasonable defaults, but allow >> the user to override them. >> >> Q1: is there are way to access a nested (sub)module from outside its >> parent scope? >> >> I was hoping I could find a way to do something like: >> >> module French_Cleat() { >>     module >> define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) >>     {fc_angle=angle; ...} >> function cleat_profile () =....; // uses fc_angle >> } >> >> and then be able to call it: >> >> French_Cleat.define_solid_params(angle=30); >> cp = French_Cleat.cleat_profile(); >> >> That did not work at all... >> >> Q2: it looks like functions are compiled when I include/use a file, >> and not when used.  Is there a way around this. >> >> Using the above code, I called: >>     define_solid_params(angle=30); >>     cp30 = cleat_profile(); >> >> and OpenSCAD returned a profile which used the default 45, and not >> the overwritten angle=30, because it appears that any global >> variables are set at compile time.  Is there a way to work around that? >> >> Sorry for the very basic questions, but I am still learning the >> syntax and semantics. >> >>   EBo -- >> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >
J
jon
Sun, Mar 10, 2024 1:04 PM

I don't think he wants to use the customizer for these variable, since
they are all inter-related using the formulas in define_solid_params().

Jon

On 3/10/2024 8:49 AM, Raymond West via Discuss wrote:

Yes. Put the user adjustable variables at the front, comment them for
use with the customizer
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer

On 10/03/2024 12:00, jon via Discuss wrote:

Others may have better answers, but I would have
define_solid_params() set global variables like sp_thickness,
sp_width, etc.

Jon

On 3/10/2024 5:57 AM, John David via Discuss wrote:

I am working on rewriting/generalizing a little project which builds
French cleat toolholders, and have a number of questions.  I have a
couple of variants of this using simple cubes, triangles, cylinders,
but I am trying to understand more advanced features of OpenSCAD.

For people that care about such things, French cleats normally come
with a 45 or 30-degree angle, but can be set to any desired angle. 
The choice of materials are often 3/4" plywood, 3/4" lumber (ie
1x*), 1/4" plywood...  Since the angle, thickness, width can all be
defined up front, I would like to set up some reasonable defaults,
but allow the user to override them.

Q1: is there are way to access a nested (sub)module from outside its
parent scope?

I was hoping I could find a way to do something like:

module French_Cleat() {
    module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
    {fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

and then be able to call it:

French_Cleat.define_solid_params(angle=30);
cp = French_Cleat.cleat_profile();

That did not work at all...

Q2: it looks like functions are compiled when I include/use a file,
and not when used.  Is there a way around this.

Using the above code, I called:
    define_solid_params(angle=30);
    cp30 = cleat_profile();

and OpenSCAD returned a profile which used the default 45, and not
the overwritten angle=30, because it appears that any global
variables are set at compile time.  Is there a way to work around that?

Sorry for the very basic questions, but I am still learning the
syntax and semantics.

  EBo --


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

--
This email has been checked for viruses by AVG antivirus software.
www.avg.com

I don't think he wants to use the customizer for these variable, since they are all inter-related using the formulas in define_solid_params(). Jon On 3/10/2024 8:49 AM, Raymond West via Discuss wrote: > Yes. Put the user adjustable variables at the front, comment them for > use with the customizer > https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer > > On 10/03/2024 12:00, jon via Discuss wrote: >> Others may have better answers, but I would have >> define_solid_params() set global variables like sp_thickness, >> sp_width, etc. >> >> Jon >> >> On 3/10/2024 5:57 AM, John David via Discuss wrote: >>> I am working on rewriting/generalizing a little project which builds >>> French cleat toolholders, and have a number of questions.  I have a >>> couple of variants of this using simple cubes, triangles, cylinders, >>> but I am trying to understand more advanced features of OpenSCAD. >>> >>> For people that care about such things, French cleats normally come >>> with a 45 or 30-degree angle, but can be set to any desired angle.  >>> The choice of materials are often 3/4" plywood, 3/4" lumber (ie >>> 1x*), 1/4" plywood...  Since the angle, thickness, width can all be >>> defined up front, I would like to set up some reasonable defaults, >>> but allow the user to override them. >>> >>> Q1: is there are way to access a nested (sub)module from outside its >>> parent scope? >>> >>> I was hoping I could find a way to do something like: >>> >>> module French_Cleat() { >>>     module >>> define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) >>>     {fc_angle=angle; ...} >>> function cleat_profile () =....; // uses fc_angle >>> } >>> >>> and then be able to call it: >>> >>> French_Cleat.define_solid_params(angle=30); >>> cp = French_Cleat.cleat_profile(); >>> >>> That did not work at all... >>> >>> Q2: it looks like functions are compiled when I include/use a file, >>> and not when used.  Is there a way around this. >>> >>> Using the above code, I called: >>>     define_solid_params(angle=30); >>>     cp30 = cleat_profile(); >>> >>> and OpenSCAD returned a profile which used the default 45, and not >>> the overwritten angle=30, because it appears that any global >>> variables are set at compile time.  Is there a way to work around that? >>> >>> Sorry for the very basic questions, but I am still learning the >>> syntax and semantics. >>> >>>   EBo -- >>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org -- This email has been checked for viruses by AVG antivirus software. www.avg.com
RW
Raymond West
Sun, Mar 10, 2024 1:25 PM

Maybe. I was thinking more of ease of use for the end user - select
thickness, width, angle, chamfer kerf, etc. You'd already pointed out to
use global variables, since the more 'traditional linear program
structure?'  is not how Openscad works.

On 10/03/2024 13:04, jon wrote:

I don't think he wants to use the customizer for these variable, since
they are all inter-related using the formulas in define_solid_params().

Jon

On 3/10/2024 8:49 AM, Raymond West via Discuss wrote:

Yes. Put the user adjustable variables at the front, comment them for
use with the customizer
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer

On 10/03/2024 12:00, jon via Discuss wrote:

Others may have better answers, but I would have
define_solid_params() set global variables like sp_thickness,
sp_width, etc.

Jon

On 3/10/2024 5:57 AM, John David via Discuss wrote:

I am working on rewriting/generalizing a little project which
builds French cleat toolholders, and have a number of questions.  I
have a couple of variants of this using simple cubes, triangles,
cylinders, but I am trying to understand more advanced features of
OpenSCAD.

For people that care about such things, French cleats normally come
with a 45 or 30-degree angle, but can be set to any desired angle. 
The choice of materials are often 3/4" plywood, 3/4" lumber (ie
1x*), 1/4" plywood...  Since the angle, thickness, width can all be
defined up front, I would like to set up some reasonable defaults,
but allow the user to override them.

Q1: is there are way to access a nested (sub)module from outside
its parent scope?

I was hoping I could find a way to do something like:

module French_Cleat() {
    module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
    {fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

and then be able to call it:

French_Cleat.define_solid_params(angle=30);
cp = French_Cleat.cleat_profile();

That did not work at all...

Q2: it looks like functions are compiled when I include/use a file,
and not when used.  Is there a way around this.

Using the above code, I called:
    define_solid_params(angle=30);
    cp30 = cleat_profile();

and OpenSCAD returned a profile which used the default 45, and not
the overwritten angle=30, because it appears that any global
variables are set at compile time.  Is there a way to work around
that?

Sorry for the very basic questions, but I am still learning the
syntax and semantics.

  EBo --


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Maybe. I was thinking more of ease of use for the end user - select thickness, width, angle, chamfer kerf, etc. You'd already pointed out to use global variables, since the more 'traditional linear program structure?'  is not how Openscad works. On 10/03/2024 13:04, jon wrote: > I don't think he wants to use the customizer for these variable, since > they are all inter-related using the formulas in define_solid_params(). > > Jon > > On 3/10/2024 8:49 AM, Raymond West via Discuss wrote: >> Yes. Put the user adjustable variables at the front, comment them for >> use with the customizer >> https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer >> >> On 10/03/2024 12:00, jon via Discuss wrote: >>> Others may have better answers, but I would have >>> define_solid_params() set global variables like sp_thickness, >>> sp_width, etc. >>> >>> Jon >>> >>> On 3/10/2024 5:57 AM, John David via Discuss wrote: >>>> I am working on rewriting/generalizing a little project which >>>> builds French cleat toolholders, and have a number of questions.  I >>>> have a couple of variants of this using simple cubes, triangles, >>>> cylinders, but I am trying to understand more advanced features of >>>> OpenSCAD. >>>> >>>> For people that care about such things, French cleats normally come >>>> with a 45 or 30-degree angle, but can be set to any desired angle.  >>>> The choice of materials are often 3/4" plywood, 3/4" lumber (ie >>>> 1x*), 1/4" plywood...  Since the angle, thickness, width can all be >>>> defined up front, I would like to set up some reasonable defaults, >>>> but allow the user to override them. >>>> >>>> Q1: is there are way to access a nested (sub)module from outside >>>> its parent scope? >>>> >>>> I was hoping I could find a way to do something like: >>>> >>>> module French_Cleat() { >>>>     module >>>> define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) >>>>     {fc_angle=angle; ...} >>>> function cleat_profile () =....; // uses fc_angle >>>> } >>>> >>>> and then be able to call it: >>>> >>>> French_Cleat.define_solid_params(angle=30); >>>> cp = French_Cleat.cleat_profile(); >>>> >>>> That did not work at all... >>>> >>>> Q2: it looks like functions are compiled when I include/use a file, >>>> and not when used.  Is there a way around this. >>>> >>>> Using the above code, I called: >>>>     define_solid_params(angle=30); >>>>     cp30 = cleat_profile(); >>>> >>>> and OpenSCAD returned a profile which used the default 45, and not >>>> the overwritten angle=30, because it appears that any global >>>> variables are set at compile time.  Is there a way to work around >>>> that? >>>> >>>> Sorry for the very basic questions, but I am still learning the >>>> syntax and semantics. >>>> >>>>   EBo -- >>>> >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >
JB
Jordan Brown
Sun, Mar 10, 2024 4:08 PM

On 3/10/2024 1:57 AM, John David via Discuss wrote:

Q1: is there are way to access a nested (sub)module from outside its
parent scope?

No.  OpenSCAD modules are black holes.  No information comes out of them.

Also, OpenSCAD variables ... don't vary.  Once set, they cannot be
changed.  (You can create a new instance of a variable in a block, but
that's a new variable and the outer one is unchanged.)

I was hoping I could find a way to do something like:

module French_Cleat() {
    module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
    {fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

Nope.  Can't do anything like that, because there is absolutely no way
for a module to set a global variable.

Q2: it looks like functions are compiled when I include/use a file,
and not when used.  Is there a way around this.

No.  (Though given the scoping rules, it doesn't matter when the file is
"compiled", or that it's compiled at all.)

Advice:

For a simple model where the user wants to set values, put the values
together at the top of the file, so that the user can set them using the
Customizer.  See
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for
information on how to add comments to control the labels and other user
interface aspects.

For a subassembly where a program is going to set the values and control
them, the usual strategy is to have your subassembly's module take
parameters, and have the parameters have defaults.

Combining those two concepts:

cube1_size = 10;
cube2_size = 15;
ratio = 2;

twoCubes();
translate([30,0,0]) twoCubes(cube1_size, cube2_size);
translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio);

module twoCubes(c1=5, c2=7) {
    cube(c1, center=true);
    cube(c2);
}

Note that the file has default values (that the user can change with the
customizer) that control the middle pair of cubes, and the module has
its own defaults that control the first pair.

The usual variable scoping is lexical:  you can look at the layout of
the program to see what variables are visible at any given point.  Just
walk up the enclosing blocks until you get to the first variable with
that name.

You can use $ variables to pass information into modules or
functions.  They are still immutable, but are dynamically scoped.  They
are passed down through calls, no matter how the program is laid out. 
Walk up the call stack until you find the first variable with that name.

$ variables don't lend themselves to module-level defaults, though you
can do it using is_undef().

You can write a variation on the example above as:

$cube1_size = 10;
$cube2_size = 15;

twoCubes();
translate([30,0,0]) {
    $cube1_size = 20;
    $cube2_size = 30;
    twoCubes();
}

module twoCubes() {
    cube($cube1_size, center=true);
    cube($cube2_size);
}

... but mostly I wouldn't recommend it.

Related notes:

  * In any given block, all assignments are processed before all
    module invocations.  Don't expect to be able to call a module,
    then set a $ variable, then call it again.  Both calls will get
    the value from the assignment.
  * With respect to the model, it almost doesn't matter what order
    modules get called in.  (Exceptions:  echo(), rands(), and some
    cases involving transparency and coloring that are probably best
    viewed as weaknesses in the previewer.)
  * The customizer will suck up all constant assignments, until it
    hits the first module definition.  If you want to have a global
    variable that the user *shouldn't* customize, put in a dummy
    "module stop();" after the customizable variables.
On 3/10/2024 1:57 AM, John David via Discuss wrote: > Q1: is there are way to access a nested (sub)module from outside its > parent scope? No.  OpenSCAD modules are black holes.  No information comes out of them. Also, OpenSCAD variables ... don't vary.  Once set, they cannot be changed.  (You can create a new instance of a variable in a block, but that's a new variable and the outer one is unchanged.) > I was hoping I could find a way to do something like: > > module French_Cleat() { >     module > define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) >     {fc_angle=angle; ...} > function cleat_profile () =....; // uses fc_angle > } Nope.  Can't do anything like that, because there is absolutely no way for a module to set a global variable. > Q2: it looks like functions are compiled when I include/use a file, > and not when used.  Is there a way around this. No.  (Though given the scoping rules, it doesn't matter when the file is "compiled", or that it's compiled at all.) Advice: For a simple model where the *user* wants to set values, put the values together at the top of the file, so that the user can set them using the Customizer.  See https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for information on how to add comments to control the labels and other user interface aspects. For a subassembly where a program is going to set the values and control them, the usual strategy is to have your subassembly's module take parameters, and have the parameters have defaults. Combining those two concepts: cube1_size = 10; cube2_size = 15; ratio = 2; twoCubes(); translate([30,0,0]) twoCubes(cube1_size, cube2_size); translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio); module twoCubes(c1=5, c2=7) { cube(c1, center=true); cube(c2); } Note that the file has default values (that the user can change with the customizer) that control the middle pair of cubes, and the module has its own defaults that control the first pair. The usual variable scoping is lexical:  you can look at the layout of the program to see what variables are visible at any given point.  Just walk up the enclosing blocks until you get to the first variable with that name. You *can* use $ variables to pass information into modules or functions.  They are still immutable, but are dynamically scoped.  They are passed down through calls, no matter how the program is laid out.  Walk up the call stack until you find the first variable with that name. $ variables don't lend themselves to module-level defaults, though you can do it using is_undef(). You can write a variation on the example above as: $cube1_size = 10; $cube2_size = 15; twoCubes(); translate([30,0,0]) { $cube1_size = 20; $cube2_size = 30; twoCubes(); } module twoCubes() { cube($cube1_size, center=true); cube($cube2_size); } ... but mostly I wouldn't recommend it. Related notes: * In any given block, all assignments are processed before all module invocations.  Don't expect to be able to call a module, then set a $ variable, then call it again.  Both calls will get the value from the assignment. * With respect to the model, it almost doesn't matter what order modules get called in.  (Exceptions:  echo(), rands(), and some cases involving transparency and coloring that are probably best viewed as weaknesses in the previewer.) * The customizer will suck up all constant assignments, until it hits the first module definition.  If you want to have a global variable that the user *shouldn't* customize, put in a dummy "module stop();" after the customizable variables.
JD
John David
Mon, Mar 11, 2024 1:00 AM

OK.  It is starting to take shape.  What I did was to build a named
parameter list that looks like:

[["cleat","type",type],
 ["cleat","thickness",thickness],
 ["cleat","width",width],
 ["cleat","chamfer",chamfer],
 ["cleat","kerf",kerf],
 ["cleat","angle",angle],
 ["cleat","profile",_fc_cleat_profile

(width,thickness,chamfer,angle,kerf)]];

and then pass these parameters through to other functions.  This is only a
small example, but is starting to feel about right:

include <BOSL2/std.scad>
include <BOSL2/trigonometry.scad>

function
French_Cleat_params(type="solid",thickness=0.75,width=5.5/2,chamfer=0.125,kerf=0.125,angle=45.0)

[["cleat","type",type],
 ["cleat","thickness",thickness],
 ["cleat","width",width],
 ["cleat","chamfer",chamfer],
 ["cleat","kerf",kerf],
 ["cleat","angle",angle],
 ["cleat","profile",_fc_cleat_profile

(width,thickness,chamfer,angle,kerf)]];

function _fc_cleat_profile
(cleat_width,cleat_thickness,cleat_chamfer,cleat_angle,kerf) =
let(
c = cleat_thickness,
b = adj_ang_to_hyp(c,cleat_angle),
a = adj_ang_to_opp(c,cleat_angle),
a2 = adj_ang_to_opp(c-cleat_chamfer,cleat_angle),
c2 = c-cleat_chamfer
) [[0.0,0.0], [-c,0.0], [-c,a2],[-c2,a2]]; // FIXME: adjust for kerfs

function fc_param(params,catigory,var) =
let(
cat =
search(catigory,params,num_returns_per_match=0,index_col_num=0),
vals = [for(i=cat) for(j=i) params[j]],
scat = search(var,vals,num_returns_per_match=1,index_col_num=1),
val = [for(z=scat) for(k=z) vals[k]],
itm = val[0][2],
echo(".....",itm)
) itm;

// generate a set of parameters for the French Cleats (with user modified
angle).
cp = French_Cleat_params(angle=30);

// grab the French Cleat Parameters (as modified by user), and display
pc = fc_param(cp,"cleat","profile");
echo("  pc = ",pc);
color("yellow") linear_extrude(.25) polygon(pc);

I will clean this up as I start using it, but I think I am starting to
figure out OpenSCAD's syntax and semantics - the variables being static
after defined threw me for a loop).

I am confused about the warnings that are being generated by the search.
Anyone have suggestions on how to fix those?

EBo --

On Sun, Mar 10, 2024 at 12:08 PM Jordan Brown openscad@jordan.maileater.net
wrote:

On 3/10/2024 1:57 AM, John David via Discuss wrote:

Q1: is there are way to access a nested (sub)module from outside its
parent scope?

No.  OpenSCAD modules are black holes.  No information comes out of them.

Also, OpenSCAD variables ... don't vary.  Once set, they cannot be
changed.  (You can create a new instance of a variable in a block, but
that's a new variable and the outer one is unchanged.)

I was hoping I could find a way to do something like:

module French_Cleat() {
module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
{fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

Nope.  Can't do anything like that, because there is absolutely no way for
a module to set a global variable.

Q2: it looks like functions are compiled when I include/use a file, and
not when used.  Is there a way around this.

No.  (Though given the scoping rules, it doesn't matter when the file is
"compiled", or that it's compiled at all.)

Advice:

For a simple model where the user wants to set values, put the values
together at the top of the file, so that the user can set them using the
Customizer.  See
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for
information on how to add comments to control the labels and other user
interface aspects.

For a subassembly where a program is going to set the values and control
them, the usual strategy is to have your subassembly's module take
parameters, and have the parameters have defaults.

Combining those two concepts:

cube1_size = 10;
cube2_size = 15;
ratio = 2;

twoCubes();
translate([30,0,0]) twoCubes(cube1_size, cube2_size);
translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio);

module twoCubes(c1=5, c2=7) {
cube(c1, center=true);
cube(c2);
}

Note that the file has default values (that the user can change with the
customizer) that control the middle pair of cubes, and the module has its
own defaults that control the first pair.

The usual variable scoping is lexical:  you can look at the layout of the
program to see what variables are visible at any given point.  Just walk up
the enclosing blocks until you get to the first variable with that name.

You can use $ variables to pass information into modules or functions.
They are still immutable, but are dynamically scoped.  They are passed down
through calls, no matter how the program is laid out.  Walk up the call
stack until you find the first variable with that name.

$ variables don't lend themselves to module-level defaults, though you can
do it using is_undef().

You can write a variation on the example above as:

$cube1_size = 10;
$cube2_size = 15;

twoCubes();
translate([30,0,0]) {
$cube1_size = 20;
$cube2_size = 30;
twoCubes();
}

module twoCubes() {
cube($cube1_size, center=true);
cube($cube2_size);
}

... but mostly I wouldn't recommend it.

Related notes:

- In any given block, all assignments are processed before all module
invocations.  Don't expect to be able to call a module, then set a $
variable, then call it again.  Both calls will get the value from the
assignment.
- With respect to the model, it almost doesn't matter what order
modules get called in.  (Exceptions:  echo(), rands(), and some cases
involving transparency and coloring that are probably best viewed as
weaknesses in the previewer.)
- The customizer will suck up all constant assignments, until it hits
the first module definition.  If you want to have a global variable that
the user *shouldn't* customize, put in a dummy "module stop();" after the
customizable variables.
OK. It is starting to take shape. What I did was to build a named parameter list that looks like: [["cleat","type",type], ["cleat","thickness",thickness], ["cleat","width",width], ["cleat","chamfer",chamfer], ["cleat","kerf",kerf], ["cleat","angle",angle], ["cleat","profile",_fc_cleat_profile (width,thickness,chamfer,angle,kerf)]]; and then pass these parameters through to other functions. This is only a small example, but is starting to feel about right: include <BOSL2/std.scad> include <BOSL2/trigonometry.scad> function French_Cleat_params(type="solid",thickness=0.75,width=5.5/2,chamfer=0.125,kerf=0.125,angle=45.0) = [["cleat","type",type], ["cleat","thickness",thickness], ["cleat","width",width], ["cleat","chamfer",chamfer], ["cleat","kerf",kerf], ["cleat","angle",angle], ["cleat","profile",_fc_cleat_profile (width,thickness,chamfer,angle,kerf)]]; function _fc_cleat_profile (cleat_width,cleat_thickness,cleat_chamfer,cleat_angle,kerf) = let( c = cleat_thickness, b = adj_ang_to_hyp(c,cleat_angle), a = adj_ang_to_opp(c,cleat_angle), a2 = adj_ang_to_opp(c-cleat_chamfer,cleat_angle), c2 = c-cleat_chamfer ) [[0.0,0.0], [-c,0.0], [-c,a2],[-c2,a2]]; // FIXME: adjust for kerfs function fc_param(params,catigory,var) = let( cat = search(catigory,params,num_returns_per_match=0,index_col_num=0), vals = [for(i=cat) for(j=i) params[j]], scat = search(var,vals,num_returns_per_match=1,index_col_num=1), val = [for(z=scat) for(k=z) vals[k]], itm = val[0][2], echo(".....",itm) ) itm; // generate a set of parameters for the French Cleats (with user modified angle). cp = French_Cleat_params(angle=30); // grab the French Cleat Parameters (as modified by user), and display pc = fc_param(cp,"cleat","profile"); echo(" pc = ",pc); color("yellow") linear_extrude(.25) polygon(pc); I will clean this up as I start using it, but I think I am starting to figure out OpenSCAD's syntax and semantics - the variables being static after defined threw me for a loop). I am confused about the warnings that are being generated by the search. Anyone have suggestions on how to fix those? EBo -- On Sun, Mar 10, 2024 at 12:08 PM Jordan Brown <openscad@jordan.maileater.net> wrote: > On 3/10/2024 1:57 AM, John David via Discuss wrote: > > Q1: is there are way to access a nested (sub)module from outside its > parent scope? > > > No. OpenSCAD modules are black holes. No information comes out of them. > > Also, OpenSCAD variables ... don't vary. Once set, they cannot be > changed. (You can create a new instance of a variable in a block, but > that's a new variable and the outer one is unchanged.) > > I was hoping I could find a way to do something like: > > module French_Cleat() { > module > define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) > {fc_angle=angle; ...} > function cleat_profile () =....; // uses fc_angle > } > > > Nope. Can't do anything like that, because there is absolutely no way for > a module to set a global variable. > > Q2: it looks like functions are compiled when I include/use a file, and > not when used. Is there a way around this. > > > No. (Though given the scoping rules, it doesn't matter when the file is > "compiled", or that it's compiled at all.) > > Advice: > > For a simple model where the *user* wants to set values, put the values > together at the top of the file, so that the user can set them using the > Customizer. See > https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for > information on how to add comments to control the labels and other user > interface aspects. > > For a subassembly where a program is going to set the values and control > them, the usual strategy is to have your subassembly's module take > parameters, and have the parameters have defaults. > > Combining those two concepts: > > cube1_size = 10; > cube2_size = 15; > ratio = 2; > > twoCubes(); > translate([30,0,0]) twoCubes(cube1_size, cube2_size); > translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio); > > module twoCubes(c1=5, c2=7) { > cube(c1, center=true); > cube(c2); > } > > Note that the file has default values (that the user can change with the > customizer) that control the middle pair of cubes, and the module has its > own defaults that control the first pair. > > The usual variable scoping is lexical: you can look at the layout of the > program to see what variables are visible at any given point. Just walk up > the enclosing blocks until you get to the first variable with that name. > > You *can* use $ variables to pass information into modules or functions. > They are still immutable, but are dynamically scoped. They are passed down > through calls, no matter how the program is laid out. Walk up the call > stack until you find the first variable with that name. > > $ variables don't lend themselves to module-level defaults, though you can > do it using is_undef(). > > You can write a variation on the example above as: > > $cube1_size = 10; > $cube2_size = 15; > > twoCubes(); > translate([30,0,0]) { > $cube1_size = 20; > $cube2_size = 30; > twoCubes(); > } > > module twoCubes() { > cube($cube1_size, center=true); > cube($cube2_size); > } > > ... but mostly I wouldn't recommend it. > > Related notes: > > > - In any given block, all assignments are processed before all module > invocations. Don't expect to be able to call a module, then set a $ > variable, then call it again. Both calls will get the value from the > assignment. > - With respect to the model, it almost doesn't matter what order > modules get called in. (Exceptions: echo(), rands(), and some cases > involving transparency and coloring that are probably best viewed as > weaknesses in the previewer.) > - The customizer will suck up all constant assignments, until it hits > the first module definition. If you want to have a global variable that > the user *shouldn't* customize, put in a dummy "module stop();" after the > customizable variables. > > >
JD
John David
Mon, Mar 11, 2024 1:45 AM

also with regards to manipulating named parameters, I remember reading
about something similar another project was using.  Is there a pre-built
library for this functionality?  I might have not found it yet, and should
scrap my use that instead.

EBo --

On Sun, Mar 10, 2024 at 9:00 PM John David ebo.2112@gmail.com wrote:

OK.  It is starting to take shape.  What I did was to build a named
parameter list that looks like:

 [["cleat","type",type],
  ["cleat","thickness",thickness],
  ["cleat","width",width],
  ["cleat","chamfer",chamfer],
  ["cleat","kerf",kerf],
  ["cleat","angle",angle],
  ["cleat","profile",_fc_cleat_profile

(width,thickness,chamfer,angle,kerf)]];

and then pass these parameters through to other functions.  This is only a
small example, but is starting to feel about right:

include <BOSL2/std.scad>
include <BOSL2/trigonometry.scad>

function
French_Cleat_params(type="solid",thickness=0.75,width=5.5/2,chamfer=0.125,kerf=0.125,angle=45.0)

 [["cleat","type",type],
  ["cleat","thickness",thickness],
  ["cleat","width",width],
  ["cleat","chamfer",chamfer],
  ["cleat","kerf",kerf],
  ["cleat","angle",angle],
  ["cleat","profile",_fc_cleat_profile

(width,thickness,chamfer,angle,kerf)]];

function _fc_cleat_profile
(cleat_width,cleat_thickness,cleat_chamfer,cleat_angle,kerf) =
let(
c = cleat_thickness,
b = adj_ang_to_hyp(c,cleat_angle),
a = adj_ang_to_opp(c,cleat_angle),
a2 = adj_ang_to_opp(c-cleat_chamfer,cleat_angle),
c2 = c-cleat_chamfer
) [[0.0,0.0], [-c,0.0], [-c,a2],[-c2,a2]]; // FIXME: adjust for kerfs

function fc_param(params,catigory,var) =
let(
cat =
search(catigory,params,num_returns_per_match=0,index_col_num=0),
vals = [for(i=cat) for(j=i) params[j]],
scat = search(var,vals,num_returns_per_match=1,index_col_num=1),
val = [for(z=scat) for(k=z) vals[k]],
itm = val[0][2],
echo(".....",itm)
) itm;

// generate a set of parameters for the French Cleats (with user modified
angle).
cp = French_Cleat_params(angle=30);

// grab the French Cleat Parameters (as modified by user), and display
pc = fc_param(cp,"cleat","profile");
echo("  pc = ",pc);
color("yellow") linear_extrude(.25) polygon(pc);

I will clean this up as I start using it, but I think I am starting to
figure out OpenSCAD's syntax and semantics - the variables being static
after defined threw me for a loop).

I am confused about the warnings that are being generated by the search.
Anyone have suggestions on how to fix those?

EBo --

On Sun, Mar 10, 2024 at 12:08 PM Jordan Brown <
openscad@jordan.maileater.net> wrote:

On 3/10/2024 1:57 AM, John David via Discuss wrote:

Q1: is there are way to access a nested (sub)module from outside its
parent scope?

No.  OpenSCAD modules are black holes.  No information comes out of them.

Also, OpenSCAD variables ... don't vary.  Once set, they cannot be
changed.  (You can create a new instance of a variable in a block, but
that's a new variable and the outer one is unchanged.)

I was hoping I could find a way to do something like:

module French_Cleat() {
module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
{fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

Nope.  Can't do anything like that, because there is absolutely no way
for a module to set a global variable.

Q2: it looks like functions are compiled when I include/use a file, and
not when used.  Is there a way around this.

No.  (Though given the scoping rules, it doesn't matter when the file is
"compiled", or that it's compiled at all.)

Advice:

For a simple model where the user wants to set values, put the values
together at the top of the file, so that the user can set them using the
Customizer.  See
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for
information on how to add comments to control the labels and other user
interface aspects.

For a subassembly where a program is going to set the values and control
them, the usual strategy is to have your subassembly's module take
parameters, and have the parameters have defaults.

Combining those two concepts:

cube1_size = 10;
cube2_size = 15;
ratio = 2;

twoCubes();
translate([30,0,0]) twoCubes(cube1_size, cube2_size);
translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio);

module twoCubes(c1=5, c2=7) {
cube(c1, center=true);
cube(c2);
}

Note that the file has default values (that the user can change with the
customizer) that control the middle pair of cubes, and the module has its
own defaults that control the first pair.

The usual variable scoping is lexical:  you can look at the layout of the
program to see what variables are visible at any given point.  Just walk up
the enclosing blocks until you get to the first variable with that name.

You can use $ variables to pass information into modules or functions.
They are still immutable, but are dynamically scoped.  They are passed down
through calls, no matter how the program is laid out.  Walk up the call
stack until you find the first variable with that name.

$ variables don't lend themselves to module-level defaults, though you
can do it using is_undef().

You can write a variation on the example above as:

$cube1_size = 10;
$cube2_size = 15;

twoCubes();
translate([30,0,0]) {
$cube1_size = 20;
$cube2_size = 30;
twoCubes();
}

module twoCubes() {
cube($cube1_size, center=true);
cube($cube2_size);
}

... but mostly I wouldn't recommend it.

Related notes:

- In any given block, all assignments are processed before all module
invocations.  Don't expect to be able to call a module, then set a $
variable, then call it again.  Both calls will get the value from the
assignment.
- With respect to the model, it almost doesn't matter what order
modules get called in.  (Exceptions:  echo(), rands(), and some cases
involving transparency and coloring that are probably best viewed as
weaknesses in the previewer.)
- The customizer will suck up all constant assignments, until it hits
the first module definition.  If you want to have a global variable that
the user *shouldn't* customize, put in a dummy "module stop();" after the
customizable variables.
also with regards to manipulating named parameters, I remember reading about something similar another project was using. Is there a pre-built library for this functionality? I might have not found it yet, and should scrap my use that instead. EBo -- On Sun, Mar 10, 2024 at 9:00 PM John David <ebo.2112@gmail.com> wrote: > OK. It is starting to take shape. What I did was to build a named > parameter list that looks like: > > [["cleat","type",type], > ["cleat","thickness",thickness], > ["cleat","width",width], > ["cleat","chamfer",chamfer], > ["cleat","kerf",kerf], > ["cleat","angle",angle], > ["cleat","profile",_fc_cleat_profile > (width,thickness,chamfer,angle,kerf)]]; > > and then pass these parameters through to other functions. This is only a > small example, but is starting to feel about right: > > include <BOSL2/std.scad> > include <BOSL2/trigonometry.scad> > > > function > French_Cleat_params(type="solid",thickness=0.75,width=5.5/2,chamfer=0.125,kerf=0.125,angle=45.0) > = > [["cleat","type",type], > ["cleat","thickness",thickness], > ["cleat","width",width], > ["cleat","chamfer",chamfer], > ["cleat","kerf",kerf], > ["cleat","angle",angle], > ["cleat","profile",_fc_cleat_profile > (width,thickness,chamfer,angle,kerf)]]; > > function _fc_cleat_profile > (cleat_width,cleat_thickness,cleat_chamfer,cleat_angle,kerf) = > let( > c = cleat_thickness, > b = adj_ang_to_hyp(c,cleat_angle), > a = adj_ang_to_opp(c,cleat_angle), > a2 = adj_ang_to_opp(c-cleat_chamfer,cleat_angle), > c2 = c-cleat_chamfer > ) [[0.0,0.0], [-c,0.0], [-c,a2],[-c2,a2]]; // FIXME: adjust for kerfs > > function fc_param(params,catigory,var) = > let( > cat = > search(catigory,params,num_returns_per_match=0,index_col_num=0), > vals = [for(i=cat) for(j=i) params[j]], > scat = search(var,vals,num_returns_per_match=1,index_col_num=1), > val = [for(z=scat) for(k=z) vals[k]], > itm = val[0][2], > echo(".....",itm) > ) itm; > > // generate a set of parameters for the French Cleats (with user modified > angle). > cp = French_Cleat_params(angle=30); > > // grab the French Cleat Parameters (as modified by user), and display > pc = fc_param(cp,"cleat","profile"); > echo(" pc = ",pc); > color("yellow") linear_extrude(.25) polygon(pc); > > I will clean this up as I start using it, but I think I am starting to > figure out OpenSCAD's syntax and semantics - the variables being static > after defined threw me for a loop). > > I am confused about the warnings that are being generated by the search. > Anyone have suggestions on how to fix those? > > EBo -- > > On Sun, Mar 10, 2024 at 12:08 PM Jordan Brown < > openscad@jordan.maileater.net> wrote: > >> On 3/10/2024 1:57 AM, John David via Discuss wrote: >> >> Q1: is there are way to access a nested (sub)module from outside its >> parent scope? >> >> >> No. OpenSCAD modules are black holes. No information comes out of them. >> >> Also, OpenSCAD variables ... don't vary. Once set, they cannot be >> changed. (You can create a new instance of a variable in a block, but >> that's a new variable and the outer one is unchanged.) >> >> I was hoping I could find a way to do something like: >> >> module French_Cleat() { >> module >> define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) >> {fc_angle=angle; ...} >> function cleat_profile () =....; // uses fc_angle >> } >> >> >> Nope. Can't do anything like that, because there is absolutely no way >> for a module to set a global variable. >> >> Q2: it looks like functions are compiled when I include/use a file, and >> not when used. Is there a way around this. >> >> >> No. (Though given the scoping rules, it doesn't matter when the file is >> "compiled", or that it's compiled at all.) >> >> Advice: >> >> For a simple model where the *user* wants to set values, put the values >> together at the top of the file, so that the user can set them using the >> Customizer. See >> https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for >> information on how to add comments to control the labels and other user >> interface aspects. >> >> For a subassembly where a program is going to set the values and control >> them, the usual strategy is to have your subassembly's module take >> parameters, and have the parameters have defaults. >> >> Combining those two concepts: >> >> cube1_size = 10; >> cube2_size = 15; >> ratio = 2; >> >> twoCubes(); >> translate([30,0,0]) twoCubes(cube1_size, cube2_size); >> translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio); >> >> module twoCubes(c1=5, c2=7) { >> cube(c1, center=true); >> cube(c2); >> } >> >> Note that the file has default values (that the user can change with the >> customizer) that control the middle pair of cubes, and the module has its >> own defaults that control the first pair. >> >> The usual variable scoping is lexical: you can look at the layout of the >> program to see what variables are visible at any given point. Just walk up >> the enclosing blocks until you get to the first variable with that name. >> >> You *can* use $ variables to pass information into modules or functions. >> They are still immutable, but are dynamically scoped. They are passed down >> through calls, no matter how the program is laid out. Walk up the call >> stack until you find the first variable with that name. >> >> $ variables don't lend themselves to module-level defaults, though you >> can do it using is_undef(). >> >> You can write a variation on the example above as: >> >> $cube1_size = 10; >> $cube2_size = 15; >> >> twoCubes(); >> translate([30,0,0]) { >> $cube1_size = 20; >> $cube2_size = 30; >> twoCubes(); >> } >> >> module twoCubes() { >> cube($cube1_size, center=true); >> cube($cube2_size); >> } >> >> ... but mostly I wouldn't recommend it. >> >> Related notes: >> >> >> - In any given block, all assignments are processed before all module >> invocations. Don't expect to be able to call a module, then set a $ >> variable, then call it again. Both calls will get the value from the >> assignment. >> - With respect to the model, it almost doesn't matter what order >> modules get called in. (Exceptions: echo(), rands(), and some cases >> involving transparency and coloring that are probably best viewed as >> weaknesses in the previewer.) >> - The customizer will suck up all constant assignments, until it hits >> the first module definition. If you want to have a global variable that >> the user *shouldn't* customize, put in a dummy "module stop();" after the >> customizable variables. >> >> >>
JG
Jonathan Gilbert
Mon, Mar 11, 2024 1:47 AM

https://github.com/jon-gilbert/openscad_objects is perhaps what you're
thinking of?

On Sun, Mar 10, 2024 at 6:46 PM John David via Discuss <
discuss@lists.openscad.org> wrote:

also with regards to manipulating named parameters, I remember reading
about something similar another project was using.  Is there a pre-built
library for this functionality?  I might have not found it yet, and should
scrap my use that instead.

EBo --

On Sun, Mar 10, 2024 at 9:00 PM John David ebo.2112@gmail.com wrote:

OK.  It is starting to take shape.  What I did was to build a named
parameter list that looks like:

 [["cleat","type",type],
  ["cleat","thickness",thickness],
  ["cleat","width",width],
  ["cleat","chamfer",chamfer],
  ["cleat","kerf",kerf],
  ["cleat","angle",angle],
  ["cleat","profile",_fc_cleat_profile

(width,thickness,chamfer,angle,kerf)]];

and then pass these parameters through to other functions.  This is only
a small example, but is starting to feel about right:

include <BOSL2/std.scad>
include <BOSL2/trigonometry.scad>

function
French_Cleat_params(type="solid",thickness=0.75,width=5.5/2,chamfer=0.125,kerf=0.125,angle=45.0)

 [["cleat","type",type],
  ["cleat","thickness",thickness],
  ["cleat","width",width],
  ["cleat","chamfer",chamfer],
  ["cleat","kerf",kerf],
  ["cleat","angle",angle],
  ["cleat","profile",_fc_cleat_profile

(width,thickness,chamfer,angle,kerf)]];

function _fc_cleat_profile
(cleat_width,cleat_thickness,cleat_chamfer,cleat_angle,kerf) =
let(
c = cleat_thickness,
b = adj_ang_to_hyp(c,cleat_angle),
a = adj_ang_to_opp(c,cleat_angle),
a2 = adj_ang_to_opp(c-cleat_chamfer,cleat_angle),
c2 = c-cleat_chamfer
) [[0.0,0.0], [-c,0.0], [-c,a2],[-c2,a2]]; // FIXME: adjust for kerfs

function fc_param(params,catigory,var) =
let(
cat =
search(catigory,params,num_returns_per_match=0,index_col_num=0),
vals = [for(i=cat) for(j=i) params[j]],
scat = search(var,vals,num_returns_per_match=1,index_col_num=1),
val = [for(z=scat) for(k=z) vals[k]],
itm = val[0][2],
echo(".....",itm)
) itm;

// generate a set of parameters for the French Cleats (with user modified
angle).
cp = French_Cleat_params(angle=30);

// grab the French Cleat Parameters (as modified by user), and display
pc = fc_param(cp,"cleat","profile");
echo("  pc = ",pc);
color("yellow") linear_extrude(.25) polygon(pc);

I will clean this up as I start using it, but I think I am starting to
figure out OpenSCAD's syntax and semantics - the variables being static
after defined threw me for a loop).

I am confused about the warnings that are being generated by the search.
Anyone have suggestions on how to fix those?

EBo --

On Sun, Mar 10, 2024 at 12:08 PM Jordan Brown <
openscad@jordan.maileater.net> wrote:

On 3/10/2024 1:57 AM, John David via Discuss wrote:

Q1: is there are way to access a nested (sub)module from outside its
parent scope?

No.  OpenSCAD modules are black holes.  No information comes out of them.

Also, OpenSCAD variables ... don't vary.  Once set, they cannot be
changed.  (You can create a new instance of a variable in a block, but
that's a new variable and the outer one is unchanged.)

I was hoping I could find a way to do something like:

module French_Cleat() {
module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
{fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

Nope.  Can't do anything like that, because there is absolutely no way
for a module to set a global variable.

Q2: it looks like functions are compiled when I include/use a file, and
not when used.  Is there a way around this.

No.  (Though given the scoping rules, it doesn't matter when the file is
"compiled", or that it's compiled at all.)

Advice:

For a simple model where the user wants to set values, put the values
together at the top of the file, so that the user can set them using the
Customizer.  See
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for
information on how to add comments to control the labels and other user
interface aspects.

For a subassembly where a program is going to set the values and control
them, the usual strategy is to have your subassembly's module take
parameters, and have the parameters have defaults.

Combining those two concepts:

cube1_size = 10;
cube2_size = 15;
ratio = 2;

twoCubes();
translate([30,0,0]) twoCubes(cube1_size, cube2_size);
translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio);

module twoCubes(c1=5, c2=7) {
cube(c1, center=true);
cube(c2);
}

Note that the file has default values (that the user can change with the
customizer) that control the middle pair of cubes, and the module has its
own defaults that control the first pair.

The usual variable scoping is lexical:  you can look at the layout of
the program to see what variables are visible at any given point.  Just
walk up the enclosing blocks until you get to the first variable with that
name.

You can use $ variables to pass information into modules or
functions.  They are still immutable, but are dynamically scoped.  They are
passed down through calls, no matter how the program is laid out.  Walk up
the call stack until you find the first variable with that name.

$ variables don't lend themselves to module-level defaults, though you
can do it using is_undef().

You can write a variation on the example above as:

$cube1_size = 10;
$cube2_size = 15;

twoCubes();
translate([30,0,0]) {
$cube1_size = 20;
$cube2_size = 30;
twoCubes();
}

module twoCubes() {
cube($cube1_size, center=true);
cube($cube2_size);
}

... but mostly I wouldn't recommend it.

Related notes:

- In any given block, all assignments are processed before all
module invocations.  Don't expect to be able to call a module, then set a $
variable, then call it again.  Both calls will get the value from the
assignment.
- With respect to the model, it almost doesn't matter what order
modules get called in.  (Exceptions:  echo(), rands(), and some cases
involving transparency and coloring that are probably best viewed as
weaknesses in the previewer.)
- The customizer will suck up all constant assignments, until it
hits the first module definition.  If you want to have a global variable
that the user *shouldn't* customize, put in a dummy "module stop();" after
the customizable variables.

OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

https://github.com/jon-gilbert/openscad_objects is perhaps what you're thinking of? On Sun, Mar 10, 2024 at 6:46 PM John David via Discuss < discuss@lists.openscad.org> wrote: > also with regards to manipulating named parameters, I remember reading > about something similar another project was using. Is there a pre-built > library for this functionality? I might have not found it yet, and should > scrap my use that instead. > > EBo -- > > On Sun, Mar 10, 2024 at 9:00 PM John David <ebo.2112@gmail.com> wrote: > >> OK. It is starting to take shape. What I did was to build a named >> parameter list that looks like: >> >> [["cleat","type",type], >> ["cleat","thickness",thickness], >> ["cleat","width",width], >> ["cleat","chamfer",chamfer], >> ["cleat","kerf",kerf], >> ["cleat","angle",angle], >> ["cleat","profile",_fc_cleat_profile >> (width,thickness,chamfer,angle,kerf)]]; >> >> and then pass these parameters through to other functions. This is only >> a small example, but is starting to feel about right: >> >> include <BOSL2/std.scad> >> include <BOSL2/trigonometry.scad> >> >> >> function >> French_Cleat_params(type="solid",thickness=0.75,width=5.5/2,chamfer=0.125,kerf=0.125,angle=45.0) >> = >> [["cleat","type",type], >> ["cleat","thickness",thickness], >> ["cleat","width",width], >> ["cleat","chamfer",chamfer], >> ["cleat","kerf",kerf], >> ["cleat","angle",angle], >> ["cleat","profile",_fc_cleat_profile >> (width,thickness,chamfer,angle,kerf)]]; >> >> function _fc_cleat_profile >> (cleat_width,cleat_thickness,cleat_chamfer,cleat_angle,kerf) = >> let( >> c = cleat_thickness, >> b = adj_ang_to_hyp(c,cleat_angle), >> a = adj_ang_to_opp(c,cleat_angle), >> a2 = adj_ang_to_opp(c-cleat_chamfer,cleat_angle), >> c2 = c-cleat_chamfer >> ) [[0.0,0.0], [-c,0.0], [-c,a2],[-c2,a2]]; // FIXME: adjust for kerfs >> >> function fc_param(params,catigory,var) = >> let( >> cat = >> search(catigory,params,num_returns_per_match=0,index_col_num=0), >> vals = [for(i=cat) for(j=i) params[j]], >> scat = search(var,vals,num_returns_per_match=1,index_col_num=1), >> val = [for(z=scat) for(k=z) vals[k]], >> itm = val[0][2], >> echo(".....",itm) >> ) itm; >> >> // generate a set of parameters for the French Cleats (with user modified >> angle). >> cp = French_Cleat_params(angle=30); >> >> // grab the French Cleat Parameters (as modified by user), and display >> pc = fc_param(cp,"cleat","profile"); >> echo(" pc = ",pc); >> color("yellow") linear_extrude(.25) polygon(pc); >> >> I will clean this up as I start using it, but I think I am starting to >> figure out OpenSCAD's syntax and semantics - the variables being static >> after defined threw me for a loop). >> >> I am confused about the warnings that are being generated by the search. >> Anyone have suggestions on how to fix those? >> >> EBo -- >> >> On Sun, Mar 10, 2024 at 12:08 PM Jordan Brown < >> openscad@jordan.maileater.net> wrote: >> >>> On 3/10/2024 1:57 AM, John David via Discuss wrote: >>> >>> Q1: is there are way to access a nested (sub)module from outside its >>> parent scope? >>> >>> >>> No. OpenSCAD modules are black holes. No information comes out of them. >>> >>> Also, OpenSCAD variables ... don't vary. Once set, they cannot be >>> changed. (You can create a new instance of a variable in a block, but >>> that's a new variable and the outer one is unchanged.) >>> >>> I was hoping I could find a way to do something like: >>> >>> module French_Cleat() { >>> module >>> define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) >>> {fc_angle=angle; ...} >>> function cleat_profile () =....; // uses fc_angle >>> } >>> >>> >>> Nope. Can't do anything like that, because there is absolutely no way >>> for a module to set a global variable. >>> >>> Q2: it looks like functions are compiled when I include/use a file, and >>> not when used. Is there a way around this. >>> >>> >>> No. (Though given the scoping rules, it doesn't matter when the file is >>> "compiled", or that it's compiled at all.) >>> >>> Advice: >>> >>> For a simple model where the *user* wants to set values, put the values >>> together at the top of the file, so that the user can set them using the >>> Customizer. See >>> https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for >>> information on how to add comments to control the labels and other user >>> interface aspects. >>> >>> For a subassembly where a program is going to set the values and control >>> them, the usual strategy is to have your subassembly's module take >>> parameters, and have the parameters have defaults. >>> >>> Combining those two concepts: >>> >>> cube1_size = 10; >>> cube2_size = 15; >>> ratio = 2; >>> >>> twoCubes(); >>> translate([30,0,0]) twoCubes(cube1_size, cube2_size); >>> translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio); >>> >>> module twoCubes(c1=5, c2=7) { >>> cube(c1, center=true); >>> cube(c2); >>> } >>> >>> Note that the file has default values (that the user can change with the >>> customizer) that control the middle pair of cubes, and the module has its >>> own defaults that control the first pair. >>> >>> The usual variable scoping is lexical: you can look at the layout of >>> the program to see what variables are visible at any given point. Just >>> walk up the enclosing blocks until you get to the first variable with that >>> name. >>> >>> You *can* use $ variables to pass information into modules or >>> functions. They are still immutable, but are dynamically scoped. They are >>> passed down through calls, no matter how the program is laid out. Walk up >>> the call stack until you find the first variable with that name. >>> >>> $ variables don't lend themselves to module-level defaults, though you >>> can do it using is_undef(). >>> >>> You can write a variation on the example above as: >>> >>> $cube1_size = 10; >>> $cube2_size = 15; >>> >>> twoCubes(); >>> translate([30,0,0]) { >>> $cube1_size = 20; >>> $cube2_size = 30; >>> twoCubes(); >>> } >>> >>> module twoCubes() { >>> cube($cube1_size, center=true); >>> cube($cube2_size); >>> } >>> >>> ... but mostly I wouldn't recommend it. >>> >>> Related notes: >>> >>> >>> - In any given block, all assignments are processed before all >>> module invocations. Don't expect to be able to call a module, then set a $ >>> variable, then call it again. Both calls will get the value from the >>> assignment. >>> - With respect to the model, it almost doesn't matter what order >>> modules get called in. (Exceptions: echo(), rands(), and some cases >>> involving transparency and coloring that are probably best viewed as >>> weaknesses in the previewer.) >>> - The customizer will suck up all constant assignments, until it >>> hits the first module definition. If you want to have a global variable >>> that the user *shouldn't* customize, put in a dummy "module stop();" after >>> the customizable variables. >>> >>> >>> _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org > -- - Jon Gilbert jong@jong.org / jgilbertsjc@gmail.com
JD
John David
Mon, Mar 11, 2024 2:13 AM

Thank you, Jon, for that pointer.  It looks interesting.  It gave me some
ideas on where to look inside BOSL2 as well -- where I found hashmap.

I will look at both of them, but I like some of the functionality with the
hashmap (specifically del and additems).  I'll give that a try.

EBo --

On Sun, Mar 10, 2024 at 9:48 PM Jonathan Gilbert via Discuss <
discuss@lists.openscad.org> wrote:

https://github.com/jon-gilbert/openscad_objects is perhaps what you're
thinking of?

On Sun, Mar 10, 2024 at 6:46 PM John David via Discuss <
discuss@lists.openscad.org> wrote:

also with regards to manipulating named parameters, I remember reading
about something similar another project was using.  Is there a pre-built
library for this functionality?  I might have not found it yet, and should
scrap my use that instead.

EBo --

On Sun, Mar 10, 2024 at 9:00 PM John David ebo.2112@gmail.com wrote:

OK.  It is starting to take shape.  What I did was to build a named
parameter list that looks like:

 [["cleat","type",type],
  ["cleat","thickness",thickness],
  ["cleat","width",width],
  ["cleat","chamfer",chamfer],
  ["cleat","kerf",kerf],
  ["cleat","angle",angle],
  ["cleat","profile",_fc_cleat_profile

(width,thickness,chamfer,angle,kerf)]];

and then pass these parameters through to other functions.  This is only
a small example, but is starting to feel about right:

include <BOSL2/std.scad>
include <BOSL2/trigonometry.scad>

function
French_Cleat_params(type="solid",thickness=0.75,width=5.5/2,chamfer=0.125,kerf=0.125,angle=45.0)

 [["cleat","type",type],
  ["cleat","thickness",thickness],
  ["cleat","width",width],
  ["cleat","chamfer",chamfer],
  ["cleat","kerf",kerf],
  ["cleat","angle",angle],
  ["cleat","profile",_fc_cleat_profile

(width,thickness,chamfer,angle,kerf)]];

function _fc_cleat_profile
(cleat_width,cleat_thickness,cleat_chamfer,cleat_angle,kerf) =
let(
c = cleat_thickness,
b = adj_ang_to_hyp(c,cleat_angle),
a = adj_ang_to_opp(c,cleat_angle),
a2 = adj_ang_to_opp(c-cleat_chamfer,cleat_angle),
c2 = c-cleat_chamfer
) [[0.0,0.0], [-c,0.0], [-c,a2],[-c2,a2]]; // FIXME: adjust for kerfs

function fc_param(params,catigory,var) =
let(
cat =
search(catigory,params,num_returns_per_match=0,index_col_num=0),
vals = [for(i=cat) for(j=i) params[j]],
scat = search(var,vals,num_returns_per_match=1,index_col_num=1),
val = [for(z=scat) for(k=z) vals[k]],
itm = val[0][2],
echo(".....",itm)
) itm;

// generate a set of parameters for the French Cleats (with user
modified angle).
cp = French_Cleat_params(angle=30);

// grab the French Cleat Parameters (as modified by user), and display
pc = fc_param(cp,"cleat","profile");
echo("  pc = ",pc);
color("yellow") linear_extrude(.25) polygon(pc);

I will clean this up as I start using it, but I think I am starting to
figure out OpenSCAD's syntax and semantics - the variables being static
after defined threw me for a loop).

I am confused about the warnings that are being generated by the
search.  Anyone have suggestions on how to fix those?

EBo --

On Sun, Mar 10, 2024 at 12:08 PM Jordan Brown <
openscad@jordan.maileater.net> wrote:

On 3/10/2024 1:57 AM, John David via Discuss wrote:

Q1: is there are way to access a nested (sub)module from outside its
parent scope?

No.  OpenSCAD modules are black holes.  No information comes out of
them.

Also, OpenSCAD variables ... don't vary.  Once set, they cannot be
changed.  (You can create a new instance of a variable in a block, but
that's a new variable and the outer one is unchanged.)

I was hoping I could find a way to do something like:

module French_Cleat() {
module
define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06)
{fc_angle=angle; ...}
function cleat_profile () =....; // uses fc_angle
}

Nope.  Can't do anything like that, because there is absolutely no way
for a module to set a global variable.

Q2: it looks like functions are compiled when I include/use a file, and
not when used.  Is there a way around this.

No.  (Though given the scoping rules, it doesn't matter when the file
is "compiled", or that it's compiled at all.)

Advice:

For a simple model where the user wants to set values, put the values
together at the top of the file, so that the user can set them using the
Customizer.  See
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for
information on how to add comments to control the labels and other user
interface aspects.

For a subassembly where a program is going to set the values and
control them, the usual strategy is to have your subassembly's module take
parameters, and have the parameters have defaults.

Combining those two concepts:

cube1_size = 10;
cube2_size = 15;
ratio = 2;

twoCubes();
translate([30,0,0]) twoCubes(cube1_size, cube2_size);
translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio);

module twoCubes(c1=5, c2=7) {
cube(c1, center=true);
cube(c2);
}

Note that the file has default values (that the user can change with
the customizer) that control the middle pair of cubes, and the module has
its own defaults that control the first pair.

The usual variable scoping is lexical:  you can look at the layout of
the program to see what variables are visible at any given point.  Just
walk up the enclosing blocks until you get to the first variable with that
name.

You can use $ variables to pass information into modules or
functions.  They are still immutable, but are dynamically scoped.  They are
passed down through calls, no matter how the program is laid out.  Walk up
the call stack until you find the first variable with that name.

$ variables don't lend themselves to module-level defaults, though you
can do it using is_undef().

You can write a variation on the example above as:

$cube1_size = 10;
$cube2_size = 15;

twoCubes();
translate([30,0,0]) {
$cube1_size = 20;
$cube2_size = 30;
twoCubes();
}

module twoCubes() {
cube($cube1_size, center=true);
cube($cube2_size);
}

... but mostly I wouldn't recommend it.

Related notes:

- In any given block, all assignments are processed before all
module invocations.  Don't expect to be able to call a module, then set a $
variable, then call it again.  Both calls will get the value from the
assignment.
- With respect to the model, it almost doesn't matter what order
modules get called in.  (Exceptions:  echo(), rands(), and some cases
involving transparency and coloring that are probably best viewed as
weaknesses in the previewer.)
- The customizer will suck up all constant assignments, until it
hits the first module definition.  If you want to have a global variable
that the user *shouldn't* customize, put in a dummy "module stop();" after
the customizable variables.

OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

--


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Thank you, Jon, for that pointer. It looks interesting. It gave me some ideas on where to look inside BOSL2 as well -- where I found hashmap. I will look at both of them, but I like some of the functionality with the hashmap (specifically del and additems). I'll give that a try. EBo -- On Sun, Mar 10, 2024 at 9:48 PM Jonathan Gilbert via Discuss < discuss@lists.openscad.org> wrote: > https://github.com/jon-gilbert/openscad_objects is perhaps what you're > thinking of? > > On Sun, Mar 10, 2024 at 6:46 PM John David via Discuss < > discuss@lists.openscad.org> wrote: > >> also with regards to manipulating named parameters, I remember reading >> about something similar another project was using. Is there a pre-built >> library for this functionality? I might have not found it yet, and should >> scrap my use that instead. >> >> EBo -- >> >> On Sun, Mar 10, 2024 at 9:00 PM John David <ebo.2112@gmail.com> wrote: >> >>> OK. It is starting to take shape. What I did was to build a named >>> parameter list that looks like: >>> >>> [["cleat","type",type], >>> ["cleat","thickness",thickness], >>> ["cleat","width",width], >>> ["cleat","chamfer",chamfer], >>> ["cleat","kerf",kerf], >>> ["cleat","angle",angle], >>> ["cleat","profile",_fc_cleat_profile >>> (width,thickness,chamfer,angle,kerf)]]; >>> >>> and then pass these parameters through to other functions. This is only >>> a small example, but is starting to feel about right: >>> >>> include <BOSL2/std.scad> >>> include <BOSL2/trigonometry.scad> >>> >>> >>> function >>> French_Cleat_params(type="solid",thickness=0.75,width=5.5/2,chamfer=0.125,kerf=0.125,angle=45.0) >>> = >>> [["cleat","type",type], >>> ["cleat","thickness",thickness], >>> ["cleat","width",width], >>> ["cleat","chamfer",chamfer], >>> ["cleat","kerf",kerf], >>> ["cleat","angle",angle], >>> ["cleat","profile",_fc_cleat_profile >>> (width,thickness,chamfer,angle,kerf)]]; >>> >>> function _fc_cleat_profile >>> (cleat_width,cleat_thickness,cleat_chamfer,cleat_angle,kerf) = >>> let( >>> c = cleat_thickness, >>> b = adj_ang_to_hyp(c,cleat_angle), >>> a = adj_ang_to_opp(c,cleat_angle), >>> a2 = adj_ang_to_opp(c-cleat_chamfer,cleat_angle), >>> c2 = c-cleat_chamfer >>> ) [[0.0,0.0], [-c,0.0], [-c,a2],[-c2,a2]]; // FIXME: adjust for kerfs >>> >>> function fc_param(params,catigory,var) = >>> let( >>> cat = >>> search(catigory,params,num_returns_per_match=0,index_col_num=0), >>> vals = [for(i=cat) for(j=i) params[j]], >>> scat = search(var,vals,num_returns_per_match=1,index_col_num=1), >>> val = [for(z=scat) for(k=z) vals[k]], >>> itm = val[0][2], >>> echo(".....",itm) >>> ) itm; >>> >>> // generate a set of parameters for the French Cleats (with user >>> modified angle). >>> cp = French_Cleat_params(angle=30); >>> >>> // grab the French Cleat Parameters (as modified by user), and display >>> pc = fc_param(cp,"cleat","profile"); >>> echo(" pc = ",pc); >>> color("yellow") linear_extrude(.25) polygon(pc); >>> >>> I will clean this up as I start using it, but I think I am starting to >>> figure out OpenSCAD's syntax and semantics - the variables being static >>> after defined threw me for a loop). >>> >>> I am confused about the warnings that are being generated by the >>> search. Anyone have suggestions on how to fix those? >>> >>> EBo -- >>> >>> On Sun, Mar 10, 2024 at 12:08 PM Jordan Brown < >>> openscad@jordan.maileater.net> wrote: >>> >>>> On 3/10/2024 1:57 AM, John David via Discuss wrote: >>>> >>>> Q1: is there are way to access a nested (sub)module from outside its >>>> parent scope? >>>> >>>> >>>> No. OpenSCAD modules are black holes. No information comes out of >>>> them. >>>> >>>> Also, OpenSCAD variables ... don't vary. Once set, they cannot be >>>> changed. (You can create a new instance of a variable in a block, but >>>> that's a new variable and the outer one is unchanged.) >>>> >>>> I was hoping I could find a way to do something like: >>>> >>>> module French_Cleat() { >>>> module >>>> define_solid_params(thickness=0.75,width=5.5/2,angle=45.0,chamfer=0.0625,saw_kerf=0.125,laseer_kerf=0.06) >>>> {fc_angle=angle; ...} >>>> function cleat_profile () =....; // uses fc_angle >>>> } >>>> >>>> >>>> Nope. Can't do anything like that, because there is absolutely no way >>>> for a module to set a global variable. >>>> >>>> Q2: it looks like functions are compiled when I include/use a file, and >>>> not when used. Is there a way around this. >>>> >>>> >>>> No. (Though given the scoping rules, it doesn't matter when the file >>>> is "compiled", or that it's compiled at all.) >>>> >>>> Advice: >>>> >>>> For a simple model where the *user* wants to set values, put the values >>>> together at the top of the file, so that the user can set them using the >>>> Customizer. See >>>> https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer for >>>> information on how to add comments to control the labels and other user >>>> interface aspects. >>>> >>>> For a subassembly where a program is going to set the values and >>>> control them, the usual strategy is to have your subassembly's module take >>>> parameters, and have the parameters have defaults. >>>> >>>> Combining those two concepts: >>>> >>>> cube1_size = 10; >>>> cube2_size = 15; >>>> ratio = 2; >>>> >>>> twoCubes(); >>>> translate([30,0,0]) twoCubes(cube1_size, cube2_size); >>>> translate([60,0,0]) twoCubes(cube1_size * ratio, cube2_size * ratio); >>>> >>>> module twoCubes(c1=5, c2=7) { >>>> cube(c1, center=true); >>>> cube(c2); >>>> } >>>> >>>> Note that the file has default values (that the user can change with >>>> the customizer) that control the middle pair of cubes, and the module has >>>> its own defaults that control the first pair. >>>> >>>> The usual variable scoping is lexical: you can look at the layout of >>>> the program to see what variables are visible at any given point. Just >>>> walk up the enclosing blocks until you get to the first variable with that >>>> name. >>>> >>>> You *can* use $ variables to pass information into modules or >>>> functions. They are still immutable, but are dynamically scoped. They are >>>> passed down through calls, no matter how the program is laid out. Walk up >>>> the call stack until you find the first variable with that name. >>>> >>>> $ variables don't lend themselves to module-level defaults, though you >>>> can do it using is_undef(). >>>> >>>> You can write a variation on the example above as: >>>> >>>> $cube1_size = 10; >>>> $cube2_size = 15; >>>> >>>> twoCubes(); >>>> translate([30,0,0]) { >>>> $cube1_size = 20; >>>> $cube2_size = 30; >>>> twoCubes(); >>>> } >>>> >>>> module twoCubes() { >>>> cube($cube1_size, center=true); >>>> cube($cube2_size); >>>> } >>>> >>>> ... but mostly I wouldn't recommend it. >>>> >>>> Related notes: >>>> >>>> >>>> - In any given block, all assignments are processed before all >>>> module invocations. Don't expect to be able to call a module, then set a $ >>>> variable, then call it again. Both calls will get the value from the >>>> assignment. >>>> - With respect to the model, it almost doesn't matter what order >>>> modules get called in. (Exceptions: echo(), rands(), and some cases >>>> involving transparency and coloring that are probably best viewed as >>>> weaknesses in the previewer.) >>>> - The customizer will suck up all constant assignments, until it >>>> hits the first module definition. If you want to have a global variable >>>> that the user *shouldn't* customize, put in a dummy "module stop();" after >>>> the customizable variables. >>>> >>>> >>>> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > > > -- > - Jon Gilbert > jong@jong.org / jgilbertsjc@gmail.com > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >