Working with chains
Chain theory
Find a single chain
Let's start with a drawing of a rectangle. A rectangle is a model, but we also implicitly know that a rectangle comprises a chain of 4 paths which connect end to end.
Let's find this chain now using makerjs.model.findSingleChain(model):
Now, let's combine two rectangle models in a union.
Notice that a chain will continue unconstrained by the fact that the two rectangle models are independent:
Chain links
Each path in the chain is represented by a ChainLink wrapper object in the links array.
This ChainLink wrapper tells us how the path relates to the rest of the chain.
Each ChainLink array element is connected to the next and previous element.
If the chain is endless, then the last array element is connected to the first, and vice-versa.
ChainLink object
- endPoints: Point array - The endpoints of the path, in absolute coords.
- pathLength: number - Length of the path.
- reversed: boolean - Path flows forwards or reverse.
- walkedPath: WalkPath - Reference to the path.
Natural path flow
The three types of paths in Maker.js are line, arc and circle. A circle has no end points, and therefore cannot connect to other paths to form a chain. Lines and arcs however, may connect to other lines or arcs at their end points to form chains. In context of a chain, lines and arcs each have a concept of a directional flow:- 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.
Order of chain links
You may have already noticed that we have not specified the order of the links array. For example,
given a chain with 3 links A, B, C - the order may also be C, B, A. So, what is the order of the links in a chain?
The answer is: it is quite arbitrary. There is no guarantee that the order will be the same each time across JavaScript runtime environments.
Reverse a chain
If you have deduced that your chain needs to be reversed, you can call makerjs.chain.reverse(chain).Beginning of a chain
Another issue with endless chains is, which link is at the beginning of the links array? The answer once again, is that it is unpredictable. If you need to specify which link is at the beginning of an endless chain, you have 2 functions at your disposal:- makerjs.chain.cycle(chain, amount) - Shift the links of an endless chain.
- makerjs.chain.startAt(chain, routeKey) - Set the beginning of an endless chain to a known routeKey of a path.
Clockwise
If you have an endless chain, you also have the option to see if your links flow in a clockwise direction. Call makerjs.measure.isChainClockwise(chain) which returns a boolean, unless your chain has one link which is a circle - in which case it will return null.Find multiple chains
You can find multiple chains by calling makerjs.model.findChains(model),
which will return an array of chains, sorted by largest to smallest on the pathLength property.
We can find 2 chains in this drawing with 2 rectangles:
Containment
Instead of a "flat" array, we can see the containment of chains by also passing an{ contain: true }
object to
makerjs.model.findChains(model, options):
Alternating flow directions
There are scenarios where you may need contained chains to flow in the opposite direction of their containing chain. This will require extra computation on each chain to test its direction. If you need this, use{ contain: { alternateDirection: true } }
in your options. In the returned chains array,
the outmost chains will flow clockwise:
Isolating within layers
You can find chains within layers by passing{ byLayers: true }
in your options.
This will not return an array, but it will return an object map with keys being the layer names, and values being the
array of chains for that layer:
Finding loose paths
You may also wish to find paths that are not part of a chain. This will require you to pass a callback function
which 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.
Chain to key points
If you want a "low poly" representation of a chain, call
makerjs.model.toKeyPoints(chain, [optional] maxArcFacet)
passing your chain, and the maximum length of facets on arcs & circles:
Chain to points
To get points consistently spaced along a chains, call
makerjs.model.toPoints(chain, distance)
passing your chain, and the distance between points:
Hint: you can use the pathLength property of the chain to make sure your distance divides equally on the entire chain:
Chain fillet
A fillet can be added between all paths in a chain by calling
makerjs.chain.fillet with these parameters:
- chainToFillet: the chain containing paths which will be modified to have fillets at their joints.
- filletRadius: radius of the fillets.
Basic example
Let's draw a few lines that we know will form a chain: Next we will find all of the chains in our model. We are expecting that there will only be one chain, so we will just takechains[0]
.
Then we will add fillets to that chain:
Advanced example
We can improve upon the design of the truss example by adding fillets to the interior shapes. Let's review the truss design: We know that there are 5 chains in this drawing. When we find chains, the array of found chains will be sorted by pathLength (the total length of all paths in each chain), so we know that the first chain represents the outermost perimeter of the drawing. Therefore we will ignorechains[0]
and create a for...loop beginning at chains[1]
:
Chain to new model
Once you have a chain, you can also convert it to a model, so that you can return to using the familiar model API
with your shapes. Call makerjs.chain.toNewModel(chain, detachFromOldModel: boolean).
Layout on a chain
Similar to layout on a path, you can use a chain as a layout guide for a row of child models within a model.
Call makerjs.layout.childrenOnChain(parentModel: Model, onChain: chain),
the x-axis will be projected onto your onChain:
There are additional optional parameters to this makerjs.layout.childrenOnChain:
- baseline: number [default: 0]
- reversed: boolean [default: false]
- contain: boolean [default: false]
- rotate: boolean [default: true]
When 2 or more paths connect end to end, we call this a chain. Here are 3 lines that connect end to end, forming a chain with 3 links; each line path is considered a link in the chain:
When the links do not have any loose ends and connect to each other, we call this an endless chain. Frequently, endless chains are used to represent a closed geometry. Here is an endless chain made up of 2 lines and an arc:
A circle is a closed geometry by nature. In Maker.js, a single circle comprises an endless chain with only one link.
A chain may contain other chains, recursively. A chain may only contain others if it is an endless chain itself. Here are some examples of one chain containing another:
Here is a model which does not have any chains. Although the lines overlap, they do not connect end to end.
Chains are implicit
You do not explicitly define chains in your drawing, chains are something that Maker.js finds in your model(s).Finding
Call one of these two functions to find chains, which will return one or more Chain objects:Chain object type