diff --git a/src/components/RenderViewerArtifacts.tsx b/src/components/RenderViewerArtifacts.tsx index de86fef5e..3d6faacc6 100644 --- a/src/components/RenderViewerArtifacts.tsx +++ b/src/components/RenderViewerArtifacts.tsx @@ -1,6 +1,14 @@ import { useRef, useState, useEffect, useMemo } from 'react' -import { CallExpression, ArrayExpression } from '../lang/abstractSyntaxTree' -import { getNodePathFromSourceRange, getNodeFromPath } from '../lang/queryAst' +import { + CallExpression, + ArrayExpression, + PipeExpression, +} from '../lang/abstractSyntaxTree' +import { + getNodePathFromSourceRange, + getNodeFromPath, + getNodeFromPathCurry, +} from '../lang/queryAst' import { changeSketchArguments } from '../lang/std/sketch' import { ExtrudeGroup, @@ -18,6 +26,7 @@ import { isOverlap, roundOff } from '../lib/utils' import { Vector3, DoubleSide, Quaternion } from 'three' import { useSetCursor } from '../hooks/useSetCursor' import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos' +import { createCallExpression, createPipeSubstitution } from '../lang/modifyAst' function MovingSphere({ geo, @@ -352,8 +361,11 @@ function PathRender({ rotation: Rotation position: Position }) { - const { selectionRanges } = useStore(({ selectionRanges }) => ({ - selectionRanges, + const { selectionRanges, updateAstAsync, ast, guiMode } = useStore((s) => ({ + selectionRanges: s.selectionRanges, + updateAstAsync: s.updateAstAsync, + ast: s.ast, + guiMode: s.guiMode, })) const [editorCursor, setEditorCursor] = useState(false) useEffect(() => { @@ -397,6 +409,37 @@ function PathRender({ forceHighlight={forceHighlight || editorCursor} rotation={rotation} position={position} + onClick={() => { + if ( + !ast || + !(guiMode.mode === 'sketch' && guiMode.sketchMode === 'line') + ) + return + const path = getNodePathFromSourceRange( + ast, + geoInfo.__geoMeta.sourceRange + ) + const getNode = getNodeFromPathCurry(ast, path) + const maybeStartSketchAt = + getNode('CallExpression') + const pipe = getNode('PipeExpression') + if ( + maybeStartSketchAt?.node.callee.name === 'startSketchAt' && + pipe.node && + pipe.node.body.length > 2 + ) { + const modifiedAst = JSON.parse(JSON.stringify(ast)) + const _pipe = getNodeFromPath( + modifiedAst, + path, + 'PipeExpression' + ) + _pipe.node.body.push( + createCallExpression('close', [createPipeSubstitution()]) + ) + updateAstAsync(modifiedAst) + } + }} /> ) })} @@ -410,12 +453,14 @@ function LineRender({ forceHighlight = false, rotation, position, + onClick: _onClick = () => {}, }: { geo: BufferGeometry sourceRange: [number, number] forceHighlight?: boolean rotation: Rotation position: Position + onClick?: () => void }) { const { setHighlightRange, guiMode, ast } = useStore((s) => ({ setHighlightRange: s.setHighlightRange, @@ -457,7 +502,10 @@ function LineRender({ setHover(false) setHighlightRange([0, 0]) }} - onClick={onClick} + onClick={() => { + _onClick() + onClick() + }} > { - const { ast, guiMode, updateAst, programMemory } = useStore((s) => ({ - guiMode: s.guiMode, - ast: s.ast, - updateAst: s.updateAst, - programMemory: s.programMemory, - })) + const { ast, guiMode, updateAst, programMemory, updateAstAsync } = useStore( + (s) => ({ + guiMode: s.guiMode, + ast: s.ast, + updateAst: s.updateAst, + updateAstAsync: s.updateAstAsync, + programMemory: s.programMemory, + }) + ) if (guiMode.mode !== 'sketch') { return null } @@ -71,7 +74,7 @@ export const SketchPlane = () => { fnName: guiMode.sketchMode, pathToNode: guiMode.pathToNode, }) - updateAst(modifiedAst) + updateAstAsync(modifiedAst) }} > diff --git a/src/lang/std/sketch.ts b/src/lang/std/sketch.ts index c475e2cbd..78249d7c6 100644 --- a/src/lang/std/sketch.ts +++ b/src/lang/std/sketch.ts @@ -1479,12 +1479,14 @@ function addTagWithTo( } } -export const closee: InternalFn = ( - { sourceRange, programMemory }, +export const close: InternalFn = ( + { sourceRange }, sketchGroup: SketchGroup ): SketchGroup => { const from = getCoordsFromPaths(sketchGroup, sketchGroup.value.length - 1) - const to = getCoordsFromPaths(sketchGroup, 0) + const to = sketchGroup.start + ? sketchGroup.start.from + : getCoordsFromPaths(sketchGroup, 0) const geo = lineGeo({ from: [...from, 0], to: [...to, 0], @@ -1509,7 +1511,7 @@ export const closee: InternalFn = ( }, } const newValue = [...sketchGroup.value] - newValue[0] = currentPath + newValue.push(currentPath) return { ...sketchGroup, value: newValue, diff --git a/src/lang/std/std.ts b/src/lang/std/std.ts index 54de308a1..89f72871b 100644 --- a/src/lang/std/std.ts +++ b/src/lang/std/std.ts @@ -10,7 +10,7 @@ import { angledLineToX, angledLineOfYLength, angledLineToY, - closee, + close, startSketchAt, angledLineThatIntersects, } from './sketch' @@ -127,7 +127,7 @@ export const internalFns: { [key in InternalFnNames]: InternalFn } = { angledLineToY: angledLineToY.fn, angledLineThatIntersects: angledLineThatIntersects.fn, startSketchAt, - closee, + close, } function rotateOnAxis( diff --git a/src/lang/std/stdTypes.ts b/src/lang/std/stdTypes.ts index 3838faf35..fb715372d 100644 --- a/src/lang/std/stdTypes.ts +++ b/src/lang/std/stdTypes.ts @@ -48,7 +48,7 @@ export type InternalFnNames = | 'angledLineOfYLength' | 'angledLineToY' | 'startSketchAt' - | 'closee' + | 'close' | 'angledLineThatIntersects' export interface ModifyAstBase { diff --git a/src/useStore.ts b/src/useStore.ts index f23a6fe6c..2a27091a4 100644 --- a/src/useStore.ts +++ b/src/useStore.ts @@ -93,6 +93,7 @@ interface StoreState { ast: Program | null setAst: (ast: Program | null) => void updateAst: (ast: Program, focusPath?: PathToNode) => void + updateAstAsync: (ast: Program, focusPath?: PathToNode) => void code: string setCode: (code: string) => void formatCode: () => void @@ -107,6 +108,8 @@ interface StoreState { setIsShiftDown: (isShiftDown: boolean) => void } +let pendingAstUpdates: number[] = [] + export const useStore = create()( persist( (set, get) => ({ @@ -159,6 +162,7 @@ export const useStore = create()( }, updateAst: async (ast, focusPath) => { const newCode = recast(ast) + console.log('running update Ast', ast) const astWithUpdatedSource = abstractSyntaxTree( await asyncLexer(newCode) ) @@ -173,6 +177,17 @@ export const useStore = create()( }) } }, + updateAstAsync: async (ast, focusPath) => { + // clear any pending updates + pendingAstUpdates.forEach((id) => clearTimeout(id)) + pendingAstUpdates = [] + // setup a new update + pendingAstUpdates.push( + setTimeout(() => { + get().updateAst(ast, focusPath) + }, 100) as unknown as number + ) + }, code: '', setCode: (code) => { set({ code })