discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Piping and easily readable (and maintainable) module code in OpenSCAD

B
benjaminwand
Wed, Sep 26, 2018 10:45 AM

Hi,

this is a question about coding style. (I’m new to functional programming.)

I prefer to write in small enough pieces/functionality that I can already
understand it before the first coffee in the morning. In OpenSCAD I use this
pattern:

  • variables
  • stuff that gets calculated from them (so I don’t need to repeat same
    things in the code many times)
  • functions and modules (that can be located in other files as well,
    depending on how much maintenance and space they take)
  • the ‘logic’ of the actual item that gets made, where I try to have not
    more than three indentations.

For individual objects that works well but now I’m trying to make more
complex modules and there seems to be a piping problem, informations don’t
always go through all the elements that a module-to-be is made of.
What are the things I need to know about OpenSCAD if I don’t want to write
in two-pages-functions-of-horror but in smaller, easier to maintain parts,
especially when writing modules? Is there a guide or discussion about coding
style in OpenSCAD anywhere?

As an example, these two files makes the same thing. As small-parts-code:
https://github.com/benjaminwand/OpenSCAD-loft-module/blob/master/two%20layers/two%20layers%20example.scad

and as working module:
https://github.com/benjaminwand/OpenSCAD-loft-module/blob/master/two%20layers/two%20layers%20module.scad

Thanks for the help!
Yes, if the answer is “read xyz book”, I would consider that helpful.

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

Hi, this is a question about coding style. (I’m new to functional programming.) I prefer to write in small enough pieces/functionality that I can already understand it before the first coffee in the morning. In OpenSCAD I use this pattern: * variables * stuff that gets calculated from them (so I don’t need to repeat same things in the code many times) * functions and modules (that can be located in other files as well, depending on how much maintenance and space they take) * the ‘logic’ of the actual item that gets made, where I try to have not more than three indentations. For individual objects that works well but now I’m trying to make more complex modules and there seems to be a piping problem, informations don’t always go through all the elements that a module-to-be is made of. What are the things I need to know about OpenSCAD if I don’t want to write in two-pages-functions-of-horror but in smaller, easier to maintain parts, especially when writing modules? Is there a guide or discussion about coding style in OpenSCAD anywhere? As an example, these two files makes the same thing. As small-parts-code: https://github.com/benjaminwand/OpenSCAD-loft-module/blob/master/two%20layers/two%20layers%20example.scad and as working module: https://github.com/benjaminwand/OpenSCAD-loft-module/blob/master/two%20layers/two%20layers%20module.scad Thanks for the help! Yes, if the answer is “read xyz book”, I would consider that helpful. -- Sent from: http://forum.openscad.org/
R
Ronaldo
Thu, Sep 27, 2018 10:34 PM

About style:

I don't know any guide to style for OpenSCAD coding. Some people prefer to
define functions and module bottom up following the order that is common in
C programming: define objects above its usage points. Some other prefer the
reverse top down style: the main module is defined first. I prefer the
bottom up style. Anyway I use smaller identifiers than you used in your
codes. I prefer to precede each function or module with a short comment
describing succinctly what it does (not how it does) and have a more
condensed code where they are called. This is a matter of taste but to me
lengthy identifiers usually overcrowd the text and spoil the readability.

About your code:

I don't define as many global variables as you do. I prefer to define
functions to compute intermediate results and to include all values needed
for a function computation as function arguments. For instance, in one of
your codes, the the middle point computation is partially done by a function
and a variable initialization. However, it is simple enough to be computed
just in one function as I show bellow. In some points of your code, you use
the function concat to build a vector like concat(1,2,3) instead of [1,2,3]
which is simpler and clearer. In fact, I consider idiosyncratic this
functionality of concat which should be restricted to concatenate lists.

Find bellow my version of your loft. That code may be used as a library to
be included in other codes. The global variables and the intersection at the
end serves only as a example of usage and would be ignored by the clause
include. Finally, you may find useful to study the loft of the library
skin.scad found in

https://github.com/openscad/list-comprehension-demos

// compute intermediate points between the vertices of a upper polygon
// and a lower polygon both with same number of vertices
function middle_points(upper, lower) =
let( l=len(upper) )
l!= len(lower) ?
[]:
[ for(i=[0:l-1])
(upper[i] + lower[i] + upper[(i+1)%l] + lower[(i+1)%l])/4
];

// a loft of two planar polygons with the same number of vertices
// the function result is a pair [list of points, list of faces]
// appropriate to build a polyhedron
function simple_loft(poly1, poly2, rev=false) =
let(l = len(poly1))
l!=len(poly2) ?
[] :
[ // points
concat(poly1, poly2),
// faces
if(rev) // revert?
[ [for(i=[0:l-1]) i], // poly1 facet
for(i=[0:l]) [(i+1)%l + l, (i+1)%l, i+l], // triangular faces
for(i=[0:l]) [i,  i+l,  (i+1)%l],
[for(i=[l-1:-1:0]) i+l]] // poly2 facet
else
[ [for(i=[l-1:-1:0]) i], // poly1 facet
for(i=[0:l-1]) [(i+1)%l, (i+1)%l + l,  i+l], // triangular faces
for(i=[0:l-1]) [i,  (i+1)%l,  i+l],
[for(i=[0:l-1]) i+l] ] // poly2 facet
];

