diff --git a/src/App.tsx b/src/App.tsx index b97d402ec..7448b3cc7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -41,6 +41,7 @@ function App() { ast, setError, errorState, + setProgramMemory, } = useStore((s) => ({ editorView: s.editorView, setEditorView: s.setEditorView, @@ -58,6 +59,7 @@ function App() { formatCode: s.formatCode, setError: s.setError, errorState: s.errorState, + setProgramMemory: s.setProgramMemory, })) // const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => { const onChange = (value: string, viewUpdate: ViewUpdate) => { @@ -103,6 +105,7 @@ function App() { }, _sketch: [], }) + setProgramMemory(programMemory) const geos: ViewerArtifact[] = programMemory?.return?.flatMap( ({ name }: { name: string }) => diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index 7ad9b3c1b..fe39deca2 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -28,6 +28,7 @@ export const Toolbar = () => { sketchMode: 'sketchEdit', pathToNode: guiMode.pathToNode, quaternion: guiMode.quaternion, + position: guiMode.position, }) }} className="border m-1 px-1 rounded" diff --git a/src/components/BasePlanes.tsx b/src/components/BasePlanes.tsx index 905078434..ec8217177 100644 --- a/src/components/BasePlanes.tsx +++ b/src/components/BasePlanes.tsx @@ -65,6 +65,7 @@ export const BasePlanes = () => { mode: 'sketch', sketchMode: 'sketchEdit', quaternion, + position: [0, 0, 0], pathToNode, }) diff --git a/src/components/SketchLine.tsx b/src/components/SketchLine.tsx index 3c80d6bb6..db21a279a 100644 --- a/src/components/SketchLine.tsx +++ b/src/components/SketchLine.tsx @@ -4,6 +4,7 @@ import { getNodeFromPath, CallExpression, changeArguments, + VariableDeclarator, } from '../lang/abstractSyntaxTree' import { ViewerArtifact } from '../lang/executor' import { BufferGeometry } from 'three' @@ -11,6 +12,7 @@ import { useStore } from '../useStore' import { isOverlapping } from '../lib/utils' import { LineGeos } from '../lang/engine' import { Vector3, DoubleSide, Quaternion, Vector2 } from 'three' +import { combineTransformsAlt } from '../lang/sketch' function SketchLine({ geo, @@ -129,9 +131,7 @@ function MovingSphere({ const { setHighlightRange, guiMode, ast, updateAst } = useStore((s) => ({ setHighlightRange: s.setHighlightRange, - selectionRange: s.selectionRange, guiMode: s.guiMode, - setGuiMode: s.setGuiMode, ast: s.ast, updateAst: s.updateAst, })) @@ -173,11 +173,13 @@ function MovingSphere({ }, [isMouseDown, ast]) let clickDetectPlaneQuaternion = new Quaternion() + let position = new Vector3(0, 0, 0) if ( guiMode.mode === 'canEditSketch' || (guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit') ) { clickDetectPlaneQuaternion = guiMode.quaternion.clone() + position = new Vector3(...guiMode.position) } return ( @@ -201,7 +203,7 @@ function MovingSphere({ {isMouseDown && ( { const point = a.point @@ -264,12 +266,13 @@ export function RenderViewerArtifacts({ artifact: ViewerArtifact forceHighlight?: boolean }) { - const { selectionRange, guiMode, ast, setGuiMode } = useStore( - ({ selectionRange, guiMode, ast, setGuiMode }) => ({ + const { selectionRange, guiMode, ast, setGuiMode, programMemory } = useStore( + ({ selectionRange, guiMode, ast, setGuiMode, programMemory }) => ({ selectionRange, guiMode, ast, setGuiMode, + programMemory, }) ) const [editorCursor, setEditorCursor] = useState(false) @@ -287,20 +290,16 @@ export function RenderViewerArtifacts({ ast ) { const pathToNode = getNodePathFromSourceRange(ast, artifact.sourceRange) - const piper = getNodeFromPath(ast, pathToNode, 'PipeExpression') - const quaternion = new Quaternion() - if (piper.type === 'PipeExpression') { - const rotateName = piper?.body?.[1]?.callee?.name - const rotateValue = piper?.body?.[1]?.arguments[0].value - let rotateAxis = new Vector3(1, 0, 0) - if (rotateName === 'ry') { - rotateAxis = new Vector3(0, 1, 0) - } else if (rotateName === 'rz') { - rotateAxis = new Vector3(0, 0, 1) - } - quaternion.setFromAxisAngle(rotateAxis, (Math.PI * rotateValue) / 180) - } - setGuiMode({ mode: 'canEditSketch', pathToNode, quaternion }) + const varDec: VariableDeclarator = getNodeFromPath( + ast, + pathToNode, + 'VariableDeclarator' + ) + const varName = varDec?.id?.name + const { quaternion, position } = combineTransformsAlt( + programMemory.root[varName] + ) + setGuiMode({ mode: 'canEditSketch', pathToNode, quaternion, position }) } else if ( !shouldHighlight && guiMode.mode === 'canEditSketch' && diff --git a/src/components/SketchPlane.tsx b/src/components/SketchPlane.tsx index 95953ae8f..fcd47c8cd 100644 --- a/src/components/SketchPlane.tsx +++ b/src/components/SketchPlane.tsx @@ -26,6 +26,7 @@ export const SketchPlane = () => { new Vector3(1, 0, 0), Math.PI / 2 ) + let position = guiMode.position const gridQuaternion = new Quaternion().multiplyQuaternions( guiMode.quaternion, temp @@ -35,6 +36,7 @@ export const SketchPlane = () => { <> { if (guiMode.sketchMode !== 'points') { @@ -77,6 +79,7 @@ export const SketchPlane = () => { ) diff --git a/src/lang/abstractSyntaxTree.ts b/src/lang/abstractSyntaxTree.ts index 337830967..a081959f3 100644 --- a/src/lang/abstractSyntaxTree.ts +++ b/src/lang/abstractSyntaxTree.ts @@ -377,7 +377,7 @@ function makeValue( throw new Error('Expected a previous Value if statement to match') } -interface VariableDeclarator extends GeneralStatement { +export interface VariableDeclarator extends GeneralStatement { type: 'VariableDeclarator' id: Identifier init: Value diff --git a/src/lang/sketch.ts b/src/lang/sketch.ts index f668077d8..7d9d481c6 100644 --- a/src/lang/sketch.ts +++ b/src/lang/sketch.ts @@ -193,8 +193,6 @@ export const sketchFns = { if (!_programMemory._sketch) { throw new Error('No sketch to draw on') } - const lastPath: Path = - _programMemory._sketch[_programMemory._sketch.length - 1] let from = getCoordsFromPaths( programMemory?._sketch, programMemory?._sketch.length - 1 @@ -238,33 +236,8 @@ export const sketchFns = { : (sketchVal as SketchGeo) // TODO fix types } - type PreviousTransforms = { - rotation: Quaternion - transform: [number, number, number] - }[] - const collectTransforms = ( - sketchVal: SketchGeo | ExtrudeGeo | Transform, - previousTransforms: PreviousTransforms = [] - ): PreviousTransforms => { - if (sketchVal.type !== 'transform') return previousTransforms - const newTransforms = [ - ...previousTransforms, - { - rotation: sketchVal.rotation, - transform: sketchVal.transform, - }, - ] - return collectTransforms(sketchVal.sketch, newTransforms) - } const sketch = getSketchGeo(sketchVal) - const previousTransforms = collectTransforms(sketchVal) - const position = new Vector3(0, 0, 0) - const quaternion = new Quaternion() - previousTransforms.forEach(({ rotation, transform }) => { - quaternion.multiply(rotation) - position.applyQuaternion(rotation.clone().invert()) - position.add(new Vector3(...transform)) - }) + const { position, quaternion } = combineTransforms(sketchVal) const extrudeFaces: ExtrudeFace[] = [] sketch.sketch.map((line, index) => { @@ -284,7 +257,7 @@ export const sketchFns = { extrudeFaces.push({ type: 'extrudeFace', quaternion, - translate: [position.x, position.y, position.z], + translate: position, geo, sourceRanges: [line.sourceRange, sourceRange], }) @@ -333,3 +306,65 @@ function translate( sourceRange, } } + +type PreviousTransforms = { + rotation: Quaternion + transform: [number, number, number] +}[] + +function collectTransforms( + sketchVal: SketchGeo | ExtrudeGeo | Transform, + previousTransforms: PreviousTransforms = [] +): PreviousTransforms { + if (sketchVal.type !== 'transform') return previousTransforms + const newTransforms = [ + ...previousTransforms, + { + rotation: sketchVal.rotation, + transform: sketchVal.transform, + }, + ] + return collectTransforms(sketchVal.sketch, newTransforms) +} + +export function combineTransforms( + sketchVal: SketchGeo | ExtrudeGeo | Transform +): { + quaternion: Quaternion + position: [number, number, number] +} { + const previousTransforms = collectTransforms(sketchVal) + const position = new Vector3(0, 0, 0) + const quaternion = new Quaternion() + previousTransforms.forEach(({ rotation, transform }) => { + quaternion.multiply(rotation) + position.applyQuaternion(rotation.clone().invert()) + position.add(new Vector3(...transform)) + }) + return { + quaternion, + position: [position.x, position.y, position.z], + } +} + +export function combineTransformsAlt( + sketchVal: SketchGeo | ExtrudeGeo | Transform +): { + quaternion: Quaternion + position: [number, number, number] +} { + const previousTransforms = collectTransforms(sketchVal) + let rotationQuaternion = new Quaternion() + let position = new Vector3(0, 0, 0) + previousTransforms.reverse().forEach(({ rotation, transform }) => { + const newQuant = rotation.clone() + newQuant.multiply(rotationQuaternion) + rotationQuaternion.copy(newQuant) + position.applyQuaternion(rotation) + position.add(new Vector3(...transform)) + }) + return { + quaternion: rotationQuaternion, + position: [position.x, position.y, position.z], + } +} diff --git a/src/useStore.ts b/src/useStore.ts index 8681272ac..c854ad8d4 100644 --- a/src/useStore.ts +++ b/src/useStore.ts @@ -1,14 +1,15 @@ import create from 'zustand' import { addLineHighlight, EditorView } from './editor/highlightextension' import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree' +import { ProgramMemory } from './lang/executor' import { recast } from './lang/recast' import { lexer } from './lang/tokeniser' import { Quaternion } from 'three' export type Range = [number, number] -type Plane = 'xy' | 'xz' | 'yz' type PathToNode = (string | number)[] +type Position = [number, number, number] type GuiModes = | { @@ -18,6 +19,7 @@ type GuiModes = mode: 'sketch' sketchMode: 'points' quaternion: Quaternion + position: Position id?: string pathToNode: PathToNode } @@ -25,6 +27,7 @@ type GuiModes = mode: 'sketch' sketchMode: 'sketchEdit' quaternion: Quaternion + position: Position pathToNode: PathToNode } | { @@ -35,6 +38,7 @@ type GuiModes = mode: 'canEditSketch' pathToNode: PathToNode quaternion: Quaternion + position: Position } interface StoreState { @@ -62,6 +66,8 @@ interface StoreState { error: string } setError: (error?: string) => void + programMemory: ProgramMemory + setProgramMemory: (programMemory: ProgramMemory) => void } export const useStore = create()((set, get) => ({ @@ -123,4 +129,6 @@ export const useStore = create()((set, get) => ({ setError: (error = '') => { set({ errorState: { isError: !!error, error } }) }, + programMemory: { root: {}, _sketch: [] }, + setProgramMemory: (programMemory) => set({ programMemory }), }))