Tag Archives: Parametric CAD

3D Modeling: Elegant Pieces in OpenSCAD with rcube(), rcylinder() and chainhull()

Updates:

  • 2020/12/31: rcube() extended, RCUBE_FLAT{BOTTOM, TOP, FRONT, BACK, LEFT, RIGHT} support added, rcylinder() with RCYLINDER_FLAT{TOP, BOTTOM}
  • 2020/12/30: rcube() source code extended, support RCUBE_FLATX, RCUBE_FLATY, RCUBE_FLATZ
  • 2020/12/28: inital post

While working on Ashtar D (Classic XY) I looked at some pieces I rushed to design with cube() and hull() and they didn’t appeal to me – yes, it kind of hurt my eyes.

A while back I coded a simple rcube([x,y,z],r) which takes r as a radius for the edges, internally it’s an OpenSCAD module which uses 8 spheres and hulls them together, providing round edges; but I hesitated to actually use it in my designs – until now. Further I thought, let’s do the same with cylinder() using rcylinder(d=10,h=5,r=1) providing round edges by using two torii and hull them together.

These two new functions, rcube([x,y,z],r) and rcylinder(h,d,r) allow to create more organic and elegant pieces, see for yourself:

From Bulky To Elegance

The position of the Y pulley mount is given, a bit of an X- & Y-offset to ensure printable area is not sacrificed for the Y carriage:

Using Chained Hulls

And another example . . . replacing hull() with chainhull():

The final version is composed by only 3 pieces chain hulled together:

difference() {
   chainhull() {
      rcylinder(...);
      translate([0,0,-20]) rcube(...);
      translate([...,-60]) rcube([5,20,50],2); // 2020 mount plate
   }
   rcube(...);     // pulley cutout
}

rcube() & rcylinder()

rcube();
translate([5,0,0]) rcube(0.75);
translate([10,0,0]) rcube([2,1,1],0.2);

translate([0,2,0]) rcube([2,1,1],0.2,false);
translate([5,2,0]) rcube([2,1,1],0.2,true);

translate([0,4,0]) rcube([2,1,1],0.2,RCUBE_FLATX);
translate([5,4,0]) rcube([2,1,1],0.2,RCUBE_FLATY);
translate([10,4,0]) rcube([2,1,1],0.2,RCUBE_FLATZ);

translate([0,6,0]) rcube([2,1,1],0.2,RCUBE_FLATBOTTOM);
translate([5,6,0]) rcube([2,1,1],0.2,RCUBE_FLATTOP);

translate([0,8,0]) rcube([2,1,1],0.2,RCUBE_FLATFRONT);
translate([5,8,0]) rcube([2,1,1],0.2,RCUBE_FLATBACK);

translate([0,10,0]) rcube([2,1,1],0.2,RCUBE_FLATLEFT);
translate([5,10,0]) rcube([2,1,1],0.2,RCUBE_FLATRIGHT);

translate([0+1,14,0]) rcylinder(3,1.5,0.2);
translate([3+1,14,0]) rcylinder(3,1.5,0.2,false);
translate([6+1,14,0]) rcylinder(3,1.5,0.2,RCYLINDER_FLATBOTTOM);
translate([9+1,14,0]) rcylinder(3,1.5,0.2,RCYLINDER_FLATTOP);

The library code (I might later release it as a separate library):

// Title: rcube(), rcylinder() & torus()
// Author: Rene K. Mueller
// License: MIT License 2020
// Version: 0.0.2

RCUBE_FLATX = [false,true,true];
RCUBE_FLATY = [true,false,true];
RCUBE_FLATZ = [true,true,false];
RCUBE_FLATBOTTOM = [false,false,false,false,true,true,true,true];
RCUBE_FLATTOP = [true,true,true,true,false,false,false,false];
RCUBE_FLATFRONT = [false,false,true,true,false,false,true,true];
RCUBE_FLATBACK = [true,true,false,false,true,true,false,false];
RCUBE_FLATLEFT = [false,true,true,false,false,true,true,false];
RCUBE_FLATRIGHT = [true,false,false,true,true,false,false,true];

module rcube(a=1,r=0.1,rd=[true,true,true],center=false,$fn=32) {
    if(FAST_RCUBE)
       cube(a);
    else {
       x = len(a) ? a[0] : a;
       y = len(a) ? a[1] : a;
       z = len(a) ? a[2] : a;
       rd = len(rd) ? rd : [rd,rd,rd];

          if((len(rd)==3 && rd[0] && rd[1] && rd[2]) || (len(a)==0 && rd)) // rd=[true,true,true] or true
             hull() {
                translate([r,r,r]) sphere(r);
                translate([x-r,r,r]) sphere(r);
                translate([x-r,y-r,r]) sphere(r);
                translate([r,y-r,r]) sphere(r);
                translate([r,r,z-r]) sphere(r);
                translate([x-r,r,z-r]) sphere(r);
                translate([x-r,y-r,z-r]) sphere(r);
                translate([r,y-r,z-r]) sphere(r);
             } 
          else                                                        // anything else
             hull() {
                translate([r,r,r]) rcube_prim(r,rd,0);
                translate([x-r,r,r]) rcube_prim(r,rd,1);
                translate([x-r,y-r,r]) rcube_prim(r,rd,2);
                translate([r,y-r,r]) rcube_prim(r,rd,3);
                translate([r,r,z-r]) rcube_prim(r,rd,4);
                translate([x-r,r,z-r]) rcube_prim(r,rd,5);
                translate([x-r,y-r,z-r]) rcube_prim(r,rd,6);
                translate([r,y-r,z-r]) rcube_prim(r,rd,7);
             }
    }
 } 

module rcube_prim(r,rd,i) {
    a = len(rd);
    if(a<=3) {
       if(a && rd[0] && rd[1] && rd[2]) 
          sphere(r);
       else if(a && rd[0] && rd[1])
          translate([0,0,-r]) cylinder(r=r,h=r*2);
       else if(a && rd[1] && rd[2])
          translate([-r,0,0]) rotate([0,90,0]) cylinder(r=r,h=r*2);
       else if(a && rd[0] && rd[2])
          translate([0,-r,0]) rotate([-90,0,0]) cylinder(r=r,h=r*2);
       else
          translate([-r,-r,-r]) cube(r*2);
    } else 
       if(rd[i]) 
          sphere(r);
       else 
          translate([-r,-r,-r]) cube(r*2);
 }

RCYLINDER_FLATBOTTOM = [false,true];
RCYLINDER_FLATTOP = [true,false];

module rcylinder(h=2,d=1,r=0.1,rd=[true,true],$fn=40) {
    if(FAST_RCYLINDER)
       cylinder(d=d,h=h);
    else
       hull() { 
          translate([0,0,r]) 
             if(len(rd) && rd[0]) torus(do=d,di=r*2); else translate([0,0,-r]) cylinder(d=d,h=r);          
          translate([0,0,h-r]) 
             if(len(rd) && rd[1]) torus(do=d,di=r*2); else cylinder(d=d,h=r);
       }
 }

 module torus(do=2,di=0.1,a=360) {
    rotate_extrude(convexity=10,angle=a) {
       translate([do/2-di/2,0,0]) circle(d=di,$fn=20);
    }
 }

chainhull()

module chainhull() {
    for(i=[0:1:$children-2])
       hull() {
          children(i);
          children(i+1);
       }
 }

There is one drawback using chainhull() { } as you can’t use conditional if else with { } within as it combines them as a group and becomes a child structure and so it will act as hull(), so you only can list non-conditional pieces within chainhull() as of OpenSCAD 2019.05, perhaps at a later time this limit vanishes.

That’s it.

Parametric Part Cooler

Status: fully tested, but not yet released

Updates:

  • 2020/12/27: individual renderings for each application
  • 2020/12/21: improve documentation, with application variables
  • 2019/06/16: design solidified, multiple variants tested (Triple Micro Swiss, Dual Micro Swiss, Chimera, Cyclops NF, Dual V6, Single V6)

Introduction

Back in May 2019 I started to customize dedicated printheads, e.g. combining CR10 hotends / Micro Swiss Hotends in dual and triple mode – and thereby required a dedicated Part Cooler. This lead me to develop my own Parametric Part Cooler in OpenSCAD, adapting the design of Radial Fan Fang by Lion4H as I used that one successful for E3D V6 – now a general approach coded entirely in OpenSCAD:

I started with the central heatsink fan in the geometric center, and route the pipes (symmetrically) around it, back to the nozzle; on top using 5015mm fan blower – after a couple of hours the basic form was defined.

As long I am in edit or tune mode, the part cooler is rendered with a few corners – yet, when exporting STL format, the pipe is calculated with refined spline and smooth surface:

Screenshot from 2019-06-17 07-21-14
Parametric Part Cooler for Triple Micro Swiss Hotends
Screenshot from 2019-06-17 07-22-05
Parametric Part Cooler for Triple Micro Swiss Hotends

Variables

part_cooler() takes following variables with their defaults:

  • m=40: size of heatsink fan
  • t=2: thickness of fan mount
  • zoff=17: z-offset of air outputs
  • yoff=8: y-offset of air outputs
  • ws=12: extra width space
  • wx=35: cutout width X at the bottom
  • sq2=0.6: relative squeeze Y-wise at air outputs
  • sq3=0.6: relative squeeze Z-wise at air outputs
  • zb=0.5: relative Z bend
  • smooth=false: switch of smooth pipe rendering (false: fast rendering / editing mode, true: export to STL)
  • name="noname": label on both sides
  • tscale=1: text/label x/y scale

Needless to say, to set or alter those variables you require the fan and the hotend as a model so you can model the part_cooler() around it.

Applications

After a couple of weeks the part_cooler() was designed for various hotends:

Parametric Part Cooler: Triple Micro Swiss, Chimera, Cyclops NF, Volcano, V6 Lite
  • Triple Micro Swiss (3x CR10 Hotends): largest part cooler, and first application
  • Chimera 2-in-2: two filament/material and two nozzles, yet, a small common heatsink with E3D V6 nozzles
  • Cyclops NF or Lerdge 2-in-1 V2: simple non-mixing 2-in-1 printhead – in use currently on the Ashtar C #1 (Core XY)
  • E3D Volcano: although designs exist, I just wanted to see how my cooler performs in comparison – in use currently on Ashtar K #1 (Prusa i3-like) with 0.6mm nozzle
  • E3D V6 Lite: just an excercise to make it work for this popular setup as well – in use currently on CTC DIY I3 Pro B Y3228

Application Variables

Triple Micro Swiss

name=”triple swiss”
m=50
wx=50
yoff=17
sq3=1
wx=54

* requires a dedicated fan mount: Triple Nozzle Printhead

Dual Micro Swiss

name=”dual swiss”
wx=50

* requires a dedicated fan mount: Dual Nozzle Printhead

Chimera 2-in-2

name=”chimera”
m=30
yoff=10
zoff=18
ws=18
wx=42

Cyclops NF

name=”cyclops nf”
m=30
wx=25
yoff=9

see Cyclops NF

E3D Volcano

name=”volcano”
m=30
wx=24
yoff=12
zoff=21
zb=0.3
tscale=0.9

E3D V6 Lite

name=”e3d v6″
m=30
wx=24
yoff=12
zoff=14
zb=0.2

Pros / Cons

Pros:

  • parametric, reusable design
  • source code available (OpenSCAD) [not yet]
  • modular/stack use:

Cons:

  • other parts must be available as models in order to determine parameters of the part cooler
  • heatblock(s) should wear silicon cover, as air outputs partially affect heatblock which should be avoided

Download

https://www.thingiverse.com/thing:3680198 (not yet released)

Currently all my parts reside in a single large parts.scad for all Ashtar 3D printers, it helps me to improve designs quickly, but hinders me to release part designs in OpenSCAD source individually – it’s all interconnected and therefore avoid split it into separate files for now. As soon it’s resolved I will release the OpenSCAD sources.

For now three part coolers I released in STL downloadable on the dedicated pages:

Impressions

I’m quite happy with the result and use this Parametric Part Cooler for all my planned use cases.

References

or

ScriptCAD.org

Introduction

Faces + Edges

Back in 2003 I adapted OpenJsCad framework and extended it and documented it properly so it can be used in an easy way and then handed OpenJSCAD.org over to JS devs, who transformed it into the several NPM packages so it becomes usable for 3rd party packages easier.

I realized I wanted to have CAD with strong script capability with the focus on easy use and leave all the NPM package and JavaScript module conformity aside but refocus on easy and powerful 3D scene language, and so I started 2019/01 again, this time from scratch:

illu-stages
  1. Modeling in JavaScript
  2. Hierarchical Scene (computeScene())
  3. Compute Form
    • computeSolid()
      • computeSolidPolygon(): creates polyhedral approximation of solids
      • computeSolidImplicit(): create code for implicit rendering
  4. Rendering (renderScene())
    • polygon: throw triangles into WebGL space (e.g. with threejs)
    • implicit: render using GL shader
  5. Export
    • polygonal:
      • Browser: .stl, .amf, .obj, .3mf, .3mj
      • CLI: .stl, .amf, .obj, .3mf, .3mj, .png

Goals

  • true scripting capability like JavaScript (OpenSCAD’s .scad is a reduced language)
  • keep it simple (no NPM module export or alike complexity)
  • CSG capability (union, intersection, difference)
  • name parts and enumerate parts/id
  • parametrical designs (expose certain variables)
  • extrude 2D polygons to another (not just hull)
  • morph from one solid to another
  • simple fillets and chamfers (reduce sharp edges for real world parts)
  • extensive and detailed documentation
  • 100% API backward compatibility (no depreciation)

Few notes how to achieve those:

  • using three.js and ThreeCSG: reliable and well maintained package somewhat, ThreeCSG seems outdated/abandoned; alternatively using csg.js and/or OpenJSCAD packages as fallback, but hiding all the module handling
  • separate creating scene and rendering scene altogether:
    • polygons or rendering triangles are a stage, but not the core of the CAD, but true solids like a (perfect) sphere
    • use WebGL as one backend among others
    • use implict representations alternatives
  • fillets and chamfers: even those look simple, those are hard(er) to do in polygonal/triangulated stage, but simple to do with implicit representation
    • referencing individual edges (using consistent notion)

Prototype

Visit ScriptCAD.org

  • Chrome/Chromium provides full functionality
  • Firefox lacks marked source code from 3D space or Tree view

Status

  • 3D Forms: cube(), sphere(), cylinder(), polyhedron()
  • 2D Forms: square(), circle(), polygon(): line, lines, closed/open polygon
  • Operations: linear_extrude(), rotate_extrude(), transform(), scale(), rotate(), hull(), mesh()
  • CSG Operations: union(), difference(), intersection(), exclusive()
  • Import: STL (ASCII/Binary), AMF, OBJ, DXF, SCCAD (ScriptCAD), 3MJ
  • Export: STL (ASCII/Binary), AMF, OBJ, 3MF, SCAD (OpenSCAD), SCCAD (ScriptCAD), 3MJ, Gcode, PNG (CLI only)
  • Browser Support: Chrome (fully functional), Firefox (source select missing)

History

  • 2019/12/27: 0.3.3: experimental export of SCAD (OpenSCAD), 3MF, and SVG (only 2D) added:
  • 2019/12/05: 0.3.0: ScriptCAD.org Reference online, for now hosted on this site
  • 2019/11/25: 0.2.5: ScriptCAD.org launched as preview and a limited ScriptCAD Reference
  • 2019/11/05: 0.2.3: various features implemented, e.g. exclusive(), 3MJ import and export support added.
  • 2019/03/04: 0.1.5: more drawing models, selectable windows, more examples:

Brief overview of features of 0.1.5:

  • 2019/01/18: select Source -> TreeView -> 3D Scene or any stage and respective part is highlighted

Applications

I used ScriptCAD to generate paths with certain colors, and visualize it within ScriptCAD Web-GUI, and on the command-line the same code generated actual Gcode for one of my 3D printers, Ashtar K#2 equipped with Diamond Hotend 3-in-1 (mixing colors) – it allowed me to control every detail, something an ordinate slicer would not have provided:

Related Projects

CAD & 3D Printing: Parametric CAD with OpenSCAD

The past year (2018/2019) I printed with my own designed 3D Printers with various printheads and required for each variant a dedicated Part Cooler, and I realized it was worth the time to approach this in a parametric manner using OpenSCAD, using the “bull horn” design and so I came up with a general approach, and as a result was able to create various variants for my use-cases:

part-cooler-selection2

  • Custom Triple (& Dual) Micro Swiss hotends (3 nozzles, 3 heatsinks)
  • Chimera 2-in-2 (2 nozzles, 1 heatsink)
  • Cyclops NF 2-in-1 (2 intake, 1 nozzle, 1 heatsink, non-mixing)
  • E3D Volcano (1 nozzle, 1 heatsink, large nozzle diameter)
  • E3D V6 Lite (1 nozzle, 1 heatsink, small / medium nozzle diameter)
  • a few other specialized setups

Needless to say, the proven design allowed me to quickly design and print a new variant, and have expected results when using the part cooler – which is a huge time saver.

Yet, one drawback is obvious: in order to adapt a new variant, one requires the surrounding parts like heat sink and nozzle as a model or design it yourself (which can be time consuming as well) so the proper variables can be found to render the part cooler for the setup.

After I designed my 3D Printers also in parametric manner, like the Ashtar K (Prusa i3-like) 380x300x300mm build volume or Ashtar C (Core XY) 380x400x380mm build volume, using this parametric approach for a Part Cooler as well, turned out quite successful too.

It makes sense to develop and design a parametric piece which

  • maintains a few constraints, a general form, a base functionality
  • requires a few variables to adapt certain customization, sometimes it makes sense to introduce a lot of variables, and while testing and adapting customized pieces, some variables may become constants and unforeseen variables become more important

It certainly requires expertise about the piece in order to discern the constraints from the variable aspects while still maintaining its base functionality.

That’s it.

PS: See Parametric Part Cooler project page for new developments.