Maker.js, a Microsoft Garage project, is a JavaScript library for creating and sharing modular line drawings for CNC and laser cutters.

View project on GitHub Star

Intermediate drawing

Point functions

Browse the makerjs.point module API documentation to see the full list of point functions, such as adding and subtracting points.

var p1 = [50, 50];
var p2 = [25, 10];
var p3 = makerjs.point.add(p1, p2);

Scaling

To proportionately scale a simple point, use makerjs.point.scale. To proportionately scale paths and models, use these functions:

Each of these functions return the original object, so that we can "chain" on the same line of code.

Scale path example:

var makerjs = require('makerjs');
var arc1 = new makerjs.paths.Arc([0, 0], 25, 0, 90);
var arc2 = new makerjs.paths.Arc([0, 0], 25, 0, 90);
var svg = makerjs.exporter.toSVG([arc1, makerjs.path.scale(arc2, 2)]);
document.write(svg);
try it in JsFiddle ⇗

Scale model example:

var makerjs = require('makerjs');
var model = {
                                    models: {
                                    inner: new makerjs.models.Polygon(6, 40),
                                    outer: makerjs.model.scale(new makerjs.models.Polygon(6, 40), 1.7)
                                    }
};
var svg = makerjs.exporter.toSVG(model);
document.write(svg);
try it in JsFiddle ⇗

Rotating

To rotate a single point, see makerjs.point.fromPolar and makerjs.point.rotate depending on what you are trying to achieve.

You can rotate paths and models with these functions:

Each of these functions return the original object, so that we can "chain" on the same line of code.

Rotate path example:

var makerjs = require('makerjs');
var line1 = new makerjs.paths.Line([0, 0], [100, 0]);
var line2 = new makerjs.paths.Line([0, 0], [100, 0]);
var paths = [line1, makerjs.path.rotate(line2, -30, [100, 0])];
var svg = makerjs.exporter.toSVG(paths);
document.write(svg);
try it in JsFiddle ⇗

Rotate model example:

var makerjs = require('makerjs');
var rect1 = new makerjs.models.Rectangle(40, 80);
makerjs.model.rotate(rect1, 45, [0, 0]);
var svg = makerjs.exporter.toSVG(rect1);
document.write(svg);
try it in JsFiddle ⇗

Mirroring

Use makerjs.angle.mirror to get a mirror of an angle, and makerjs.point.mirror to get a mirror of a simple point.

You can create a mirrored copy of paths and models with the following functions. The mirroring can occur on the x axis, the y axis, or both.

Each of these functions returns a new object and does not modify the original.

Mirror path example:

var makerjs = require('makerjs');
var line1 = new makerjs.paths.Line([0, 0], [100, 100]);
var line2 = makerjs.path.mirror(line1, true, false);
var paths = [line1, line2];
var svg = makerjs.exporter.toSVG(paths);
document.write(svg);
try it in JsFiddle ⇗

Mirror model example:

var makerjs = require('makerjs');
var ovalArc1 = new makerjs.models.OvalArc(45, 135, 50, 10);
var model = {
                                    models: {
                                    ovalArc1: ovalArc1,
                                    ovalArc2: makerjs.model.mirror(ovalArc1, false, true)
                                    }
};
var svg = makerjs.exporter.toSVG(model);
document.write(svg);
try it in JsFiddle ⇗

Hint: When creating symmetrical models, it may be easier to create one half, and then use mirror to generate the other half.

SVG styling

It may be helpful to add visual styles to your drawing when debugging. You can do this using CSS and referencing elements in your SVG. Of course this will only add style in the browser, it will not affect SVG exported to a file for the laser cutter.

The keys you use within your paths and models collections become an id attribute in the generated SVG. For Example:

var makerjs = require('makerjs');
var model = {
                                    paths: {
                                    circle1: new makerjs.paths.Circle([0, 0], 30)
                                    },
                                    models: {
                                    bigOvalArc: new makerjs.models.OvalArc(120, 60, 50, 10)
                                    }
};
var svg = makerjs.exporter.toSVG(model, {useSvgPathOnly: false});
document.write(svg);
document.write('<hr/><code>');
document.write(svg.replace(/</g, '&lt;')); //show raw xml in browser
document.write('</code><hr/>');
try it in JsFiddle ⇗

We see that circle1 was a key within our paths collection and was translated into an id in an SVG element. We also see that the model bigOvalArc has geen translated to an SVG group with that id.

We can use a CSS id selector and the stroke property to give them individual color:

<style type="text/css">
                                    svg #circle1 {
                                    stroke: lime;
                                    }
                                    svg #bigOvalArc {
                                    stroke: orange;
                                    }
                                    svg #EndCap {
                                    stroke: black;
                                    }
</style>

Measuring

Browse to the makerjs.measure module documentation to see all functions related to measuring.

To get the bounding rectangle of a path or a model, use:

  • makerjs.measure.pathExtents(path: object)
  • makerjs.model.rotate(model: object)

These functions return an object with high and low points.

Measure path example:

var makerjs = require('makerjs');
var arc = new makerjs.paths.Arc([0, 0], 100, 45, 135);
var m = makerjs.measure.pathExtents(arc);
var totalWidth = m.high[0] - m.low[0];
var totalHeight = m.high[1] - m.low[1];
var measureRect = new makerjs.models.Rectangle(totalWidth, totalHeight);
measureRect.origin = m.low;
var model = {
                                    paths: {
                                    arc: arc
                                    },
                                    models: {
                                    measureRect: measureRect
                                    }
};
var svg = makerjs.exporter.toSVG(model);
document.write(svg);
try it in JsFiddle ⇗

Measure model example:

var makerjs = require('makerjs');
var oval = new makerjs.models.Oval(100, 20);
makerjs.model.rotate(oval, 30);
var m = makerjs.measure.modelExtents(oval);
var totalWidth = m.high[0] - m.low[0];
var totalHeight = m.high[1] - m.low[1];
var measureRect = new makerjs.models.Rectangle(totalWidth, totalHeight);
measureRect.origin = m.low;
var model = {
                                    models: {
                                    measureRect: measureRect,
                                    oval: oval
                                    }
};
var svg = makerjs.exporter.toSVG(model);
document.write(svg);
try it in JsFiddle ⇗

We can highlight the bounding box with some CSS:

<style type="text/css">
                                    svg g #measureRect {
                                    stroke: lightblue;
                                    }
</style>

Originating

To move a model or a path, you simply change it's origin property. A path within a model is referenced relatively to its parent model. There may be times when you want all objects to be within the same coordinate space. Let's create a simple demonstration model:

var makerjs = require('makerjs');
function box(origin) {
                                    this.models = {
                                    outer: new makerjs.models.RoundRectangle(100, 100, 1),
                                    inner: new makerjs.models.RoundRectangle(50, 50, 1)
                                    };
                                    this.models.inner.origin = [25, 25];
                                    this.origin = origin;
}
var box1 = new box([0, 0]);
var box2 = new box([150, 0]);
var model = {
                                    models: {
                                    box1: box1,
                                    box2: box2
                                    }
};
var svg = makerjs.exporter.toSVG(model);
document.write(svg);
try it in JsFiddle ⇗

In this example, both box1.models.inner.paths.Left.origin and box2.models.inner.paths.Left.origin have an origin of [0, 49] even though they are not in the same place, because they are located relative to the model that contains them. To make all models and paths occupy a singular coordinate space, we can use makerjs.model.originate:

var makerjs = require('makerjs');
function box(origin) {
                                    this.models = {
                                    outer: new makerjs.models.RoundRectangle(100, 100, 1),
                                    inner: new makerjs.models.RoundRectangle(50, 50, 1)
                                    };
                                    this.models.inner.origin = [25, 25];
                                    this.origin = origin;
}
var box1 = new box([0, 0]);
var box2 = new box([150, 0]);
var model = makerjs.model.originate({
                                    models: {
                                    box1: box1,
                                    box2: box2
                                    }
});
var svg = makerjs.exporter.toSVG(model);
document.write(svg);
try it in JsFiddle ⇗

