Source: swcad-js-profiles/src/joint-panel/index.js

"use strict"

/**
 * Jointed panels
 * @author R. J. Salvador
 * @namespace jointPanel
 * @memberof profiles
 * @since 0.13.5
 */

const jointPanelInit = ({ jscad, swcadJs }) => {
    const {
        cuboid,
    } = jscad.primitives

    const {
        align,
        rotate,
    } = jscad.transforms

    const {
        subtract,
        union,
        scission
    } = jscad.booleans

    const {
        project
    } = jscad.extrusions

    const { TAU } = jscad.maths.constants

    const {
        math,
        position
    } = swcadJs.calcs

    const {
        standards,
    } = swcadJs.data

    const {
        connections,
    } = swcadJs.profiles


    //==============================================================================


    /**
     * Builds default values and opts for the model
     * @param {*} opts 
     * @returns default values and opts
     * @memberof newModelName
     * @access private
     */
    const jointPanelDefaults = () => {
        /** Specific value declarations */
        const defaultValues = {
            constants: {
                sampleThickness: 1, // scission only works with 3D objects. Need a filler thickness for now
            },
            opts: {
                axis: 'x',
                jointNumConnectors: 3,
            },
            dims: {
                size: [
                    math.inchesToMm(2),
                    math.inchesToMm(4),
                    math.inchesToMm(1),
                ],
                jointWidth: math.inchesToMm(3 / 8),
                jointMargin: math.inchesToMm(1 / 4),
            },
            points: {
                centre: [0, 0, 0]
            },
            types: {
                tab: { id: 'tab', desc: 'Tab' },
                dovetail: { id: 'dovetail', desc: 'Dovetail' },
            },
        }

        /** Options used by SW models */
        const standardOpts = {
            type: defaultValues.types.tab.id,
            scale: 1,
            interfaceThickness: standards.INTERFACE_THICKNESS,
            fitGap: standards.FIT_GAP,
        }

        /** Computed values for option defaults */
        const defaultOpts = {
            ...standardOpts,
            size: defaultValues.dims.size,
            jointWidth: defaultValues.dims.jointWidth,
            jointMargin: defaultValues.dims.jointMargin,
            jointNumConnectors: defaultValues.opts.jointNumConnectors,
            axis: defaultValues.opts.axis,
        }

        return {
            opts: defaultOpts,
            vals: defaultValues,
        }
    }


    //------------------------------------------------------------------------------


    /**
     * Initializes options with user input
     * @param {*} opts 
     * @returns model properties
     * @memberof newModelName
     * @access private
     */
    const jointPanelOpts = (opts) => {
        const defaults = jointPanelDefaults()

        // User options
        const {
            size = defaults.opts.size,
            jointWidth = defaults.opts.jointWidth,
            jointMargin = defaults.opts.jointMargin,
            jointNumConnectors = defaults.opts.jointNumConnectors,
            axis = defaults.opts.axis,
            type = defaults.opts.type,
            scale = defaults.opts.scale,
            interfaceThickness = defaults.opts.interfaceThickness,
            fitGap = defaults.opts.fitGap,
        } = opts

        const stdOpts = {
            type,
            scale,
            interfaceThickness,
            fitGap,
        }

        const initOpts = {
            size,
            jointWidth,
            jointMargin,
            jointNumConnectors,
            axis,
            ...stdOpts,
        }


        return initOpts
    }


    //------------------------------------------------------------------------------


    /**
     * Builds model properties from the given opts
     * @param {*} opts 
     * @returns model properties
     * @memberof newModelName
     * @access private
     */
    const modelProps = (opts) => {
        const defaults = jointPanelDefaults()

        const {
            size,
            jointWidth,
            jointMargin,
            jointNumConnectors,
            axis,
            type,
            scale,
            interfaceThickness,
            fitGap,
        } = opts

        /* ----------------------------------------
        * Prop calculations
        * ------------------------------------- */

        const width = size[0]
        const depth = size[1]

        const midPoint = [
            width / 2,
            depth / 2,
        ]

        let jMargin = jointMargin
        if (typeof jointMargin == 'number') {
            jMargin = [jointMargin, jointMargin]
        }
        const totalJointWidth = jMargin[1] * 2 + jointWidth

        /* ----------------------------------------
        * Preparing Model Properties, Dimensions
        * ------------------------------------- */

        /** Constant values for model */
        const modelConstants = {
            sampleThickness: defaults.vals.constants.sampleThickness,
        }

        /** Derived user options for the model */
        const modelOpts = {
            type,
            scale,
            axis,
            jointNumConnectors,
        }

        /** Various dimensions for model */
        const modelDims = {
            size,
            width,
            depth,
            jointWidth,
            jointMargin: jMargin,
            totalJointWidth,
            interfaceThickness,
            fitGap,
        }

        /** Various key points for model */
        const modelPoints = {
            centrePt: midPoint,
        }

        /** Components used by model */
        const modelComponents = {
        }

        /* ---------------------------------------------
        *  Model Properties
        * ----------------------------------------------
        * Properties accessible to all model functions.
        * --------------------------------------------- */

        const modelProperties = {
            metadata: {
                id: '9999',
                name: 'New Model',
                project: 'New Project',
                author: 'Somebody Somewhere',
                organization: 'Salvador Workshop',
                client: null,
            },
            constants: modelConstants,
            opts: modelOpts,
            dims: modelDims,
            points: modelPoints,
            components: modelComponents,
        }


        return modelProperties
    }


    //------------------------------------------------------------------------------

    /* ----------------------------------------
    * MODELLING FUNCTIONS
    * ------------------------------------- */

    /**
     * Generate one-joint rectangular panel
     * @param {*} opts 
     * @returns Array with model, parts, and properties: [`geom3`, `Object.<string, geom3>`, `Object.<string, any>`]
     * @memberof profiles.jointPanel
     * @since 0.13.5
     */

    const oneJointRectPanel = (opts) => {
        const defaults = jointPanelDefaults()
        const initOpts = jointPanelOpts(opts)
        const modelProperties = modelProps(initOpts)

        const {
            sampleThickness
        } = modelProperties.constants

        const {
            type,
            axis,
            jointNumConnectors,
        } = modelProperties.opts

        const {
            size,
            width,
            depth,
            jointWidth,
            jointMargin,
            totalJointWidth,
        } = modelProperties.dims

        const {
            centrePt
        } = modelProperties.points

        // along X axis
        let jointSize = [width, totalJointWidth]

        if (axis == 'y') {
            // along Y axis
            jointSize = [depth, totalJointWidth]
        }

        const jointOpts = {
            size: jointSize,
            interfaceMargin: jointMargin,
            numConnectors: jointNumConnectors,
        }

        let jointData = connections.tabRow(jointOpts)
        if (type == 'dovetail') {
            jointData = connections.dovetailRow(jointOpts)
        }

        let jointCut = position.ctr(jointData[1].cut)
        if (axis == 'y') {
            jointCut = rotate([0, 0, TAU / 4], jointCut)
        }
        const oneJointRectBasePanel = cuboid({
            size: [width, depth, sampleThickness],
        })

        const cutPanel = subtract(
            oneJointRectBasePanel,
            jointCut
        )
        const cutParts = scission(cutPanel)
        const oProfiles = [
            align({ modes: ['center', 'center', 'center'] }, cutParts[1]),
            align({ modes: ['center', 'center', 'center'] }, cutParts[0]),
        ]
        const outProfiles = [
            project({}, oProfiles[0]),
            project({}, oProfiles[1]),
        ]

        const mainModel = [
            outProfiles[0],
            outProfiles[1],
        ]

        const modelParts = {
            male: outProfiles[0],
            female: outProfiles[1],
            cut: jointCut,
        }

        return [mainModel, modelParts, modelProperties]
    }

    /**
     * Generate two-joint rectangular panel
     * @param {*} opts 
     * @returns Array with model, parts, and properties: [`geom3`, `Object.<string, geom3>`, `Object.<string, any>`]
     * @memberof profiles.jointPanel
     * @since 0.13.5
     */

    const twoJointRectPanel = (opts) => {
        const defaults = jointPanelDefaults()
        const initOpts = jointPanelOpts(opts)
        const modelProperties = modelProps(initOpts)

        const {
            sampleThickness
        } = modelProperties.constants

        const {
            type,
            axis,
            jointNumConnectors,
        } = modelProperties.opts

        const {
            size,
            width,
            depth,
            jointWidth,
            jointMargin,
            totalJointWidth,
        } = modelProperties.dims

        const {
            centrePt
        } = modelProperties.points

        let jNumConnectors = jointNumConnectors
        if (typeof jointNumConnectors == 'number') {
            jNumConnectors = [
                jointNumConnectors,
                jointNumConnectors
            ]
        }

        const jointSizeX = [width, totalJointWidth]
        const jointOptsX = {
            size: jointSizeX,
            interfaceMargin: jointMargin,
            numConnectors: jNumConnectors[0],
        }
        let jointDataX = connections.tabRow(jointOptsX)
        if (type == 'dovetail') {
            jointDataX = connections.dovetailRow(jointOptsX)
        }
        const jointCutX = position.ctr(jointDataX[1].cut)

        const jointSizeY = [depth, totalJointWidth]
        const jointOptsY = {
            size: jointSizeY,
            interfaceMargin: jointMargin,
            numConnectors: jNumConnectors[1],
        }
        let jointDataY = connections.tabRow(jointOptsY)
        if (type == 'dovetail') {
            jointDataY = connections.dovetailRow(jointOptsY)
        }
        const jointCutY = rotate([0, 0, TAU / 4], position.ctr(jointDataY[1].cut))

        const comboCut = union(jointCutX, jointCutY)

        const twoJointRectBasePanel = cuboid({
            size: [width, depth, sampleThickness],
        })

        const cutPanel = subtract(
            twoJointRectBasePanel,
            comboCut,
        )
        const cutParts = scission(cutPanel)
        const oProfiles = [
            align({ modes: ['center', 'center', 'center'] }, cutParts[0]),
            align({ modes: ['center', 'center', 'center'] }, cutParts[1]),
            align({ modes: ['center', 'center', 'center'] }, cutParts[2]),
            align({ modes: ['center', 'center', 'center'] }, cutParts[3]),
        ]
        const outProfiles = [
            project({}, oProfiles[0]),
            project({}, oProfiles[1]),
            project({}, oProfiles[2]),
            project({}, oProfiles[3]),
        ]

        const mainModel = outProfiles

        const modelParts = {
            model: outProfiles,
            cut: comboCut,
        }

        return [mainModel, modelParts, modelProperties]
    }

    return {
        defaults: jointPanelDefaults,
        props: modelProps,
        oneJointRectPanel,
        twoJointRectPanel,
    }
}

module.exports = {
    init: jointPanelInit
}