parameterized models

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

parameterized models

jon_bondy
I have an OpenSCAD file that creates a family of objects.  Think of it
as if I want to create a family of crescent wrenches.  There are about a
dozen parameters that have to be set for each  object.

At the moment, I use global variables for these parameters.  I have
about 10 blocks of code that define the parameters for each of my 10
objects, and I ensure that the object of interest has its code block at
the end.  This approach is clear, but those code blocks now take up a
few pages, so skipping over them to get to the real code is laborious.

I considered creating an object/module with those dozen parameters, but
I think that would become almost unreadable.

Any hints on a good way to handle this?  Or options I should consider?

Thanks!

Jon
_______________________________________________
OpenSCAD mailing list
[hidden email]
http://rocklinux.net/mailman/listinfo/openscad
http://openscad.org - https://flattr.com/thing/121566
Reply | Threaded
Open this post in threaded view
|

Re: parameterized models

drxenocide
well, you could make the parameters lists, which are global variables, and then implement standard code using the size as the parameter of all the lists.

radius={1,4,9,16,25};

wrench(size){
      sphere[radius(size)];
      }

//then to make the family

for(i=[1:5]){
    translate([offset*i,0,0]) 
        wrench(i);
    }





On Sat, Jun 7, 2014 at 11:53 AM, jon <[hidden email]> wrote:
I have an OpenSCAD file that creates a family of objects.  Think of it
as if I want to create a family of crescent wrenches.  There are about a
dozen parameters that have to be set for each  object.

At the moment, I use global variables for these parameters.  I have
about 10 blocks of code that define the parameters for each of my 10
objects, and I ensure that the object of interest has its code block at
the end.  This approach is clear, but those code blocks now take up a
few pages, so skipping over them to get to the real code is laborious.

I considered creating an object/module with those dozen parameters, but
I think that would become almost unreadable.

Any hints on a good way to handle this?  Or options I should consider?

Thanks!

Jon
_______________________________________________
OpenSCAD mailing list
[hidden email]
http://rocklinux.net/mailman/listinfo/openscad
http://openscad.org - https://flattr.com/thing/121566


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://rocklinux.net/mailman/listinfo/openscad
http://openscad.org - https://flattr.com/thing/121566
Reply | Threaded
Open this post in threaded view
|

Re: parameterized models

nophead
I would use a list to describe all the parameters for each wrench and give them names. Then I would put them in a list of wrenches and iterate through that passing each wrench's description to the module that draws it.

I would use functions to access each element in the list to make it readable rather than using just its index.

M3_wrench = [ 5.5, ... ];
M4_wrench = [7.0, ...];
...

function wrench_across_flats(w) = w[0];
...

module wrench(w) {
      gap = wrench_across_flats(w);
      ...
      ...
}

wrenches = [M3_wrench, M4_wrench, ...];

for(i = [0, len(wrenches) - 1])
    translate([i * offset, 0, 0])
            wrench(wrenches[i]);




On 7 June 2014 18:09, Ari Diacou <[hidden email]> wrote:
well, you could make the parameters lists, which are global variables, and then implement standard code using the size as the parameter of all the lists.

radius={1,4,9,16,25};

wrench(size){
      sphere[radius(size)];
      }

//then to make the family

for(i=[1:5]){
    translate([offset*i,0,0]) 
        wrench(i);
    }





On Sat, Jun 7, 2014 at 11:53 AM, jon <[hidden email]> wrote:
I have an OpenSCAD file that creates a family of objects.  Think of it
as if I want to create a family of crescent wrenches.  There are about a
dozen parameters that have to be set for each  object.

At the moment, I use global variables for these parameters.  I have
about 10 blocks of code that define the parameters for each of my 10
objects, and I ensure that the object of interest has its code block at
the end.  This approach is clear, but those code blocks now take up a
few pages, so skipping over them to get to the real code is laborious.

I considered creating an object/module with those dozen parameters, but
I think that would become almost unreadable.

Any hints on a good way to handle this?  Or options I should consider?

Thanks!

Jon
_______________________________________________
OpenSCAD mailing list
[hidden email]
http://rocklinux.net/mailman/listinfo/openscad
http://openscad.org - https://flattr.com/thing/121566


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://rocklinux.net/mailman/listinfo/openscad
http://openscad.org - https://flattr.com/thing/121566


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://rocklinux.net/mailman/listinfo/openscad
http://openscad.org - https://flattr.com/thing/121566
Reply | Threaded
Open this post in threaded view
|

Re: parameterized models

runsun
In reply to this post by jon_bondy
I encountered this situation quite often and here is how I dealt with it.

1)

  First define an array to encapsulate/group similar parameters in a pattern of key-value pairs:

   wrench_default = ["len", 10, "w", 2, "color", "red"];

2)

  Secondly define a hash function to get value:

  function hash(h,k, _i_=0)=
  (
        _i_>len(h)-1? undef  // key not found
                : h[_i_]==k?    // key found
                         h[_i_+1]  // return v
                        : hash(h,k,_i_+2)          
 );

  This would give you:  

     hash( wrench_default, "len"); ==> 10

  And since functions and variables use different name spaces, we can take the advantage and define this function:

     function wrench_default(k) = hash( wrench_ops, k);

  such that:  

     wrench_default("len");  ==> 10
     wrench_default("w");  ==> 2
     wrench_default("color");  ==> "red"

  We can then define the module:

     module Wrench( ops = wrench_default ){

         function ops(k) = hash( ops, k);  
         len = ops("len");                      // convert to local scope
         (blah blah blah ...)
     }

  With this, we are able to encapsulate the parameters and throw them around as a whole "hash bundle". It means we save a lot of hassle on re-typing the same things, and the global space will not be polluted by variables that are not supposed to be global in the first place.

3)

  But this is not quite enough. If there are 5 types of wrenches, although they share lots of common wrench parameters, we can't expect all types to share all parameters. For example, "len" might be different for different types. So there's a need to modify the hash bundle on the fly. Usually such an action is performed by an update function:

    ops = update( wrench_default, individual_ops );

  which converts, for example,

    ["len", 10, "w", 2, "color", "red"]

  to

    ["len", 12, "w", 2, "color", "red"]

  In OpenScad, however, we don't have hash, let alone update. And it will take much more than just a function to come up with a feature like that. But, we can cheat this by a simple concat :

    ops = concat( individual_ops, wrench_default );
 
  which gives us the "working as updated" data :

    ["len", 12, "len", 10, "w", 2, "color", "red"]

  Note that the key "len" appears twice. But since the hash() reads data from left to right and stops whenever the key is found so it will give us the new "len", 12, making the data look like it is updated.

4)

  With this coding pattern, we can define the basic wrench, the mother of all wrenches, like:

     module Wrench( ops ){

         ops = concat( ops                                              // user-input ops                                              
                           , [ "len", 10  , "w", 2, "color", "red"]    // wrench_default
                           );
         function ops(k) = hash( ops, k);  
         len = ops("len");                      // convert to local scope

         (blah blah blah ...)
     }

   And then, "subclassing" from Wrench, to make a specific type of wrench like:

     module Wrench_1( individual_ops ){

         Wrench_1_ops = concat(  [ "len", 12 ]    //-- Wrench_1-specific default
                                         ,  individual_ops //-- user-input ops for Wrench_1
                                         );

         Wrench( Wrench_1_ops );    // calling the "mother wrench", in which this Wrench_1_ops
                                                // is passed to "update" the wrench_default of mother wrench.
     }

     Note that in Wrench_1, users can set any parameter but "len", 'cos it will be set to 12 no matter what.  So the users or next-step-developer can enter any individual_ops to Wrench_1, in which the len be set to 12, and then the whole "hash bundle" ( Wrench_1_ops ) be sent to Wrench(), in which it is processed against the wrench_default. With this, we actually achieve a subclassing-style coding pattern, so that we can design complex module and inherit from it.
 
  Also note that this "mother-child" relationship can go on forever. For example, a next-level wrench:

    module Wrench_grandkid( grandkid_ops ){

         grandkid_ops = concat(  [ "w", 1 ]      //-- grandkid-specific default
                                        ,  grandkid_ops //-- user-input ops for grandkid
                                        );

         Wrench_1( grandkid_ops );  
     }



To wrap up, there are many advantages of using this pattern:

(a) Encapsulation: Parameters of same type is grouped together in a hash. With that we save a lot of typing --- only type when there's something new;
(b) There's NO global variable hanging around at all, no matter how complex the module is;
(c) Inheritance : we can kind of "subclass" a module easily.
(d) Multi-level customization is possible (module build-level, developer-level, ..., user-level/or Runtime level);
(e) As a result, complex modules can be designed without sacrificing readability.
$ Runsun Pan, PhD
$ libs: scadx, doctest, faces(git), offline doc(git), runscad.py(2,git), editor of choice: CudaText ( OpenSCAD lexer); $ Tips; $ Snippets
Reply | Threaded
Open this post in threaded view
|

Re: parameterized models

MichaelAtOz
Administrator
In reply to this post by jon_bondy
Have a look at MCAD/stepper.scad, and then at my Thing for Fans which used the base concept from steppers to hold the definitions with support functions to extract the parameters.
Admin - email* me if you need anything,
or if I've done something stupid...
* click on my MichaelAtOz label, there is a link to email me.

Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work.
Obviously inclusion of works of previous authors is not included in the above.


The TPP is no simple “trade agreement.” Fight it! http://www.ourfairdeal.org/ time is running out!
Reply | Threaded
Open this post in threaded view
|

Re: parameterized models

jon_bondy
Thanks!

Yikes!  This approach would have me spending more time writing and
maintaining the support structure for my code than on the code itself.  
Surely there is a better way.  I hope.

Jon

On 6/8/2014 1:11 AM, MichaelAtOz wrote:
> Have a look at MCAD/stepper.scad, and then at my Thing for  Fans
> <http://www.thingiverse.com/thing:111187>   which used the base concept from
> steppers to hold the definitions with support functions to extract the
> parameters.
>

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://rocklinux.net/mailman/listinfo/openscad
http://openscad.org - https://flattr.com/thing/121566
Reply | Threaded
Open this post in threaded view
|

Re: parameterized models

jon_bondy
In reply to this post by nophead
So far, a variant on this approach is my favorite!  Thanks!

On 6/7/2014 2:32 PM, nop head wrote:

> I would use a list to describe all the parameters for each wrench and
> give them names. Then I would put them in a list of wrenches and
> iterate through that passing each wrench's description to the module
> that draws it.
>
> I would use functions to access each element in the list to make it
> readable rather than using just its index.
>
> M3_wrench = [ 5.5, ... ];
> M4_wrench = [7.0, ...];
> ...
>
> function wrench_across_flats(w) = w[0];
> ...
>
> module wrench(w) {
>       gap = wrench_across_flats(w);
>       ...
>       ...
> }
>
> wrenches = [M3_wrench, M4_wrench, ...];
>
> for(i = [0, len(wrenches) - 1])
>
>
>

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://rocklinux.net/mailman/listinfo/openscad
http://openscad.org - https://flattr.com/thing/121566