### need help in understanding diff and children and placements

BR
Bob Roos
Sun, Jan 15, 2023 2:51 PM

I had this almost together but one little part was not getting removed.  Then I reread Adrian's message about the semicolon keeping things from being children so I removed them and things went crazy.

I looked in the help topics for child or children but did not see anything.  It appears that placements are relative to the prior piece and not absolute.  Pointers to the help sections would be great.

Now trying to make a phone mount.  This was supposed to be the sliding piece to hold the top of the phone in place.

Thank you

ps Adrian that tripod mount is too big for my tripod, but I did get one that works  Thank you.

// phone tripod mount
// Bob Roos
// January 14, 2023

H = 9.5;
W = 34.75;
L = 36;
L1 = 25.5;
A = 45;
T = 1.8;
Oh = 100;
Wedge = (L-L1)/2;

/*  Bottom Part
wedge([W,Wedge,H]);                                              // wedge edge
fwd(L1)wedge([W,Wedge,H],spin=180,anchor=[1,-1,-1]);              // wedge edge
cuboid([W,L1,H],anchor=[-1,1,-1])                                // cebtral part
up(H/2)back(L1/2-T/2)cuboid([W,T,Oh],anchor=[0,0,-1],rounding=.74,except=BOTTOM);  // main tower
up(1.5*H)fwd(L1/2+3)right(W/2)cuboid([W,T,H],rounding=.74,except=BOTTOM );          // small holder
*/

// top/sliding part
diff(){
rect_tube(size=[W+2.25T,3.1T],h=2H,wall=T,rounding=.74)
#tag("keep")back(L1/2+3)up(H/2)cuboid([W+2.25
T,T,H],rounding=.74,except=TOP )
tag("keep")back(H/1.35)up(H2)cuboid([W+2.25T,H2.1,T],rounding=.74,except=TOP )          // flat on top
#tag("remove")back(T
.5)up(H)cuboid([W-2T,T2.1,2.5*H]) ;          // small holder
}

--
Best regards,
Bob                          mailto:roosbob@wybatap.com

Hello OpenSCAD, I had this almost together but one little part was not getting removed. Then I reread Adrian's message about the semicolon keeping things from being children so I removed them and things went crazy. I looked in the help topics for child or children but did not see anything. It appears that placements are relative to the prior piece and not absolute. Pointers to the help sections would be great. Now trying to make a phone mount. This was supposed to be the sliding piece to hold the top of the phone in place. Thank you ps Adrian that tripod mount is too big for my tripod, but I did get one that works Thank you. // phone tripod mount // Bob Roos // January 14, 2023 include <BOSL2/std.scad> // or screws or threading H = 9.5; W = 34.75; L = 36; L1 = 25.5; A = 45; T = 1.8; Oh = 100; Wedge = (L-L1)/2; /* Bottom Part wedge([W,Wedge,H]); // wedge edge fwd(L1)wedge([W,Wedge,H],spin=180,anchor=[1,-1,-1]); // wedge edge cuboid([W,L1,H],anchor=[-1,1,-1]) // cebtral part up(H/2)back(L1/2-T/2)cuboid([W,T,Oh],anchor=[0,0,-1],rounding=.74,except=BOTTOM); // main tower up(1.5*H)fwd(L1/2+3)right(W/2)cuboid([W,T,H],rounding=.74,except=BOTTOM ); // small holder */ // top/sliding part diff(){ rect_tube(size=[W+2.25*T,3.1*T],h=2*H,wall=T,rounding=.74) #tag("keep")back(L1/2+3)up(H/2)cuboid([W+2.25*T,T,H],rounding=.74,except=TOP ) tag("keep")back(H/1.35)up(H*2)cuboid([W+2.25*T,H*2.1,T],rounding=.74,except=TOP ) // flat on top #tag("remove")back(T*.5)up(H)cuboid([W-2*T,T*2.1,2.5*H]) ; // small holder } -- Best regards, Bob mailto:roosbob@wybatap.com
AM
Sun, Jan 15, 2023 4:49 PM

If you have a model for some other standard tripod mount we may want to add
it to BOSL2.  My two tripods both use the manfrotto mount.

In BOSL2 when you make an object a child of another object, the child is
positioned relative to the parent.  This is called attachment.

https://github.com/revarbat/BOSL2/wiki/Tutorial-Attachments

The section Positioning Children is where it starts to talk about how
children are handled.  Note that this is a BOSL2 feature.  You can't make
objects children of other objects in regular OpenSCAD.  Full docs for this

The only reason you have  to use diff() is that you want to do difference
operations on objects that you have positioned relative to the parent
object.  Regular difference() cannot handle this situation.  If you don't
need that, you should probably use difference().  The tags like
tag("remove") are to control which objects are subtracted, so they only
work if you use diff().  But if you aren't using relative positioning of
the children then you can just use difference() in the normal way and you
don't need tags.  (I guess you could also use diff() instead of difference
if you thought that it was easier to read the code when you have several
objects.)  The "keep" tag is meant to allow you to have child objects
unaffected by the difference, so they get unioned in after the difference
is performed.

So:

diff()
parent()
tag("remove") child();

which is equivalent to

diff()
parent(){
tag("remove") child();
}

Positions child relative to parent and subtracts it from the parent.

diff() parent();
tag("remove") child();

Positions child in absolute coordinates and does not do a difference
operation.  The diff() doesn't have any effect in the second case because
the parent has no descendents tagged with the removal tag.  The "remove"
tag has no effect because child() has no diff() as an ancestor, nor
anything else that looks at the tags.

On Sun, Jan 15, 2023 at 9:51 AM Bob Roos roosbob@wybatap.com wrote:

I had this almost together but one little part was not getting removed.
being children so I removed them and things went crazy.

I looked in the help topics for child or children but did not see
anything.  It appears that placements are relative to the prior piece and
not absolute.  Pointers to the help sections would be great.

Now trying to make a phone mount.  This was supposed to be the sliding
piece to hold the top of the phone in place.

Thank you

ps Adrian that tripod mount is too big for my tripod, but I did get one
that works  Thank you.

// phone tripod mount
// Bob Roos
// January 14, 2023

H = 9.5;
W = 34.75;
L = 36;
L1 = 25.5;
A = 45;
T = 1.8;
Oh = 100;
Wedge = (L-L1)/2;

/*  Bottom Part
wedge([W,Wedge,H]);                                              // wedge
edge
fwd(L1)wedge([W,Wedge,H],spin=180,anchor=[1,-1,-1]);              // wedge
edge
cuboid([W,L1,H],anchor=[-1,1,-1])                                //
cebtral part
up(H/2)back(L1/2-T/2)cuboid([W,T,Oh],anchor=[0,0,-1],rounding=.74,except=BOTTOM);
// main tower
up(1.5*H)fwd(L1/2+3)right(W/2)cuboid([W,T,H],rounding=.74,except=BOTTOM
);          // small holder
*/

// top/sliding part
diff(){
rect_tube(size=[W+2.25T,3.1T],h=2H,wall=T,rounding=.74)
#tag("keep")back(L1/2+3)up(H/2)cuboid([W+2.25
T,T,H],rounding=.74,except=TOP
)
tag("keep")back(H/1.35)up(H2)cuboid([W+2.25T,H2.1,T],rounding=.74,except=TOP
)          // flat on top
#tag("remove")back(T
.5)up(H)cuboid([W-2T,T2.1,2.5*H]) ;          //
small holder
}

--
Best regards,
Bob                          mailto:roosbob@wybatap.com

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

JB
Jordan Brown
Sun, Jan 15, 2023 11:04 PM

It appears that placements are relative to the prior piece and not
absolute.

Nothing in OpenSCAD is absolute, except for the special case of
transformations at the top level where there is nothing to be relative
to.  Even then, everything is relative to the base coordinate system.

I looked in the help topics for child or children but did not see
anything.

You can find a tutorial introduction to children at

(starting at "If you can't see yet how this is a problem")

and in reference form at

That said, here's some first-principles explanation...

On 1/15/2023 8:49 AM, Adrian Mariano wrote:

In BOSL2 when you make an object a child of another object, the child
is positioned relative to the parent.

More generally, in OpenSCAD when you make an object a child of another
object, the child is positioned relative to the parent.

Let's start from the simplest of cases:

``````translate([10,0,0]) cube(1);
``````

Translate is a module that takes a child, and positions that child as
specified by its argument.

``````translate([0,0,10]) translate([10,0,0]) cube(1);
``````

The first translate positions its child as controlled by its argument.
That child is itself "translate([10,0,0]) cube(1)".  The second
translate positions its child, the cube(1), as controlled by its
argument, and then relative to its coordinate system.

``````One way to look at this is to read it from the inside out, that the
cube is first translated 10 units right and then is translated 10
units up.  Another way to look at it is that the first translate
sets up a local coordinate system where the [0,0,0] is located at
[0,0,10] in the global coordinate system, and the second translate
sets up a local coordinate system where [0,0,0] is located at
[10,0,0] in *its* local coordinate system, and so is at [10,0,10] in
the global coordinate system, and then the cube is constructed in
that innermost local coordinate system.
``````

Now let's bring a user module into the picture.

``````module ten_right() {
translate([10,0,0]) children();
}
``````

This:

``````ten_right() cube(1);
``````

is equivalent to this:

``````translate([10,0,0]) cube(1);
``````

When you have a user module, its children are "plugged in" where the
children() calls say to plug them in.

Here's a more complex example:

``````module ten_up() {
translate([0,0,10]) children();
}
ten_up() ten_right() cube(1);
``````

which is equivalent to

``````translate([0,0,10]) translate([10,0,0]) cube(1);
``````

and a bit more complex:

``````module zero_ten_twenty_right() {
children();
translate([10,0,0]) children();
translate([20,0,0]) children();
}

zero_ten_twenty_right() cube(1);
``````

In this case, the child cube(1) is plugged in three times:  once at the
(local) origin, once at X=+10, and once at X=+20.

> It appears that placements are relative to the prior piece and not > absolute.  Nothing in OpenSCAD is absolute, except for the special case of transformations at the top level where there is nothing to be relative to.  Even then, everything is relative to the base coordinate system. > I looked in the help topics for child or children but did not see > anything.  You can find a tutorial introduction to children at https://en.wikibooks.org/wiki/OpenSCAD_Tutorial/Chapter_5 (starting at "If you can't see yet how this is a problem") and in reference form at https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/User-Defined_Functions_and_Modules#Operator_modules That said, here's some first-principles explanation... On 1/15/2023 8:49 AM, Adrian Mariano wrote: > In BOSL2 when you make an object a child of another object, the child > is positioned relative to the parent. More generally, in OpenSCAD when you make an object a child of another object, the child is positioned relative to the parent. Let's start from the simplest of cases: translate([10,0,0]) cube(1); Translate is a module that takes a child, and positions that child as specified by its argument. translate([0,0,10]) translate([10,0,0]) cube(1); The first translate positions its child as controlled by its argument.  That child is itself "translate([10,0,0]) cube(1)".  The second translate positions *its* child, the cube(1), as controlled by its argument, and then relative to *its* coordinate system. One way to look at this is to read it from the inside out, that the cube is first translated 10 units right and then is translated 10 units up.  Another way to look at it is that the first translate sets up a local coordinate system where the [0,0,0] is located at [0,0,10] in the global coordinate system, and the second translate sets up a local coordinate system where [0,0,0] is located at [10,0,0] in *its* local coordinate system, and so is at [10,0,10] in the global coordinate system, and then the cube is constructed in that innermost local coordinate system. Now let's bring a user module into the picture. module ten_right() { translate([10,0,0]) children(); } This: ten_right() cube(1); is equivalent to this: translate([10,0,0]) cube(1); When you have a user module, its children are "plugged in" where the children() calls say to plug them in. Here's a more complex example: module ten_up() { translate([0,0,10]) children(); } ten_up() ten_right() cube(1); which is equivalent to translate([0,0,10]) translate([10,0,0]) cube(1); and a bit more complex: module zero_ten_twenty_right() { children(); translate([10,0,0]) children(); translate([20,0,0]) children(); } zero_ten_twenty_right() cube(1); In this case, the child cube(1) is plugged in three times:  once at the (local) origin, once at X=+10, and once at X=+20.
AM
Mon, Jan 16, 2023 12:45 AM

Jordan, I think you're missing the point of the question.  I believe that
the original poster understands how to use transformation operators.  He
was asking about what happens when you make an object like a cube a child
of another object, like a cube.

You said "when you make an object a child of another object, the child is
positioned relative to the parent.".  I contend that this is false.  If you
make a base OpenSCAD object a child of another such object in vanilla
OpenSCAD you get an warning like

WARNING: module cube() does not support child modules in file test4.scad,

and then if you don't have stop on first warnings turned on, you'll get the
parent displayed but the child is ignored.

There is no way to position an object relative to another object in basic
OpenSCAD. When you create an object like cube() it always appears in
absolute coordinates. If you want it elsewhere you can then apply
transformations. Or if you don't like that explanation you can say that
when you create an object it appears in the coordinate system defined by
the composition of all the transformations that appear as its ancestors.
Whichever way you think about it, that is not at all the same thing as an
object appearing relative to another, like saying "put this cube to the
right of that cube".

Let's start from the simplest of cases:

`````` translate([10,0,0]) cube(1);
``````

Translate is a module that takes a child, and positions that child as
specified by its argument.

`````` translate([0,0,10]) translate([10,0,0]) cube(1);
``````

The translate() module is not an object. It doesn't create any geometry.
It's a transformation operator. So it has nothing to do with the question
at hand.

Consider the code below. Where does the cylinder appear?

cube(50) cylinder(r=10,h=75);

The answer is that the cylinder appears with its base at [25,25,25], the
center of the cube, because as a child of a cube it is positioned relative
to the cube. If you change the size of the cube, the cylinder will appear
somewhere else, with no other changes to the code.

And you can write

cube(50) position(RIGHT) cube(25);

to do what I said before, "put this cube on the right of that cube". There
are some details having to do with which point on the child is placed on
the right of the parent cube that I won't go into.

On Sun, Jan 15, 2023 at 6:05 PM Jordan Brown openscad@jordan.maileater.net
wrote:

It appears that placements are relative to the prior piece and not
absolute.

Nothing in OpenSCAD is absolute, except for the special case of
transformations at the top level where there is nothing to be relative to.
Even then, everything is relative to the base coordinate system.

I looked in the help topics for child or children but did not see
anything.

You can find a tutorial introduction to children at

(starting at "If you can't see yet how this is a problem")

and in reference form at

That said, here's some first-principles explanation...

On 1/15/2023 8:49 AM, Adrian Mariano wrote:

In BOSL2 when you make an object a child of another object, the child is
positioned relative to the parent.

More generally, in OpenSCAD when you make an object a child of another
object, the child is positioned relative to the parent.

Let's start from the simplest of cases:

translate([10,0,0]) cube(1);

Translate is a module that takes a child, and positions that child as
specified by its argument.

translate([0,0,10]) translate([10,0,0]) cube(1);

The first translate positions its child as controlled by its argument.
That child is itself "translate([10,0,0]) cube(1)".  The second translate
positions its child, the cube(1), as controlled by its argument, and then
relative to its coordinate system.

One way to look at this is to read it from the inside out, that the cube
is first translated 10 units right and then is translated 10 units up.
Another way to look at it is that the first translate sets up a local
coordinate system where the [0,0,0] is located at [0,0,10] in the global
coordinate system, and the second translate sets up a local coordinate
system where [0,0,0] is located at [10,0,0] in its local coordinate
system, and so is at [10,0,10] in the global coordinate system, and then
the cube is constructed in that innermost local coordinate system.

Now let's bring a user module into the picture.

module ten_right() {
translate([10,0,0]) children();
}

This:

ten_right() cube(1);

is equivalent to this:

translate([10,0,0]) cube(1);

When you have a user module, its children are "plugged in" where the
children() calls say to plug them in.

Here's a more complex example:

module ten_up() {
translate([0,0,10]) children();
}
ten_up() ten_right() cube(1);

which is equivalent to

translate([0,0,10]) translate([10,0,0]) cube(1);

and a bit more complex:

module zero_ten_twenty_right() {
children();
translate([10,0,0]) children();
translate([20,0,0]) children();
}

zero_ten_twenty_right() cube(1);

In this case, the child cube(1) is plugged in three times:  once at the
(local) origin, once at X=+10, and once at X=+20.

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

BC
Bob Carlson
Wed, Jan 18, 2023 12:26 AM

I have been using OpenScad for years and BOSL2 almost as long. I have a decades long programming background. This led me to misunderstand what was going on in OpenScad when you say:

a(args) b();

My intuition is the this is evaluated as if it is a(args, b()). In most programming languages args and b() are evaluated and then passed to a then a is evaluated. A looks like a function applied to the result of b. In OS that’s not true.

Some languages have arguments that are evaluate BY NAME. This means the argument is evaluated each time it is referenced in the function a().

So yes, you can think of a(args) b() as a(args, b()) BUT b() is evaluate BY  NAME.

So the function a() can reference children() multiple times and children() will be evaluated each time. This is the way BOSL2 does its magic. An attachable object in BOSL2 sets \$variables that the children() can reference to preform the attachment magic. It also means that different \$ variables can be set for multiple calls to children(). So each evaluation can result in something different.

Realizing this key fact made it a lot easier for me to figure out what was happening in

A() B() C();

A is setting up the environment that B runs in, and again B controls the environment for C.

-Bob
Tucson AZ

On Jan 15, 2023, at 5:45 PM, Adrian Mariano avm4@cornell.edu wrote:

Jordan, I think you're missing the point of the question.  I believe that the original poster understands how to use transformation operators.  He was asking about what happens when you make an object like a cube a child of another object, like a cube.

You said "when you make an object a child of another object, the child is positioned relative to the parent.".  I contend that this is false.  If you make a base OpenSCAD object a child of another such object in vanilla OpenSCAD you get an warning like

and then if you don't have stop on first warnings turned on, you'll get the parent displayed but the child is ignored.

There is no way to position an object relative to another object in basic OpenSCAD.  When you create an object like cube() it always appears in absolute coordinates.  If you want it elsewhere you can then apply transformations.  Or if you don't like that explanation you can say that when you create an object it appears in the coordinate system defined by the composition of all the transformations that appear as its ancestors.  Whichever way you think about it, that is not at all the same thing as an object appearing relative to another, like saying "put this cube to the right of that cube".

Let's start from the simplest of cases:

`````` translate([10,0,0]) cube(1);
``````

Translate is a module that takes a child, and positions that child as
specified by its argument.

`````` translate([0,0,10]) translate([10,0,0]) cube(1);
``````

The translate() module is not an object.  It doesn't create any geometry.  It's a transformation operator.  So it has nothing to do with the question at hand.

Consider the code below.  Where does the cylinder appear?

cube(50) cylinder(r=10,h=75);

The answer is that the cylinder appears with its base at [25,25,25], the center of the cube, because as a child of a cube it is positioned relative to the cube.  If you change the size of the cube, the cylinder will appear somewhere else, with no other changes to the code.

And you can write

cube(50) position(RIGHT) cube(25);

to do what I said before, "put this cube on the right of that cube".  There are some details having to do with which point on the child is placed on the right of the parent cube that I won't go into.

On Sun, Jan 15, 2023 at 6:05 PM Jordan Brown <openscad@jordan.maileater.net mailto:openscad@jordan.maileater.net> wrote:

It appears that placements are relative to the prior piece and not absolute.

Nothing in OpenSCAD is absolute, except for the special case of transformations at the top level where there is nothing to be relative to.  Even then, everything is relative to the base coordinate system.

I looked in the help topics for child or children but did not see anything.

You can find a tutorial introduction to children at

(starting at "If you can't see yet how this is a problem")

and in reference form at

That said, here's some first-principles explanation...

On 1/15/2023 8:49 AM, Adrian Mariano wrote:

In BOSL2 when you make an object a child of another object, the child is positioned relative to the parent.

More generally, in OpenSCAD when you make an object a child of another object, the child is positioned relative to the parent.

Let's start from the simplest of cases:
translate([10,0,0]) cube(1);
Translate is a module that takes a child, and positions that child as specified by its argument.

translate([0,0,10]) translate([10,0,0]) cube(1);
The first translate positions its child as controlled by its argument.  That child is itself "translate([10,0,0]) cube(1)".  The second translate positions its child, the cube(1), as controlled by its argument, and then relative to its coordinate system.

One way to look at this is to read it from the inside out, that the cube is first translated 10 units right and then is translated 10 units up.  Another way to look at it is that the first translate sets up a local coordinate system where the [0,0,0] is located at [0,0,10] in the global coordinate system, and the second translate sets up a local coordinate system where [0,0,0] is located at [10,0,0] in its local coordinate system, and so is at [10,0,10] in the global coordinate system, and then the cube is constructed in that innermost local coordinate system.

Now let's bring a user module into the picture.

module ten_right() {
translate([10,0,0]) children();
}
This:

ten_right() cube(1);
is equivalent to this:

translate([10,0,0]) cube(1);
When you have a user module, its children are "plugged in" where the children() calls say to plug them in.

Here's a more complex example:
module ten_up() {
translate([0,0,10]) children();
}
ten_up() ten_right() cube(1);
which is equivalent to

translate([0,0,10]) translate([10,0,0]) cube(1);
and a bit more complex:

module zero_ten_twenty_right() {
children();
translate([10,0,0]) children();
translate([20,0,0]) children();
}

zero_ten_twenty_right() cube(1);
In this case, the child cube(1) is plugged in three times:  once at the (local) origin, once at X=+10, and once at X=+20.

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

FH
Father Horton
Wed, Jan 18, 2023 12:32 AM

OpenSCAD is a functional language, more or less. So think of A() B() C() as
A(B(C())) in a procedural language, and you're on the right track.

On Tue, Jan 17, 2023 at 6:27 PM Bob Carlson bob@rjcarlson.com wrote:

I have been using OpenScad for years and BOSL2 almost as long. I have a
decades long programming background. This led me to misunderstand what was
going on in OpenScad when you say:

a(args) b();

My intuition is the this is evaluated as if it is a(args, b()). In most
programming languages args and b() are evaluated and then passed to a then
a is evaluated. A looks like a function applied to the result of b. In OS
that’s not true.

Some languages have arguments that are evaluate BY NAME. This means the
argument is evaluated each time it is referenced in the function a().

So yes, you can think of a(args) b() as a(args, b()) BUT b() is evaluate
BY  NAME.

So the function a() can reference children() multiple times and children()
will be evaluated each time. This is the way BOSL2 does its magic. An
attachable object in BOSL2 sets \$variables that the children() can
reference to preform the attachment magic. It also means that different \$
variables can be set for multiple calls to children(). So each evaluation
can result in something different.

Realizing this key fact made it a lot easier for me to figure out what was
happening in

A() B() C();

A is setting up the environment that B runs in, and again B controls the
environment for C.

-Bob
Tucson AZ

On Jan 15, 2023, at 5:45 PM, Adrian Mariano avm4@cornell.edu wrote:

Jordan, I think you're missing the point of the question.  I believe that
the original poster understands how to use transformation operators.  He
was asking about what happens when you make an object like a cube a child
of another object, like a cube.

You said "when you make an object a child of another object, the child is
positioned relative to the parent.".  I contend that this is false.  If you
make a base OpenSCAD object a child of another such object in vanilla
OpenSCAD you get an warning like

WARNING: module cube() does not support child modules in file test4.scad,
line 3

and then if you don't have stop on first warnings turned on, you'll get
the parent displayed but the child is ignored.

There is no way to position an object relative to another object in basic
OpenSCAD. When you create an object like cube() it always appears in
absolute coordinates. If you want it elsewhere you can then apply
transformations. Or if you don't like that explanation you can say that
when you create an object it appears in the coordinate system defined by
the composition of all the transformations that appear as its ancestors.
Whichever way you think about it, that is not at all the same thing as an
object appearing relative to another, like saying "put this cube to the
right of that cube".

Let's start from the simplest of cases:

`````` translate([10,0,0]) cube(1);
``````

Translate is a module that takes a child, and positions that child as
specified by its argument.

`````` translate([0,0,10]) translate([10,0,0]) cube(1);
``````

The translate() module is not an object. It doesn't create any geometry.
It's a transformation operator. So it has nothing to do with the question
at hand.

Consider the code below. Where does the cylinder appear?

cube(50) cylinder(r=10,h=75);

The answer is that the cylinder appears with its base at [25,25,25], the
center of the cube, because as a child of a cube it is positioned relative
to the cube. If you change the size of the cube, the cylinder will appear
somewhere else, with no other changes to the code.

And you can write

cube(50) position(RIGHT) cube(25);

to do what I said before, "put this cube on the right of that cube". There
are some details having to do with which point on the child is placed on
the right of the parent cube that I won't go into.

On Sun, Jan 15, 2023 at 6:05 PM Jordan Brown <

It appears that placements are relative to the prior piece and not
absolute.

Nothing in OpenSCAD is absolute, except for the special case of
transformations at the top level where there is nothing to be relative to.
Even then, everything is relative to the base coordinate system.

I looked in the help topics for child or children but did not see
anything.

You can find a tutorial introduction to children at

(starting at "If you can't see yet how this is a problem")

and in reference form at

That said, here's some first-principles explanation...

On 1/15/2023 8:49 AM, Adrian Mariano wrote:

In BOSL2 when you make an object a child of another object, the child is
positioned relative to the parent.

