discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

workflow/approach request

JB
Jordan Brown
Wed, Aug 21, 2019 5:58 PM

[ Sorry, sent the first copy from the wrong e-mail address. ]

On 8/21/2019 5:38 AM, fred_dot_u via Discuss wrote:

That bit confused me slightly as well, as there were no curly braces after the module call, [...]

Like most languages with sort-of-C-derived structure, you only need
braces if you want to enclose more than one statement.  Also, line
breaks are not important.

Thus these are equivalent:

translate(...) rotate(...) scale(...) cube(...);

translate(...)
    rotate(...)
        scale(...)
            cube(...);

translate(...) {
    rotate(...) {
        scale(...) {
            cube(...);
        }
    }
}

As for children( ), the way that I think of it is that modules can have
arguments that are values - the ones in the parentheses, like the 10 in
sphere(10) - and they can have arguments that are objects.  When you say
"rotate(...) cube(...);" the cube is an argument to the rotate.

children( ) is how the module accesses those object-arguments.

For instance, here's a really simple module that replicates an object:

module twin(dist)
{
    children();            // First copy of the object

    translate([dist,0,0])
        children();        // Second copy of the object
}

Thus you could say

twin(10) cube(5);

Or, equivalently

twin(10) {
	cube(5);
}

Here's a more elaborate example.  Suppose I want to design a door, in a
generic way, and I want to be able to put any number of different
doorknobs on it.

I'm going to invoke it like this:

door(30) round_knob();	// make a 30" door with a round knob
door(36) handle();	// make a 36" door with a handle

The door module knows how to make a door.  In particular, it knows where
the doorknob gets attached.

// Make a door of the specified width, with a knob supplied
// by the caller.
//
// Rules for the knob:
// * Its [0,0,0] is at the center of the mounting hole,
//   on the face of the door.
// * -X is towards the hinges; +X is towards the latch.
// * -Y is out of the door toward the user.
module door(width)
{
    // dimensions in inches
    height = 80;
    thick = 1.5;
    knob_inset = 2.5;
    knob_height = 36;
    
    cube([width, thick, height]);
    translate([width - knob_inset, 0, knob_height])
        children();
}

Now, here's a round doorknob:

module round_knob() {
    translate([0,-2,0])
        sphere(d=2);
    rotate([90,0,0])
        cylinder(d=1, h=2);
}

and here's a handle:

module handle() {
    rotate([90,0,0])
        cylinder(d=1, h=2);
    translate([0,-2,0]) {
        rotate([0,-90,0])
            cylinder(d=1, h=4);
        sphere(d=1);
    }
}

Note that, just like in the physical world, the door and the knob are
separately designed.  You can put any knob onto any door, as long as
they both follow the rules.

But wait.  That door only has a knob on one side.

// Make a door of the specified width, with a knob supplied
// by the caller on each side.
//
// Rules for the knob:
// * Its [0,0,0] is at the center of the mounting hole,
//   on the face of the door.
// * -X is towards the hinges; +X is towards the latch.
// * -Y is out of the door toward the user.
module door(width)
{
    // dimensions in inches
    height = 80;
    thick = 1.5;
    knob_inset = 2.5;
    knob_height = 36;
    
    cube([width, thick, height]);
    translate([width - knob_inset, 0, knob_height])
        children();
    translate([width - knob_inset, thick, knob_height])
        mirror([0,1,0]) children();
}

Now it has a knob on each side.  (Technical nit:  mirroring might not
really be the right technique, but it works for the example.)  Note that
the knob knows only its "local" coordinate system; it doesn't know nor
care that it gets mirrored and translated to get put onto the back of
the door.

But but... what if you sometimes have a round knob on one side and a
handle on the other side?

// Make a door of the specified width, with a knob supplied
// by the caller on each side.
//
// Rules for the knob:
// * Its [0,0,0] is at the center of the mounting hole,
//   on the face of the door.
// * -X is towards the hinges; +X is towards the latch.
// * -Y is out of the door toward the user.
//
// With one child, put it on both sides.
// With two children, put the first child on the front
// and the second child on the back.
module door(width)
{
    // dimensions in inches
    height = 80;
    thick = 1.5;
    knob_inset = 2.5;
    knob_height = 36;
    
