discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Barnsley Fern recursive

EB
Eric Buijs
Wed, Sep 12, 2018 12:45 PM

Thanks again but unfortunately I'm not able to increase the recursive loop
with this (on OSX).

--
Sent from: http://forum.openscad.org/

Thanks again but unfortunately I'm not able to increase the recursive loop with this (on OSX). -- Sent from: http://forum.openscad.org/
R
Ronaldo
Wed, Sep 12, 2018 3:42 PM

Eric,

The probabilities in your code does not agree with the definition found in
Wikipedia https://en.wikipedia.org/wiki/Barnsley_fern  . Besides, a full
recursion stop rule is missing in the recursive module so regardless the
memory allocation size you will always get a stack overflow with probability
one. Anyway, as this fractal may be generated without recursion that would
be a preferable path to go. Finally, you would need a very large number of
circles to get a reasonable picture of the fractal and this may surpass the
limits of the OpenSCAD node tree.

--
Sent from: http://forum.openscad.org/

Eric, The probabilities in your code does not agree with the definition found in Wikipedia <https://en.wikipedia.org/wiki/Barnsley_fern> . Besides, a full recursion stop rule is missing in the recursive module so regardless the memory allocation size you will always get a stack overflow with probability one. Anyway, as this fractal may be generated without recursion that would be a preferable path to go. Finally, you would need a very large number of circles to get a reasonable picture of the fractal and this may surpass the limits of the OpenSCAD node tree. -- Sent from: http://forum.openscad.org/
DM
doug moen
Wed, Sep 12, 2018 4:49 PM

Ronaldo said: " Anyway, as this fractal may be generated without recursion
that would
be a preferable path to go."

Yes, but how do you code it without recursion in OpenSCAD?
The Barnsley Fern is an Iterated Function System, where the result at
iteration i+1
depends on the result of iteration i.

The only language feature that supports this kind of iteration, without
using recursion,
is the C-style for loop, which is only available in list comprehensions,
not in modules.
For this, you need a development snapshot with the "C-style for" option
enabled
in Preferences.

So, I would try generating all of the coordinates into a list, using a
C-style for loop
inside a list comprehension. And I'd use a simpler polygon for each point,
to further
reduce resource consumption: like a triangle, square, or hexagon.

On 12 September 2018 at 11:42, Ronaldo rcmpersiano@gmail.com wrote:

Eric,

The probabilities in your code does not agree with the definition found in
Wikipedia https://en.wikipedia.org/wiki/Barnsley_fern  . Besides, a full
recursion stop rule is missing in the recursive module so regardless the
memory allocation size you will always get a stack overflow with
probability
one. Anyway, as this fractal may be generated without recursion that would
be a preferable path to go. Finally, you would need a very large number of
circles to get a reasonable picture of the fractal and this may surpass the
limits of the OpenSCAD node tree.

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Ronaldo said: " Anyway, as this fractal may be generated without recursion that would be a preferable path to go." Yes, but how do you code it without recursion in OpenSCAD? The Barnsley Fern is an Iterated Function System, where the result at iteration i+1 depends on the result of iteration i. The only language feature that supports this kind of iteration, without using recursion, is the C-style for loop, which is only available in list comprehensions, not in modules. For this, you need a development snapshot with the "C-style for" option enabled in Preferences. So, I would try generating all of the coordinates into a list, using a C-style for loop inside a list comprehension. And I'd use a simpler polygon for each point, to further reduce resource consumption: like a triangle, square, or hexagon. On 12 September 2018 at 11:42, Ronaldo <rcmpersiano@gmail.com> wrote: > Eric, > > The probabilities in your code does not agree with the definition found in > Wikipedia <https://en.wikipedia.org/wiki/Barnsley_fern> . Besides, a full > recursion stop rule is missing in the recursive module so regardless the > memory allocation size you will always get a stack overflow with > probability > one. Anyway, as this fractal may be generated without recursion that would > be a preferable path to go. Finally, you would need a very large number of > circles to get a reasonable picture of the fractal and this may surpass the > limits of the OpenSCAD node tree. > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
R
Ronaldo
Wed, Sep 12, 2018 5:54 PM