More generally, in OpenSCAD when you make an object a child of another
object, the child is positioned relative to the parent.

Let's start from the simplest of cases:

translate([10,0,0]) cube(1);

Translate is a module that takes a child, and positions that child as
specified by its argument.

translate([0,0,10]) translate([10,0,0]) cube(1);

The first translate positions its child as controlled by its argument.
That child is itself "translate([10,0,0]) cube(1)".  The second translate
positions its child, the cube(1), as controlled by its argument, and then
relative to its coordinate system.

One way to look at this is to read it from the inside out, that the cube
is first translated 10 units right and then is translated 10 units up.
Another way to look at it is that the first translate sets up a local
coordinate system where the [0,0,0] is located at [0,0,10] in the global
coordinate system, and the second translate sets up a local coordinate
system where [0,0,0] is located at [10,0,0] in its local coordinate
system, and so is at [10,0,10] in the global coordinate system, and then
the cube is constructed in that innermost local coordinate system.

Now let's bring a user module into the picture.

module ten_right() {
translate([10,0,0]) children();
}

This:

ten_right() cube(1);

is equivalent to this:

translate([10,0,0]) cube(1);

When you have a user module, its children are "plugged in" where the
children() calls say to plug them in.

Here's a more complex example:

module ten_up() {
translate([0,0,10]) children();
}
ten_up() ten_right() cube(1);

which is equivalent to

translate([0,0,10]) translate([10,0,0]) cube(1);

and a bit more complex:

module zero_ten_twenty_right() {
children();
translate([10,0,0]) children();
translate([20,0,0]) children();
}

zero_ten_twenty_right() cube(1);

In this case, the child cube(1) is plugged in three times:  once at the
(local) origin, once at X=+10, and once at X=+20.

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

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

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

AM
Wed, Jan 18, 2023 12:46 AM

I think Bob's point is that A() B() is NOT just A(B()) because the latter
suggests that B is evaluated first and then A runs on the result of B, and
that's actually not what happens in OpenSCAD with children.  The children
run when they are invoked by their parent, possibly many times, possibly
with different \$ variables values set each timet.  This is NOT how a
functional language works!

OpenSCAD is a functional language when it comes to how functions work, but
for modules and their children, not exactly.

On Tue, Jan 17, 2023 at 7:32 PM Father Horton fatherhorton@gmail.com
wrote:

OpenSCAD is a functional language, more or less. So think of A() B() C()
as A(B(C())) in a procedural language, and you're on the right track.

On Tue, Jan 17, 2023 at 6:27 PM Bob Carlson bob@rjcarlson.com wrote:

I have been using OpenScad for years and BOSL2 almost as long. I have a
decades long programming background. This led me to misunderstand what was
going on in OpenScad when you say:

a(args) b();

My intuition is the this is evaluated as if it is a(args, b()). In most
programming languages args and b() are evaluated and then passed to a then
a is evaluated. A looks like a function applied to the result of b. In OS
that’s not true.

Some languages have arguments that are evaluate BY NAME. This means the
argument is evaluated each time it is referenced in the function a().

So yes, you can think of a(args) b() as a(args, b()) BUT b() is evaluate
BY  NAME.

So the function a() can reference children() multiple times and
children() will be evaluated each time. This is the way BOSL2 does its
magic. An attachable object in BOSL2 sets \$variables that the children()
can reference to preform the attachment magic. It also means that different
\$ variables can be set for multiple calls to children(). So each evaluation
can result in something different.

Realizing this key fact made it a lot easier for me to figure out what
was happening in

A() B() C();

A is setting up the environment that B runs in, and again B controls the
environment for C.

-Bob
Tucson AZ

On Jan 15, 2023, at 5:45 PM, Adrian Mariano avm4@cornell.edu wrote:

Jordan, I think you're missing the point of the question.  I believe that
the original poster understands how to use transformation operators.  He
was asking about what happens when you make an object like a cube a child
of another object, like a cube.

You said "when you make an object a child of another object, the child is
positioned relative to the parent.".  I contend that this is false.  If you
make a base OpenSCAD object a child of another such object in vanilla
OpenSCAD you get an warning like

WARNING: module cube() does not support child modules in file test4.scad,
line 3

and then if you don't have stop on first warnings turned on, you'll get
the parent displayed but the child is ignored.

There is no way to position an object relative to another object in basic
OpenSCAD. When you create an object like cube() it always appears in
absolute coordinates. If you want it elsewhere you can then apply
transformations. Or if you don't like that explanation you can say that
when you create an object it appears in the coordinate system defined by
the composition of all the transformations that appear as its ancestors.
Whichever way you think about it, that is not at all the same thing as an
object appearing relative to another, like saying "put this cube to the
right of that cube".

Let's start from the simplest of cases:

`````` translate([10,0,0]) cube(1);
``````

Translate is a module that takes a child, and positions that child as
specified by its argument.

`````` translate([0,0,10]) translate([10,0,0]) cube(1);
``````

The translate() module is not an object. It doesn't create any geometry.
It's a transformation operator. So it has nothing to do with the question
at hand.

Consider the code below. Where does the cylinder appear?

cube(50) cylinder(r=10,h=75);

