Intermediate drawing
Zeroing and Centering
edit on GitHubOriginating
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:
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 is 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.
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
You can break paths into two pieces if you have a point that lies on the path (from an intersection, for example) by using makerjs.path.breakAtPoint. This function will change the path that you pass it, so that it is broken at that point, and it will return a new path object which is the other broken piece:
For Circle, the original path will be converted in place to an Arc, and null is returned.
Fillets
Fillets are round corners where two paths meet. Rounding a corner can add strength to your part, as well as make it faster to print. Maker.js provides two types of fillets: traditional fillets and dogbone fillets.
Traditional fillet
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 fillet
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.
Bezier curves
Bezier curves are a fascinating and complex topic too large to cover here, it is recommended that you visit A Primer on Bezier Curves by Mike Kamermans (aka Pomax). Maker.js depends on Pomax's Bezier.js which is a vital site to visit for understanding Bezier curve functionality in depth.
It is important to understand how Maker.js manages the complexity of these mathematical wonders. For this explanation, we will start at the end and work our way backwards to the beginning of the process.
The final representation of a Bezier curve in Maker.js is a model containing a series of circular arc paths which closely approximate the curve. Closer approximation means more calculation time and more arcs.
Prior to generating the arcs, the curve is broken down into a series of sub-curves. It is from the sub-curves that the arcs are generated. Each sub-curve is guaranteed to not have an "S" shape so that it more closely resembles a circular arc. The sub-curves are also broken at their rectangular "boundary box" points so that we are guaranteed that the boundary box tangent points are truly points on the curve and not approximations. In the Bezier.js terminology, these breaking points are known as extrema.
Now we are at the beginning of the process, where you call makerjs.models.BezierCurve with the new operator. You can create both quadratic and cubic Bezier curves. For either type, you may optionally pass the accuracy - the maximum distance between the true curve and the arc approximations. The default accuracy coefficient in Maker.js will produce an accurate and visually smooth curve in a reasonable calculation timeframe.
Create a quadratic Bezier curve in by passing an array of three points - an origin point, a control point, and an end point:
Create a cubic Bezier curve in by passing an array of four points - an origin point, a first control point (relating to the origin point), a second control point (relating to the end point), and an end point:
Be sure to Play these examples, then click "show path names" to see the arcs representing the curve.
Next: learn more in Advanced Drawing.
To move a model so that its bottom left edge is at [0, 0] use model.zero:
To move a model so that it is centered on [0, 0] use model.center: