diff --git a/src/lang/executor.ts b/src/lang/executor.ts index b428f70ee3..d8718dc45e 100644 --- a/src/lang/executor.ts +++ b/src/lang/executor.ts @@ -8,7 +8,12 @@ import { Identifier, CallExpression, } from './abstractSyntaxTree' -import { sketchFns } from './sketch' +import { + sketchFns, + internalFns, + InternalFnNames, + SketchFnNames, +} from './sketch' import { BufferGeometry } from 'three' export type SourceRange = [number, number] @@ -274,7 +279,6 @@ export const executor = ( __meta, } } else if (declaration.init.type === 'CallExpression') { - // TODO: use executeCallExpression here instead const functionName = declaration.init.callee.name const fnArgs = declaration.init.arguments.map((arg) => { if (arg.type === 'Literal') { @@ -288,20 +292,19 @@ export const executor = ( `Unexpected argument type ${arg.type} in function call` ) }) - if ( - 'lineTo' === functionName || - 'close' === functionName // || - // 'base' === functionName - ) { + if (functionName in sketchFns) { + const sketchFnName = functionName as SketchFnNames if (options.bodyType !== 'sketch') { throw new Error( `Cannot call ${functionName} outside of a sketch declaration` ) } - const result = sketchFns[functionName]( - _programMemory, - variableName, - [declaration.start, declaration.end], + const result = sketchFns[sketchFnName]( + { + programMemory: _programMemory, + name: variableName, + sourceRange: [declaration.start, declaration.end], + }, ...fnArgs ) _programMemory._sketch = result.programMemory._sketch @@ -310,66 +313,20 @@ export const executor = ( value: result.currentPath, __meta, } - } else if ( - 'rx' === functionName || - 'ry' === functionName || - 'rz' === functionName - ) { - const sketch = declaration.init.arguments[1] - if (sketch.type !== 'Identifier') - throw new Error('rx must be called with an identifier') - const sketchVal = _programMemory.root[sketch.name] - const result = sketchFns[functionName]( - _programMemory, - [declaration.start, declaration.end], - fnArgs[0], - sketchVal as any // todo memory redo - ) - _programMemory.root[variableName] = result as any // todo memory redo - } else if (functionName === 'extrude') { - const sketch = declaration.init.arguments[1] - if (sketch.type !== 'Identifier') - throw new Error('extrude must be called with an identifier') - const sketchVal = _programMemory.root[sketch.name] - const result = sketchFns[functionName]( - _programMemory, - 'yo', - [declaration.start, declaration.end], - fnArgs[0], - sketchVal as any // todo memory redo - ) - _programMemory.root[variableName] = result as any // todo memory redo - } else if ( - functionName === 'translate' || - functionName === 'transform' - ) { - const sketch = declaration.init.arguments[1] - if (sketch.type !== 'Identifier') - throw new Error('rx must be called with an identifier') - const sketchVal = _programMemory.root[sketch.name] - const result = sketchFns[functionName]( + } else if (functionName in internalFns) { + const result = executeCallExpression( _programMemory, - [declaration.start, declaration.end], - fnArgs[0], - sketchVal as any // todo memory redo + declaration.init, + previousPathToNode, + { + sourceRangeOverride: [declaration.start, declaration.end], + isInPipe: false, + previousResults: [], + expressionIndex: 0, + body: [], + } ) _programMemory.root[variableName] = result as any // todo memory redo - } else if (functionName === 'getExtrudeWallTransform') { - const extrude = declaration.init.arguments[1] - if (extrude.type !== 'Identifier') - throw new Error('rx must be called with an identifier') - const sketchVal = _programMemory.root[extrude.name] - const value = sketchFns[functionName]( - _programMemory, - [declaration.start, declaration.end], - fnArgs[0], - sketchVal as any // todo memory redo - ) - _programMemory.root[variableName] = { - type: 'userVal', - value, - __meta, - } } else { _programMemory.root[variableName] = { type: 'userVal', @@ -394,20 +351,18 @@ export const executor = ( return _programMemory.root[arg.name].value } }) - if ( - 'lineTo' === functionName || - 'close' === functionName - // || 'base' === functionName - ) { + if (functionName in sketchFns) { if (options.bodyType !== 'sketch') { throw new Error( `Cannot call ${functionName} outside of a sketch declaration` ) } - const result = sketchFns[functionName]( - _programMemory, - '', - [statement.start, statement.end], + const sketchFnName = functionName as SketchFnNames + const result = sketchFns[sketchFnName]( + { + programMemory: _programMemory, + sourceRange: [statement.start, statement.end], + }, ...args ) _programMemory._sketch = [...(result.programMemory._sketch || [])] @@ -607,6 +562,7 @@ function executeCallExpression( previousResults: any[] expressionIndex: number body: PipeExpression['body'] + sourceRangeOverride?: SourceRange } = { isInPipe: false, previousResults: [], @@ -614,7 +570,13 @@ function executeCallExpression( body: [], } ) { - const { isInPipe, previousResults, expressionIndex, body } = pipeInfo + const { + isInPipe, + previousResults, + expressionIndex, + body, + sourceRangeOverride, + } = pipeInfo const functionName = expression.callee.name const fnArgs = expression.arguments.map((arg) => { if (arg.type === 'Literal') { @@ -645,45 +607,24 @@ function executeCallExpression( } throw new Error('Invalid argument type') }) - if ('rx' === functionName || 'ry' === functionName || 'rz' === functionName) { - const result = sketchFns[functionName]( - programMemory, - [expression.start, expression.end], - fnArgs[0], - fnArgs[1] - ) - return isInPipe - ? executePipeBody( - body, - programMemory, - previousPathToNode, - expressionIndex + 1, - [...previousResults, result] - ) - : result - } - if (functionName === 'extrude') { - const result = sketchFns[functionName]( - programMemory, - 'yo', - [expression.start, expression.end], - fnArgs[0], - fnArgs[1] - ) - return isInPipe - ? executePipeBody( - body, - programMemory, - previousPathToNode, - expressionIndex + 1, - [...previousResults, result] - ) - : result - } - if (functionName === 'translate' || functionName === 'transform') { - const result = sketchFns[functionName]( - programMemory, - [expression.start, expression.end], + if ( + functionName in internalFns && + [ + 'rx', + 'ry', + 'rz', + 'translate', + 'transform', + 'extrude', + 'getExtrudeWallTransform', + ].includes(functionName) + ) { + const fnNameWithSketchOrExtrude = functionName as InternalFnNames + const result = internalFns[fnNameWithSketchOrExtrude]( + { + programMemory, + sourceRange: sourceRangeOverride || [expression.start, expression.end], + }, fnArgs[0], fnArgs[1] ) @@ -697,12 +638,14 @@ function executeCallExpression( ) : result } - if (functionName === 'getExtrudeWallTransform') { - const result = sketchFns[functionName]( - programMemory, - [expression.start, expression.end], - fnArgs[0], - fnArgs[1] + if (functionName in sketchFns) { + const sketchFnName = functionName as SketchFnNames + const result = sketchFns[sketchFnName]( + { + programMemory, + sourceRange: sourceRangeOverride || [expression.start, expression.end], + }, + ...fnArgs ) return isInPipe ? executePipeBody( diff --git a/src/lang/sketch.ts b/src/lang/sketch.ts index ee22cadbb7..6738fc6a38 100644 --- a/src/lang/sketch.ts +++ b/src/lang/sketch.ts @@ -32,38 +32,29 @@ function getCoordsFromPaths(paths: Path[], index = 0): Coords2d { return [0, 0] } -export const sketchFns = { - // base: ( - // programMemory: ProgramMemory, - // name: string = '', - // sourceRange: SourceRange, - // ...args: any[] - // ): PathReturn => { - // if ((programMemory?._sketch?.length || 0) > 0) { - // throw new Error('Base can only be called once') - // } - // const [x, y] = args as [number, number] - // let from: [number, number] = [x, y] - // const geo = baseGeo({ from: [x, y, 0] }) - // const newPath: Path = { - // type: 'base', - // from, - // sourceRange, - // geo, - // } - // return { - // programMemory: { - // ...programMemory, - // _sketch: [...(programMemory?._sketch || []), newPath], - // }, - // currentPath: newPath, - // } - // }, - close: ( - programMemory: ProgramMemory, - name: string = '', - sourceRange: SourceRange - ): PathReturn => { +interface InternalFirstArg { + programMemory: ProgramMemory + name?: string + sourceRange: SourceRange +} + +type SketchFn = (internals: InternalFirstArg, ...args: any[]) => PathReturn + +export type SketchFnNames = 'close' | 'lineTo' + +type InternalFn = (internals: InternalFirstArg, ...args: any[]) => any + +export type InternalFnNames = + | 'extrude' + | 'translate' + | 'transform' + | 'getExtrudeWallTransform' + | 'rx' + | 'ry' + | 'rz' + +export const sketchFns: { [key in SketchFnNames]: SketchFn } = { + close: ({ programMemory, name = '', sourceRange }) => { const firstPath = programMemory?._sketch?.[0] as Path let from = getCoordsFromPaths( @@ -110,12 +101,7 @@ export const sketchFns = { currentPath: newPath, } }, - lineTo: ( - programMemory: ProgramMemory, - name: string = '', - sourceRange: SourceRange, - ...args: any[] - ): PathReturn => { + lineTo: ({ programMemory, name = '', sourceRange }, ...args) => { const [x, y] = args if (!programMemory._sketch) { throw new Error('No sketch to draw on') @@ -155,100 +141,12 @@ export const sketchFns = { currentPath, } }, - rx: rotateOnAxis([1, 0, 0]), - ry: rotateOnAxis([0, 1, 0]), - rz: rotateOnAxis([0, 0, 1]), - extrude: ( - programMemory: ProgramMemory, - name: string = '', - sourceRange: SourceRange, - length: number, - sketchVal: SketchGroup - ): ExtrudeGroup => { - const getSketchGeo = (sketchVal: SketchGroup): SketchGroup => { - return sketchVal - } - - const sketch = getSketchGeo(sketchVal) - const { position, rotation } = sketchVal - - const extrudeSurfaces: ExtrudeSurface[] = [] - const extrusionDirection = clockwiseSign( - sketch.value.map((line) => line.to) - ) - sketch.value.map((line, index) => { - if (line.type === 'toPoint') { - let from: [number, number] = line.from - const to = line.to - const { - geo, - position: facePosition, - rotation: faceRotation, - } = extrudeGeo({ - from: [from[0], from[1], 0], - to: [to[0], to[1], 0], - length, - extrusionDirection, - }) - const groupQuaternion = new Quaternion(...rotation) - const currentWallQuat = new Quaternion(...faceRotation) - const unifiedQuit = new Quaternion().multiplyQuaternions( - currentWallQuat, - groupQuaternion.clone().invert() - ) - - const facePositionVector = new Vector3(...facePosition) - facePositionVector.applyQuaternion(groupQuaternion.clone()) - const unifiedPosition = new Vector3().addVectors( - facePositionVector, - new Vector3(...position) - ) - const surface: ExtrudeSurface = { - type: 'extrudePlane', - position: unifiedPosition.toArray() as Position, - rotation: unifiedQuit.toArray() as Rotation, - __geoMeta: { - geo, - sourceRange: line.__geoMeta.sourceRange, - pathToNode: line.__geoMeta.pathToNode, - }, - } - line.name && (surface.name = line.name) - extrudeSurfaces.push(surface) - } - }) - return { - type: 'extrudeGroup', - value: extrudeSurfaces, - height: length, - position, - rotation, - __meta: [ - { - sourceRange, - pathToNode: [], // TODO - }, - { - sourceRange: sketchVal.__meta[0].sourceRange, - pathToNode: sketchVal.__meta[0].pathToNode, - }, - ], - } - }, - translate, - transform, - getExtrudeWallTransform, } function rotateOnAxis( axisMultiplier: [number, number, number] -) { - return ( - programMemory: ProgramMemory, - sourceRange: SourceRange, - rotationD: number, - sketch: T - ): T => { +): InternalFn { + return ({ sourceRange }, rotationD: number, sketch: T): T => { const rotationR = rotationD * (Math.PI / 180) const rotateVec = new Vector3(...axisMultiplier) const quaternion = new Quaternion() @@ -275,12 +173,85 @@ function rotateOnAxis( } } -function translate( - programMemory: ProgramMemory, - sourceRange: SourceRange, +const extrude: InternalFn = ( + { sourceRange }, + length: number, + sketchVal: SketchGroup +): ExtrudeGroup => { + const getSketchGeo = (sketchVal: SketchGroup): SketchGroup => { + return sketchVal + } + + const sketch = getSketchGeo(sketchVal) + const { position, rotation } = sketchVal + + const extrudeSurfaces: ExtrudeSurface[] = [] + const extrusionDirection = clockwiseSign(sketch.value.map((line) => line.to)) + sketch.value.map((line, index) => { + if (line.type === 'toPoint') { + let from: [number, number] = line.from + const to = line.to + const { + geo, + position: facePosition, + rotation: faceRotation, + } = extrudeGeo({ + from: [from[0], from[1], 0], + to: [to[0], to[1], 0], + length, + extrusionDirection, + }) + const groupQuaternion = new Quaternion(...rotation) + const currentWallQuat = new Quaternion(...faceRotation) + const unifiedQuit = new Quaternion().multiplyQuaternions( + currentWallQuat, + groupQuaternion.clone().invert() + ) + + const facePositionVector = new Vector3(...facePosition) + facePositionVector.applyQuaternion(groupQuaternion.clone()) + const unifiedPosition = new Vector3().addVectors( + facePositionVector, + new Vector3(...position) + ) + const surface: ExtrudeSurface = { + type: 'extrudePlane', + position: unifiedPosition.toArray() as Position, + rotation: unifiedQuit.toArray() as Rotation, + __geoMeta: { + geo, + sourceRange: line.__geoMeta.sourceRange, + pathToNode: line.__geoMeta.pathToNode, + }, + } + line.name && (surface.name = line.name) + extrudeSurfaces.push(surface) + } + }) + return { + type: 'extrudeGroup', + value: extrudeSurfaces, + height: length, + position, + rotation, + __meta: [ + { + sourceRange, + pathToNode: [], // TODO + }, + { + sourceRange: sketchVal.__meta[0].sourceRange, + pathToNode: sketchVal.__meta[0].pathToNode, + }, + ], + } +} + +const translate: InternalFn = ( + { sourceRange }: InternalFirstArg, vec3: [number, number, number], sketch: T -): T { +): T => { const oldPosition = new Vector3(...sketch.position) const newPosition = oldPosition.add(new Vector3(...vec3)) return { @@ -296,15 +267,14 @@ function translate( } } -function transform( - programMemory: ProgramMemory, - sourceRange: SourceRange, +const transform: InternalFn = ( + { sourceRange }: InternalFirstArg, transformInfo: { position: Position quaternion: Rotation }, sketch: T -): T { +): T => { const quaternionToApply = new Quaternion(...transformInfo.quaternion) const newQuaternion = new Quaternion(...sketch.rotation).multiply( quaternionToApply.invert() @@ -328,15 +298,14 @@ function transform( } } -function getExtrudeWallTransform( - programMemory: ProgramMemory, - sourceRange: SourceRange, +const getExtrudeWallTransform: InternalFn = ( + _, pathName: string, extrudeGroup: ExtrudeGroup ): { position: Position quaternion: Rotation -} { +} => { const path = extrudeGroup.value.find((path) => path.name === pathName) if (!path) throw new Error(`Could not find path with name ${pathName}`) return { @@ -345,6 +314,16 @@ function getExtrudeWallTransform( } } +export const internalFns: { [key in InternalFnNames]: InternalFn } = { + rx: rotateOnAxis([1, 0, 0]), + ry: rotateOnAxis([0, 1, 0]), + rz: rotateOnAxis([0, 0, 1]), + extrude, + translate, + transform, + getExtrudeWallTransform, +} + function clockwiseSign(points: [number, number][]): number { let sum = 0 for (let i = 0; i < points.length; i++) {