in packages/maker.js/src/core/combine.ts [348:422]
export function combine(modelA: IModel, modelB: IModel, includeAInsideB: boolean = false, includeAOutsideB: boolean = true, includeBInsideA: boolean = false, includeBOutsideA: boolean = true, options?: ICombineOptions) {
var opts: ICombineOptions = {
trimDeadEnds: true,
pointMatchingDistance: .005,
out_deleted: [{ paths: {} }, { paths: {} }]
};
extendObject(opts, options);
opts.measureA = opts.measureA || new measure.Atlas(modelA);
opts.measureB = opts.measureB || new measure.Atlas(modelB);
//make sure model measurements capture all paths
opts.measureA.measureModels();
opts.measureB.measureModels();
if (!opts.farPoint) {
var measureBoth = measure.increase(measure.increase({ high: [null, null], low: [null, null] }, opts.measureA.modelMap['']), opts.measureB.modelMap['']);
opts.farPoint = point.add(measureBoth.high, [1, 1]);
}
var pathsA = breakAllPathsAtIntersections(modelA, modelB, true, opts.measureA, opts.measureB, opts.farPoint);
var pathsB = breakAllPathsAtIntersections(modelB, modelA, true, opts.measureB, opts.measureA, opts.farPoint);
checkForEqualOverlaps(pathsA.overlappedSegments, pathsB.overlappedSegments, opts.pointMatchingDistance);
function trackDeleted(which: number, deletedPath: IPath, routeKey: string, reason: string) {
addPath(opts.out_deleted[which], deletedPath, 'deleted');
var p = deletedPath as IPathRemoved;
p.reason = reason;
p.routeKey = routeKey;
}
for (var i = 0; i < pathsA.crossedPaths.length; i++) {
addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, true, opts.measureA, (p, id, reason) => trackDeleted(0, p, id, reason));
}
for (var i = 0; i < pathsB.crossedPaths.length; i++) {
addOrDeleteSegments(pathsB.crossedPaths[i], includeBInsideA, includeBOutsideA, false, opts.measureB, (p, id, reason) => trackDeleted(1, p, id, reason));
}
var result: IModel = { models: { a: modelA, b: modelB } };
if (opts.trimDeadEnds) {
var shouldKeep: IWalkPathBooleanCallback;
//union
if (!includeAInsideB && !includeBInsideA) {
shouldKeep = function (walkedPath: IWalkPath): boolean {
//When A and B share an outer contour, the segments marked as duplicate will not pass the "inside" test on either A or B.
//Duplicates were discarded from B but kept in A
for (var i = 0; i < pathsA.overlappedSegments.length; i++) {
if (pathsA.overlappedSegments[i].duplicate && walkedPath.pathContext === pathsA.overlappedSegments[i].addedPath) {
return false;
}
}
//default - keep the path
return true;
}
}
removeDeadEnds(result, null, shouldKeep, (wp, reason) => {
var which = wp.route[1] === 'a' ? 0 : 1;
trackDeleted(which, wp.pathContext, wp.routeKey, reason)
});
}
//pass options back to caller
extendObject(options, opts);
return result;
}