OpenSCAD programming question, recursion, functions, and modules

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

OpenSCAD programming question, recursion, functions, and modules

DanS
I'm going to attach some code (only look at it if you need it to get an idea of what I am trying to do, I am sure there are bugs plus I didn't copy any of the helper functions...).

I am having trouble figuring out how to deal with recursion on something I am drawing.  My problems are:

1) a Module can't return anything, correct?  At the same time I don't see how to (in a Function) do a lot of code setup, call helper functions, etc.  I guess I could setup a variable in a Function with "let" but how do I do a block of code with conditionals etc.  I would either like to return a list of points from a Module, or figure out how to do "real work" (helper calls, conditionals, etc) from a Function

2) If I have something that generates a list of points, assuming I recursively call that how do I elegantly keep the list one dimensional (not nested lists).  Do I just a call to some flatten function at the end?  Ideally I'd like not to generate nested lists to begin with, but I'm having a little trouble seeing how to do that?

Basically, what I am doing below is what I call a "bat curve" because I imagine it will look slightly like the stylized bats in traditional Chinese art (somewhat, not exactly, I can go into differences later, I don't need it to look exactly like that and am just using that as a convenient description).  The shape is going to be a fractal of arcs of increasingly smaller circles (generations determines how many levels deep the fractal goes).  Basically one large arc subdivides on both sides into a spiral of increasingly smaller radii arcs.  You could think of a letter D where the straight line is removed and at both ends of the D there is a spiral.



------------------------
module bCurve2(radius, number, generations, quadrant, pointsPerGeneration, direction, angle) {
    if (direction > 0) {
        newQuadrant = (quadrant + 1) % 4;
        startAngle = angle;
        endAngle = computeAngle(number, newQuadrant);
    }
    else {
        newQuadrant = (quadrant + 3) % 4;
        startAngle = computeAngle(number, newQuadrant);
        endAngle = angle;
    }
}

//quadrant is the graph quadrant for the next circle except it is C indexed so it is 0 - 3 rather than 1 - 4
module bCurve(radius, number, generations, quadrant, pointsPerGeneration) {
   
    if (generations > -1) {
      angle1 = computeAngle(number, quadrant);
      angle2 = computeAngle(number, nextQuadrant(quadrant));
      
      span = angle1 - angle2;
      stepSize = span/pointsPerGeneration;
      newRadius = radius * sin(atan(2/(number-1))); 
       
      quadrantA = (quadrant + 3)%4;
      quadrantB = (quadrant + 2) %4; 
       
      points = [ for (i = [0:pointsPerGeneration]) [ bCurve2(newRadius, number, (generations -1), quadrantA, pointsPerGeneration, -1, angle1), xArc(radius, angle1, stepSize, i), yArc(radius, angle1, stepSize, i)], bCurve2(newRadius, number, (generations -1), quadrantB, pointsPerGeneration, 1, angle2) ];
        
    difference() {
      offset(0.01) polygon(points);
      polygon(points);
      }
      } 
}

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: OpenSCAD programming question, recursion, functions, and modules

frankv
1) A module "returns" an object... whatever object  is generated by a module is its return "value". So you could return a polygon created from a list of points, for example, by having a polygon ([list]) statement at the end of the module. 

You can use the ternary operator to do if-then-else evaluations in a let() call in a function. And a function can call other functions. 

2) The concat () function flattens multiple lists into a single list.

On Friday, February 9, 2018, Dan Shriver <[hidden email]> wrote:
I'm going to attach some code (only look at it if you need it to get an idea of what I am trying to do, I am sure there are bugs plus I didn't copy any of the helper functions...).

I am having trouble figuring out how to deal with recursion on something I am drawing.  My problems are:

1) a Module can't return anything, correct?  At the same time I don't see how to (in a Function) do a lot of code setup, call helper functions, etc.  I guess I could setup a variable in a Function with "let" but how do I do a block of code with conditionals etc.  I would either like to return a list of points from a Module, or figure out how to do "real work" (helper calls, conditionals, etc) from a Function

