openscad

Is there a way to set two outer variable value in a test in openSCAD?


As far as I understand, openSCAD trying to be a functional language makes it unable to set an outside variable value from inside a context. eg :

(p, q) = true ? (1, 2) : (3, 4);
if (faces==4) {
  p=3; q=3;
} else if (faces==6) {
  p=4; q=3;
} else if (faces==8) {
  p=3; q=4;
} else if (faces==12) {
  p=5; q=3;
} else if (faces==20) {
  p=3; q=5;
} else {
  message = str("platon solid cannot have ", faces, " faces (only 4, 6, 8, 12 and 20).");
  assert (false, message);
}
dihedral = 2*asin(cos(180/p)/sin(180/p)); // Won't work

In a language like python, the simple solution to such a problem would be to return a tuple. e.g.:

def platon(faces):
    if faces==4: return 3, 3
    if faces==6: return 4, 3
    if faces==8: return 3, 4
    if faces==12: return 5, 3
    if faces==20: return 3, 5
    raise ValueError(f"platon solid cannot have {faces} faces (only 4, 6, 8, 12 and 20)."

p, q = platon(N)
dihedral = 2*asin(cos(180/p)/sin(180/p))

But it looks like openSCAD is not able to return more that one variable at a time... Is there a way (other than a workaround) to solve such a problem ?


Solution

  • Indeed:

    OpenSCAD is a Functional programming language, as such variables are bound to expressions and keep a single value during their entire lifetime due to the requirements of referential transparency. In imperative languages, such as C, the same behavior is seen as constants, which are typically contrasted with normal variables.

    Also note that:

    OpenSCAD variables are created by a statement with a name or identifier, assignment via an expression and a semicolon. The role of arrays, found in many imperative languages, is handled in OpenSCAD via vectors.

    Furthermore, conditional statements can be used to solve this specific problem, you need to make sure that there is a single assignment of the variables.

    faces = 8;
    vector = faces == 4 ? [3,3] : faces == 6 ? [4,3] : faces == 8 ? [3,4] : faces == 12 ? [5,3] : faces == 20 ? [3,5] : [0,0];
    echo("Vector is ", vector);
    if (vector[0] == 0 && vector[1] == 0) {
        message = str("platon solid cannot have ", faces, " faces (only 4, 6, 8, 12 and 20).");
        assert (false, message);
        } else {
        // do note that this is a different equation than in the example 
        // to demonstrate usage of both vector elements!!!
        dihedral = 2*asin(cos(180/vector[0])/sin(180/vector[1])); // Will work
        echo("Dihedral is ", dihedral);
        }
    

    The above results in:

    Parsing design (AST generation)...
    Saved backup file: C:/Users/User/Documents/OpenSCAD/backups/unsaved-backup-VmsPOXsj.scad
    Compiling design (CSG Tree generation)...
    ECHO: "Vector is ", [3, 4]
    ECHO: "Dihedral is ", 90
    Compiling design (CSG Products generation)...
    Geometries in cache: 0
    Geometry cache size in bytes: 0
    CGAL Polyhedrons in cache: 0
    CGAL cache size in bytes: 0
    Compiling design (CSG Products normalization)...
    Normalized tree has 1 elements!
    Compile and preview finished.
    Total rendering time: 0:00:00.023
    

    When faces = 5 the script will throw the assertion:

     
    Parsing design (AST generation)...
    Saved backup file: C:/Users/User/Documents/OpenSCAD/backups/unsaved-backup-VmsPOXsj.scad
    Compiling design (CSG Tree generation)...
    ECHO: "Vector is ", [0, 0]
    ERROR: Assertion 'false' failed: "platon solid cannot have 5 faces (only 4, 6, 8, 12 and 20)." in file openscad-2021.01, line 7 
    TRACE: called by 'assert' in file openscad-2021.01, line 7 
    TRACE: called by 'if' in file openscad-2021.01, line 5 
    Compiling design (CSG Products generation)...
    Geometries in cache: 0
    Geometry cache size in bytes: 0
    CGAL Polyhedrons in cache: 0
    CGAL cache size in bytes: 0
    Compiling design (CSG Products normalization)...
    Normalized tree has 1 elements!
    Compile and preview finished.
    Total rendering time: 0:00:00.051
    ```