"use strict"
const moduleInit = ({ jscad, swcadJs }) => {
const {
cylinderElliptic,
cuboid,
} = jscad.primitives
const {
translate,
rotate,
} = jscad.transforms
const {
intersect,
union,
} = jscad.booleans
const {
measureDimensions,
measureBoundingBox,
} = jscad.measurements
const {
hull,
} = jscad.hulls
const { TAU } = jscad.maths.constants
const {
constants,
standards,
} = swcadJs.data
const {
math,
position,
geometry,
} = swcadJs.calcs
const {
beadsBits,
} = swcadJs.components
//==============================================================================
/**
* Builds default values and opts for the model
* @param {*} opts
* @returns default values and opts
* @access private
*/
const modelDefaults = () => {
/** Specific value declarations */
const defaultValues = {
dims: {
size: [
math.inchesToMm(2),
math.inchesToMm(4),
math.inchesToMm(1),
],
},
points: {
centre: [0, 0, 0],
startPt: [math.inchesToMm(0.5), math.inchesToMm(0.5)],
endPt: [math.inchesToMm(3), math.inchesToMm(4)],
},
types: {
default: standards.types.TYPE_DEFAULT,
alt: standards.types.TYPE_ALT,
},
component: cuboid({
size: [
math.inchesToMm(6 / 16),
math.inchesToMm(9 / 16),
math.inchesToMm(3 / 16),
]
}),
}
/** 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,
unitLength: math.inchesToMm(1),
startPt: defaultValues.points.startPt,
endPt: defaultValues.points.endPt,
endOffset: 0,
baseLineOffset: 0,
component: defaultValues.component,
}
return {
opts: defaultOpts,
vals: defaultValues,
}
}
//------------------------------------------------------------------------------
/**
* Initializes options with user input
* @param {*} opts
* @returns model properties
* @access private
*/
const modelOpts = (opts) => {
const defaults = modelDefaults()
// User options
const {
interfaceThickness = defaults.opts.interfaceThickness,
fitGap = defaults.opts.fitGap,
unitLength = defaults.opts.unitLength,
type = defaults.opts.type,
scale = defaults.opts.scale,
startPt = defaults.opts.startPt,
endPt = defaults.opts.endPt,
endOffset = defaults.opts.endOffset,
baseLineOffset = defaults.opts.baseLineOffset,
component = defaults.opts.component,
} = opts
const stdOpts = {
type,
scale,
interfaceThickness,
fitGap,
}
const initOpts = {
unitLength,
startPt,
endPt,
endOffset,
baseLineOffset,
component,
...stdOpts,
}
return initOpts
}
//------------------------------------------------------------------------------
/**
* Builds model properties from the given opts
* @param {*} opts
* @returns model properties
* @access private
*/
const modelProps = (opts) => {
const defaults = modelDefaults()
const {
interfaceThickness,
fitGap,
unitLength,
type,
scale,
startPt,
endPt,
endOffset,
baseLineOffset,
component,
} = opts
/* ----------------------------------------
* Prop calculations
* ------------------------------------- */
const coordDiffs = [
endPt[0] - startPt[0],
endPt[1] - startPt[1],
]
const midPt = [
startPt[0] + (coordDiffs[0] / 2),
startPt[1] + (coordDiffs[1] / 2),
]
const lineLength = Math.hypot(coordDiffs[0], coordDiffs[1])
const offsetLineLength = lineLength - (endOffset * 2)
const numSubLengths = Math.floor(offsetLineLength / unitLength)
const componentUnits = numSubLengths + 1
const componentDims = measureDimensions(component)
const componentBoundingBox = measureBoundingBox(component)
const componentLineLength = offsetLineLength - componentDims[0]
const componentUnitLength = componentLineLength / numSubLengths
const compLineAngle = geometry.angleOfTwoPtLine(startPt, endPt)
const lgProfileBeadWidth = standards.LG_PROFILE_BEAD_WIDTH
const mdProfileBeadWidth = standards.MD_PROFILE_BEAD_WIDTH
const smProfileBeadWidth = standards.SM_PROFILE_BEAD_WIDTH
/* ----------------------------------------
* Preparing Model Properties, Dimensions
* ------------------------------------- */
/** Constant values for model */
const modelConstants = {
type,
scale,
}
/** Derived user options for the model */
const modelOpts = {
type,
scale,
types: defaults.vals.types,
componentUnits,
}
/** Various dimensions for model */
const modelDims = {
interfaceThickness,
fitGap,
unitLength,
componentUnitLength,
lineLength,
offsetLineLength,
componentLineLength,
componentDims,
componentBoundingBox,
endOffset,
baseLineOffset,
lgProfileBeadWidth,
mdProfileBeadWidth,
smProfileBeadWidth,
compLineAngle,
}
/** Various key points for model */
const modelPoints = {
centre: defaults.vals.points.centre,
startPt,
midPt,
endPt,
}
/** Components used by model */
const modelComponents = {
component,
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
}
/**
* Component Line
* @param {*} opts
* @returns `geom3` or `geom3[]`
* @author R. J. Salvador (Salvador Workshop)
* @memberof components
*/
const componentLine = (opts) => {
const defaults = modelDefaults()
const initOpts = modelOpts(opts)
const modelProperties = modelProps(initOpts)
/* ----------------------------------------
* Modelling, Component/Assembly Modules
* ------------------------------------- */
const interfaceProfileBeads = (modelProps) => {
const {
interfaceThickness,
lgProfileBeadWidth,
mdProfileBeadWidth,
smProfileBeadWidth,
} = modelProps.dims
const edgeOffset = interfaceThickness / constants.TRI_30_FACTOR / 2
const lgTopWidth = lgProfileBeadWidth - (edgeOffset * 2)
const mdTopWidth = mdProfileBeadWidth - (edgeOffset * 2)
const smTopWidth = smProfileBeadWidth - (edgeOffset * 2)
const lgProfile = cylinderElliptic({
height: interfaceThickness,
startRadius: [lgProfileBeadWidth / 2, lgProfileBeadWidth / 2],
endRadius: [lgTopWidth / 2, lgTopWidth / 2]
})
const mdProfile = cylinderElliptic({
height: interfaceThickness,
startRadius: [mdProfileBeadWidth / 2, mdProfileBeadWidth / 2],
endRadius: [mdTopWidth / 2, mdTopWidth / 2]
})
const smProfile = cylinderElliptic({
height: interfaceThickness,
startRadius: [smProfileBeadWidth / 2, smProfileBeadWidth / 2],
endRadius: [smTopWidth / 2, smTopWidth / 2]
})
return {
lg: lgProfile,
md: mdProfile,
sm: smProfile,
}
}
/** Straight line from start point to end point. */
const baseLine = (modelProps) => {
const {
startPt,
endPt
} = modelProps.points
const beadProfile = interfaceProfileBeads(modelProps).md
return hull(
translate([startPt[0], startPt[1], 0], beadProfile),
translate([endPt[0], endPt[1], 0], beadProfile),
)
}
/** Sub-component 02 */
const componentSet = (modelProps) => {
const {
component
} = modelProps.components
const {
componentUnits
} = modelProps.opts
const {
interfaceThickness,
fitGap,
unitLength,
componentUnitLength,
lineLength,
componentDims,
componentBoundingBox,
} = modelProps.dims
const lineComponents = []
for (let idx = 0; idx < componentUnits; idx++) {
lineComponents.push(translate(
[idx * componentUnitLength, 0, 0],
position.ctr(component)
))
}
const cSet = position.ctr(union(...lineComponents))
return cSet
}
/** Assembly 01 */
const cutBox = (modelProps) => {
const {
interfaceThickness,
fitGap,
unitLength,
lineLength,
componentDims,
componentBoundingBox,
} = modelProps.dims
return cuboid({
size: [
lineLength,
interfaceThickness * 2 + componentDims[1],
interfaceThickness * 2 + componentDims[2],
]
})
}
/* ----------------------------------------
* Complete Assembly
* ------------------------------------- */
/** Final Assembly */
const finalAssembly = (modelProps) => {
const {
interfaceThickness,
fitGap,
unitLength,
lineLength,
componentDims,
componentBoundingBox,
baseLineOffset,
compLineAngle
} = modelProps.dims
const {
startPt,
midPt,
endPt,
} = modelProps.points
let bLine = baseLine(modelProps)
let cSet = componentSet(modelProps)
const midPoint = [midPt[0], midPt[1], 0]
let a1 = cutBox(modelProps)
const rotatedCompLine = rotate(
[0, 0, compLineAngle],
cSet
)
const shiftedCompLine = translate(
midPoint,
rotatedCompLine
)
const axisOffsetAngle = compLineAngle - (TAU / 4)
const lineMidPointAdj = geometry.pointFromAngleAndDist(axisOffsetAngle, baseLineOffset)
const lineMidPoint = [
midPoint[0] + lineMidPointAdj[0],
midPoint[1] + lineMidPointAdj[1],
]
const combinedAssembly = union(
position.ctrMin(bLine, lineMidPoint),
position.ctrMin(shiftedCompLine, midPoint),
)
const cBox = rotate(
[0, 0, compLineAngle],
cutBox(modelProps)
)
const cutAssembly = intersect(
position.ctrMin(cBox, midPoint),
combinedAssembly
)
return cutAssembly
}
/* ----------------------------------------
* Outputs
* ------------------------------------- */
let baseLineInst = baseLine(modelProperties)
let componentSetInst = componentSet(modelProperties)
let cutBoxInst = cutBox(modelProperties)
let mainPart = finalAssembly(modelProperties)
let auxParts = {
baseLine: baseLineInst,
componentSet: componentSetInst,
cutBox: cutBoxInst,
}
return [mainPart, auxParts, modelProperties]
}
return componentLine
}
module.exports = {
init: moduleInit
}