Source: trim-aranea.js

"use strict"

/**
 * ...
 * @namespace families.trimAranea
 */

const trimFamilyAranea = ({ lib, swLib }) => {
    const { polygon, square } = lib.primitives
    const { subtract, union } = lib.booleans
    const { rotate, translate, mirror, center } = lib.transforms

    const PHI = 1.6180339887;
    const PHI_INV = 0.6180339887;

    const detailCorner = ({ sideLength }) => {
        const baseSquare = square({ size: Math.hypot(sideLength, sideLength) });

        return rotate([0, 0, Math.PI / 4], baseSquare);
    }

    const extraSmall = ({ controlPoints, detailDepth, styleOpts }) => {
        const cornerPt1 = controlPoints.l0.t1;
        const cornerPt2 = controlPoints.lHalf.t1;
        const baseShape = polygon({
            points: [
                controlPoints.l0.t0,
                cornerPt1,
                cornerPt2,
                controlPoints.lHalf.t0,
            ]
        });
        const baseCorner = detailCorner({ sideLength: detailDepth });
        const corner1 = translate([...cornerPt1, 0], baseCorner);
        const corner2 = translate([...cornerPt2, 0], baseCorner);

        let cutShape = subtract(baseShape, corner1);
        if (!['crown', 'base'].includes(styleOpts)) {
            cutShape = subtract(cutShape, corner2);
        }

        return cutShape;
    }

    const small = ({ controlPoints, detailDepth, styleOpts }) => {
        const cornerPt1 = controlPoints.l0.t1;
        const cornerPt2 = controlPoints.l1.t1;
        const baseShape = polygon({
            points: [
                controlPoints.l0.t0,
                cornerPt1,
                cornerPt2,
                controlPoints.l1.t0,
            ]
        });
        const baseCorner = detailCorner({ sideLength: detailDepth });
        const corner1 = translate([...cornerPt1, 0], baseCorner);
        const corner2 = translate([...cornerPt2, 0], baseCorner);

        let cutShape = subtract(baseShape, corner1);
        if (!['crown', 'base'].includes(styleOpts)) {
            cutShape = subtract(cutShape, corner2);
        }

        return cutShape;
    }

    const smallOrnament1 = ({ controlPoints, detailDepth, styleOpts }) => {
        const baseShape = small({ controlPoints, detailDepth, styleOpts });

        const oPt = controlPoints.o1.t1;
        const bCorner = detailCorner({ sideLength: detailDepth * PHI_INV });
        const oCorner = translate([...oPt, 0], bCorner);

        return subtract(baseShape, oCorner);
    }

    const medium = ({ controlPoints, detailDepth, styleOpts }) => {
        const cornerPt1 = controlPoints.l0.t1;
        const cornerPt2 = controlPoints.l1.t1;
        const cornerPt3 = controlPoints.l1.t2;
        const cornerPt4 = controlPoints.l2.t2;

        const baseShape = polygon({
            points: [
                controlPoints.l0.t0,
                cornerPt1,
                cornerPt2,
                cornerPt3,
                cornerPt4,
                controlPoints.l2.t0,
            ]
        })

        const baseCorner = detailCorner({ sideLength: detailDepth });
        const corner1 = translate([...cornerPt1, 0], baseCorner);
        const corner2 = translate([...cornerPt2, 0], baseCorner);
        const corner3 = translate([...cornerPt3, 0], baseCorner);
        const corner4 = translate([...cornerPt4, 0], baseCorner);

        let cutShape = subtract(baseShape, corner1);
        cutShape = union(cutShape, corner2);
        cutShape = subtract(cutShape, corner3);
        if (!['crown', 'base'].includes(styleOpts)) {
            cutShape = subtract(cutShape, corner4);
        }

        return cutShape;
    }

    const mediumOrnament1 = ({ controlPoints, detailDepth, styleOpts }) => {
        const baseShape = medium({ controlPoints, detailDepth, styleOpts });

        const oPt1 = controlPoints.o2.t2;
        const oPt2 = controlPoints.o1.t1;

        const bCorner = detailCorner({ sideLength: detailDepth * PHI_INV });
        const oCorner1 = translate([...oPt1, 0], bCorner);
        let oCorner2 = translate([...oPt2, 0], bCorner);
        oCorner2 = mirror({ origin: [0, controlPoints.l1.t1[1] / 2, 0], normal: [0, 1, 0] }, oCorner2);

        let cutShape = subtract(baseShape, oCorner1);
        cutShape = subtract(cutShape, oCorner2);

        return cutShape;
    }

    const large = ({ controlPoints, detailDepth, styleOpts }) => {
        const cornerPt1 = controlPoints.l0.t1;
        const cornerPt2 = controlPoints.l1.t1;
        const cornerPt3 = controlPoints.l1.t2;
        const cornerPt4 = controlPoints.l2.t2;
        const cornerPt5 = controlPoints.l2.t3;
        const cornerPt6 = controlPoints.l3.t3;

        const baseShape = polygon({
            points: [
                controlPoints.l0.t0,
                cornerPt1,
                cornerPt2,
                cornerPt3,
                cornerPt4,
                cornerPt5,
                cornerPt6,
                controlPoints.l3.t0,
            ]
        })

        const baseCorner = detailCorner({ sideLength: detailDepth });
        const corner1 = translate([...cornerPt1, 0], baseCorner);
        const corner2 = translate([...cornerPt2, 0], baseCorner);
        const corner3 = translate([...cornerPt3, 0], baseCorner);
        const corner4 = translate([...cornerPt4, 0], baseCorner);
        const corner5 = translate([...cornerPt5, 0], baseCorner);
        const corner6 = translate([...cornerPt6, 0], baseCorner);

        let cutShape = subtract(baseShape, corner1);
        cutShape = union(cutShape, corner2);
        cutShape = subtract(cutShape, corner3);
        cutShape = union(cutShape, corner4);
        cutShape = subtract(cutShape, corner5);
        if (!['crown', 'base'].includes(styleOpts)) {
            cutShape = subtract(cutShape, corner6);
        }

        return cutShape;
    }

    const largeOrnament1 = ({ controlPoints, detailDepth, styleOpts }) => {
        const baseShape = large({ controlPoints, detailDepth, styleOpts });

        const oPt1 = controlPoints.o3.t3;
        const oPt2 = controlPoints.o1.t1;

        const bCorner = detailCorner({ sideLength: detailDepth * PHI_INV });
        const oCorner1 = translate([...oPt1, 0], bCorner);
        let oCorner2 = translate([...oPt2, 0], bCorner);
        oCorner2 = mirror({ origin: [0, controlPoints.l1.t1[1] / 2, 0], normal: [0, 1, 0] }, oCorner2);

        let cutShape = subtract(baseShape, oCorner1);
        cutShape = subtract(cutShape, oCorner2);

        return cutShape;
    }

    /**
     * Creates a set of trimwork profiles
     * @memberof families.trimAranea
     * @instance
     * @param {Object} opts 
     * @param {number} opts.unitHeight - Typical height for basic trim unit
     * @param {number} opts.unitDepth - Typical depth for basic trim unit
     * @param {number} opts.detailDepth - Size of corner details (mm). Defaults to 1/3 of `unitDepth`
     * @param {number} opts.styleOpts - Style options ("base", "crown", "dado"). Defaults to "dado"
     */
    const buildTrimFamily = ({
        unitHeight,
        unitDepth,
        detailDepth,
        styleOpts = 'dado',
    }) => {
        const numLevels = 3;
        const dDepth = detailDepth || unitDepth / 3;
        const levelPoints = {};
        const ornamentPoints = {};
        const thicknessPoints = {};

        for (let levelIdx = 0; levelIdx <= numLevels; levelIdx++) {
            levelPoints[`l${levelIdx}`] = unitHeight * levelIdx;
            thicknessPoints[`t${levelIdx}`] = unitDepth * levelIdx;
            ornamentPoints[`o${levelIdx + 1}`] = unitHeight * levelIdx + (unitHeight * PHI_INV);
        }
        levelPoints[`lHalf`] = unitHeight / 2;

        console.log(levelPoints, thicknessPoints, ornamentPoints);

        const controlPoints = {};

        const getPointsForLevel = (levelPt) => {
            const lPoints = {};
            for (const [tPtName, tPtValue] of Object.entries(thicknessPoints)) {
                lPoints[tPtName] = [tPtValue, levelPt];
            }
            return lPoints;
        }

        for (const [ptName, ptValue] of Object.entries(levelPoints)) {
            controlPoints[ptName] = getPointsForLevel(ptValue);
        }
        for (const [ptName, ptValue] of Object.entries(ornamentPoints)) {
            controlPoints[ptName] = getPointsForLevel(ptValue);
        }

        console.log(controlPoints);

        const crown = {
            extraSmall: center({}, extraSmall({ controlPoints, detailDepth: dDepth, styleOpts: 'crown' })),
            small: center({}, small({ controlPoints, detailDepth: dDepth, styleOpts: 'crown' })),
            medium: center({}, medium({ controlPoints, detailDepth: dDepth, styleOpts: 'crown' })),
            large: center({}, large({ controlPoints, detailDepth: dDepth, styleOpts: 'crown' })),
            smallOrn1: center({}, smallOrnament1({ controlPoints, detailDepth: dDepth, styleOpts: 'crown' })),
            mediumOrn1: center({}, mediumOrnament1({ controlPoints, detailDepth: dDepth, styleOpts: 'crown' })),
            largeOrn1: center({}, largeOrnament1({ controlPoints, detailDepth: dDepth, styleOpts: 'crown' })),
        };
        const dado = {
            extraSmall: center({}, mirror(
                { normal: [0, 1, 0] },
                extraSmall({ controlPoints, detailDepth: dDepth, styleOpts })
            )),
            small: center({}, mirror(
                { normal: [0, 1, 0] },
                small({ controlPoints, detailDepth: dDepth, styleOpts })
            )),
            smallOrn1: center({}, mirror(
                { normal: [0, 1, 0] },
                smallOrnament1({ controlPoints, detailDepth: dDepth, styleOpts })
            )),
            medium: center({}, mirror(
                { normal: [0, 1, 0] },
                medium({ controlPoints, detailDepth: dDepth, styleOpts })
            )),
            mediumOrn1: center({}, mirror(
                { normal: [0, 1, 0] },
                mediumOrnament1({ controlPoints, detailDepth: dDepth, styleOpts })
            )),
            large: center({}, mirror(
                { normal: [0, 1, 0] },
                large({ controlPoints, detailDepth: dDepth, styleOpts })
            )),
            largeOrn1: center({}, mirror(
                { normal: [0, 1, 0] },
                largeOrnament1({ controlPoints, detailDepth: dDepth, styleOpts })
            )),
        };
        const base = {
            extraSmall: center({}, mirror(
                { normal: [0, 1, 0] },
                crown.extraSmall
            )),
            small: center({}, mirror(
                { normal: [0, 1, 0] },
                crown.small
            )),
            smallOrn1: center({}, mirror(
                { normal: [0, 1, 0] },
                crown.smallOrn1
            )),
            medium: center({}, mirror(
                { normal: [0, 1, 0] },
                crown.medium
            )),
            mediumOrn1: center({}, mirror(
                { normal: [0, 1, 0] },
                crown.mediumOrn1
            )),
            large: center({}, mirror(
                { normal: [0, 1, 0] },
                crown.large
            )),
            largeOrn1: center({}, mirror(
                { normal: [0, 1, 0] },
                crown.largeOrn1
            )),
        };

        return {
            crown,
            dado,
            base,
        }
    }

    return {
        buildTrimFamily
    }
}

module.exports = { init: trimFamilyAranea }