The answer is that the cylinder appears with its base at [25,25,25], the
center of the cube, because as a child of a cube it is positioned relative
to the cube. If you change the size of the cube, the cylinder will appear
somewhere else, with no other changes to the code.

And you can write

cube(50) position(RIGHT) cube(25);

to do what I said before, "put this cube on the right of that cube".
There are some details having to do with which point on the child is placed
on the right of the parent cube that I won't go into.

On Sun, Jan 15, 2023 at 6:05 PM Jordan Brown <

It appears that placements are relative to the prior piece and not
absolute.

Nothing in OpenSCAD is absolute, except for the special case of
transformations at the top level where there is nothing to be relative to.
Even then, everything is relative to the base coordinate system.

I looked in the help topics for child or children but did not see
anything.

You can find a tutorial introduction to children at

(starting at "If you can't see yet how this is a problem")

and in reference form at

That said, here's some first-principles explanation...

On 1/15/2023 8:49 AM, Adrian Mariano wrote:

In BOSL2 when you make an object a child of another object, the child is
positioned relative to the parent.

More generally, in OpenSCAD when you make an object a child of another
object, the child is positioned relative to the parent.

Let's start from the simplest of cases:

translate([10,0,0]) cube(1);

Translate is a module that takes a child, and positions that child as
specified by its argument.

translate([0,0,10]) translate([10,0,0]) cube(1);

The first translate positions its child as controlled by its argument.
That child is itself "translate([10,0,0]) cube(1)".  The second translate
positions its child, the cube(1), as controlled by its argument, and then
relative to its coordinate system.

One way to look at this is to read it from the inside out, that the cube
is first translated 10 units right and then is translated 10 units up.
Another way to look at it is that the first translate sets up a local
coordinate system where the [0,0,0] is located at [0,0,10] in the global
coordinate system, and the second translate sets up a local coordinate
system where [0,0,0] is located at [10,0,0] in its local coordinate
system, and so is at [10,0,10] in the global coordinate system, and then
the cube is constructed in that innermost local coordinate system.

Now let's bring a user module into the picture.

module ten_right() {
translate([10,0,0]) children();
}

This:

ten_right() cube(1);

is equivalent to this:

translate([10,0,0]) cube(1);

When you have a user module, its children are "plugged in" where the
children() calls say to plug them in.

Here's a more complex example:

module ten_up() {
translate([0,0,10]) children();
}
ten_up() ten_right() cube(1);

which is equivalent to

translate([0,0,10]) translate([10,0,0]) cube(1);

and a bit more complex:

module zero_ten_twenty_right() {
children();
translate([10,0,0]) children();
translate([20,0,0]) children();
}

zero_ten_twenty_right() cube(1);

In this case, the child cube(1) is plugged in three times:  once at the
(local) origin, once at X=+10, and once at X=+20.

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

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

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

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

FH
Father Horton
Wed, Jan 18, 2023 12:54 AM

On Tue, Jan 17, 2023 at 6:48 PM Adrian Mariano avm4@cornell.edu wrote:

I think Bob's point is that A() B() is NOT just A(B()) because the latter
suggests that B is evaluated first and then A runs on the result of B, and
that's actually not what happens in OpenSCAD with children.  The children
run when they are invoked by their parent, possibly many times, possibly
with different \$ variables values set each timet.  This is NOT how a
functional language works!

OpenSCAD is a functional language when it comes to how functions work, but
for modules and their children, not exactly.

On Tue, Jan 17, 2023 at 7:32 PM Father Horton fatherhorton@gmail.com
wrote:

OpenSCAD is a functional language, more or less. So think of A() B() C()
as A(B(C())) in a procedural language, and you're on the right track.

On Tue, Jan 17, 2023 at 6:27 PM Bob Carlson bob@rjcarlson.com wrote:

I have been using OpenScad for years and BOSL2 almost as long. I have a
decades long programming background. This led me to misunderstand what was
going on in OpenScad when you say:

a(args) b();

My intuition is the this is evaluated as if it is a(args, b()). In most
programming languages args and b() are evaluated and then passed to a then
a is evaluated. A looks like a function applied to the result of b. In OS
that’s not true.

Some languages have arguments that are evaluate BY NAME. This means the
argument is evaluated each time it is referenced in the function a().

So yes, you can think of a(args) b() as a(args, b()) BUT b() is evaluate
BY  NAME.

So the function a() can reference children() multiple times and
children() will be evaluated each time. This is the way BOSL2 does its
magic. An attachable object in BOSL2 sets \$variables that the children()
can reference to preform the attachment magic. It also means that different
\$ variables can be set for multiple calls to children(). So each evaluation
can result in something different.

Realizing this key fact made it a lot easier for me to figure out what
was happening in

A() B() C();

A is setting up the environment that B runs in, and again B controls the
environment for C.

-Bob
Tucson AZ

On Jan 15, 2023, at 5:45 PM, Adrian Mariano avm4@cornell.edu wrote:

Jordan, I think you're missing the point of the question.  I believe
that the original poster understands how to use transformation operators.
He was asking about what happens when you make an object like a cube a
child of another object, like a cube.

You said "when you make an object a child of another object, the child
is positioned relative to the parent.".  I contend that this is false.  If
you make a base OpenSCAD object a child of another such object in vanilla
OpenSCAD you get an warning like

WARNING: module cube() does not support child modules in file

and then if you don't have stop on first warnings turned on, you'll get
the parent displayed but the child is ignored.

There is no way to position an object relative to another object in
basic OpenSCAD. When you create an object like cube() it always appears in
absolute coordinates. If you want it elsewhere you can then apply
transformations. Or if you don't like that explanation you can say that
when you create an object it appears in the coordinate system defined by
the composition of all the transformations that appear as its ancestors.
Whichever way you think about it, that is not at all the same thing as an
object appearing relative to another, like saying "put this cube to the
right of that cube".

Let's start from the simplest of cases:

`````` translate([10,0,0]) cube(1);
``````

Translate is a module that takes a child, and positions that child as
specified by its argument.

`````` translate([0,0,10]) translate([10,0,0]) cube(1);
``````

The translate() module is not an object. It doesn't create any geometry.
It's a transformation operator. So it has nothing to do with the question
at hand.

Consider the code below. Where does the cylinder appear?

cube(50) cylinder(r=10,h=75);

The answer is that the cylinder appears with its base at [25,25,25], the
center of the cube, because as a child of a cube it is positioned relative
to the cube. If you change the size of the cube, the cylinder will appear
somewhere else, with no other changes to the code.

And you can write

cube(50) position(RIGHT) cube(25);

to do what I said before, "put this cube on the right of that cube".
There are some details having to do with which point on the child is placed
on the right of the parent cube that I won't go into.

On Sun, Jan 15, 2023 at 6:05 PM Jordan Brown <

It appears that placements are relative to the prior piece and not
absolute.

Nothing in OpenSCAD is absolute, except for the special case of
transformations at the top level where there is nothing to be relative to.
Even then, everything is relative to the base coordinate system.

I looked in the help topics for child or children but did not see
anything.

You can find a tutorial introduction to children at

(starting at "If you can't see yet how this is a problem")

and in reference form at

That said, here's some first-principles explanation...

On 1/15/2023 8:49 AM, Adrian Mariano wrote:

In BOSL2 when you make an object a child of another object, the child
is positioned relative to the parent.

More generally, in OpenSCAD when you make an object a child of another
object, the child is positioned relative to the parent.

Let's start from the simplest of cases:

translate([10,0,0]) cube(1);

Translate is a module that takes a child, and positions that child as
specified by its argument.

translate([0,0,10]) translate([10,0,0]) cube(1);

The first translate positions its child as controlled by its argument.
That child is itself "translate([10,0,0]) cube(1)".  The second translate
positions its child, the cube(1), as controlled by its argument, and then
relative to its coordinate system.

One way to look at this is to read it from the inside out, that the
cube is first translated 10 units right and then is translated 10 units
up.  Another way to look at it is that the first translate sets up a local
coordinate system where the [0,0,0] is located at [0,0,10] in the global
coordinate system, and the second translate sets up a local coordinate
system where [0,0,0] is located at [10,0,0] in its local coordinate
system, and so is at [10,0,10] in the global coordinate system, and then
the cube is constructed in that innermost local coordinate system.

Now let's bring a user module into the picture.

module ten_right() {
translate([10,0,0]) children();
}

This:

ten_right() cube(1);

is equivalent to this:

translate([10,0,0]) cube(1);

When you have a user module, its children are "plugged in" where the
children() calls say to plug them in.

Here's a more complex example:

module ten_up() {
translate([0,0,10]) children();
}
ten_up() ten_right() cube(1);

which is equivalent to

translate([0,0,10]) translate([10,0,0]) cube(1);

and a bit more complex:

module zero_ten_twenty_right() {
children();
translate([10,0,0]) children();
translate([20,0,0]) children();
}

zero_ten_twenty_right() cube(1);

In this case, the child cube(1) is plugged in three times:  once at the
(local) origin, once at X=+10, and once at X=+20.

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

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

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

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

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

JB
Jordan Brown
Wed, Jan 18, 2023 2:22 AM

It's not instantly obvious because we're conditioned to think of them as
flow control statements, but note that "if" is a module that either
evaluates its children or doesn't, and "for" is a module that evaluates
its children zero or more times.

They're both a bit magic - if, because it has "else", and for, because
it sets one or more variables - but they're still largely the same concept.

But note that you can write your own if-like and for-like modules.

e.g.:

module maybe() {
if (rands(0,1,1)[0] < 0.5) {
children();
}
}

maybe() cube();

module switch(i) {
children(i);
}

n = 1; // or 0 or 2

switch(n) {
cube();  // if n==0
sphere(); // if n==1
cylinder(); // if n==2
}

It's not instantly obvious because we're conditioned to think of them as flow control statements, but note that "if" is a module that either evaluates its children or doesn't, and "for" is a module that evaluates its children zero or more times. They're both a bit magic - if, because it has "else", and for, because it sets one or more variables - but they're still largely the same concept. But note that you can write your own if-like and for-like modules. e.g.: module maybe() { if (rands(0,1,1)[0] < 0.5) { children(); } } maybe() cube(); module switch(i) { children(i); } n = 1; // or 0 or 2 switch(n) { cube(); // if n==0 sphere(); // if n==1 cylinder(); // if n==2 }