2) If I have something that generates a list of points, assuming I recursively call that how do I elegantly keep the list one dimensional (not nested lists).  Do I just a call to some flatten function at the end?  Ideally I'd like not to generate nested lists to begin with, but I'm having a little trouble seeing how to do that?

Basically, what I am doing below is what I call a "bat curve" because I imagine it will look slightly like the stylized bats in traditional Chinese art (somewhat, not exactly, I can go into differences later, I don't need it to look exactly like that and am just using that as a convenient description).  The shape is going to be a fractal of arcs of increasingly smaller circles (generations determines how many levels deep the fractal goes).  Basically one large arc subdivides on both sides into a spiral of increasingly smaller radii arcs.  You could think of a letter D where the straight line is removed and at both ends of the D there is a spiral.



------------------------
module bCurve2(radius, number, generations, quadrant, pointsPerGeneration, direction, angle) {
    if (direction > 0) {
        newQuadrant = (quadrant + 1) % 4;
        startAngle = angle;
        endAngle = computeAngle(number, newQuadrant);
    }
    else {
        newQuadrant = (quadrant + 3) % 4;
        startAngle = computeAngle(number, newQuadrant);
        endAngle = angle;
    }
}

//quadrant is the graph quadrant for the next circle except it is C indexed so it is 0 - 3 rather than 1 - 4
module bCurve(radius, number, generations, quadrant, pointsPerGeneration) {
   
    if (generations > -1) {
      angle1 = computeAngle(number, quadrant);
      angle2 = computeAngle(number, nextQuadrant(quadrant));
      
      span = angle1 - angle2;
      stepSize = span/pointsPerGeneration;
      newRadius = radius * sin(atan(2/(number-1))); 
       
      quadrantA = (quadrant + 3)%4;
      quadrantB = (quadrant + 2) %4; 
       
      points = [ for (i = [0:pointsPerGeneration]) [ bCurve2(newRadius, number, (generations -1), quadrantA, pointsPerGeneration, -1, angle1), xArc(radius, angle1, stepSize, i), yArc(radius, angle1, stepSize, i)], bCurve2(newRadius, number, (generations -1), quadrantB, pointsPerGeneration, 1, angle2) ];
        
    difference() {
      offset(0.01) polygon(points);
      polygon(points);
      }
      } 
}

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: OpenSCAD programming question, recursion, functions, and modules

DanS
on 2) the wiki book says that concat() does not change the levels, just tacks one on to another.  Because of recursion I will have a heterogeneous list which has points and also members that are lists of points...

like this (ignore the values they are nonsense)

[  [ (4,7), (8,2), (7,8) ] (1,2), (5,10), (3,8) [ (11,21), (15,10), (3,18) ] ]

and I want to flatten it into a one dimensional list (not a list of lists... which will have nested lists up to how many times I am recursing)

like this

[   (4,7), (8,2), (7,8)  (1,2), (5,10), (3,8)  (11,21), (15,10), (3,18)  ]

On Thu, Feb 8, 2018 at 5:17 PM, Frank van der Hulst <[hidden email]> wrote:
1) A module "returns" an object... whatever object  is generated by a module is its return "value". So you could return a polygon created from a list of points, for example, by having a polygon ([list]) statement at the end of the module. 

You can use the ternary operator to do if-then-else evaluations in a let() call in a function. And a function can call other functions. 

2) The concat () function flattens multiple lists into a single list.

On Friday, February 9, 2018, Dan Shriver <[hidden email]> wrote:
I'm going to attach some code (only look at it if you need it to get an idea of what I am trying to do, I am sure there are bugs plus I didn't copy any of the helper functions...).

I am having trouble figuring out how to deal with recursion on something I am drawing.  My problems are:

1) a Module can't return anything, correct?  At the same time I don't see how to (in a Function) do a lot of code setup, call helper functions, etc.  I guess I could setup a variable in a Function with "let" but how do I do a block of code with conditionals etc.  I would either like to return a list of points from a Module, or figure out how to do "real work" (helper calls, conditionals, etc) from a Function

2) If I have something that generates a list of points, assuming I recursively call that how do I elegantly keep the list one dimensional (not nested lists).  Do I just a call to some flatten function at the end?  Ideally I'd like not to generate nested lists to begin with, but I'm having a little trouble seeing how to do that?

Basically, what I am doing below is what I call a "bat curve" because I imagine it will look slightly like the stylized bats in traditional Chinese art (somewhat, not exactly, I can go into differences later, I don't need it to look exactly like that and am just using that as a convenient description).  The shape is going to be a fractal of arcs of increasingly smaller circles (generations determines how many levels deep the fractal goes).  Basically one large arc subdivides on both sides into a spiral of increasingly smaller radii arcs.  You could think of a letter D where the straight line is removed and at both ends of the D there is a spiral.



------------------------
module bCurve2(radius, number, generations, quadrant, pointsPerGeneration, direction, angle) {
    if (direction > 0) {
        newQuadrant = (quadrant + 1) % 4;
        startAngle = angle;
        endAngle = computeAngle(number, newQuadrant);
    }
    else {
        newQuadrant = (quadrant + 3) % 4;
        startAngle = computeAngle(number, newQuadrant);
        endAngle = angle;
    }
}

//quadrant is the graph quadrant for the next circle except it is C indexed so it is 0 - 3 rather than 1 - 4
module bCurve(radius, number, generations, quadrant, pointsPerGeneration) {
   
    if (generations > -1) {
      angle1 = computeAngle(number, quadrant);
      angle2 = computeAngle(number, nextQuadrant(quadrant));
      
      span = angle1 - angle2;
      stepSize = span/pointsPerGeneration;
      newRadius = radius * sin(atan(2/(number-1))); 
       
      quadrantA = (quadrant + 3)%4;
      quadrantB = (quadrant + 2) %4; 
       
      points = [ for (i = [0:pointsPerGeneration]) [ bCurve2(newRadius, number, (generations -1), quadrantA, pointsPerGeneration, -1, angle1), xArc(radius, angle1, stepSize, i), yArc(radius, angle1, stepSize, i)], bCurve2(newRadius, number, (generations -1), quadrantB, pointsPerGeneration, 1, angle2) ];
        
    difference() {
      offset(0.01) polygon(points);
      polygon(points);
      }
      } 
}

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
tp3
Reply | Threaded
Open this post in threaded view
|

Re: OpenSCAD programming question, recursion, functions, and modules

tp3
The simplest way to do single level flatten is shown in
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/List_Comprehensions#Flattening_a_nested_vector

ciao,
   Torsten.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
-- Torsten
Reply | Threaded
Open this post in threaded view
|

Re: OpenSCAD programming question, recursion, functions, and modules

Ronaldo
In reply to this post by DanS
If you want to collect all 2D points in a list, whatever depth it has in it, this may help you:

function get_2D_points(L,n=0,res=[]) =
  n>=len(L) ?
    res
  : L[n]*0==[0,0] ? 
      get_2D_points(L,n+1, concat(res,[L[n]]))
    : get_2D_points(L,n+1, get_2D_points(L[n],0,res));
  
l = [[1,2],[[3,4],[[5,6]]],[[[[7,8]],[9,10]]],[11,12]];
  
echo( get_2D_points(l));
// ECHO: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]

​However, this function crashes if the list is not a nested list of 2D points like  [[1,2], 3] or [1,2,3].or ["a"], etc.


2018-02-08 22:23 GMT-02:00 Dan Shriver <[hidden email]>:
on 2) the wiki book says that concat() does not change the levels, just tacks one on to another.  Because of recursion I will have a heterogeneous list which has points and also members that are lists of points...