doug.moen wrote

The only language feature that supports this kind of iteration, without
using recursion,
is the C-style for loop, which is only available in list comprehensions,
not in modules.
For this, you need a development snapshot with the "C-style for" option
enabled
in Preferences.

You are right, Doug, and the following code takes that path. The nested for
in the module BarnsleyFern was needed to circumvent the OpenSCAD for limit
of at most 9999 elements. Anyway, this code is valid to at most 9999x9999
iterations.

m1 = [[0,0],[0,0.16]];
c1 = [0,0];
m2 = [[0.85,0.04],[-0.04,0.85]];
c2 = [0, 1.6];
m3 = [[0.2,-0.26],[0.23,0.22]];
c3 = [0, 1.6];
m4 = [[-0.15,0.28],[0.26,0.24]];
c4 = [0, 0.44];

function f1(p) = m1 * p + c1;
function f2(p) = m2 * p + c2;
function f3(p) = m3 * p + c3;
function f4(p) = m4 * p + c4;

module dot(p) translate(100 * p) square();

module BarnsleyFern(index) {
q = BarnsleyFern([0,0],index);
echo(len=len(q));
for(i=[0:9999:len(q)-1]) {
r = len(q)-i;
for(j=[0:min(r,9999)-1])
dot(q[i+j]);
}
}

function BarnsleyFern(p,index) =
[ for(i=0, q=p; i<index; i=i+1,
r =  rands(0,1,1)[0],
q =
r <= 0.01 ?
f1(q) :
r > 0.01 && r <=0.86?
f2(q) :
r > 0.86 && r <=0.93?
f3(q) :
f4(q) )
q ];

BarnsleyFern(200000);

The code generated the following image in 25sec:

http://forum.openscad.org/file/t1275/BarnsleyFern_200000.png

That is a detail look:

http://forum.openscad.org/file/t1275/BarnsleyFern_200000_detail.png

--
Sent from: http://forum.openscad.org/

doug.moen wrote > The only language feature that supports this kind of iteration, without > using recursion, > is the C-style for loop, which is only available in list comprehensions, > not in modules. > For this, you need a development snapshot with the "C-style for" option > enabled > in Preferences. You are right, Doug, and the following code takes that path. The nested for in the module BarnsleyFern was needed to circumvent the OpenSCAD for limit of at most 9999 elements. Anyway, this code is valid to at most 9999x9999 iterations. m1 = [[0,0],[0,0.16]]; c1 = [0,0]; m2 = [[0.85,0.04],[-0.04,0.85]]; c2 = [0, 1.6]; m3 = [[0.2,-0.26],[0.23,0.22]]; c3 = [0, 1.6]; m4 = [[-0.15,0.28],[0.26,0.24]]; c4 = [0, 0.44]; function f1(p) = m1 * p + c1; function f2(p) = m2 * p + c2; function f3(p) = m3 * p + c3; function f4(p) = m4 * p + c4; module dot(p) translate(100 * p) square(); module BarnsleyFern(index) { q = BarnsleyFern([0,0],index); echo(len=len(q)); for(i=[0:9999:len(q)-1]) { r = len(q)-i; for(j=[0:min(r,9999)-1]) dot(q[i+j]); } } function BarnsleyFern(p,index) = [ for(i=0, q=p; i<index; i=i+1, r = rands(0,1,1)[0], q = r &lt;= 0.01 ? f1(q) : r > 0.01 && r <=0.86? f2(q) : r > 0.86 && r <=0.93? f3(q) : f4(q) ) q ]; BarnsleyFern(200000); The code generated the following image in 25sec: <http://forum.openscad.org/file/t1275/BarnsleyFern_200000.png> That is a detail look: <http://forum.openscad.org/file/t1275/BarnsleyFern_200000_detail.png> -- Sent from: http://forum.openscad.org/
DM
doug moen
Wed, Sep 12, 2018 6:22 PM

Nice work.

On 12 September 2018 at 13:54, Ronaldo rcmpersiano@gmail.com wrote:

doug.moen wrote

The only language feature that supports this kind of iteration, without
using recursion,
is the C-style for loop, which is only available in list comprehensions,
not in modules.
For this, you need a development snapshot with the "C-style for" option
enabled
in Preferences.

You are right, Doug, and the following code takes that path. The nested for
in the module BarnsleyFern was needed to circumvent the OpenSCAD for limit
of at most 9999 elements. Anyway, this code is valid to at most 9999x9999
iterations.

m1 = [[0,0],[0,0.16]];
c1 = [0,0];
m2 = [[0.85,0.04],[-0.04,0.85]];
c2 = [0, 1.6];
m3 = [[0.2,-0.26],[0.23,0.22]];
c3 = [0, 1.6];
m4 = [[-0.15,0.28],[0.26,0.24]];
c4 = [0, 0.44];

function f1(p) = m1 * p + c1;
function f2(p) = m2 * p + c2;
function f3(p) = m3 * p + c3;
function f4(p) = m4 * p + c4;

module dot(p) translate(100 * p) square();

module BarnsleyFern(index) {
q = BarnsleyFern([0,0],index);
echo(len=len(q));
for(i=[0:9999:len(q)-1]) {
r = len(q)-i;
for(j=[0:min(r,9999)-1])
dot(q[i+j]);
}
}

function BarnsleyFern(p,index) =
[ for(i=0, q=p; i<index; i=i+1,
r =  rands(0,1,1)[0],
q =
r <= 0.01 ?
f1(q) :
r > 0.01 && r <=0.86?
f2(q) :
r > 0.86 && r <=0.93?
f3(q) :
f4(q) )
q ];

BarnsleyFern(200000);

The code generated the following image in 25sec:

http://forum.openscad.org/file/t1275/BarnsleyFern_200000.png

That is a detail look:

http://forum.openscad.org/file/t1275/BarnsleyFern_200000_detail.png

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Nice work. On 12 September 2018 at 13:54, Ronaldo <rcmpersiano@gmail.com> wrote: > doug.moen wrote > > The only language feature that supports this kind of iteration, without > > using recursion, > > is the C-style for loop, which is only available in list comprehensions, > > not in modules. > > For this, you need a development snapshot with the "C-style for" option > > enabled > > in Preferences. > > You are right, Doug, and the following code takes that path. The nested for > in the module BarnsleyFern was needed to circumvent the OpenSCAD for limit > of at most 9999 elements. Anyway, this code is valid to at most 9999x9999 > iterations. > > m1 = [[0,0],[0,0.16]]; > c1 = [0,0]; > m2 = [[0.85,0.04],[-0.04,0.85]]; > c2 = [0, 1.6]; > m3 = [[0.2,-0.26],[0.23,0.22]]; > c3 = [0, 1.6]; > m4 = [[-0.15,0.28],[0.26,0.24]]; > c4 = [0, 0.44]; > > function f1(p) = m1 * p + c1; > function f2(p) = m2 * p + c2; > function f3(p) = m3 * p + c3; > function f4(p) = m4 * p + c4; > > module dot(p) translate(100 * p) square(); > > module BarnsleyFern(index) { > q = BarnsleyFern([0,0],index); > echo(len=len(q)); > for(i=[0:9999:len(q)-1]) { > r = len(q)-i; > for(j=[0:min(r,9999)-1]) > dot(q[i+j]); > } > } > > function BarnsleyFern(p,index) = > [ for(i=0, q=p; i<index; i=i+1, > r = rands(0,1,1)[0], > q = > r &lt;= 0.01 ? > f1(q) : > r > 0.01 && r <=0.86? > f2(q) : > r > 0.86 && r <=0.93? > f3(q) : > f4(q) ) > q ]; > > > BarnsleyFern(200000); > > The code generated the following image in 25sec: > > <http://forum.openscad.org/file/t1275/BarnsleyFern_200000.png> > > That is a detail look: > > <http://forum.openscad.org/file/t1275/BarnsleyFern_200000_detail.png> > > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
EB
Eric Buijs
Wed, Sep 12, 2018 9:57 PM

