function convertChainsTo2D()

in packages/maker.js/src/core/openjscad.ts [205:295]


    function convertChainsTo2D<T>(convertToT: { (c: IChain, maxArcFacet: number): T }, union: IOperate<T>, subtraction: IOperate<T>, modelToExport: IModel, jsCadCagOptions: IJscadCagOptions = {}) {
        const adds: { [layerId: string]: IAdd<T>[] } = {};
        const status = { total: 0, complete: 0 };

        function unionize(phaseStart: number, phaseSpan: number, arr: T[]) {
            let result = arr.shift();
            arr.forEach(el => result = union(result, el));
            status.complete++;

            jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: phaseStart + phaseSpan * status.complete / status.total });

            return result;
        }

        function subtractChains(layerId: string, cs: IChain[]) {
            const subtracts: T[] = [];
            cs.forEach(c => {
                if (!c.endless) return;
                if (c.contains) {
                    addChains(layerId, c.contains);
                }
                status.total++;
                subtracts.unshift(convertToT(c, jsCadCagOptions.maxArcFacet));
            });
            return subtracts;
        }

        function addChains(layerId: string, cs: IChain[]) {
            cs.forEach(c => {
                if (!c.endless) return;
                const add: IAdd<T> = { cag: convertToT(c, jsCadCagOptions.maxArcFacet), subtracts: [] };
                if (c.contains) {
                    const subtracts = subtractChains(layerId, c.contains);
                    if (subtracts.length > 0) {
                        add.subtracts.push(subtracts);
                    }
                }
                status.total++;
                if (!(layerId in adds)) {
                    adds[layerId] = [];
                }
                adds[layerId].unshift(add);
            });
        }

        const options: IFindChainsOptions = {
            pointMatchingDistance: jsCadCagOptions.pointMatchingDistance,
            byLayers: jsCadCagOptions.byLayers,
            contain: true
        };

        jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: 25 });

        const chainsResult = model.findChains(modelToExport, options);
        if (Array.isArray(chainsResult)) {
            addChains('', chainsResult);
        } else {
            for (let layerId in chainsResult) {
                addChains(layerId, chainsResult[layerId]);
            }
        }

        jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: 50 });

        let closedCount = 0;
        for (let layerId in adds) {
            closedCount += adds[layerId].length;
        }
        if (closedCount === 0) {
            jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: 100 });
            throw ('No closed geometries found.');
        }

        const resultMap: { [layerId: string]: T } = {};

        for (let layerId in adds) {
            const flatAdds = adds[layerId].map(add => {
                let result = add.cag;
                add.subtracts.forEach(subtract => {
                    const union = unionize(50, 50, subtract);
                    result = subtraction(result, union);
                })
                return result;
            });
            resultMap[layerId] = unionize(50, 50, flatAdds);
        }

        jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: 100 });

        return options.byLayers ? resultMap : resultMap[''];
    }