JSCAD experiments

Introduction

CAD software is design engineering software used to create 3D geometry. Typically, CAD software has a graphical user interface that the designer uses to construct the geometry. However, there are alternative CAD programs that require the user to create geometry by writing code. One of such programs is OpenSCAD. OpenSCAD uses it's own programming language. An alternative is JSCAD that is implemented in JavaScript, and the models can be made right in the browser.

This article is a cursory exploration of JSCAD, version 2. All the code in this article can be copied into the online JSCAD editor. You can modify or add to the code and create your own models. You can export to a wide range of file formats, suitable for 3D printing, for example.

Next an example of some simple primitives and extrudes:


const jscad = require('@jscad/modeling');
const { polygon, torus, sphere } = jscad.primitives;
const { extrudeRotate } = jscad.extrusions;
const { translate } = jscad.transforms;

const main = () => {
  const polyRhombus = translate([4, 0, 0], polygon({ points: [[0,-1],[1,0],[0,1],[-1,0]] }));
  const extrudedRhombus = extrudeRotate({ segments: 32, startAngle: 0,
      angle: (Math.PI * 2), overflow: 'cap' }, polyRhombus);

  const torus1 = torus({ innerRadius: 1, outerRadius: 1.5, innerSegments: 4,
      outerSegments: 6, innerRotation: 0 });
  const torus2 = torus({ innerRadius: 1, outerRadius: 1.2 });
  const aSphere = sphere({ radius: 2, segments: 32 });

  return [
    translate([0, 0, 0], extrudedRhombus),
    translate([0, 0, 0], torus1),
    translate([0, 0, -4], torus2),
    translate([0, 0, 4], aSphere)
  ];
}

module.exports = { main };

Lissajous

The next is an extrude of a Lissajous curve. First it draws the curve using parametric equations, and then a solid is extruded along this curve.


const jscad = require('@jscad/modeling');
const { line} = jscad.primitives;
const { extrudeRectangular } = jscad.extrusions;

const scale = 5;
const x = function(t){return 6 * Math.sin((5*t)+1.57)};
const y = function(t){return 6 * Math.sin(4*t)};
	
const coords = [];
for (let t = 0; t <= 2.1*Math.PI; t += 0.01) {
  coords[coords.length] = [x(t)*scale, y(t)*scale];	
}

const main = () => {
  const aLine = line(coords);
  const lissajous = extrudeRectangular({size: 1.2, height: 6}, aLine);
  return lissajous;
}

module.exports = { main };

A tower

And finally a simple experiment with primitives and boolean operations:


const jscad = require('@jscad/modeling');
const { cube, cylinder, cylinderElliptic, polygon } = jscad.primitives;
const { translate, scale, rotate } = jscad.transforms;
const { subtract, union } = jscad.booleans;
const { extrudeLinear } = jscad.extrusions;

let aCube = cube();
let aCylinder = cylinder({height: 1, radius: 0.5, segments: 64}); // dfault: {height: 2, radius: 1, segments: 32};

const main = () => {
  const shapes = [];
  const body = translate([0, 0, 4], rotate([0, 0, 0], scale([4, 4, 8], aCylinder)));
  
  const gate = union(
    translate([0, 0, 1], rotate([0,0.5*Math.PI,0], scale([2, 2, 6], aCylinder))),
    translate([0, 0, 0], rotate([0,0.5*Math.PI,0], scale([1, 1, 3], aCube))),    
  );
  
  let battlements = translate([0, 0, 8], rotate([0,0,0], scale([3, 3, 2], aCylinder)));
  for (let i = 0; i<3; i++) {
    battlements = union(
      battlements,
      translate([0, 0, 8], rotate([0,0,Math.PI/3*i], scale([0.5, 2, 0.5], aCube))),
      );
  }

  const window = union(
    translate([2, 0, 0.4], rotate([0,0.5*Math.PI,0], scale([0.8, 0.8, 0.4], aCylinder))),
    translate([2, 0, 0], rotate([0,0.5*Math.PI,0], scale([0.4, 0.4, 0.2], aCube))),   
  );
  
  let towerNoRoof;
  towerNoRoof = subtract(body,gate,battlements);
  for (let i = 0; i<4; i++) {
    towerNoRoof = subtract(
      towerNoRoof,
      translate([0, 0, 5], rotate([0,0,Math.PI/2*i], scale([1, 1, 1], window))),
      );
  }
  
  let roof = subtract(  
    translate([0, 0, 7], rotate([0, 0, 0], scale([2, 2, 3.5], aCylinder))),
    union(
      translate([1, 0, 7.9], rotate([0,0.5*Math.PI,0], scale([0.8, 0.8, 1], aCylinder))),
      translate([1, 0, 7.5], rotate([0,0.5*Math.PI,0], scale([0.4, 0.4, 0.5], aCube))),
    ), 
  );
  roof = union(
    roof,
    translate([0, 0, 9.2],
    cylinderElliptic({height: 1, startRadius: [1,1], endRadius: [0,0]})
    ),
  );
  
  const towerWithRoof = union(towerNoRoof, roof);   
  
  const polyFlag = polygon({ points: [[-0.8, 0], [0, 0], [0, 2], [0, 2], [-0.4, 0.8], [-0.8, 2]] });
  let flag = union(
    translate([0, 0, 10], rotate([0, 0, 0], scale([0.05, 0.05, 2], aCylinder))),
    translate([0, 0, 10], rotate([0,0.5*Math.PI,-1], 
      extrudeLinear({ height: 0.05 }, polyFlag))),    
  );  
  
  shapes.push(towerWithRoof);
  shapes.push(flag);  
  return shapes;
}

module.exports = { main };