diff --git a/packages/maker.js/src/core/chain.ts b/packages/maker.js/src/core/chain.ts index e666af7f..bd61bc74 100644 --- a/packages/maker.js/src/core/chain.ts +++ b/packages/maker.js/src/core/chain.ts @@ -560,9 +560,15 @@ namespace MakerJs.chain { /** * @private */ - function removeDuplicateEnds(endless: boolean, points: IPoint[]) { + function removeDuplicateEnds(endless: boolean, points: IChainLinkKeyPoint[]); + function removeDuplicateEnds(endless: boolean, points: IPoint[]); + function removeDuplicateEnds(endless: boolean, points: (IPoint | IChainLinkKeyPoint)[]) { if (!endless || points.length < 2) return; - if (measure.isPointEqual(points[0], points[points.length - 1], .00001)) { + var firstPoint = points[0]; + var lastPoint = points[points.length - 1]; + var isPoint = Array.isArray(firstPoint); + if (isPoint ? measure.isPointEqual(firstPoint as IPoint, lastPoint as IPoint, .00001) : + measure.isPointEqual((firstPoint as IChainLinkKeyPoint).keyPoint, (lastPoint as IChainLinkKeyPoint).keyPoint, .00001)) { points.pop(); } } @@ -575,8 +581,40 @@ namespace MakerJs.chain { * @param maxPoints Maximum number of points to retrieve. * @returns Array of points which are on the chain spread at a uniform interval. */ - export function toPoints(chainContext: IChain, distanceOrDistances: number | number[], maxPoints?: number): IPoint[] { - var result: IPoint[] = []; + export function toPoints(chainContext: IChain, distanceOrDistances: number | number[], maxPoints?: number): IPoint[]; + + /** + * Get points along a chain of paths. + * + * @param chainContext Chain of paths to get points from. + * @param distance Numeric distance along the chain between points, or numeric array of distances along the chain between each point. + * @param callback Callback function when points are found. + * @param maxPoints Maximum number of points to retrieve. + * @returns Array of points which are on the chain spread at a uniform interval. + */ + export function toPoints(chainContext: IChain, distanceOrDistances: number | number[], callback: IChainPointsCallback, maxPoints?: number): IPoint[]; + + export function toPoints(chainContext: IChain, distanceOrDistances: number | number[], ... args: any): IPoint[] { + + var maxPoints: number; + var callback: IChainPointsCallback; + + switch (args.length) { + case 1: + if (typeof args[0] === 'function') { + callback = args[0]; + } else { + maxPoints = args[0]; + } + break; + + case 2: + callback = args[0]; + maxPoints = args[1]; + break; + } + + var result: IChainLinkKeyPoint[] = []; var di = 0; var t = 0; var distanceArray: number[]; @@ -595,10 +633,17 @@ namespace MakerJs.chain { if (link.reversed) { r = 1 - r; } + let chainPoint: IChainLinkKeyPoint = { + keyPoint: point.add(point.middle(wp.pathContext, r), wp.offset), + link: link, + ratio: r + }; + result.push(chainPoint); - result.push(point.add(point.middle(wp.pathContext, r), wp.offset)); - - if (maxPoints && result.length >= maxPoints) return result; + if (maxPoints && result.length >= maxPoints) { + if (callback) callback(result); + return result.map(x => x.keyPoint); + } var distance: number; if (distanceArray) { @@ -606,7 +651,8 @@ namespace MakerJs.chain { di++; if (di > distanceArray.length) { - return result; + if (callback) callback(result); + return result.map(x => x.keyPoint); } } else { @@ -615,12 +661,12 @@ namespace MakerJs.chain { t += distance; } - t -= len; } removeDuplicateEnds(chainContext.endless, result); - return result; + if (callback) callback(result); + return result.map(x => x.keyPoint); } /** @@ -646,6 +692,7 @@ namespace MakerJs.chain { } var offsetPathPoints = keyPoints.map(p => point.add(p, wp.offset)); + result.push.apply(result, offsetPathPoints); } } diff --git a/packages/maker.js/src/core/layout.ts b/packages/maker.js/src/core/layout.ts index d8fa09d2..a6e4675c 100644 --- a/packages/maker.js/src/core/layout.ts +++ b/packages/maker.js/src/core/layout.ts @@ -181,9 +181,10 @@ namespace MakerJs.layout { * @param reversed Flag to travel along the chain in reverse. Default is false. * @param contain Flag to contain the children layout within the length of the chain. Default is false. * @param rotate Flag to rotate the child to mitered angle. Default is true. + * @param rotateAlongPath Flag to rotate the child along the layout path. Default is false. Works only if rotate is set true. * @returns The parentModel, for cascading. */ - export function childrenOnChain(parentModel: IModel, onChain: IChain, baseline = 0, reversed = false, contain = false, rotated = true) { + export function childrenOnChain(parentModel: IModel, onChain: IChain, baseline = 0, reversed = false, contain = false, rotated = true, rotateAlongPath = false) { var result = getChildPlacement(parentModel, baseline); var cpa = result.cpa; @@ -206,8 +207,15 @@ namespace MakerJs.layout { relatives.shift(); } + var alongPathAngles: number[] = []; //chain.toPoints always follows the chain in its order, from beginning to end. This is why we needed to contort the points input - points = chain.toPoints(onChain, relatives); + points = chain.toPoints(onChain, relatives, + (chainPoints)=> { + for(let i = 0; i < chainPoints.length; i++) + { + alongPathAngles.push(angle.ofPointInDegrees(chainPoints[i].link.endPoints[0], chainPoints[i].link.endPoints[1])); + } + }); if (points.length < cpa.length) { //add last point of chain, since our distances exceeded the chain @@ -231,7 +239,7 @@ namespace MakerJs.layout { if (cpa.length > 1) { cpa.forEach((cp, i) => { - cp.angle = angles[i]; + cp.angle = rotateAlongPath ? alongPathAngles[i+1] : angles[i]; cp.origin = points[i]; }); } else { diff --git a/packages/maker.js/src/core/maker.ts b/packages/maker.js/src/core/maker.ts index 58949606..481a187a 100644 --- a/packages/maker.js/src/core/maker.ts +++ b/packages/maker.js/src/core/maker.ts @@ -782,6 +782,23 @@ namespace MakerJs { alternateDirection?: boolean; } + /** + * A point reference in a path. + */ + export interface IChainLinkKeyPoint{ + keyPoint: IPoint; + link: IChainLink; + ratio: number | undefined; + } + + /** + * Callback signature for chain.toPoints(). + */ + export interface IChainPointsCallback { + + (chainPoints: IChainLinkKeyPoint[]): void; + } + /** * Reference to a model within a model. */