Source: swcad-js-profiles/src/shapes/rectangle/reinforced-rect.js

"use strict"

const reinforcedRectInit = ({ jscad, swcadJs }) => {
    const {
        square,
        circle,
    } = jscad.primitives

    const {
        translate,
    } = jscad.transforms

    const {
        union,
    } = jscad.booleans

    const {
        hull,
        hullChain
    } = jscad.hulls

    const {
        math,
    } = swcadJs.calcs

    const {
        standards,
    } = swcadJs.data

    const {
        reinforcement,
    } = swcadJs.calcs.geometry


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


    /**
     * Builds default values and opts for the model
     * @param {*} opts 
     * @returns default values and opts
     * @memberof reinforcedRect
     * @access private
     */
    const reinforcedRectDefaults = () => {
        /** Specific value declarations */
        const defaultValues = {
            constants: {
                reinforcementPatterns: ['x', 'cross', 'diamond', 'full'],
            },
            dims: {
                size: [
                    math.inchesToMm(3),
                    math.inchesToMm(4),
                ],
                reinforcementThickness: [
                    standards.LG_REINFORCEMENT_WIDTH,
                    standards.MD_REINFORCEMENT_WIDTH,
                    standards.SM_REINFORCEMENT_WIDTH,
                ],
            },
            points: {
                centrePt: [0, 0, 0]
            },
            types: {
                default: standards.types.TYPE_DEFAULT,
                alt: standards.types.TYPE_ALT,
            },
        }

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

        /** Computed values for option defaults */
        const defaultOpts = {
            ...standardOpts,
            size: defaultValues.dims.size,
            reinforcementPattern: defaultValues.constants.reinforcementPatterns[0],
            reinforcementThickness: defaultValues.dims.reinforcementThickness,
        }

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


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


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

        // User options
        const {
            size = defaults.opts.size,
            reinforcementPattern = defaults.opts.reinforcementPattern,
            reinforcementThickness = defaults.opts.reinforcementThickness,
            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,
            reinforcementPattern,
            reinforcementThickness,
            ...stdOpts,
        }


        return initOpts
    }

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


    /**
     * Builds model properties from the given opts
     * @param {*} opts 
     * @returns model properties
     * @memberof reinforcedRect
     * @access private
     */
    const reinforcedRectProps = (opts) => {
        const defaults = reinforcedRectDefaults()

        const {
            size,
            reinforcementPattern,
            reinforcementThickness,
            type,
            scale,
            interfaceThickness,
            fitGap,
        } = opts

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

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

        let rThickness = [
            defaults.vals.dims.reinforcementThickness,
            defaults.vals.dims.reinforcementThickness,
            defaults.vals.dims.reinforcementThickness,
        ]

        if (typeof reinforcementThickness == 'number') {
            rThickness = [
                reinforcementThickness,
                reinforcementThickness,
                reinforcementThickness,
            ]
        } else if (Array.isArray(reinforcementThickness)) {
            if (reinforcementThickness.length == 3 && typeof reinforcementThickness[0] == 'number') {
                rThickness = [
                    reinforcementThickness[0],
                    reinforcementThickness[1],
                    reinforcementThickness[2],
                ]
            }
            if (reinforcementThickness.length == 2 && typeof reinforcementThickness[0] == 'number') {
                rThickness = [
                    reinforcementThickness[0],
                    reinforcementThickness[1],
                    reinforcementThickness[1],
                ]
            }
        }

        const reinforcementDataSize = [
            size[0] - reinforcementThickness[0],
            size[1] - reinforcementThickness[0],
        ]
        const reinforcementData = reinforcement.reinforcedRectangle({
            size: reinforcementDataSize,
            reinforcementPattern: reinforcementPattern,
        })

        const reinforcementNodes = [
            circle({ radius: rThickness[0] / 2 }),
            circle({ radius: rThickness[1] / 2 }),
            circle({ radius: rThickness[2] / 2 }),
        ]

        const cornerNode = square({ size: rThickness[0] })

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

        /** Constant values for model */
        const modelConstants = {
        }

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

        /** Various dimensions for model */
        const modelDims = {
            size,
            width,
            depth,
            reinforcementThickness: rThickness,
            interfaceThickness,
            fitGap,
        }

        /** Various key points for model */
        const modelPoints = {
            centrePt: defaults.vals.points.centrePt,
            ...reinforcementData.points,
        }

        /** Components used by model */
        const modelComponents = {
            reinforcementNodes,
            cornerNode,
        }

        /* ---------------------------------------------
        *  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
    }


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


    /**
     * Reinforced rectangle
     * @param {*} opts 
     * @returns Array with model, parts, and properties: [`geom3`, `Object.<string, geom3>`, `Object.<string, any>`]
     * @memberof profiles.shapes.rectangle
     */
    const reinforcedRect = (opts) => {
        const defaults = reinforcedRectDefaults()
        const initOpts = reinforcedRectOpts(opts)
        const modelProperties = reinforcedRectProps(initOpts)

        /* ----------------------------------------
         * Modelling, Component/Assembly Modules
         * ------------------------------------- */

        const rectOutline = (modelProps) => {
            const {
                corners,
            } = modelProps.points
            const {
                cornerNode,
            } = modelProps.components

            const hullPts = corners
            hullPts.push(hullPts[0])

            const hullNodes = hullPts.map(hullPt => {
                return translate([hullPt[0], hullPt[1], 0], cornerNode)
            })

            return hullChain(hullNodes)
        }

        const rectPrimaryBracing = (modelProps) => {
            const {
                primaryLines,
            } = modelProps.points

            const {
                reinforcementNodes,
            } = modelProps.components

            let returnBracing = null

            const braceLines = primaryLines.map(pLine => {
                const lineStartPt = pLine[0]
                const lineEndPt = pLine[1]

                const nodeStart = translate(lineStartPt, reinforcementNodes[1])
                const nodeEnd = translate(lineEndPt, reinforcementNodes[1])

                return hull(nodeStart, nodeEnd)
            })

            if (braceLines.length > 0) {
                returnBracing = union(...braceLines)
            }

            return braceLines
        }

        const rectSecondaryBracing = (modelProps) => {
            const {
                secondaryLines,
            } = modelProps.points

            const {
                reinforcementNodes,
            } = modelProps.components

            let returnBracing = null

            const braceLines = secondaryLines.map(pLine => {
                const lineStartPt = pLine[0]
                const lineEndPt = pLine[1]

                const nodeStart = translate(lineStartPt, reinforcementNodes[2])
                const nodeEnd = translate(lineEndPt, reinforcementNodes[2])

                return hull(nodeStart, nodeEnd)
            })

            if (braceLines.length > 0) {
                returnBracing = union(...braceLines)
            }

            return braceLines
        }

        /* ----------------------------------------
         * Complete Assembly
         * ------------------------------------- */

        /** Final Assembly */
        const finalAssembly = (modelProps) => {
            const rectOutlineInst = rectOutline(modelProps)
            const rectPrimaryBracingInst = rectPrimaryBracing(modelProperties)
            const rectSecondaryBracingInst = rectSecondaryBracing(modelProperties)

            let finalShape = rectOutlineInst

            if (rectPrimaryBracingInst) {
                finalShape = union(finalShape, rectPrimaryBracingInst)
            }

            if (rectSecondaryBracingInst) {
                finalShape = union(finalShape, rectSecondaryBracingInst)
            }

            return finalShape
        }

        /* ----------------------------------------
         * Outputs
         * ------------------------------------- */

        const rectOutlineInst = rectOutline(modelProperties)
        const rectPrimaryBracingInst = rectPrimaryBracing(modelProperties)
        const rectSecondaryBracingInst = rectSecondaryBracing(modelProperties)

        let mainModel = finalAssembly(modelProperties)

        let modelParts = {
            rectOutline: rectOutlineInst,
            rectPrimaryBracing: rectPrimaryBracingInst,
            rectSecondaryBracing: rectSecondaryBracingInst,
        }

        return [mainModel, modelParts, modelProperties]
    }

    return reinforcedRect
}

module.exports = {
    init: reinforcedRectInit
}