discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

recursion limit on concat()

K
kitwallace
Tue, Oct 15, 2019 6:40 AM

The (corrected and as a result simpler) recursive version of this function
is used in this version of the  l-system code

https://github.com/KitWallace/openscad/blob/master/tiling/lsystem-recursive.scad

The Hilbert curve fails at k=5

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

The (corrected and as a result simpler) recursive version of this function is used in this version of the l-system code https://github.com/KitWallace/openscad/blob/master/tiling/lsystem-recursive.scad The Hilbert curve fails at k=5 -- Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Tue, Oct 15, 2019 2:31 PM

kitwallace kit.wallace@gmail.com wrote:

The (corrected and as a result simpler) recursive version of this function
is used in this version of the  l-system code

The last version you referred defines a string_to_points() function which
still is unable to have the tail recursion eliminated.
Applying your correction to my alternative recursion version, it becomes:

function string_to_points(s,step=1,angle=90,pos=[0,0,0],dir=0,i=0,ps=[]) =

i == len(s)
  ? ps
  : string_to_points(s,
                     step,
                     angle,
                     s[i]=="F" ||s[i]=="A" || s[i]=="B" // newpos
                       ? pos + step*[cos(dir), sin(dir)]
                       : pos,
                     s[i]=="+"                          // newdir
                       ? dir + angle
                       : s[i]=="-"
                          ? dir - angle
                          : dir,
                    i+1,
                    concat([pos],ps) );

And now it will be eligible to tail recursion elimination.

With that correction, the iterative version also gets simpler and don't
need any explicit concat() (neither 'each'):

function string_to_points(s,step=1,angle=90,pos=[0,0,0],dir=0) =
[for( i  = 0;

    i <= len(s);

    c   = s[i],
    pos = c=="F" ||c=="A" || c=="B"
           ? pos + step*[cos(dir), sin(dir)]
           : pos,
    dir = c=="+"
          ? dir + angle
          : c=="-"
            ? dir - angle
            : dir,
    i   = i+1 )
   pos ];

The Hilbert curve fails at k=5

This failure is due to something else: the 'for' in module path() has more
then 9999 elements. This is an arbitrary (and low) limit in OpenSCAD range
'for's.
As an workaround, you may consider the following code with two nested 'for'
meant to 'cheat' the limit rule:

module path(points,width,closed=false) {
r=width/2;
for (j=[0:len(points)/1000]) { // this { brace pair is mandatory
for(i=[0:min(999,len(points)-2-1000j)]) {
hull() {
translate(points[1000
j+i]) circle(r);
translate(points[1000*j+i+1]) circle(r);
}
}
}
if (closed) {
hull() {
translate(points[len(points)-1]) circle(r);
translate(points[0]) circle(r);
}
}
};

It should work up to 999999 elements in 'points'.

Now, Hilbert curve works with k=6 which has 13651 points and with the 54611
points generated when k=7.