    cube([width, thick, height]);
    translate([width - knob_inset, 0, knob_height])
        children(0);
    translate([width - knob_inset, thick, knob_height])
        mirror([0,1,0]) children($children > 1 ? 1 : 0);
}

and you can invoke it as

door(30) {
    handle();
    round_knob();
}

With$fs=0.2 to make nicer curves, here's what we get:

[ Sorry, sent the first copy from the wrong e-mail address. ] On 8/21/2019 5:38 AM, fred_dot_u via Discuss wrote: > That bit confused me slightly as well, as there were no curly braces after the module call, [...] Like most languages with sort-of-C-derived structure, you only need braces if you want to enclose more than one statement.  Also, line breaks are not important. Thus these are equivalent: translate(...) rotate(...) scale(...) cube(...); translate(...) rotate(...)  scale(...) cube(...); translate(...) {     rotate(...) {         scale(...) {             cube(...);         }     } } As for children( ), the way that I think of it is that modules can have arguments that are values - the ones in the parentheses, like the 10 in sphere(10) - and they can have arguments that are objects.  When you say "rotate(...) cube(...);" the cube is an argument to the rotate. children( ) is how the module accesses those object-arguments. For instance, here's a really simple module that replicates an object: module twin(dist) { children(); // First copy of the object translate([dist,0,0]) children(); // Second copy of the object } Thus you could say twin(10) cube(5); Or, equivalently twin(10) { cube(5); } Here's a more elaborate example.  Suppose I want to design a door, in a generic way, and I want to be able to put any number of different doorknobs on it. I'm going to invoke it like this: door(30) round_knob(); // make a 30" door with a round knob door(36) handle(); // make a 36" door with a handle The door module knows how to make a door.  In particular, it knows where the doorknob gets attached. // Make a door of the specified width, with a knob supplied // by the caller. // // Rules for the knob: // * Its [0,0,0] is at the center of the mounting hole, // on the face of the door. // * -X is towards the hinges; +X is towards the latch. // * -Y is out of the door toward the user. module door(width) { // dimensions in inches height = 80; thick = 1.5; knob_inset = 2.5; knob_height = 36; cube([width, thick, height]); translate([width - knob_inset, 0, knob_height]) children(); } Now, here's a round doorknob: module round_knob() { translate([0,-2,0]) sphere(d=2); rotate([90,0,0]) cylinder(d=1, h=2); } and here's a handle: module handle() { rotate([90,0,0]) cylinder(d=1, h=2); translate([0,-2,0]) { rotate([0,-90,0]) cylinder(d=1, h=4); sphere(d=1); } } Note that, just like in the physical world, the door and the knob are separately designed.  You can put any knob onto any door, as long as they both follow the rules. But wait.  That door only has a knob on one side. // Make a door of the specified width, with a knob supplied // by the caller on each side. // // Rules for the knob: // * Its [0,0,0] is at the center of the mounting hole, // on the face of the door. // * -X is towards the hinges; +X is towards the latch. // * -Y is out of the door toward the user. module door(width) { // dimensions in inches height = 80; thick = 1.5; knob_inset = 2.5; knob_height = 36; cube([width, thick, height]); translate([width - knob_inset, 0, knob_height]) children(); translate([width - knob_inset, thick, knob_height]) mirror([0,1,0]) children(); } Now it has a knob on each side.  (Technical nit:  mirroring might not really be the right technique, but it works for the example.)  Note that the knob knows only its "local" coordinate system; it doesn't know nor care that it gets mirrored and translated to get put onto the back of the door. But but... what if you sometimes have a round knob on one side and a handle on the other side? // Make a door of the specified width, with a knob supplied // by the caller on each side. // // Rules for the knob: // * Its [0,0,0] is at the center of the mounting hole, // on the face of the door. // * -X is towards the hinges; +X is towards the latch. // * -Y is out of the door toward the user. // // With one child, put it on both sides. // With two children, put the first child on the front // and the second child on the back. module door(width) { // dimensions in inches height = 80; thick = 1.5; knob_inset = 2.5; knob_height = 36; cube([width, thick, height]); translate([width - knob_inset, 0, knob_height]) children(0); translate([width - knob_inset, thick, knob_height]) mirror([0,1,0]) children($children > 1 ? 1 : 0); } and you can invoke it as door(30) { handle(); round_knob(); } With$fs=0.2 to make nicer curves, here's what we get: