2. Computer aided design (CAD)#

Designing “support” module with OpenSCAD#

First, I designed the code of a “support” : a simple element that can attach to lego :

module support (r_hole, w_support, h_support, n_holes, ir_holes) {
    // this module is making a bar support for lego, with the parameters : 
    //r_hole = radius of the holes
    // w_support = with of the support
    // h_support = height of the support
    // n_holes = number of holes
    // ir_holes = inter-radius between holes
    difference(){
       hull(){
        for(i=[0:n_holes-1])  {
            translate([0, ir_holes*i, 0]) cylinder(h = h_support, r = w_support/2, center = true);   
            }
        }
        for(i=[0:n_holes-1])  {
            translate([0, ir_holes*i, 0]) cylinder(h=h_support+1, r=r_hole, center=true);   
            }
    }
};

So when the module is loaded I can generate different kind of support parametrically, for example : support (4, 15, 5, 5, 12); will generate : support1

support (4, 10, 5, 15, 20); will generate : support2

Spiral spring#

I watched videos about compliant a mechanism and I found a nice idea : a kind of spring made of three spirals.

The stl is avaible on printables and it look like this : model_printable

I found it was a good challenge to code it with OpenSCAD and make it parametric (size, height, space between spirals).

Draw a (Archemdian) spiral#

First I needed to know how to code a spiral in OpenSCAD :
I found a tutorial on the website openhome.cc to draw a spiral.

© Since 2003 Justin Lin, All Rights Reserved

It used two modules : line and polyline, that can draw lines between a set of points :

module line(point1, point2, width = 1, cap_round = true) {
    // draw a line between to points with a specified width
    // if cap_round = true, the angle will be smoothed with a circle
    angle = 90 - atan((point2[1] - point1[1]) / (point2[0] - point1[0]));
    offset_x = 0.5 * width * cos(angle);
    offset_y = 0.5 * width * sin(angle);

    offset1 = [-offset_x, offset_y];
    offset2 = [offset_x, -offset_y];

    if(cap_round) {
        translate(point1) circle(d = width, $fn = 24);
        translate(point2) circle(d = width, $fn = 24);
    }

    polygon(points=[
        point1 + offset1, point2 + offset1,  
        point2 + offset2, point1 + offset2
    ]);
}

module polyline(points, width = 1) {
    // recursive function : draw lines between a set of points
    module polyline_inner(points, index) {
       n_width = width;
        if(index < len(points)) {
            n_width = width;
                line(points[index - 1], points[index], n_width);
            polyline_inner(points, index + 1);           
        }    
    }
    polyline_inner(points, 1);
}

Further explainations of this modules here

And then a code that generate points in an Archemedian spiral with the formula r = a + bθ : (arm_len beign the “space” between to turn of spiral)

PI = 3.14159;
step = 0.25;
circles = 5;
arm_len = 10;

b = arm_len / 2 / PI;
// one radian is almost 57.2958 degrees
points = [for(theta = [0:step:2 * PI * circles])
    [b * theta * cos(theta * 57.2958), b * theta * sin(theta * 57.2958)]
];

polyline(points, 1);

There was an issued with the code outlined by the author : as the dots are far from the center they are more and more spaced.
This conduct to a resolution lower at the edges of the spiral then at the center, and we dont want that.
The author of the website proposed a solution but I found it not nice.
I found a simpler way to resolve this problem : incremanting the steps of the square of the theta angle (instead of the theta angle) :

