zaterdag 7 november 2015

Scripting vector graphics with iPath

Since a while I've been creating a language (or DSL) in Javascript for scripting designs, images, contours etc. I called it iPath and it's used in my website StretchSketch.

Originally iPath is intended for creating cnc files. The idea was to create customisable templates, which can be cut by cnc devices like routers, lasercutter or watercutters. At first I thought that SVG was the lingua franca for vectorised graphics but this is only true in the world of internet (browsers). CNC shops still hold to their good old DXF. Converting SVG to DXF turned out to be less straightforward than anticipated. Inkscape did do a poor job (at that time), and other solutions where costly. So I decided to make iPath format agnostic. Currently it can generate SVG and DXF but theoretically other generators can be used as well.

In iPath one can formulate a 2D drawing by instantiating a new iPath and calling methods on it in a way known as a fluent interface. A square can be formed by the following snippet:

var square = new iPath().line(20,0).line(0,20).line(-20,0).line(0,-20);

A line takes 2 arguments an x and y coordinate, so above will generate a square with a length of 20.

var l = 30;
var square = new iPath().line(l,0).line(0,l).line(-l,0).line(0,-l);

This will create a square with a length of 30 but its easier to adapt its dimension. So far we've only been creating the model, we can't show the model yet. If we want to display the square as SVG (as the path's d-attribute) we need to call dPath(significance).

<path id="s3" d="M 0 0"/>
..
document.getElementById("s3").setAttribute('d', "M 10 10" + square.dPath(3));

iPath's lines, moves etc are all relative: every movement is measured from the pen's current position. The only time absolute coordinates are needed, is for positioning your iPath model. In SVG path capitalised Characters means absolute parameters, and this is exactly what the "M 10 10" means: go to (10,10) in your current view).

So far we've only been looking at x,y coordinates (Cartesian), but we can also use polar coordinates. In this case we need to give an angle and distance as argument. Seasoned programmers may remember turtle graphics, part of the logo programming language, and iPath borrows this for indicating cartesian lines. The argument for turtleLine is an object with the angle (a) an distance (r) as properties. When repeated it is possible to create circular figures.

var gear = {};
gear.n = 20;
gear.r=100;
gear.h=0.7;

gear.ribbon = gear.r * Math.sin(Math.PI/gear.n) * 2 ;
gear.toothAngle = Math.atan(2* gear.h, gear.ribbon);
gear.toothSide = gear.ribbon / (2*Math.cos(gear.toothAngle));
gear.iPath = new iPath().turtleLine({a: (2*Math.PI / gear.n) - gear.toothAngle, r: gear.toothSide})
       .turtleLine({a: 2*gear.toothAngle, r: gear.toothSide}).turtleLine({a:-gear.toothAngle})
       .repeat(gear.n);

This will create a gear:

Feel free to experiment with the customizable gear.

As stated earlier iPath has been created for CNC applications, one of the features is to generate dxf alongside to SVG. This possiblity lets you adapt and view interactively SVG designs in the browser. Once a design is completely to your liking, it can be generated in DXF. Generating DXF is not as simple as it is for SVG, paths are not enough. DXF only comes in complete files and needs to be organized in layers. It is advised to use one layer per iPath model.

dxfBuilder = new DxfBuilder(new Blobber(), 3);
      gear.iPath.dxf(dxfBuilder,{layer: {name: 'jeroen', layer_color: 6}});
      saveAs(dxfBuilder.getBlob(), gear.filename);

saveAs is from Eli Grey's FileSaver. The DxfBuilder is from iPath's DXF library.

iPath's can be concatenated with concat:

new iPath().line(20,20).concat(new iPath().line(-20,40));