export function combine()

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;
    }