like this (ignore the values they are nonsense)

[  [ (4,7), (8,2), (7,8) ] (1,2), (5,10), (3,8) [ (11,21), (15,10), (3,18) ] ]

and I want to flatten it into a one dimensional list (not a list of lists... which will have nested lists up to how many times I am recursing)

like this

[   (4,7), (8,2), (7,8)  (1,2), (5,10), (3,8)  (11,21), (15,10), (3,18)  ]



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: OpenSCAD programming question, recursion, functions, and modules

Ronaldo
If your lists contain either 2D points or simple lists of 2D points, a simpler non-recursive solution (by Parkinbot) is:

l = [[1,2],[[3,4]],[[5,6],[7,8]],[[9,10],[11,12],[13,14] ]];
  
function get_2D_points(L) = [for(a=L, v = (a[0][0]!=undef ? a: [a]) ) v ];
  
echo( get_2D_points(l));
// ECHO: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14]]

2018-02-08 23:40 GMT-02:00 Ronaldo Persiano <[hidden email]>:
If you want to collect all 2D points in a list, whatever depth it has in it, this may help you:

function get_2D_points(L,n=0,res=[]) =
  n>=len(L) ?
    res
  : L[n]*0==[0,0] ? 
      get_2D_points(L,n+1, concat(res,[L[n]]))
    : get_2D_points(L,n+1, get_2D_points(L[n],0,res));
  
l = [[1,2],[[3,4],[[5,6]]],[[[[7,8]],[9,10]]],[11,12]];
  
echo( get_2D_points(l));
// ECHO: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]

​However, this function crashes if the list is not a nested list of 2D points like  [[1,2], 3] or [1,2,3].or ["a"], etc.


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: OpenSCAD programming question, recursion, functions, and modules

nophead
One can also avoid extra nesting with the new keyword each. Here is an example (a shortened version of Ronaldo's Bezier subdivision avoiding concat)

function subdivBezier3(p, n=4) =
  n<=0 ?
    p
    :
    subdivBezier3( [p[0], for(i=[0:3:len(p)-4]) each _subdivB(p,i) ], n-1);

function _subdivB(p, from=0) =
      [ //[1,0,0,0], // to avoid repetitions
        [1/2, 1/2, 0,   0],
        [1/4, 1/2, 1/4, 0],
        [1/8, 3/8, 3/8, 1/8],
        [0,   1/4, 1/2, 1/4],
        [0,   0,   1/2, 1/2] ,
        [0,   0,   0,   1]  ]
      * [p[from], p[from+1], p[from+2], p[from+3] ];



On 9 February 2018 at 01:59, Ronaldo Persiano <[hidden email]> wrote:
If your lists contain either 2D points or simple lists of 2D points, a simpler non-recursive solution (by Parkinbot) is:

l = [[1,2],[[3,4]],[[5,6],[7,8]],[[9,10],[11,12],[13,14] ]];
  
function get_2D_points(L) = [for(a=L, v = (a[0][0]!=undef ? a: [a]) ) v ];
  
echo( get_2D_points(l));
// ECHO: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14]]

2018-02-08 23:40 GMT-02:00 Ronaldo Persiano <[hidden email]>:
If you want to collect all 2D points in a list, whatever depth it has in it, this may help you:

function get_2D_points(L,n=0,res=[]) =
  n>=len(L) ?
    res
  : L[n]*0==[0,0] ? 
      get_2D_points(L,n+1, concat(res,[L[n]]))
    : get_2D_points(L,n+1, get_2D_points(L[n],0,res));
  
l = [[1,2],[[3,4],[[5,6]]],[[[[7,8]],[9,10]]],[11,12]];
  
echo( get_2D_points(l));
// ECHO: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]

​However, this function crashes if the list is not a nested list of 2D points like  [[1,2], 3] or [1,2,3].or ["a"], etc.


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org