Ronaldo, first I take my hat of for that code. It works perfectly (once I
translated the < to smaller than, <). I wasn't even aware of the C-style
for option in OpenSCAD and therefore discarded the possibility to use
iteration. The Barnsley Fern looks pretty neat taking only 1 minute and 18
seconds on my old computer. Thank you all guys.

--
Sent from: http://forum.openscad.org/

Ronaldo, first I take my hat of for that code. It works perfectly (once I translated the &lt; to smaller than, <). I wasn't even aware of the C-style for option in OpenSCAD and therefore discarded the possibility to use iteration. The Barnsley Fern looks pretty neat taking only 1 minute and 18 seconds on my old computer. Thank you all guys. -- Sent from: http://forum.openscad.org/
R
Ronaldo
Wed, Sep 12, 2018 11:56 PM

The C-style for loop available for list comprehension is not the only way to
address the Barnsley Fern fractal drawing because its building process is
tail recursive. As OpenSCAD is able to eliminate recursion of some forms of
tail recursive functions (not modules), it is in principle possible to have
an iteration written in a recursive way. Find bellow an alternative
recursion to the function and module defined in my previous post.

module BarnsleyFern(index) {
q = BarnsleyFern(index);
for(i=[0:9999:len(q)-1]) {
r = len(q)-i;
for(j=[0:min(r,9999)-1])
dot(q[i+j]);
}
}

function nextP(p) =
let(r =  rands(0,1,1)[0])
r <= 0.01 ?
f1(p) :
r > 0.01 && r <=0.86?
f2(p) :
r > 0.86 && r <=0.93?
f3(p) :
f4(p) ;

function BarnsleyFern(index, bag=[[0,0]]) =
index==0 ?
bag :
BarnsleyFern(index-1, concat( [nextP(bag[0])], bag ));

Although exploring the tail recursion elimination in an elegant solution,
this approach is not so efficient as the previous one. My previous code have
a linear behavior: the running time is approximately proportional to the
parameter index (O(n)). For my surprise, although the above code run as an
iteration, its running time is O(n2). A closer look solves the apparent
mystery: the main operation in the function BarnsleyFern is a concat of a
single element list with a growing list (bag); possibly concat operates by
rewriting the output list from the input lists so the total number of
operations of all concats is O(n2). This approach would benefit from a
better implementation of concat.

--
Sent from: http://forum.openscad.org/

The C-style for loop available for list comprehension is not the only way to address the Barnsley Fern fractal drawing because its building process is tail recursive. As OpenSCAD is able to eliminate recursion of some forms of tail recursive functions (not modules), it is in principle possible to have an iteration written in a recursive way. Find bellow an alternative recursion to the function and module defined in my previous post. module BarnsleyFern(index) { q = BarnsleyFern(index); for(i=[0:9999:len(q)-1]) { r = len(q)-i; for(j=[0:min(r,9999)-1]) dot(q[i+j]); } } function nextP(p) = let(r = rands(0,1,1)[0]) r <= 0.01 ? f1(p) : r > 0.01 && r <=0.86? f2(p) : r > 0.86 && r <=0.93? f3(p) : f4(p) ; function BarnsleyFern(index, bag=[[0,0]]) = index==0 ? bag : BarnsleyFern(index-1, concat( [nextP(bag[0])], bag )); Although exploring the tail recursion elimination in an elegant solution, this approach is not so efficient as the previous one. My previous code have a linear behavior: the running time is approximately proportional to the parameter index (O(n)). For my surprise, although the above code run as an iteration, its running time is O(n2). A closer look solves the apparent mystery: the main operation in the function BarnsleyFern is a concat of a single element list with a growing list (bag); possibly concat operates by rewriting the output list from the input lists so the total number of operations of all concats is O(n2). This approach would benefit from a better implementation of concat. -- Sent from: http://forum.openscad.org/
EB
Eric Buijs
Thu, Sep 13, 2018 1:14 PM

