Source: swcad-js-components/src/sheet-mould/index.js

"use strict"

const moduleInit = ({ jscad, swcadJs }) => {
    const {
        cylinder,
        cylinderElliptic,
        cuboid,
    } = jscad.primitives

    const {
        align,
    } = jscad.transforms

    const {
        union,
    } = jscad.booleans

    const {
        hullChain
    } = jscad.hulls

    const {
        math,
        position,
    } = swcadJs.calcs

    const {
        standards,
    } = swcadJs.data

    const {
        beadsBits,
        mesh: meshLib,
    } = swcadJs.components


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


    /**
     * Builds default values and opts for the model
     * @param {*} opts 
     * @returns default values and opts
     * @memberof components.sheetMould
     * @access private
     */
    const modelDefaults = () => {
        /** Specific value declarations */
        const interfaceThickness = standards.INTERFACE_THICKNESS
        const edgeMargin = math.inchesToMm(1 / 4)
        const modelTypes = {
            default: standards.types.TYPE_DEFAULT,
            alt: standards.types.TYPE_ALT,
        }

        const defaultValues = {
            opts: {
                type: modelTypes.default.id,
                scale: 1,
                bead: {
                    type: 'cone'
                },
                mesh: {
                    segments: 8
                }
            },
            dims: {
                interfaceThickness,
                fitGap: standards.FIT_GAP,
                edgeMargin,
                bead: {
                    radius: math.inchesToMm(1 / 16),
                    height: math.inchesToMm(1 / 16),
                },
                mesh: {
                    radius: math.inchesToMm(3 / 32),
                    distance: interfaceThickness * 0.75 + math.inchesToMm(3 / 32),
                    thickness: interfaceThickness,
                    margin: edgeMargin,
                },
            },
            points: {
                dots: [
                    [
                        math.inchesToMm(2),
                        math.inchesToMm(2),
                    ],
                    [
                        math.inchesToMm(-2),
                        math.inchesToMm(2),
                    ],
                    [
                        math.inchesToMm(2),
                        math.inchesToMm(-2),
                    ],
                    [
                        math.inchesToMm(-2),
                        math.inchesToMm(-2),
                    ],
                ],
                lines: [
                    [
                        [
                            math.inchesToMm(1.5),
                            math.inchesToMm(-1.5),
                        ],
                        [
                            math.inchesToMm(1.5),
                            math.inchesToMm(1.5),
                        ],
                    ],
                    [
                        [
                            math.inchesToMm(-1.5),
                            math.inchesToMm(-1.5),
                        ],
                        [
                            math.inchesToMm(-1.5),
                            math.inchesToMm(1.5),
                        ],
                    ],
                ],
            }
        }

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

        /** Computed values for option defaults */
        const defaultOpts = {
            ...standardOpts,
            bead: {
                type: defaultValues.opts.bead.type,
                radius: defaultValues.dims.bead.radius,
                height: defaultValues.dims.bead.height,
            },
            mesh: {
                segments: defaultValues.opts.mesh.segments,
                radius: defaultValues.dims.mesh.radius,
                distance: defaultValues.dims.mesh.distance,
                thickness: defaultValues.dims.mesh.thickness,
                margin: defaultValues.dims.mesh.margin,
            },
            edgeMargin: defaultValues.dims.edgeMargin,
            dots: defaultValues.points.dots,
            lines: defaultValues.points.lines,
        }

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


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


    /**
     * Initializes options with user input
     * @param {*} opts 
     * @returns model properties
     * @memberof components.sheetMould
     * @access private
     */
    const modelOpts = (opts) => {
        const defaults = modelDefaults()

        // User options
        const {
            size = defaults.opts.size,
            type = defaults.opts.type,
            scale = defaults.opts.scale,
            interfaceThickness = defaults.opts.interfaceThickness,
            fitGap = defaults.opts.fitGap,
            bead = defaults.opts.bead,
            mesh = defaults.opts.mesh,
            edgeMargin = defaults.opts.edgeMargin,
            dots = defaults.opts.dots,
            lines = defaults.opts.lines,
        } = opts

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

        const initOpts = {
            bead,
            mesh,
            edgeMargin,
            dots,
            lines,
            ...stdOpts,
        }


        return initOpts
    }

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


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

        const {
            size,
            type,
            scale,
            interfaceThickness,
            fitGap,
            bead,
            mesh,
            edgeMargin,
            dots,
            lines,
        } = opts

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

        const lgProfileBeadWidth = standards.LG_PROFILE_BEAD_WIDTH
        const mdProfileBeadWidth = standards.MD_PROFILE_BEAD_WIDTH
        const smProfileBeadWidth = standards.SM_PROFILE_BEAD_WIDTH

        let width = null
        let depth = null

        let minX = Number.MAX_VALUE
        let minY = Number.MAX_VALUE

        let maxX = Number.MIN_VALUE
        let maxY = Number.MIN_VALUE

        const checkPt = (pt) => {
            const currentX = pt[0]
            const currentY = pt[1]

            if (currentX < minX) {
                minX = currentX
            } else if (currentX > maxX) {
                maxX = currentX
            }

            if (currentY < minY) {
                minY = currentY
            } else if (currentY > maxY) {
                maxY = currentY
            }
        }

        dots.forEach(dot => {
            checkPt(dot)
        })

        lines.forEach(line => {
            line.forEach(linePt => {
                checkPt(linePt)
            })
        })

        const distX = maxX - minX
        const distY = maxY - minY
        const distHalfX = distX / 2
        const distHalfY = distY / 2

        let ctrX = minX + distHalfX
        let ctrY = minY + distHalfY

        let ctrOffsetX = 0
        let ctrOffsetY = 0

        if (size) {
            width = size[0]
            depth = size[1]
        } else {
            width = edgeMargin * 2 + distX
            depth = edgeMargin * 2 + distY
        }

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

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

        /** Derived user options for the model */
        const modelOpts = {
            type,
            scale,
            bead: {
                type: bead.type,
            },
            mesh: {
                segments: mesh.segments,
            },
        }

        /** Various dimensions for model */
        const modelDims = {
            interfaceThickness,
            fitGap,
            width,
            depth,
            bead: {
                radius: bead.radius,
                height: bead.height,
            },
            mesh: {
                radius: mesh.radius,
                distance: mesh.distance,
                margin: mesh.margin,
                thickness: mesh.thickness,
            },
        }

        /** Various key points for model */
        const modelPoints = {
            centre: [ctrX, ctrY, 0],
            dots,
            lines,
        }

        /** Components used by model */
        const modelComponents = {
            interface: {
                profileBeads: beadsBits.profileBeads(
                    interfaceThickness,
                    smProfileBeadWidth,
                    mdProfileBeadWidth,
                    lgProfileBeadWidth,
                ),
            }
        }

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


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


    /**
     * Sheet Mould Module
     * @param {*} opts 
     * @returns Array with model, parts, and properties: [`geom3`, `Object.<string, geom3>`, `Object.<string, any>`]
     * @memberof components
     */
    const sheetMould = (opts) => {
        const defaults = modelDefaults()
        const initOpts = modelOpts(opts)
        const modelProperties = modelProps(initOpts)

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

        const mouldBase = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps

            const meshTextureSlab = meshLib.meshPanel({
                size: [dims.width, dims.depth, dims.interfaceThickness],
                holeRadius: dims.mesh.radius,
                holeDistance: dims.mesh.distance,
                edgeMargin: dims.mesh.margin,
            })

            return meshTextureSlab
        }


        const mouldPtSupportCore = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps

            const edgeMouldSupport = cylinder({
                height: dims.interfaceThickness,
                radius: dims.bead.radius
            })

            return edgeMouldSupport
        }

        const mouldPointSupport = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps

            const edgeMouldSupport = mouldPtSupportCore(modelProps)
            const mouldSupportFlange = cuboid({
                size: [
                    dims.mesh.radius * 2.125,
                    dims.interfaceThickness,
                    dims.interfaceThickness
                ]
            })
            let holeMouldSupport = union(edgeMouldSupport, mouldSupportFlange)

            return holeMouldSupport
        }


        const mouldPoint = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps

            const holeMouldPoint = cylinderElliptic({
                height: dims.bead.height,
                startRadius: [dims.bead.radius, dims.bead.radius],
                endRadius: [0, 0]
            })

            return holeMouldPoint
        }

        const mouldPtAssembly = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps

            const hMouldPoint = mouldPoint(modelProps)

            const holeMould = union(
                position.ctrMax(mouldPtSupportCore(modelProps)),
                position.ctrMin(hMouldPoint),
            )
            return holeMould
        }

        const mouldPtFlangedAssembly = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps

            const hMouldPoint = mouldPoint(modelProps)

            const holeMould = union(
                position.ctrMax(mouldPointSupport(modelProps)),
                position.ctrMin(hMouldPoint),
            )
            return holeMould
        }


        const dotMoulds = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps


            let holeMoulds = []
            points.dots.forEach((pt) => {
                holeMoulds.push(
                    align(
                        {
                            modes: ['center', 'center', 'min'],
                            relativeTo: [pt[0], pt[1], 0],
                        },
                        mouldPtFlangedAssembly(modelProps)
                    )
                )
            })

            let holeMouldSet = null

            if (holeMoulds.length > 0) {
                holeMouldSet = union(...holeMoulds)
            }

            return holeMouldSet
        }


        const lineMoulds = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps

            const lineMouldPoints = []
            points.lines.forEach((linePoints) => {

                const lineSet = linePoints.map((linePt) => {

                    return align(
                        {
                            modes: ['center', 'center', 'min'],
                            relativeTo: [linePt[0], linePt[1], 0],
                        },
                        mouldPtAssembly(modelProps)
                    )
                })
                lineMouldPoints.push(hullChain(...lineSet))
            })


            const lineMouldAssemblies = union(...lineMouldPoints)

            return lineMouldAssemblies
        }


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

        /** Final Assembly */
        const finalAssembly = (modelProps) => {
            const {
                metadata,
                opts,
                dims,
                points,
            } = modelProps

            let mainPart = position.ctrMin(
                mouldBase(modelProps),
                [points.centre[0], points.centre[1], 0],
            )

            const dMould = dotMoulds(modelProps)
            const lMould = lineMoulds(modelProps)

            if (dMould) {
                mainPart = union(
                    mainPart,
                    dMould,
                )
            }

            if (lMould) {
                mainPart = union(
                    mainPart,
                    lMould,
                )
            }

            return mainPart
        }


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

        let mouldPointInst = mouldPoint(modelProperties)
        let mouldPointSupportInst = mouldPointSupport(modelProperties)
        let dotMouldsInst = dotMoulds(modelProperties)
        let lineMouldsInst = lineMoulds(modelProperties)

        let mainModel = finalAssembly(modelProperties)

        let modelParts = {
            mouldPoint: mouldPointInst,
            mouldPointSupport: mouldPointSupportInst,
            dotMoulds: dotMouldsInst,
            lineMoulds: lineMouldsInst,
        }

        return [mainModel, modelParts, modelProperties]
    }

    return sheetMould
}

module.exports = {
    init: moduleInit
}