OpenJSCAD User Guide

Just for sake of providing context, OpenJSCAD.org is built on OpenJsCad (Github), which itself was inspired by OpenSCAD.org, and essentially provides a programmers approach to develop 3D models. In particular, this enhancement is tuned towards creating precise models for 3D printing.

In general, designs are written using the JavaScript language. Training and help about JavaScript can be found online.

Creating a new design starts by writing simple scripts which call CSG functions, and other special functions, as provided by OpenJSCAD. OpenJSCAD executes the script, and renders the 3D design for viewing.

OpenJSCAD adheres to specific standards for passing parameters to functions. But most parameters are optional as default values are provided.

When 3D vectors are required, parameters can be passed as an array. If a scalar (single value) is passed for a parameter which expects a 3D vector, the scalar is used for the x, y and z values. In other words: radius: 1 will give radius: [1,1,1].

All rounded solids have a resolution parameter which controls tesselation. If resolution is set to 8, then 8 polygons per 360 degree of revolution are used. Beware that rendering time will increase dramatically when increasing the resolution. For a sphere, the number of polygons increases quadratically with the resolution used. If the resolution parameter is omitted, the following two global defaults are used

CSG.defaultResolution2D

CSG.defaultResolution3D

The former is used for 2D curves (circle, cylinder), the latter for 3D curves (sphere, 3D expand).

OpenSCAD like functions support the fn parameter, which is the numer of segments to approximate a sphere (default 32, total polygons per sphere fn*fn).

Create a polyhedron with a list of points and a list of triangles or polygons. The point list is all the vertexes of the shape, the triangle list is how the points relates to the surfaces of the polyhedron:

polyhedron({// openscad-like (e.g. pyramid)points:[[10,10,0],[10,-10,0],[-10,-10,0],[-10,10,0],// the four points at base[0,0,10]],// the apex point triangles:[[0,1,4],[1,2,4],[2,3,4],[3,0,4],// each triangle side[1,0,3],[2,1,3]]// two triangles for square base});

Additionally you can also define `polygons: [ [0,1,4,5], [..] ]` too, not just `triangles:`.

You can also create a polyhedron at a more low-level:

varpolygons=[];polygons.push(newCSG.Polygon([newCSG.Vertex(newCSG.Vector3D(-5,-5,0)),newCSG.Vertex(newCSG.Vector3D(2,2,5)),newCSG.Vertex(newCSG.Vector3D(3,3,15))]));// add more polygons and finally:solid=CSG.fromPolygons(polygons);

varm=newCSG.Matrix4x4();m=m.multiply(CSG.Matrix4x4.rotationX(40));m=m.multiply(CSG.Matrix4x4.rotationZ(40));m=m.multiply(CSG.Matrix4x4.translation([-.5,0,0]));m=m.multiply(CSG.Matrix4x4.scaling([1.1,1.2,1.3]));// and apply the transform:varcube3=cube().transform(m);

mirror([10,20,90],cube(1));// openscad likevarcube=CSG.cube().translate([1,0,0]);// built in method chainingvarcube2=cube.mirroredX();// mirrored in the x=0 planevarcube3=cube.mirroredY();// mirrored in the y=0 planevarcube4=cube.mirroredZ();// mirrored in the z=0 plane// create a plane by specifying 3 points:varplane=CSG.Plane.fromPoints([5,0,0],[5,1,0],[3,1,7]);// and mirror in that plane:varcube5=cube.mirrored(plane);

circle();// openscad likecircle(1);circle({r:2,fn:5});// fn = number of segments to approximate the circlecircle({r:3,center:true});// center: false (default)CAG.circle({center:[0,0],radius:3,resolution:32});// using CSG objects' built in methods

A path is simply a series of points, connected by lines. A path can be open or closed (an additional line is drawn between the first and last point). 2D paths are supported through the CSG.Path2D class. The difference between a 2D Path and a 2D CAG is that a path is a 'thin' line, whereas a CAG is an enclosed area.

Paths can be constructed either by giving the constructor an array of 2D coordinates, or through the various CSG.Path2D member functions, which include:

appendPoint([x,y]): create & return a new Path2D containing the callee's points followed by the given point.

appendPoints([[x,y],...]): create & return a new Path2D containing the callee's points followed by the given points. [Note: as of 2016/08/13, this method also modifies the callee; this is probably a bug and might be changed in the future; see issue:165]

appendBezier([[x,y],...], options): create & return a new Path2D containing the callee's points followed by a Bezier curve ending at the last point given; all but the last point given are the control points of the Bezier; a null initial control point means use the last two points of the callee as control points for the new Bezier curve. options can specify {resolution: <NN>}.

Paths can be concatenated with .concat(), the result is a new path.

A path can be converted to a CAG in two ways:

expandToCAG(pathradius, resolution) traces the path with a circle, in effect making the path's line segments thick.

innerToCAG() creates a CAG bounded by the path. The path should be a closed path.

Creating a 3D solid is currently supported by the rectangularExtrude() function. This creates a 3D shape by following the path with a 2D rectangle (upright, perpendicular to the path direction):

varpath=newCSG.Path2D([[10,10],[-10,10]],/* closed = */false);varanotherpath=newCSG.Path2D([[-10,-10]]);path=path.concat(anotherpath);path=path.appendPoint([10,-10]);path=path.close();// close the path// of course we could simply have done:// var path = new CSG.Path2D([ [10,10], [-10,10], [-10,-10], [10,-10] ], /* closed = */ true);// We can make arcs and circles:varcurvedpath=CSG.Path2D.arc({center:[0,0,0],radius:10,startangle:0,endangle:180,resolution:16,});

Linear extrusion of 2D shape, with optional twist. The 2d shape is placed in in z=0 plane and extruded into direction <offset> (a CSG.Vector3D). The final face is rotated <twistangle> degrees. Rotation is done around the origin of the 2d shape (i.e. x=0, y=0) twiststeps determines the resolution of the twist (should be >= 1), returns a CSG object:

// openscad-likerotate_extrude(translate([4,0,0],circle({r:1,fn:30,center:true})));// using CSG objects' built in methods to translate rotate_extrude({fn:4},square({size:[1,1],center:true}).translate([4,0,0]));rotate_extrude(polygon({points:[[0,0],[2,1],[1,2],[1,3],[3,4],[0,5]]}));rotate_extrude({fn:4},polygon({points:[[0,0],[2,1],[1,2],[1,3],[3,4],[0,5]]}));

You can essentially extrude any 2D polygon (circle, square or polygon).

The 'property' property of a solid can be used to store metadata for the object, for example the coordinate of a specific point of interest of the solid. Whenever the object is transformed (i.e. rotated, scaled or translated), the properties are transformed with it. So the property will keep pointing to the same point of interest even after several transformations have been applied to the solid.

Properties can have any type, but only the properties of classes supporting a 'transform' method will actually be transformed. This includes CSG.Vector3D, CSG.Plane and CSG.Connector. In particular CSG.Connector properties (see below) can be very useful: these can be used to attach a solid to another solid at a predetermined location regardless of the current orientation.

It's even possible to include a CSG solid as a property of another solid. This could be used for example to define the cutout cylinders to create matching screw holes for an object. Those 'solid properties' get the same transformations as the owning solid but they will not be visible in the result of CSG operations such as union().

Other kind of properties (for example, strings) will still be included in the properties of the transformed solid, but the properties will not get any transformation when the owning solid is transformed.

All primitive solids have some predefined properties, such as the center point of a sphere (TODO: document).

The solid resulting from CSG operations (union(), subtract(), intersect()) will get the merged properties of both source solids. If identically named properties exist, only one of them will be kept.

varcube=CSG.cube({radius:1.0});cube.properties.aCorner=newCSG.Vector3D([1,1,1]);cube=cube.translate([5,0,0]);cube=cube.scale(2);// cube.properties.aCorner will now point to [12, 2, 2],// which is still the same corner point // Properties can be stored in arrays; all properties in the array// will be transformed if the solid is transformed:cube.properties.otherCorners=[newCSG.Vector3D([-1,1,1]),newCSG.Vector3D([-1,-1,1])];// and we can create sub-property objects; these must be of the // CSG.Properties class. All sub properties will be transformed with// the solid:cube.properties.myProperties=newCSG.Properties();cube.properties.myProperties.someProperty=newCSG.Vector3D([-1,-1,-1]);

The CSG.Connector class is intended to facilitate attaching two solids to each other at a predetermined location and orientation. For example suppose we have a CSG solid depicting a servo motor and a solid of a servo arm: by defining a Connector property for each of them, we can easily attach the servo arm to the servo motor at the correct position (i.e. the motor shaft) and orientation (i.e. arm perpendicular to the shaft) even if we don't know their current position and orientation in 3D space.

In other words Connector give us the freedom to rotate and translate objects at will without the need to keep track of their positions and boundaries. And if a third party library exposes connectors for its solids, the user of the library does not have to know the actual dimensions or shapes, only the names of the connector properties.

A CSG.Connector consist of 3 properties:

point: a CSG.Vector3D defining the connection point in 3D space

axis: a CSG.Vector3D defining the direction vector of the connection (in the case of the servo motor example it would point in the direction of the shaft)

normal: a CSG.Vector3D direction vector somewhat perpendicular to axis; this defines the "12 o'clock" orientation of the connection.

When connecting two connectors, the solid is transformed such that the point properties will be identical, the axis properties will have the same direction (or opposite direction if mirror == true), and the normals match as much as possible.

Connectors can be connected by means of two methods:
A CSG solid's connectTo() function transforms a solid such that two connectors become connected.
Alternatively we can use a connector's getTransformationTo() method to obtain a transformation matrix which would connect the connectors. This can be used if we need to apply the same transform to multiple solids.

varcube1=CSG.cube({radius:10});varcube2=CSG.cube({radius:4});// define a connector on the center of one face of cube1// The connector's axis points outwards and its normal points// towards the positive z axis:cube1.properties.myConnector=newCSG.Connector([10,0,0],[1,0,0],[0,0,1]);// define a similar connector for cube 2:cube2.properties.myConnector=newCSG.Connector([0,-4,0],[0,-1,0],[0,0,1]);// do some random transformations on cube 1:cube1=cube1.rotateX(30);cube1=cube1.translate([3.1,2,0]);// Now attach cube2 to cube 1:cube2=cube2.connectTo(cube2.properties.myConnector,cube1.properties.myConnector,true,// mirror 0// normalrotation);// Or alternatively:varmatrix=cube2.properties.myConnector.getTransformationTo(cube1.properties.myConnector,true,// mirror 0// normalrotation);cube2=cube2.transform(matrix);varresult=cube2.union(cube1);

The getBounds() function can be used to retrieve the bounding box of an object, returning an array with two CSG.Vector3Ds specifying the minimum X,Y,Z coordinates and the maximum X,Y,Z coordinates.

The lieFlat() function lays an object onto the Z surface, in such a way that the Z-height is minimized and the object is centered around the Z axis. This can be useful for CNC milling, allowing an object to be transform into the space of the stock material during milling. Or for 3D printing: it is laid in such a way that it can be printed with minimal number of layers. Instead of the lieFlat() function, the getTransformationToFlatLying() function can be used, which returns a CSG.Matrix4x4 for the transformation.

varcube1=CSG.cube({radius:10});varcube2=CSG.cube({radius:5});// get the right bound of cube1 and the left bound of cube2:vardeltax=cube1.getBounds()[1].x-cube2.getBounds()[0].x;// align cube2 so it touches cube1:cube2=cube2.translate([deltax,0,0]);varcube3=CSG.cube({radius:[100,120,10]});// do some random transformations:cube3=cube3.rotateZ(31).rotateX(50).translate([30,50,20]);// now place onto the z=0 plane:cube3=cube3.lieFlat();// or instead we could have used:vartransformation=cube3.getTransformationToFlatLying();cube3=cube3.transform(transformation);returncube3;

color([1,0.5,0.3],sphere(1));// openscad likecolor([1,0.5,0.3],sphere(1),cube(2));color("Red",sphere(),cube().translate([2,0,0]));// named color (case-insensitive)sphere().setColor(1,0.5,0.3);// built in methodssphere().setColor([1,0.5,0.3]);

functionmain(){varo=[];for(vari=0;i&lt;8;i++){o.push(cylinder({r:3,h:20}).setColor(hsl2rgb(i/8,1,0.5).// hsl to rgb, creating rainbow [r,g,b]concat(1/8+i/8)// and add to alpha to make it [r,g,b,a]).translate([(i-3)*7.5,0,0]));}o.push(color("red",cube(5)).translate([-4,-10,0]));o.push(color("red",0.5,cube(5)).translate([4,-10,0]));returno;}

Note: There are some OpenGL Transparency Limitations, e.g. and depending on the order of colors, you might not see through otherwise partially transparent objects.

Models can have interactive parameters, allowing users to change values via familiar forms, i.e. typing in numbers, sliding bars, pulling down menus, etc. This allows the user to change values and create any number of possible combinations, allowing the model to become dynamic in nature. Any number of custom designs can be created, which can then be down loaded in any supported format.

Interactive parameters are possible by adding a specific function called getParameterDefinitions(). This function can be added anywhere in to your JSCAD script, but must return an array of parameter definitions.

functiongetParameterDefinitions(){return[{name:'width',type:'float',initial:10,caption:"Width of the cube:"}];}

The parameter definitions are used to create a set of fields which the user can change, i.e. options. The values of the fields are supplied to the main() function of your JSCAD script. For example, the value of the 'width' parameter is supplied as the 'params.width' attribute, and so on.

All the common HTML5 field types are available as interactive parameters. This includes checkbox (boolean), color, date, email, float, int, number, password, slider, text and url. As well as two special parameter types for pull down choices, and grouping parameters.

A minimum parameter specification contains only 'name' and 'type'. However, a complete parameter specification should have a 'caption' and 'initial' value. In addition, there are 'min', 'max', 'step', 'checked', 'size, 'maxlength', and 'placeholder' which are relevant to specific parameter types. Just keep try different combinations to get a nice parameter specification. Here's an good example.

{name:'width',type:'float',initial:1.5,caption:'Width of the thingy:',min:1.0,max:5.0,step:0.5}

In addition, there is the 'choice' type which creates a drop down list of choices for the user. The choices are provided by specifying the 'values' and 'captions'. The chosen value is passed into the main() function of the model.

{name:'shape',type:'choice',values:["TRI","SQU","CIR"],// these are the values that will be supplied to your scriptcaptions:["Triangle","Square","Circle"],// optional, these values are shown in the listbox// if omitted, the items in the 'values' array are usedcaption:'Shape:',// optional, displayed left of the input fieldinitial:"SQU",// optional, default selected value// if omitted, the first item is selected by default// NOTE: parameter "default" is deprecated}

A complete example:

functiongetParameterDefinitions(){return[{name:'width',type:'float',initial:10,caption:"Width of the cube:"},{name:'height',type:'float',initial:14,caption:"Height of the cube:"},{name:'depth',type:'float',initial:7,caption:"Depth of the cube:"},{name:'rounded',type:'choice',caption:'Round the corners?',values:[0,1],captions:["No thanks","Yes please"],initial:1}];}functionmain(params){varresult;if(params.rounded==1){result=CSG.roundedCube({radius:[params.width,params.height,params.depth],roundradius:2,resolution:32});}else{result=CSG.cube({radius:[params.width,params.height,params.depth]});}returnresult;}

There are plenty of examples of "Interactive" parameters available at OpenJSCAD.org. See the Quick Reference for more information about the available parameter types, and browser support.

See Example 50: Platonics with a recursive use of include(). Yet, it's a rather bad example of not localize the function names. A clear writing style-guide will follow how an OpenJSCAD library should look like.

Assuming you want to create a larger OpenJSCAD project, you might use include() to split up the functionality:

ProjectName/
main.jscad # this one contains the "function main()", this file will be <b>executed</b>
addon.jscad # this file could be include("addon.jscad") in main.jscad
optimizer.jscad # '' include("optimizer.jscad") in main.jscad or also in addon.jscad
Makefile # possible Makefile to do the same on CLI

Note: The file named main.jscad must be present, and must contain the "function main()" declaration.