Advanced drawing
Open vs Closed Geometry
edit on GitHubCombining with Boolean operations
You can combine models using the makerjs.model.combine function, passing these parameters:
- first model to combine, we'll call it "modelA"
- second model to combine, we'll call it "modelB"
- boolean to include modelA's paths which are inside of modelB
- boolean to include modelA's paths which are outside of modelB
- boolean to include modelB's paths which are inside of modelA
- boolean to include modelB's paths which are outside of modelA
Instead of remembering the boolean flag combinations, shortcuts are provided for:
Now it is apparent why we need a closed geometry - because we need to know what is considered the inside of a model.
Order of Boolean operations
Combining models with boolean operations is a powerful feature but it can be challenging in some scenarios. Re-modeling your drawing may be necessary to acheive certain results. We will explore the order of operations concept with a sample project. Let's first take a look at our desired end goal:
We can start with all of the building blocks of our design: a star, a plus, and a frame:
The first step is to combine the vertical and horizontal bars of the plus:
Next we will combine the star and the plus:
Let's pause and consider what the plus looks like by itself, after our union operation with the star:
And the star by itself:
They have become open geometries. We cannot call the combine function with an open geometry. But since we combined them, they are a closed geometry when they are together. So, we should create a new model for them together:
Now we can continue, with a subtraction operation. Notice that we should not subtract the starplus from the frame (try that on your own to see what happens) but only from the inner frame:
Expanding paths
Paths can be expanded to produce a closed geometry model which surrounds them perfectly.
Pass a path and a distance to makerjs.path.expand, this will return a new model:
You can also expand all the paths in a model by calling makerjs.model.expandPaths:
Beveling joints
A third parameter can be passed to makerjs.model.expandPaths to specify the number of corners to apply to each joint and end cap:
- 0 (default) - no corners (rounded)
- 1 - one corner (pointed)
- 2 - two corners (beveled)
Outlining a model
Expanding a model's path will surround every path, which sometimes can mean there is an inner and an outer surrounding chain. If you only want the outer surrounding chain, use makerjs.model.outline:
Wireframe technique
Creating a wireframe and using expansion may save you a lot of work. We will demonstrate by creating a wireframe of a truss:
Next we will expand the paths:
Simplifying paths
If you Play the wireframe example above, and click on 'show path names' you will see that many lines have been created as a result of the expansion. This is an artefact of all of the boolean operations with combine. The outmost chain for example, should be able to represented with only four lines. To remedy this, there is makerjs.model.simplify - however there is an important caveat: your model must be originated before you can call the simplify function. This is to make sure that all of the segmented paths share the same coordinate space.
Be sure to play this example, and click 'show path names' for comparison.
Fonts and text
To create models based on fonts, use makerjs.models.Text with the new operator. Pass a font object, your text, and a font size. Each character of your text string will become a child model containing the paths for that character.
Maker.js uses Opentype.js by Frederik De Bleser to read TrueType and OpenType fonts. Please visit the Opentype.js GitHub website for details on its API. You will need to know how to load font files before you can use them in Maker.js.
Loading fonts in the browser
Use opentype.load(url, callback)
to load a font from a URL. Since this method goes out the network, it is asynchronous.
The callback gets (err, font)
where font
is a Font
object. Check if the err
is null before using the font.
Previously, all of our examples ran synchronously and we could use document.write
to output a result.
But now we will need to wait for a font file to download. You will have to take this in consideration in your application.
In the Maker.js Playground we can call playgroundRender().
Here on this page we will insert our SVG into a div
in this document:
Loading fonts in Node.js
Use opentype.loadSync(url) to load a font from a file and return a Font object. Throws an error if the font could not be parsed. This only works in Node.js.
Finally, a phenomenon to be aware of is that fonts aren't always perfect. You may encounter cases where paths within a character are self-intersecting or otherwise not forming closed geometries. This is not common, but it is something to be aware of, especially during combine operations.
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:
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.
Chains
Chains are an ordered collection of paths that connect end-to-end. A chain may be endless which means the chain ends at its beginning (and is most likely forming a closed geometry). The paths in a chain might be contained in more than one model. To illustrate this, we will look at the example from the combine operations above:
Above we see two chains: all of the lines in the rectangle form an endless chain, as well as all of the paths of the oval.
Now, let's combine these in a union:
We have combined the rectangle and the oval, and they now form one chain. Yet they are still two models. Next we will add a few more paths to the example:
A circle is a closed geometry. In Maker.js, a circle is also an endless chain. Line1 and line2 form a chain. The diagonal line3 is not part of a chain.
Now that we have these concepts illustrated, let's call makerjs.model.findChains, passing our model, and a function to collect what we found. This function will be passed these three parameters:
- chains: an array of chains that were found. (both endless and non-endless)
- loose: an array of paths that did not connect in a chain.
- layer: the layer name containing the above.
chains array
As expected, we found 3 chains. But what is a chain? A chain is an object with these two properties:- endless: boolean.
- links: array of chain link objects.
- reversed: boolean, explained below.
- walkedPath: a walkPath object, which contains one path. We will explain this type of object in the next section.
- line - a line flows from its origin to its end.
- arc - an arc flows from its startAngle to its endAngle, in the polar (counter-clockwise) direction.
reversed
flag is true, then the path in the chain link was found to flow opposite of the norm.
loose paths array
This is an array of walkPath objects, explained in the next section.Walking a model tree
A model is a tree structure which may contain paths, and it may also contain other models in a heirachy. You can traverse the entire tree by calling makerjs.model.walk with your model and an object with these optional properties:
- onPath: function(walkPath object) - called for every path (in every model) in your tree.
- beforeChildWalk: function(walkModel) - called for every model in your tree, prior to diving deeper down the tree. Return false if you wish to not dive deeper.
- afterChildWalk: function(walkModel) - called for every model in your tree, after returning from a deep dive down the tree.
walkPath object
A walkPath object has these properties:
- layer: the layer name (if any) containing this path.
- modelContext: the model containing this path.
- offset: the absolute coordinates from [0, 0] where this path is located.
- pathContext: the path itself.
- pathId: the id of this path in its parent model.paths container.
- route: array of property names to locate this path from the root of the tree.
- routeKey: a string representation of the route which may safely be used as a unique key identifier for this path.
walkModel object
A walkModel object has these properties:
- childId: the id of this model in its parent model.models container.
- childModel: the model itself
- layer: the layer name (if any) containing this path.
- offset: the absolute coordinates from [0, 0] where this model is located.
- parentModel: the model containing this model.
- route: array of property names to locate this model from the root of the tree.
- routeKey: a string representation of the route which may safely be used as a unique key identifier for this model.
An open geometry is when any path in a drawing is a dead end. A closed geometry is when all path ends meet and there are no dead end paths. A closed geometry forms an enclosed shape.
Examples of Open Geometry:
Examples of Closed Geometry:
Maker.js works with both open and closed geometries. When your desire is to create a three dimensional object, you will probably be using a closed geometry.