// build a loft from the planar polygon upper to the planar polygon lower
(both
// with same number of vertices) by creating an intermediate set of vertices
// in between
module loft(upper, lower)
if (len(upper)==len(lower))
{
s1 = simple_loft(upper, middle_points(upper, lower),true ); // upper
volume
s2 = simple_loft(lower, middle_points(upper, lower),false); // lower
volume
f1 = [for(i=[0:len(s1[1])-2]) s1[1][i] ]; // remove from s1 and s2 the
faces
f2 = [for(i=[0:len(s2[1])-2]) s2[1][i] ]; // of middle points
polyhedron( points = concat(s1[0], s2[0]),
faces  = concat( f1, // upper faces
[for(f=f2)[for(v=f)
v+len(s1[0])]] ) // lower faces
);
}

my_upper_points = [ [0,20,20], [5,12.5,15], [10,5,10], [15,-2.5,5],
[20,-10,0], [10,-10,5], [0,-10,10], [-10,-10,15],
[-20,-10,20], [-15,-2.5,20], [-10,5,20], [-5,12.5,20] ];
my_lower_points = [ [0,10,-20], [12,30,-20], [8,5,-20], [35,0,-20],
[8,-5,-20], [12,-30,-20], [0,-10,-20], [-12,-30,-20],
[-8,-5,-20], [-35,0,-20], [-8,5,-20], [-12,30,-20] ];

intersection(){
//render()
loft(my_upper_points, my_lower_points);
//translate([0,0,-500]) cube(1000); // uncomment to check CGAL failure on
F6
}

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

About style: I don't know any guide to style for OpenSCAD coding. Some people prefer to define functions and module bottom up following the order that is common in C programming: define objects above its usage points. Some other prefer the reverse top down style: the main module is defined first. I prefer the bottom up style. Anyway I use smaller identifiers than you used in your codes. I prefer to precede each function or module with a short comment describing succinctly what it does (not how it does) and have a more condensed code where they are called. This is a matter of taste but to me lengthy identifiers usually overcrowd the text and spoil the readability. About your code: I don't define as many global variables as you do. I prefer to define functions to compute intermediate results and to include all values needed for a function computation as function arguments. For instance, in one of your codes, the the middle point computation is partially done by a function and a variable initialization. However, it is simple enough to be computed just in one function as I show bellow. In some points of your code, you use the function concat to build a vector like concat(1,2,3) instead of [1,2,3] which is simpler and clearer. In fact, I consider idiosyncratic this functionality of concat which should be restricted to concatenate lists. Find bellow my version of your loft. That code may be used as a library to be included in other codes. The global variables and the intersection at the end serves only as a example of usage and would be ignored by the clause include. Finally, you may find useful to study the loft of the library skin.scad found in https://github.com/openscad/list-comprehension-demos // compute intermediate points between the vertices of a upper polygon // and a lower polygon both with same number of vertices function middle_points(upper, lower) = let( l=len(upper) ) l!= len(lower) ? []: [ for(i=[0:l-1]) (upper[i] + lower[i] + upper[(i+1)%l] + lower[(i+1)%l])/4 ]; // a loft of two planar polygons with the same number of vertices // the function result is a pair [list of points, list of faces] // appropriate to build a polyhedron function simple_loft(poly1, poly2, rev=false) = let(l = len(poly1)) l!=len(poly2) ? [] : [ // points concat(poly1, poly2), // faces if(rev) // revert? [ [for(i=[0:l-1]) i], // poly1 facet for(i=[0:l]) [(i+1)%l + l, (i+1)%l, i+l], // triangular faces for(i=[0:l]) [i, i+l, (i+1)%l], [for(i=[l-1:-1:0]) i+l]] // poly2 facet else [ [for(i=[l-1:-1:0]) i], // poly1 facet for(i=[0:l-1]) [(i+1)%l, (i+1)%l + l, i+l], // triangular faces for(i=[0:l-1]) [i, (i+1)%l, i+l], [for(i=[0:l-1]) i+l] ] // poly2 facet ]; // build a loft from the planar polygon upper to the planar polygon lower (both // with same number of vertices) by creating an intermediate set of vertices // in between module loft(upper, lower) if (len(upper)==len(lower)) { s1 = simple_loft(upper, middle_points(upper, lower),true ); // upper volume s2 = simple_loft(lower, middle_points(upper, lower),false); // lower volume f1 = [for(i=[0:len(s1[1])-2]) s1[1][i] ]; // remove from s1 and s2 the faces f2 = [for(i=[0:len(s2[1])-2]) s2[1][i] ]; // of middle points polyhedron( points = concat(s1[0], s2[0]), faces = concat( f1, // upper faces [for(f=f2)[for(v=f) v+len(s1[0])]] ) // lower faces ); } my_upper_points = [ [0,20,20], [5,12.5,15], [10,5,10], [15,-2.5,5], [20,-10,0], [10,-10,5], [0,-10,10], [-10,-10,15], [-20,-10,20], [-15,-2.5,20], [-10,5,20], [-5,12.5,20] ]; my_lower_points = [ [0,10,-20], [12,30,-20], [8,5,-20], [35,0,-20], [8,-5,-20], [12,-30,-20], [0,-10,-20], [-12,-30,-20], [-8,-5,-20], [-35,0,-20], [-8,5,-20], [-12,30,-20] ]; intersection(){ //render() loft(my_upper_points, my_lower_points); //translate([0,0,-500]) cube(1000); // uncomment to check CGAL failure on F6 } -- Sent from: http://forum.openscad.org/
B
benjaminwand
Mon, Oct 8, 2018 7:39 AM

Hi, thanks for the answer! I still don’t understand 100% of it but it helps.

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

Hi, thanks for the answer! I still don’t understand 100% of it but it helps. -- Sent from: http://forum.openscad.org/