kitwallace <kit.wallace@gmail.com> wrote: > > The (corrected and as a result simpler) recursive version of this function > is used in this version of the l-system code > The last version you referred defines a string_to_points() function which still is unable to have the tail recursion eliminated. Applying your correction to my alternative recursion version, it becomes: function string_to_points(s,step=1,angle=90,pos=[0,0,0],dir=0,i=0,ps=[]) = i == len(s) ? ps : string_to_points(s, step, angle, s[i]=="F" ||s[i]=="A" || s[i]=="B" // newpos ? pos + step*[cos(dir), sin(dir)] : pos, s[i]=="+" // newdir ? dir + angle : s[i]=="-" ? dir - angle : dir, i+1, concat([pos],ps) ); And now it will be eligible to tail recursion elimination. With that correction, the iterative version also gets simpler and don't need any explicit concat() (neither 'each'): function string_to_points(s,step=1,angle=90,pos=[0,0,0],dir=0) = [for( i = 0; i <= len(s); c = s[i], pos = c=="F" ||c=="A" || c=="B" ? pos + step*[cos(dir), sin(dir)] : pos, dir = c=="+" ? dir + angle : c=="-" ? dir - angle : dir, i = i+1 ) pos ]; The Hilbert curve fails at k=5 This failure is due to something else: the 'for' in module path() has more then 9999 elements. This is an arbitrary (and low) limit in OpenSCAD range 'for's. As an workaround, you may consider the following code with two nested 'for' meant to 'cheat' the limit rule: module path(points,width,closed=false) { r=width/2; for (j=[0:len(points)/1000]) { // this { brace pair is mandatory for(i=[0:min(999,len(points)-2-1000*j)]) { hull() { translate(points[1000*j+i]) circle(r); translate(points[1000*j+i+1]) circle(r); } } } if (closed) { hull() { translate(points[len(points)-1]) circle(r); translate(points[0]) circle(r); } } }; It should work up to 999999 elements in 'points'. Now, Hilbert curve works with k=6 which has 13651 points and with the 54611 points generated when k=7.
K
kitwallace
Tue, Oct 15, 2019 2:56 PM

Re the recusion example - I posted it at Torsten's request that so that it
could be used as a test case for the enhancement which I believe should
handle that case.

The correction I made was to concatenate only new points and I couldnt get
that to work with your inlined version - maybe didnt try hard enough!

Re the for range limit, i posted separately about that and the workaround I
came up with:

for (i=to_n(len(points)-2))

where

function to_n(n) = [for (i=0;i<=n;i=i+1) i];

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

Re the recusion example - I posted it at Torsten's request that so that it could be used as a test case for the enhancement which I believe should handle that case. The correction I made was to concatenate only new points and I couldnt get that to work with your inlined version - maybe didnt try hard enough! Re the for range limit, i posted separately about that and the workaround I came up with: for (i=to_n(len(points)-2)) where function to_n(n) = [for (i=0;i<=n;i=i+1) i]; -- Sent from: http://forum.openscad.org/
TP
Torsten Paul
Tue, Oct 15, 2019 3:22 PM

On 15.10.19 16:56, kitwallace wrote:

Re the recusion example - I posted it at Torsten's
request that so that it could be used as a test case
for the enhancement which I believe should handle
that case.

Thanks for that, I can confirm it works as expected. The
nightly does not abort with stack overflow so the tail
recursion elimination works nicely.

With the range workaround it successfully generates the
design with k=8.

ciao,
Torsten.

On 15.10.19 16:56, kitwallace wrote: > Re the recusion example - I posted it at Torsten's > request that so that it could be used as a test case > for the enhancement which I believe should handle > that case. Thanks for that, I can confirm it works as expected. The nightly does not abort with stack overflow so the tail recursion elimination works nicely. With the range workaround it successfully generates the design with k=8. ciao, Torsten.
HL
Hans L
Thu, Oct 17, 2019 7:06 PM

Hi kitwallace, I also have an L-system implementation which I've posted
about previously on the list:
http://forum.openscad.org/L-systems-demo-Fractal-designs-interpreter-performance-stress-testing-tp23295p25927.html

The latest revision is here:
https://gist.github.com/thehans/a1494db8046a58832e2ebb10a5908a66

I spent a lot of time optimizing this code (and even made changes to
OpenSCAD to support recursion more efficiently specifically because of
it).  It's pretty good with high iterations.

https://gist.github.com/thehans/a1494db8046a58832e2ebb10a5908a66

On Tue, Oct 15, 2019 at 10:23 AM Torsten Paul Torsten.Paul@gmx.de wrote:

On 15.10.19 16:56, kitwallace wrote:

Re the recusion example - I posted it at Torsten's
request that so that it could be used as a test case
for the enhancement which I believe should handle
that case.

Thanks for that, I can confirm it works as expected. The
nightly does not abort with stack overflow so the tail
recursion elimination works nicely.

With the range workaround it successfully generates the
design with k=8.

ciao,
Torsten.


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

Hi kitwallace, I also have an L-system implementation which I've posted about previously on the list: http://forum.openscad.org/L-systems-demo-Fractal-designs-interpreter-performance-stress-testing-tp23295p25927.html The latest revision is here: https://gist.github.com/thehans/a1494db8046a58832e2ebb10a5908a66 I spent a lot of time optimizing this code (and even made changes to OpenSCAD to support recursion more efficiently specifically because of it). It's pretty good with high iterations. <https://gist.github.com/thehans/a1494db8046a58832e2ebb10a5908a66> On Tue, Oct 15, 2019 at 10:23 AM Torsten Paul <Torsten.Paul@gmx.de> wrote: > On 15.10.19 16:56, kitwallace wrote: > > Re the recusion example - I posted it at Torsten's > > request that so that it could be used as a test case > > for the enhancement which I believe should handle > > that case. > > Thanks for that, I can confirm it works as expected. The > nightly does not abort with stack overflow so the tail > recursion elimination works nicely. > > With the range workaround it successfully generates the > design with k=8. > > ciao, > Torsten. > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
K
kitwallace
Sun, Oct 20, 2019 9:52 AM

Hi thehans

Thanks for the pointer - sorry I missed your work earlier - my L-system
doesn't handle moves nor pop and push so that's very useful.  My code
generates a list of points first, then the curve or a polygon as a separete
step because I'm particularly interested in fractals which tesselate - like
the 5-rep-tile  ["5-rep-tile", "F-F-F-F-", [["F", "F+F-F"]], 90] and
terdragon boundary ["Terdragon boundary", "A-B--A-B--", [["A","A+B"],
["B","A-B"]],  60]. I like your handling of F synonyms and will steal that.

Kit

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

Hi thehans Thanks for the pointer - sorry I missed your work earlier - my L-system doesn't handle moves nor pop and push so that's very useful. My code generates a list of points first, then the curve or a polygon as a separete step because I'm particularly interested in fractals which tesselate - like the 5-rep-tile ["5-rep-tile", "F-F-F-F-", [["F", "F+F-F"]], 90] and terdragon boundary ["Terdragon boundary", "A-B--A-B--", [["A","A+B"], ["B","A-B"]], 60]. I like your handling of F synonyms and will steal that. Kit -- Sent from: http://forum.openscad.org/