Now box1.models.inner.paths.Left.origin and box2.models.inner.paths.Left.origin have the origins [25, 74] and [175, 74].

Modifying models

We know that models are relatively simple objects with a well known recursive structure. This allows us to modify them for different purposes. Let's modify and combine two different models in one drawing.

For this example we will use ovals to make an oval L shape. We begin by creating a model function that has two ovals:

var makerjs = require('makerjs');
function ovalL(width, height, thickness) {
                                    var ovalH = new makerjs.models.Oval(width, thickness);
                                    var ovalV = new makerjs.models.Oval(thickness, height);
                                    this.models = {
                                    h: ovalH, v: ovalV
                                    };
}
var svg = makerjs.exporter.toSVG(new ovalL(100, 100, 37));
document.write(svg);
try it in JsFiddle ⇗

There are overlapping arcs in the lower left corner. We can remove them if we know their id and position in the heirarchy. There are several ways we can inspect this model, here are a few:

  • Look at the code which created it. This may involve deep lookups. For example, the Oval source code references the RoundRectangle source code.
  • Use the browser's console, or JavaScript debugger to set a breakpoint in your model.
  • Use the browser's DOM inspector to traverse the rendered SVG.
  • Output the raw JSON or SVG on screen.

By looking at the source code we know that an Oval is a RoundRectangle and that the ids for arcs are BottomLeft, BottomRight, TopLeft and TopRight. The ids for the sides are Left, Right, Top and Bottom. Also, we need to note the orientation of these lines so we know which are origin and end points.

To remove a path we use the JavaScript delete keyword:

var makerjs = require('makerjs');
function ovalL(width, height, thickness) {
                                    var ovalH = new makerjs.models.Oval(width, thickness);
                                    var ovalV = new makerjs.models.Oval(thickness, height);
                                    //delete the lower arcs from the vertical oval
                                    delete ovalV.paths.BottomLeft;
                                    delete ovalV.paths.BottomRight;
                                    //delete the inside arc of the horizontal
                                    delete ovalH.paths.TopLeft;
                                    this.models = {
                                    h: ovalH, v: ovalV
                                    };
}
var svg = makerjs.exporter.toSVG(new ovalL(100, 100, 37));
document.write(svg);
try it in JsFiddle ⇗

The next step is to eliminate the overlap in the lines. Here are two approaches to do this:

Adjust only the x or y component of the point:

var makerjs = require('makerjs');
function ovalL(width, height, thickness) {
                                    var ovalH = new makerjs.models.Oval(width, thickness);
                                    var ovalV = new makerjs.models.Oval(thickness, height);
                                    delete ovalV.paths.BottomLeft;
                                    delete ovalV.paths.BottomRight;
                                    delete ovalH.paths.TopLeft;
                                    //move the x of the horizontal's top
                                    ovalH.paths.Top.end[0] = thickness;
                                    //move the y of the vertical's right
                                    ovalV.paths.Right.origin[1] = thickness;
                                    this.models = {
                                    h: ovalH, v: ovalV
                                    };
}
var svg = makerjs.exporter.toSVG(new ovalL(100, 100, 37));
document.write(svg);
try it in JsFiddle ⇗

Share a point on both lines:

var makerjs = require('makerjs');
function ovalL(width, height, thickness) {
                                    var ovalH = new makerjs.models.Oval(width, thickness);
                                    var ovalV = new makerjs.models.Oval(thickness, height);
                                    delete ovalV.paths.BottomLeft;
                                    delete ovalV.paths.BottomRight;
                                    delete ovalH.paths.TopLeft;
                                    //set to the same point
                                    ovalH.paths.Top.end =
                                    ovalV.paths.Right.origin =
                                    [thickness, thickness];
                                    this.models = {
                                    h: ovalH, v: ovalV
                                    };
}
var svg = makerjs.exporter.toSVG(new ovalL(100, 100, 37));
document.write(svg);
try it in JsFiddle ⇗

Let's progress this example further, by modifying an L shape into a C shape. Create a new model function for C, and immediately create an L within it. The C may create a new models object for itself, and nest the L inside; alternatively, C can just assume L's models object:

var makerjs = require('makerjs');
function ovalL(width, height, thickness) {
                                    var ovalH = new makerjs.models.Oval(width, thickness);
                                    var ovalV = new makerjs.models.Oval(thickness, height);
                                    delete ovalV.paths.BottomLeft;
                                    delete ovalV.paths.BottomRight;
                                    delete ovalH.paths.TopLeft;
                                    ovalH.paths.Top.end =
                                    ovalV.paths.Right.origin =
                                    [thickness, thickness];
                                    this.models = { h: ovalH, v: ovalV };
}
function ovalC(width, height, thickness) {
                                    //assume the same models as L
                                    this.models = new ovalL(width, height, thickness).models;
                                    //add another oval
                                    this.models.h2 = new makerjs.models.Oval(width, thickness);
                                    //move it to the top
                                    this.models.h2.origin = [0, height - thickness];
}
//using C instead of L
var svg = makerjs.exporter.toSVG(new ovalC(100, 100, 37));
document.write(svg);
try it in JsFiddle ⇗

Just as before, we need to delete the overlapping paths using the delete keyword. Let us also make a short alias for this.models to save us some keystrokes:

var makerjs = require('makerjs');
function ovalL(width, height, thickness) {
                                    var ovalH = new makerjs.models.Oval(width, thickness);
                                    var ovalV = new makerjs.models.Oval(thickness, height);
                                    delete ovalV.paths.BottomLeft;
                                    delete ovalV.paths.BottomRight;
                                    delete ovalH.paths.TopLeft;
                                    ovalH.paths.Top.end =
                                    ovalV.paths.Right.origin =
                                    [thickness, thickness];
                                    this.models = { h: ovalH, v: ovalV };
}
function ovalC(width, height, thickness) {
                                    //set local var m for easy typing
                                    var m =
                                    this.models =
                                    new ovalL(width, height, thickness).models;
                                    m.h2 = new makerjs.models.Oval(width, thickness);
                                    m.h2.origin = [0, height - thickness];
                                    //delete overlapping arcs again
                                    delete m.h2.paths.TopLeft;
                                    delete m.h2.paths.BottomLeft;
                                    delete m.v.paths.TopRight;
}
var svg = makerjs.exporter.toSVG(new ovalC(100, 100, 37));
document.write(svg);
try it in JsFiddle ⇗

Lastly, we need our overlapping lines to meet at a common point. Notice that the new oval h2 has a different origin the the previous ovals. So, we must originate for all of the ovals to share the same coordinate space. Afterwards, we can assign the common point to both lines.

In the JSFiddle, try removing the call to originate to see the results without it.

var makerjs = require('makerjs');
function ovalL(width, height, thickness) {
                                    var ovalH = new makerjs.models.Oval(width, thickness);
                                    var ovalV = new makerjs.models.Oval(thickness, height);
                                    delete ovalV.paths.BottomLeft;
                                    delete ovalV.paths.BottomRight;
                                    delete ovalH.paths.TopLeft;
                                    ovalH.paths.Top.end = 
                                    ovalV.paths.Right.origin = 
                                    [thickness, thickness];
                                    this.models = { h: ovalH, v: ovalV };
}
function ovalC(width, height, thickness) {
                                    var m = 
                                    this.models = 
                                    new ovalL(width, height, thickness).models;
                                    m.h2 = new makerjs.models.Oval(width, thickness);
                                    m.h2.origin = [0, height - thickness];
                                    delete m.h2.paths.TopLeft;
                                    delete m.h2.paths.BottomLeft;
                                    delete m.v.paths.TopRight;
    
                                    //h2 has paths relative to h2 origin, 
                                    //we need to originate to share the point
                                    makerjs.model.originate(this);
    
                                    //share the point
                                    m.h2.paths.Bottom.origin = 
                                    m.v.paths.Right.end =
                                    [thickness, height - thickness];
}
var svg = makerjs.exporter.toSVG(new ovalC(100, 100, 37));
document.write(svg);
try it in JsFiddle ⇗