angle_max = 2 * PI * circles;
angle_max_square = angle_max*angle_max;
points = [for(theta_carre = [0:step:angle_max_square])
    [b * sqrt(theta_carre) * cos(sqrt(theta_carre) * 57.2958), b * sqrt(theta_carre) * sin(sqrt(theta_carre) * 57.2958)]

The points are not perfectly equally distributed but it is working well enough for what i need.

Points distribution with incremanting theta: spiral-theta

Points distribution with incremanting theta square: spiral-theta

Adaptating the code#

From this code I added several things :

  • I draw the spiral three time with rotations to have the three spirals
  • I added a support in the center and on the edges
  • I added an outer ring (activable with a boolean)
  • I made it paramtrable with different variables in the heading of the code :
    • To define the diameter of the spirals and the inter-spiral distance
    • To define the size and shape of the supports , and if there is simple or double support on the edges
  • To have supports that fits well with legos I followed the recommandations of Victor from a previous year.

Making the connexions between the spiral and the supports stronger#

I found that the connexion between the ruban of the spiral and support (in the center and on the edges) looked really weak. So I decided to try to reinforce it.

To do so I modified the code of the polyline module so it makes incrementing (or decrementing) thinckness of the ruban on the center (or on the edges) - cf. the code under.
I added two booleans in the begining of the code to activate/desactivate this progressive thickening.

Whithout thickening With thickening
obj-no-thick obj-thick

.

Code of the v1#

The .scad file is avaible here : FBZ-SPIRAL_SPRING-v1.scad .

The complete code is as follows :

// Flexi-spiral v1 © 2024 by Simon Hanin is licensed under CC BY-SA 4.0. To view a copy of this license,
// visit https://creativecommons.org/licenses/by-sa/4.0/


$fn=100; //global resolution

// Variable to modify the parameters of the object

    // Variables defining size and shape of the spiral
    diam = 30; // diameter of the spiral, in centimeter
    inter_spir = 10; // distance between two spires of one spiral
    rb_width = 0.6; //ruban width
    rb_height = 20; //ruban height
    res = 100;   // resolution of the spiral in a range to 100 very good resolution

    // Variables defining size and shape of the supports
    double_supp = false;  // boolean, if true : support with two holes
    supp_height=20; // height of the supports
    r_hole=2.55; // radius of holes for lego insertion
    inter_r=8; // inter radius for lego
    r_supp_end=3.6; // radius of the support at the ends of the spiral
    r_supp_center=5; // radius of the support ain the center of the spiral
    n_width = 0;

    // Variables defining the length of the transition region (thickening in the center and in the edges)
    thick_c = false; // if true : center of the spiral will be thicker
    thick_e = false;  // if true : edges of the spiral will be thicker
    l_trans = 1.5; // length of trans region (aribtrary units, not mm)
    k_trans = 1/5; //trans factor

    // General variable
    bool_contour = true;
    bool_center = false; // to determine if object are centred around the axes or not
    s_trim = 1.1; // adjust to put edges supports "a fleur"  du ruban


// Variables used for calculation
circles = floor(diam/inter_spir); // number of circles made by spiral ( floor is used to have an integer)
angle_max = 2*PI*circles; // angle_max in radian
angle_max_square = angle_max*angle_max; // the same squared
b = inter_spir / 2 / PI; // variable used for tracing the spiral (distance between to rounds of one spiral)
step = 100/res; // steps for spiral
PI = 3.14159; 
n_trans = l_trans*res/4; // number of segment of spiral in trans


module line(point1, point2, width = 1, cap_round = true) {
    // trace line of a defined width between two points
    // if cap_round = true, the angle will be smoothed with a circle
    angle = 90 - atan((point2[1] - point1[1]) / (point2[0] - point1[0]));
    offset_x = 0.5 * width * cos(angle);
    offset_y = 0.5 * width * sin(angle);

    offset1 = [-offset_x, offset_y];
    offset2 = [offset_x, -offset_y];

    if(cap_round) {
        translate(point1) circle(d = width, $fn = 24);
        translate(point2) circle(d = width, $fn = 24);
    }

    polygon(points=[
        point1 + offset1, point2 + offset1,  
        point2 + offset2, point1 + offset2
    ]);
}

module polyline(points, width = 1) {
    // trace a serie of lines of a defined width between a serie of points 
    module polyline_inner(points, index) {
       n_width = width;
        if(index < len(points)) {           
            if(index < n_trans && thick_c) {  // thicker zone of the spiral on the center
                n_width=width+(n_trans-index)*k_trans;
                line(points[index - 1], points[index], n_width);             
            }
            if(index > len(points)-n_trans && thick_e ) { // thicker zone of the spiral on the edges
                n_width=width+(n_trans-len(points)+index)*k_trans;

                line(points[index - 1], points[index], n_width);       
            }       
            else {
                n_width = width;
                line(points[index - 1], points[index], n_width);
            }            
            polyline_inner(points, index + 1);
        }
    }
    polyline_inner(points, 1);
}

points = [for(theta_carre = [0:step:angle_max_square])
    // generating a serie of point in the shape of the spiral
    [b * sqrt(theta_carre) * cos(sqrt(theta_carre) * 57.2958), b * sqrt(theta_carre) * sin(sqrt(theta_carre) * 57.2958)]

];

difference()
    // difference between the first "union" that is all the solid parts and the second "union" that is the holes in the supports
{
    union(){   // union of the spirals and the supports
         linear_extrude (rb_height, center=bool_center) {   // extrudaction of the the three spirals      
            polyline(points, rb_width); // tracing of the first spiral
            rotate([0,0,120]) polyline(points, rb_width); // second spiral rotated
            rotate([0,0,-120]) polyline(points, rb_width); // third spiral rotated                
         }

         if(bool_contour){ // trace an outside ring 
             difference() {
                cylinder(h = supp_height, r = diam, center = bool_center);
                cylinder(h = supp_height*4, r = diam-(1.2/20)*diam, center = true);

                }
            }
       cylinder(h = supp_height, r = r_supp_center, center = bool_center); // center support      

        hull(){          // first edge support (can be simple or double)
            translate([b*angle_max+r_supp_end-s_trim,0,0])
                cylinder(h = supp_height, r = r_supp_end, center = bool_center);
                 if(double_supp){
                 translate([b*angle_max+r_supp_end-s_trim,inter_r,0])
                    cylinder(h = supp_height, r = r_supp_end, center = bool_center);
                 }
             }

         rotate([0,0,120]) { // second edge support (can be simple or double), rotated         
            hull(){
                translate([b*angle_max+r_supp_end-s_trim,0,0])
                    cylinder(h = supp_height, r = r_supp_end, center = bool_center);
                 if(double_supp){
                         translate([b*angle_max+r_supp_end-s_trim,inter_r,0])
                    cylinder(h = supp_height, r = r_supp_end, center = bool_center);
                 }
            }
         }
         rotate([0,0,-120]) { // third edge support (can be simple or double), rotated      
            hull(){
                translate([b*angle_max+r_supp_end-s_trim,0,0])
                    cylinder(h = supp_height, r = r_supp_end, center = bool_center);
                  if(double_supp){
                     translate([b*angle_max+r_supp_end-s_trim,inter_r,0])
                        cylinder(h = supp_height, r = r_supp_end, center = bool_center);
                     }
             }
         };        
     }



     union() {  // union of the holes
        cylinder(h = rb_height*4, r = r_hole, center = true); // hole of the center

        translate([b*angle_max+r_supp_end-s_trim,0,0]) // first edge hole
            cylinder(h = supp_height*4, r = r_hole, center = true);
            if(double_supp){
               translate([b*angle_max+r_supp_end-s_trim,inter_r,0])
                cylinder(h = supp_height*4, r = r_hole, center = true);
            }
        rotate([0,0,120]) { // second edge hole
            translate([b*angle_max+r_supp_end-s_trim,0,0])
                cylinder(h = supp_height*4, r = r_hole, center = true);
             if(double_supp){
                 translate([b*angle_max+r_supp_end-s_trim,inter_r,0])
                cylinder(h = supp_height*4, r = r_hole, center = true);
             }
         }
         rotate([0,0,-120]) { // third edge hole
            translate([b*angle_max+r_supp_end-s_trim,0,0])
                cylinder(h = supp_height*4, r = r_hole, center = true);
             if(double_supp){
             translate([b*angle_max+r_supp_end-s_trim,inter_r,0])
            cylinder(h = supp_height*4, r = r_hole, center = true);
             }
        }         
     }
    }

Exemples of models that can be generate from the code#

By changing the parameters of the code you can generate this variety of models :

obj-ls
 obj-div obj-div obj-div obj-div

Notes about the SCAD code#

  • There is still some stuff to improve this code :
  • Make it shorter by making a module for support

Useful openscad tips#

To debug your code it is very usefull to use the command echo("some text"); or echo(some_variable); to print values in console.

Licensing#

Licensing my work#

I choosed a creative commons license. I choose those two parameters : - BY: Credit must be given to you, the creator. - SA: Adaptations must be shared under the same terms.

There is a simple tool to generate the license on the website, so I get :

// Flexi-spiral © 2024 by Simon Hanin is licensed under CC BY-SA 4.0. To view a copy of this license,
// visit https://creativecommons.org/licenses/by-sa/4.0/

What about the tutorial I followed?#

As explained in the beginging, part of my code (line and polyline modules) is from the website “OpenHome.cc”. There is no licensing informations on the pages explaining the code. But on the main page you can find : * © Since 2003 Justin Lin, All Rights Reserved * So I don’t know how illegal I am by using this code.

To eventualy add if i have time : I coded another way to trace the spiral but it is less elegant :#