Ronaldo, a nice comparison of iterative vs tail recursive. The latter took
almost half an hour to finish on my PC, indeed not so efficient. Thanks.

--
Sent from: http://forum.openscad.org/

Ronaldo, a nice comparison of iterative vs tail recursive. The latter took almost half an hour to finish on my PC, indeed not so efficient. Thanks. -- Sent from: http://forum.openscad.org/
TP
Torsten Paul
Thu, Sep 13, 2018 5:01 PM

On 09/13/2018 03:14 PM, Eric Buijs wrote:

Ronaldo, a nice comparison of iterative vs tail recursive. The latter took
almost half an hour to finish on my PC, indeed not so efficient. Thanks.

That is not the fault of the tail recursion elimination. This part is
pretty much the same as a normal loop. As Ronaldo mentioned the slowness
comes from the part where it generates the data by copying everything
for each iteration.

ciao,
Torsten.

On 09/13/2018 03:14 PM, Eric Buijs wrote: > Ronaldo, a nice comparison of iterative vs tail recursive. The latter took > almost half an hour to finish on my PC, indeed not so efficient. Thanks. > That is not the fault of the tail recursion elimination. This part is pretty much the same as a normal loop. As Ronaldo mentioned the slowness comes from the part where it generates the data by copying everything for each iteration. ciao, Torsten.
RP
Ronaldo Persiano
Thu, Sep 13, 2018 5:37 PM

Torsten,

I partially disagree. I don't know the intrinsic processes of OpenSCAD but,
from the running times I got, I deduct that the iterative step of
generating one element of a list in the C-style solution is constant time
in contrast with the linear time the *concat *is done in the tail recursion
elimination strategy due to the successive copies. However, I don't see any
way to write a tail recursion solution to generate a list without resorting
to concat. So, tail recursion will be generally less competitive than
iterations for list generations.

Em qui, 13 de set de 2018 às 18:02, Torsten Paul Torsten.Paul@gmx.de
escreveu:

On 09/13/2018 03:14 PM, Eric Buijs wrote:

Ronaldo, a nice comparison of iterative vs tail recursive. The latter

took

almost half an hour to finish on my PC, indeed not so efficient. Thanks.

That is not the fault of the tail recursion elimination. This part is
pretty much the same as a normal loop. As Ronaldo mentioned the slowness
comes from the part where it generates the data by copying everything
for each iteration.

ciao,
Torsten.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Torsten, I partially disagree. I don't know the intrinsic processes of OpenSCAD but, from the running times I got, I deduct that the iterative step of generating one element of a list in the C-style solution is constant time in contrast with the linear time the *concat *is done in the tail recursion elimination strategy due to the successive copies. However, I don't see any way to write a tail recursion solution to generate a list without resorting to *concat*. So, tail recursion will be generally less competitive than iterations for list generations. Em qui, 13 de set de 2018 às 18:02, Torsten Paul <Torsten.Paul@gmx.de> escreveu: > On 09/13/2018 03:14 PM, Eric Buijs wrote: > > Ronaldo, a nice comparison of iterative vs tail recursive. The latter > took > > almost half an hour to finish on my PC, indeed not so efficient. Thanks. > > > That is not the fault of the tail recursion elimination. This part is > pretty much the same as a normal loop. As Ronaldo mentioned the slowness > comes from the part where it generates the data by copying everything > for each iteration. > > ciao, > Torsten. > > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >