I suppose I understand now your point. And I see that my proposal is not
appropriate to declarative languages. Thank you, I always have what to learn
from your messages.
--
View this message in context: http://forum.openscad.org/children-and-extra-unwanted-evaluations-tp16291p16321.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
I don't quite follow what you mean. Each call to mirror generates a
different pair of identical children. The only thing it doesn't do is allow
F6 to make the same result, which I think needs addressing in OpenScad.
It isn't functional though, so I agree with Doug's proposal. That isn't as
easy to use though because functions like m_mirror need a parameter which
effectively identifies the call site to the get different results in
different places.
This does roughly the same thing: -
gseed = rands(0,1000000,1)[0];
echo(gseed=gseed);
$fn = 32;
module m_mirror(n = 0) {
$seed = rands(0,1000000,n + 1, gseed)[n];
children(0);
mirror([1,0,0])
children(0);
}
module cut_in_half(size) {
intersection() {
translate([0, -size/2.0, -size/2.0])
cube(size=size, center=false);
children(0);
}
}
module randcyl() {
// use of rands in here shows this object being called twice
echo("randcyl called with",$seed);
rotate([rands(0,77,1, $seed)[0],45,0])
cylinder(h=rands(4,8,2, $seed)[1], d = rands(.1,5,3,
$seed)[2], center=true);
}
m_mirror() cut_in_half(100) render() randcyl();
translate([0, 5, 0])
m_mirror(1) cut_in_half(100) render() randcyl();
If you note down gseed you can put that in to get the same results again.
Each use of m_mirror with the same children needs a different number
passing if you want both pairs of children to be the same.
On 7 March 2016 at 16:59, Ronaldo rcmpersiano@gmail.com wrote:
nop head,
It seems I disregarded the $seed assignment in the beginning of module
m_mirror() of your code. Sorry.
But that code solves just one of Neon22's issue: randcyl() will generate
the
same two solids by m_mirror(). However, I don't see anyway to reproduce the
same result twice which is the random function seed is meant to. If you
comment the $seed assignment in m_mirror(), all m_mirror() calls will
produce the very same solid.
--
View this message in context:
http://forum.openscad.org/children-and-extra-unwanted-evaluations-tp16291p16320.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
kintel wrote
On Mar 5, 2016, at 06:01 AM, Neon22 <
mschafer@
> wrote:
Even though it looks to me like randcyl() should be evaluated only once
(when children passed in) - its called twice.
From the top of my head, this sounds like a bug. I’ll have to look into it
in more detail, and I’ve opened an issue for this:
https://github.com/openscad/openscad/issues/1596
From my understanding, children() looks like a func or module call, that it
generates something when it is called. In the code below:
module m_mirror() {
children(0);
mirror([1,0,0])
children(0);
}
The children is "called" twice. So anything inside the children code will be
executed twice, even if rands() is not used. This doesn't look buggy to me.
In fact it is consistent with how func_call and module_call work --- that
is, a "( )" indicates "going through the inner code to get a new data".
If we want to make a children NOT "CALLED" after the first use, then
something like $children might be better.
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py( 1 , 2 , git ), synwrite( 1 , 2 ); $ tips: hash( 1 , 2 ), sweep( 1 , 2 ), var( 1 , 2 ), lerp , animGif , prodVid , precision( 1 , 2 ), xl-control , type
--
View this message in context: http://forum.openscad.org/children-and-extra-unwanted-evaluations-tp16291p16327.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
On Mar 7, 2016, at 13:39 PM, runsun runsun@gmail.com wrote:
The children is "called" twice. So anything inside the children code will be
executed twice, even if rands() is not used. This doesn't look buggy to me.
This needs to be clarified. I’m not 100% certain I would call this a bug, but it does need some looking into.
There’s also the question whether people rely on this existing behavior.
-Marius
I think the whole point of non-imperative language is that it should be
impossible to tell if the child is executed twice. It is rands without a
seed parameter that breaks it and shouldn't really be allowed in OpenScad.
On 7 March 2016 at 19:03, Marius Kintel marius@kintel.net wrote:
On Mar 7, 2016, at 13:39 PM, runsun runsun@gmail.com wrote:
The children is "called" twice. So anything inside the children code
will be
executed twice, even if rands() is not used. This doesn't look buggy to
me.
This needs to be clarified. I’m not 100% certain I would call this a bug,
but it does need some looking into.
There’s also the question whether people rely on this existing behavior.
-Marius
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
The flip side is that if your module contains conditional code, a
children() guarded by an if, then a child module might not be called at all.
The only way people could rely on this behaviour is via APIs with side
effects, like rands() and echo(). echo() primarily exists for debugging,
but it is sometimes also used for extracting metadata from a model, most
commonly for generating a BOM.
My suggestion is to provide declarative alternatives for these use cases
first, before changing the evaluation behaviour. That way, anybody who runs
into a problem has a path to upgrade their code to work with the new
release. This means: add a declarative random() function, and add support
for object metadata, suitable for generating BOMs.
On 7 March 2016 at 14:03, Marius Kintel marius@kintel.net wrote:
On Mar 7, 2016, at 13:39 PM, runsun runsun@gmail.com wrote:
The children is "called" twice. So anything inside the children code
will be
executed twice, even if rands() is not used. This doesn't look buggy to
me.
This needs to be clarified. I’m not 100% certain I would call this a bug,
but it does need some looking into.
There’s also the question whether people rely on this existing behavior.
-Marius
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Thanks @nophead. That clinched it. The code is not that pretty but it does
work :)
Thanks everyone.
Now I just need to make the ships look good :)
(F6 warning - not for the faint hearted...)
// OpenSCAD version of the openCscad tool here:
// - https://github.com/smcameron/opencscad
// User editable parameters
// None yet
//---------------------------------
// Random seed control is critical.
// The seed defines all the variation. Using the same seed will give the
same ship.
//
$seed = floor(rands(0,1000000,1)[0]);
// $seed = 970439;
max_random_values = 300;
echo("Seed=",$seed);
function randn(n,idx=0) = floor(rands(0,n,max_random_values, $seed)[idx]);
function randf(n,idx=0) = rands(0,n*1000,max_random_values, $seed)[idx] /
1000.0;
// echo(randn(10,0), randn(10,1));
// echo(randf(12.5,0), randf(12.5,1));
cyl_res = 40;
sph_res = 80;
Gseed = false;
verbose = false;
//---------------------------------
// from opencscad
module cyl(h,r1,r2,ctr=false) {
//if (verbose) {echo("cyl", h, r1, r2, ctr);}
cylinder(h=h, r1=r1, r2=r2, center=ctr, $fn=cyl_res);
}
//---------------------------------
// cylinder_rings
//
module cylinder_rings(length, r1, r2, nrings, rand_idx) {
if (verbose) {echo("cyl_rings", nrings, rand_idx);}
ringspacing = length / (nrings + 1.0);
ringheight = ringspacing / 3.0;
z = -(ringspacing * nrings) / 2.0;
dstart = ringspacing;
rstart = r1 * 1.3;
dr = ((r2 * 1.3) - rstart) / nrings;
//
for (i = [0:nrings]) {
z = (i+1) * ringspacing + dstart;
r = (i+1) * dr + rstart;
translate([0, 0, z])
cyl(ringheight, r, r, true);
}
}
// cylinder_ribs
module cylinder_ribs(length, r1, r2, nribs, rand_idx) {
if (verbose) {echo("cyl_ribs", rand_idx);}
dangle = 360.0 / nribs/4;
extra_factor = randf(1, rand_idx)+1;
//
for (i=[0:nribs]) {
angle = danglei;
rotate([0, 0, angle])
intersection() {
union() {
translate([max(r1, r2) * 1.05, 0, 0]);
cube(size=[max(r1, r2) * 2, max(r1, r2) * 2, length + 2], center=true);
translate([max(r1, r2) * -1.05, 0, 0]);
cube(size=[max(r1, r2) * 2, max(r1, r2) * 2, length + 2], center=true);
translate([0, max(r1, r2), 0]);
cube(size=[max(r1, r2) * 2.1, max(r1, r2) * 2.1, length + 2],
center=true);
}
cyl(length, r1extra_factor, r2*(extra_factor*0.8), true);
}
}
}
// cylindrical_thing
module cylindrical_thing(length, r1, r2, rand_idx) {
if (verbose) {echo("cyl_thing", rand_idx);}
cyl(length, r1, r2, true);
cylinder_rings(length, r1, r2, randn(3, rand_idx) + 5);
}
// cylinder_protrusions
module cylinder_protrusions(length, r1, r2, nprotrusions, rand_idx,
excess=1.5) {
if (verbose) {echo("cylinder_protusions", rand_idx);}
intersection() {
// cyl(length, r1 * excess, r2 * excess, true);
cylindrical_thing(length, r1 * excess, r2 * excess, rand_idx);
//
union() {
rand_local = rand_idx+1;
for (i = [0:nprotrusions]) {
h = ((randn(80, rand_local) + 20) / 100.0) * length;
z = -((length - h) / 2.0) * (randn(100, rand_local+1) / 100.0);
x = min(r1, r2) * randf(0.2, rand_local+2);
angle = randf(360.0, rand_local+3);
translate([0, 0, z])
rotate([0, 0, angle])
cube(size=[x, max(r1, r2) * 3, h], center=true);
rand_local = rand_local + 1;
}
}
}
}
// random_cylinder_protrusions
module random_cylinder_protrusions(length, r1, r2, nelaborations, rand_idx)
{
if (verbose) {echo("rand_cyl_protrusions", rand_idx);}
nprots = randn(5, rand_idx) + 5;
pheight = randf(0.1, rand_idx+1);
l = (1.0 - randf(0.7, rand_idx+2)) * length / nelaborations;
offset = randf((length / 2.0) - (l / 2.0), rand_idx+3);
if (randn(100, rand_idx+4) < 50) {
offset = -offset; }
translate([0, 0, offset])
cylinder_protrusions(l, ((r1 + r2) / 2.0) * (1 + pheight), ((r1 + r2) /
2.0) * (1 + pheight), nprots, rand_idx+5);
}
// random_cylinder_ribs
module random_cylinder_ribs(length, r1, r2, nelaborations, rand_idx) {
if (verbose) {echo("rand_cyl_ribs", rand_idx); }
nribs = randn(5, rand_idx) + 5;
ribheight = randf(0.3, rand_idx+1);
l = (1.0 - randf(0.7, rand_idx+2)) * length / nelaborations;
offset = randf((length / 2.0) - (l / 2.0), rand_idx+3);
if (randn(100, rand_idx+4) < 50) {
offset = -offset; }
translate([0, 0, offset])
cylinder_ribs(l, ((r1 + r2) / 2.0) * (1 + ribheight), ((r1 + r2) / 2.0) *
(1 + ribheight), nribs, rand_idx+5);
}
// random_cylinder_rings
module random_cylinder_rings(length, r1, r2, nelaborations, rand_idx) {
if (verbose) {echo("rand_cyl_rings", rand_idx);}
nrings = randn(4, rand_idx) + 1;
ringheight = randf(0.05, rand_idx+1);
l = (1.0 - randf(0.7, rand_idx+2)) * length / nelaborations;
offset = randf((length / 2.0) - (l / 2.0), rand_idx+3);
if (randn(100, rand_idx+4) < 50) {
offset = -offset;}
translate([0, 0, offset])
cylinder_rings(l, r1 * (1 + ringheight), r2 * (1 + ringheight), nrings,
rand_idx+5);
}
// block_pile
module block_pile(length, r1, r2, rand_idx) {
if (verbose) {echo("block_pile", rand_idx);}
w = max(r1, r2);
pod_module(length * 0.9, w * 0.7);
block_count = randn(40, rand_idx) + 20;
rand_local = rand_idx;
for (i =[0:block_count]) {
x = 0.8 * (randf(w, rand_local+1) - (w / 2.0));
y = 0.8 * (randf(w, rand_local+2) - (w / 2.0));
z = 0.8 * (randf(length - (length / 2.0),rand_local+3));
sx = randf(w / 2.0, rand_local+4) + w / 2.0;
sy = randf(w / 2.0, rand_local+5) + w / 2.0;
sz = randf(w / 2.0, rand_local+6) + w / 2.0;
translate([x, y, z])
cube(size=[sx, sy, sz], center=true);
rand_local = rand_local + 1;
}
}
// elaborate_cylinder
module elaborate_cylinder(length, r1, r2, nelaborations, with_spheres=false,
rand_idx) {
if (verbose) {echo("elaborate_cylinder",length, r1, r2, nelaborations,
with_spheres, rand_idx);}
cyl(length, r1, r2, true);
for (i = [0:nelaborations]) {
e = randn(3, rand_idx);
if (e==0) {
random_cylinder_protrusions(length, r1, r2, nelaborations, rand_idx+1+i);
} else if (e==1) {
random_cylinder_ribs(length, r1, r2, nelaborations, rand_idx+1+i);
} else if (e==2) {
random_cylinder_rings(length, r1, r2, nelaborations, rand_idx+1+i);
}
}
//
if (with_spheres) {
translate([0, 0, -length / 2.0])
if (randn(100, rand_idx+2) < 30) {
if (verbose) {echo(" elab=sph-cyl");}
difference() {
sphere(r1, $fn=sph_res);
//
translate([0, 0, -r1/2.0])
cyl(r1 * 1.2, r1 * 0.9, r1 * 0.7, true);
}
} else if (randn(100, rand_idx+3) < 30) {
if (verbose) {echo(" elab=thrustcluster");}
rotate(180, 0, 1, 0)
thruster_cluster(r1, rand_idx+4);
} else {
if (verbose) {echo(" elab=sphonly");}
sphere(r1, $fn=sph_res);
}
//
translate([0, 0, length / 2.0])
if (randn(100, rand_idx+5) < 50) {
if (verbose) {echo(" sph-r2");}
sphere(r2, $fn=sph_res);
} else {
difference() {
if (verbose) {echo(" sph2-cyl");}
sphere(r2, $fn=sph_res);
//
translate([0, 0, r2 / 2.0])
cyl(r1 * 1.2, r2 * 0.9, r2 * 0.7, true);
}
}
}
//
}
// thruster_module
module thruster_module(length, r1, r2, rand_idx) {
if (verbose) {echo("thruster_module", length, r1, r2, rand_idx);}
nelaborations = randn(2, rand_idx)+1;
difference() {
elaborate_cylinder(length, r1, r2, nelaborations, false, rand_idx+1);
//
translate([0, 0, length * 0.05])
cyl(length, r1 * 0.95, r2 * 0.95, true);
}
}
// thruster
module thruster_variant(length, r1, r2, rand_idx) {
if (verbose) {echo("thruster_variant", length, r1, r2, rand_idx);}
v = randf(0.3, rand_idx) - 0.15 + 1.0;
v2 = randf(0.3, rand_idx+1) - 0.15 + 1.0;
thruster_module(length, r1 * v, r2 * v2, rand_idx+2);
}
// thruster_cluster
module thruster_cluster(r, rand_idx) {
if (verbose) {echo("thruster_cluster", rand_idx);}
ttype = randn(3, rand_idx);
cyl(r * 0.2, r, r, true);
v1 = randf(0.3, rand_idx+1) - 0.15 + 1.0;
v2 = randf(0.3, rand_idx+2) - 0.15 + 1.0;
v3 = randf(0.3, rand_idx+3) - 0.15 + 1.0;
translate([0, 0, r * 0.1]) {
if (ttype==0) {
if (verbose) {echo("thrust");}
thruster_variant(r * v1 * 1.5, r * v2 * 0.4, r * v3, rand_idx+4);
} else {
if (verbose) {echo("thrust xN");}
n = randn(7, rand_idx+4) + 2;
r1 = r * 0.5 * v1;
r2 = r * 0.2 * v2;
r3 = r * 0.7;
thruster_variant(r1 * v3 * 1.5, r1 * v2 * 0.4, r1 * v3, rand_idx+5);
angle = 360.0 / n;
for (i=[0:n]) {
a = i*angle;
rotate([0,0,a])
translate([r3,0,0])
thruster_module(r2 * v3 * 1.5, r2 * v2 * 0.4, r2 * v3, rand_idx+6+i);
}
}
}
}
// fuselage_module
module fuselage_module(length, r1, r2, rand_idx) {
if (verbose) {echo("fuselage_module", rand_idx);}
nelabs = randn(3, rand_idx) + 2;
squashx = 1.0;
squashy = randf(0.5, rand_idx+1) + 0.5;
if (randn(100, rand_idx+2) < 50) {
squashx = randf(0.5, rand_idx+3) + 0.5;
squashy = 1.0;
}
scale([squashx, squashy, 1.0]) {
if (randn(100, rand_idx+4) < 50)
elaborate_cylinder(length, r1, r2, nelabs, true, rand_idx+5);
else
block_pile(length, r1, r2, rand_idx+5);
}
}
// spar_module
module spar_module(length, angle, rand_idx) {
if (verbose) {echo("spar_module", rand_idx);}
squash = 0.3 + randf(0.7, rand_idx);
r = length / (10.0 + randf(10.0, rand_idx+1));
rotate([0,angle,0])
scale([1.0, squash, 1.0])
cyl(length, r, r * randf(0.4, rand_idx+2) + 0.4, false);
}
// pod_module
module pod_module(length, r, rand_idx) {
if (verbose) {echo("pod_module", rand_idx);}
scale([r / 10.0, r / 10.0, length / 20.0])
sphere(r=10.0, $fn=sph_res);
}
// ring
module ring(length, rand_idx) {
if (verbose) {echo("ring", rand_idx);}
r = length * randf(0.5, rand_idx) + 0.5;
h = (r * 0.4) - randf(0.2, rand_idx+1);
difference() {
cyl(h, r, r, true);
cyl(h + 1, r * 0.95, r * 0.95, true);
}
}
// outrigger_module
module outrigger_module(length, rand_idx) {
if (verbose) {echo("outrigger_module", rand_idx);}
dualpod = (randn(100, rand_idx) < 50);
angle = randn(60, rand_idx+1) + 90;
//
spar_length = length;//(1+randn(2));
spar_module(spar_length, angle, rand_idx+2);
translate([sin(angle) * spar_length, 0, cos(angle) * spar_length])
//translate([0,0,spar_length/3/(2+randn(4))])
if (randn(100, rand_idx+2) < 50)
pod_module(length / 3.0, length / 10.0, rand_idx+3);
else
fuselage_module(length / 3.0, length / 10.0, length / 12.0, rand_idx+3);
//
if (dualpod) {
if (verbose) {echo(" dualpod"); }
dprad = randf(spar_length * 0.8, rand_idx+3) + spar_length * 0.1;
translate([sin(angle) * dprad, 0, cos(angle) * dprad])
//translate([0,0,-spar_length/3/(1+randn(4))])
if (randn(100, rand_idx+4) < 50)
pod_module(length / 3.0, length / 10.0, rand_idx+5);
else
fuselage_module(length / 3.0, length / 10.0, length / 12.0, rand_idx+5);
}
//
if (randn(100, rand_idx+5) < 10) {
if (verbose) {echo(" ring");}
dprad = randf(length * 0.8, rand_idx+6) + length * 0.1;
translate([0, 0, cos(angle) * dprad])
ring(length, rand_idx+7);
}
}
// outrigger_set_module
module outrigger_set_module(maxlength, minlength, rand_idx) {
if (verbose) {echo("outrigger_set_module", rand_idx);}
length = randf(maxlength - minlength, rand_idx) + minlength;
if (randn(100, rand_idx+1) < 20) {
n = randn(8, rand_idx+2) + 1;
angle = 360.0 / n;
for (i=[0:n]) {
rotate([0,0,angle*i])
outrigger_module(length, rand_idx+2+i);
}
} else {
angle = randn(360, rand_idx+3);
if (randn(100, rand_idx+4) < 30) {
angle = randn(4, rand_idx+5) * 90;
}
rotate([0, 0, angle])
outrigger_module(length, rand_idx+6);
}
}
// cut_in_half
module cut_in_half(size) {
if (verbose) {echo("cut in half");}
intersection() {
translate([0, -size/2.0, -size/2.0])
cube(size=size, center=false);
//
children(0);
}
}
// bilaterally_symmetricalize
module bilaterally_symmetricalize(size) {
if (verbose) {echo("Symmetry");}
cut_in_half(size) children(0);
mirror([1,0,0])
cut_in_half(size) children(0);
}
// ship_module
module ship_module(rand_idx=0) {
if (verbose) {echo("Ship module");}
fuselage_module(30 + randn(60), randn(10) + 8, randn(10) + 8, rand_idx);
outrigger_count = randn(2, rand_idx+1)+1;
for (i=[0:outrigger_count]) {
outrigger_set_module(60, 5, rand_idx+2+i);
}
}
// Main
module ship() {
if (randn(100) < 20) {
echo("ship",$seed);
bilaterally_symmetricalize(10000) ship_module();
} else {
ship_module();
}
}
rotate([90,0,0])
ship();
--
View this message in context: http://forum.openscad.org/children-and-extra-unwanted-evaluations-tp16291p16336.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
runsun wrote
From my understanding, children() looks like a func or module call, that
it generates something when it is called. In the code below:
module m_mirror() {
children(0);
mirror([1,0,0])
children(0);
}
The children is "called" twice. So anything inside the children code will
be executed twice, even if rands() is not used. This doesn't look buggy to
me.
In fact it is consistent with how func_call and module_call work --- that
is, a "( )" indicates "going through the inner code to get a new data".
If we want to make a children NOT "CALLED" after the first use, then
something like
$children
might be better.
You could consider that it should only be evaluated again iif its scope has
changed, for example the following with a test on $face in the child would
be a normal use case where you want it evaluated twice.
module m_mirror() {
$face="front";
children(0);
$face="back";
mirror([1,0,0])
children(0);
}
Admin - PM me if you need anything, or if I've done something stupid...
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.
View this message in context: http://forum.openscad.org/children-and-extra-unwanted-evaluations-tp16291p16337.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
That example sets $face to "back" for both children because of the last
assignment rule in OpenScad. You need to use assign or let or for to get a
different scope for each child.
On 7 March 2016 at 21:57, MichaelAtOz oz.at.michael@gmail.com wrote:
runsun wrote
From my understanding, children() looks like a func or module call, that
it generates something when it is called. In the code below:
module m_mirror() {
children(0);
mirror([1,0,0])
children(0);
}
The children is "called" twice. So anything inside the children code will
be executed twice, even if rands() is not used. This doesn't look buggy
to
me.
In fact it is consistent with how func_call and module_call work --- that
is, a "( )" indicates "going through the inner code to get a new data".
If we want to make a children NOT "CALLED" after the first use, then
something like
$children
might be better.
You could consider that it should only be evaluated again iif its scope has
changed, for example the following with a test on $face in the child would
be a normal use case where you want it evaluated twice.
module m_mirror() {
$face="front";
children(0);
$face="back";
mirror([1,0,0])
children(0);
}
Admin - PM me if you need anything, or if I've done something stupid...
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.
View this message in context:
http://forum.openscad.org/children-and-extra-unwanted-evaluations-tp16291p16337.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org