Model trees
Model tree structures
Routes
We know that we are able to refer to deep objects by using the dot notation:
var bottomRight_bolt = plate.models.bolts.paths.BottomRight_bolt;
The reference from plate
to BottomRight_bolt
is hard-coded. Suppose that we had a
model plate2
which was a duplicate of plate
- we would need to have to hard-code
the reference to its BottomRight_bolt
:
var bottomRight_bolt2 = plate2.models.bolts.paths.BottomRight_bolt;
Instead of hard-coded dot notation, we can have an abstract way of referencing deep objects by using a route.
It is simply an array of strings that represent the segments names between the dots. We do not put the root object in a route.
A route that we can apply to both plate
and plate2
would be:
var route = ["models", "bolts", "paths", "BottomRight_bolt"];
Travel a route
Use makerjs.travel(rootModel, route) to get to a child object in rootModel via a route. This function will return an object with these 2 properties:
- result: model or path - the object referenced by the route
- offset: point - the offset of the result object's origin from the rootModel's origin
//mounting plate
var makerjs = require('makerjs');
var plate = {
models: {
outer: makerjs.model.center(new makerjs.models.RoundRectangle(120, 100, 10)),
bolts: makerjs.model.center(new makerjs.models.BoltRectangle(100, 80, 5))
},
paths: {
hole: new makerjs.paths.Circle(25)
}
};
var plate2 = makerjs.cloneObject(plate);
plate2.origin = [130, 0];
//route to the BottomRight_bolt circle
var route = ["models", "bolts", "paths", "BottomRight_bolt"];
//create a local variables for BottomRight_bolt holes
var bottomRight_bolt = makerjs.travel(plate, route).result;
bottomRight_bolt.radius = 2;
var bottomRight_bolt2 = makerjs.travel(plate2, route).result;
bottomRight_bolt2.radius = 3;
var plates = {
models: {
plate: plate,
plate2: plate2
}
};
var svg = makerjs.exporter.toSVG(plates);
document.write(svg);
Patterns in Routes
Notice that the schema for Maker.js models has a pattern of models.modelName
and paths.pathName
.
There are always 2 segments between model and/or path objects. So, in any given route to an object, you can always get to its parent
by subtracting the last 2 array elements of the route. We will use Array.slice(0, -2)
to make a copy of the route array without the last 2 elements:
//mounting plate
var makerjs = require('makerjs');
var plate = {
models: {
outer: makerjs.model.center(new makerjs.models.RoundRectangle(120, 100, 10)),
bolts: makerjs.model.center(new makerjs.models.BoltRectangle(100, 80, 5))
},
paths: {
hole: new makerjs.paths.Circle(25)
}
};
var plate2 = makerjs.cloneObject(plate);
plate2.origin = [130, 0];
//route to the BottomRight_bolt circle
var route = ["models", "bolts", "paths", "BottomRight_bolt"];
//create a local variables for BottomRight_bolt holes
var bottomRight_bolt = makerjs.travel(plate, route).result;
bottomRight_bolt.radius = 2;
//subtract 2 elements to get the parent
var parentRoute = route.slice(0, -2);
var bolts = makerjs.travel(plate2, parentRoute).result;
//modify children
delete bolts.paths.TopLeft_bolt;
delete bolts.paths.BottomRight_bolt;
var plates = {
models: {
plate: plate,
plate2: plate2
}
};
var svg = makerjs.exporter.toSVG(plates);
document.write(svg);
Route Keys
Additionally, we can "flatten" a route array into a string, known as a route key, by calling makerjs.createRouteKey(route) and passing a route. Every route key is of course unique in the scope of the root object. It may used as a unique id of a child path or model.
Walking a model tree
You can traverse a model tree by calling makerjs.model.walk with your model and an object with these optional properties:
property name | property type | description |
---|---|---|
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.
Example
In this example we will create a RoundRectangle and walk its tree. We have an onPath
function that will get called for every path in the model. If the path is an arc, we will invert it:
//render a model using mixed units
var makerjs = require('makerjs');
function invertArc(arc) {
var chord = new makerjs.paths.Chord(arc);
var midPoint = makerjs.point.middle(chord);
makerjs.path.rotate(arc, 180, midPoint);
}
var shape = new makerjs.models.RoundRectangle(100, 50, 10);
var walkOptions = {
onPath: function (wp) {
if (wp.pathContext.type === 'arc') {
invertArc(wp.pathContext);
}
}
};
makerjs.model.walk(shape, walkOptions);
var svg = makerjs.exporter.toSVG(shape);
document.write(svg);
Next: Working with chains.
A model is a tree structure which may contain paths, and it may also contain other models in a heirachy. Let's look at an example:
If we represent this drawing as a conceptual tree structure, it would look like this:
(You may notice that this structure is reminiscent of a folder system on your computer.) We can easily traverse the tree when starting at the root. For example, let's change the radius of the BottomRight_bolt hole:
We can access the BottomRight_bolt circle from the
plate
object downward. Notice that in this tree structure, you cannot access upwardly. Theplate
object contains thebolt
model which contains theBottomRight_bolt
path, butBottomRight_bolt
does not have a reference to its container. There are no properties of thebottomRight_bolt
circle object which access anything up the tree. It also does not have any references to its sibling circles withinbolt.paths
.Downward-only access is the nature of a simple object tree structure. We can overcome this using routes.