Intermediate drawing
Zeroing and Centering
Originating
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:
In this example, both box1.paths.inner.origin
and box2.paths.inner.origin
have an origin of [50, 50]
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:
Now box1.paths.inner.origin
and box2.paths.inner.origin
have the origins [50, 50]
and [200, 50]
.
Scaling
To proportionately scale a simple point, use makerjs.point.scale. To proportionately scale paths and models, use these functions:
- makerjs.path.scale(path: object, scaleValue: number)
- makerjs.model.scale(model: object, scaleValue: number)
Each of these functions return the original object, so that we can "chain" on the same line of code.
Scale path example:
Scale model example:
Distorting
To disproportionately scale a simple point, use makerjs.point.distort.
To disproportionately scale a path, use makerjs.path.distort(path: object, scaleX: number, scaleY: number) which returns a new object and does not modify the original. The type of returned object is dependent on the type of path being distorted:
- A line will return a line IPath object, since the distortion can be represented with another line.
- An arc will return a BezierCurve IModel object, since the distortion is not circular.
- A circle will return an Ellipse IModel object, since the distortion is not circular.
Distort path example:
To disproportionately scale a model, use makerjs.model.distort(model: object, scaleX: number, scaleY: number) which returns a new IModel object and does not modify the original.
Distort model example:
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:
- makerjs.path.rotate(path: object, angleInDegrees: number, rotationOrigin: point)
- makerjs.model.rotate(model: object, angleInDegrees: number, rotationOrigin: point)
Each of these functions return the original object, so that we can "chain" on the same line of code.
Rotate path example:
Rotate model example:
Cloning
Models and paths are simple JavaScript objects, so they are easy to clone in a way that is standard to JavaScript. Maker.js provides a few functions for cloning:
- makerjs.cloneObject - clones a model, or any other object.
- makerjs.path.clone - clones a path (quicker than cloneObject)
- makerjs.point.clone - clones a point (quicker than cloneObject)
Cloning is useful in many situations. For example, if you need many copies of a model for rotation:
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.
- makerjs.path.mirror(path: object, mirrorX: boolean, mirrorY: boolean)
- makerjs.model.mirror(model: object, mirrorX: boolean, mirrorY: boolean)
Each of these functions returns a new object and does not modify the original.
Mirror path example:
Mirror model example:
Hint: When creating symmetrical models, it may be easier to create one half, and then use mirror to generate the other half.
Repeating layouts
Columns
Call makerjs.layout.cloneToColumn(path or model, count, [optional] margin) to repeatedly clone and layout in a column. The interval will be the height of the path's or model's bounding box. Extra vertical margin is optional.Rows
Call makerjs.layout.cloneToRow(path or model, count, [optional] margin) to repeatedly clone and layout in a row. The interval will be the width of the path's or model's bounding box. Extra horizontal margin is optional.Grid
Call makerjs.layout.cloneToGrid(path or model, xcount, ycount, [optional] margin) to repeatedly clone and layout in a grid. The interval will be the path's or model's bounding box. Extra margin is optional.Brick
Call makerjs.layout.cloneToBrick(path or model, xcount, ycount, [optional] margin) to repeatedly clone and layout in a brick wall format. The interval will be the path's or model's bounding box. Extra margin is optional.Honeycomb
Call makerjs.layout.cloneToHoneycomb(path or model, xcount, ycount, [optional] margin) to repeatedly clone and layout in a honeycomb format. The interval will be the path's or model's bounding hexagon. Extra margin is optional.Radial
Call makerjs.layout.cloneToRadial(path or model, count, angleInDegrees, [optional] rotationOrigin) to repeatedly clone and layout in a radial format.Intersection
You can find the point(s) of intersection between two paths using makerjs.path.intersection. If the paths do not intersect, this function will return null. Otherwise, it will return an object with a property named intersectionPoints which is an array of points. Additionally, if either path was an arc or circle, this object will contain the angles at which an intersection occurred.
Intersection examples:
Converging lines
To make lines meet at their slope intersection point, use makerjs.path.converge. This function will only work with lines, it will not work with arcs.
The converge function will try to use the end of the line that is closest to the convergence point. If you need to specify which ends of your lines should be converged, pass two additional boolean values. The boolean value is true to use the line's origin, false to use the end.
Converge example:
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:
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.
- Use the Playground app and click "show path names".
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:
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:
Share a point on both lines:
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:
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:
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 Play editor, try removing the call to originate to see the results without it.
Breaking paths
Fillets
Fillets are round corners where two paths meet. Maker.js provides two types of fillets: traditional fillets and dogbone fillets.
Traditional fillet
Rounding a corner can add strength to your part, as well as make it faster to print. Using makerjs.path.fillet you can round a corner at the junction between two lines, two arcs, or a line and an arc. This function will clip the two paths that you pass it, and will return a new arc path which fits between the clipped ends. The paths must meet at one point, this is how it determines which ends of the paths to clip. You also provide a radius of the fillet. If the fillet cannot be created this function will return null.
Dogbone Fillets
Many CNC tools are not able to cut a sharp interior corner. The way to clear the apex of an interior corner is by encompassing the corner with a circular cut known as a dogbone fillet. Use makerjs.path.dogbone to round a corner at the junction between two lines. This function will only work for two lines which must meet at one point. It will clip the two lines that you pass it, and will return a new arc path which clears the corner where the lines meet. It will return null if a dogbone fillet cannot be created at the radius you specify.
Dogbone models
If you need a rectangle with dogbones at each corner, you can use makerjs.models.Dogbone(width, height, radius, optional style, optional bottomless). There are a 3 styles of a Dogbone model:
- 0 : (default) rounded
- -1 : horizontal
- -1 : vertical
Dogbone model corner styles:
Making them bottomless is useful for creating tongue-and-groove shapes:
Layers
Layers are a way of logically grouping your paths or models as you see fit. Simply add a layer
property to any path or model object, with the name of the layer.
Every path within a model will automatically inherit its parent model's layer, unless it has its own layer property. As you can see in this example, a layer can transcend the logical grouping boundaries of models:
A layer name can be any string. Furthermore, you can use a reserved color name from this list to get an automatic stroke color when your model is exported in DXF or SVG:
aqua, black, blue, fuchsia, green, gray, lime, maroon, navy, olive, orange, purple, red, silver, teal, white, yellow
Layers will be output during the export process in these formats:
- DXF - paths will be assigned to a DXF layer.
- SVG - in continuous mode, a new <path> element will be created for each layer.
Cascading functions
The $ function
As an alternative to cascading functions, Maker.js offers a handy way to modify your drawing in an object-oriented style, inspired by the jQuery library.
Call makerjs.$(x) to get a cascade container object returned. You can then invoke a series of cascading functions upon x. The output of each function becomes the input of the next. A cascade container will only work with functions that output the same type of object that they input as their first parameter, which must be one of these types:- Model
- Path
- Point
Container operators
A cascade container will have special properties that operate the container itself (as opposed to operating upon x). These are prefixed with $:- $initial: object Gets the original x object that you passed in.
- $result: object Gets the final result of all cascaded function calls.
- $reset(): function() - Resets the container to $initial.
Cascadable functions
Depending on the type of x, a cascade container will provide all of the functions from one of the corresponding modules.- model module / cascade-safe functions
- path module / cascade-safe functions
- point module / cascade-safe functions
Example
Let's rewrite the example from above to compare the readability of the code: This has saved us some typing - we didnt need to use makerjs.model... to access any functions. The order of operations makes more sense too: the first operation (center()) is at the top, the final operation (moveRelative([0, 15])) is at the bottom, and the function parameters are together with their call.Using addTo() instead of .$result
In some cases, you can avoid using .$result and just add a path or add a model to a parent model by calling addTo(model, id). This is particularly useful prior to a call that creates a clone (such as mirror):Captions
Captions are fragments of text that can be positioned anywhere in your model, useful for adding documentation within your drawing. Captions are unlike the Text model, which is a line drawing of glyphs in a given font.
A caption is aligned to an invisible line called an anchor. The caption text is centered both horizontally and vertically at the center point of the anchor line. The text in a caption will not wrap, it is a single line of text. The text and anchor line do not need to be the same length, the anchor line is only used to determine the center point and the slope. The anchor line may be rotated to angle the caption text. Anchor lines are moved, originated, scaled, distorted and rotated accoordingly within a model. The font size of caption text is determined when you export your model. A caption can be put on a different layer from it's model by setting the layer of it's anchor. Note: In the Playground, caption text does not scale when you zoom in or out.
Creating a caption object
A caption is an object with these two properties:
- text - String
- anchor - Line object
There is a helper function makerjs.model.addCaption(text, [optional] leftAnchorPoint, [optional] rightAnchorPoint) which lets you add a caption on one line of code:
If the anchor line is degenerate (meaning its origin and end point are the same), you can achieve text which will remain horizontally aligned regardless of model rotation:
Next: learn more in Advanced drawing.
To move a model so that its bottom and/or left edges are on the x & y axes, use model.zero. This function accepts 2 boolean parameters: zeroOnXAxis, zeroOnYAxis. If you do not pass any parameters, it will zero on both axes.
To move a model so that it is centered on on the x & y axes, use model.center. This function accepts 2 boolean parameters: centerOnXAxis, centerOnYAxis. If you do not pass any parameters, it will center on both axes.