Add UI for closing the sketch loop (#87)

This commit is contained in:
Kurt Hutten
2023-04-01 20:38:31 +11:00
committed by GitHub
parent 0593afc4ff
commit 61533fb306
6 changed files with 87 additions and 19 deletions

View File

@ -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>('CallExpression')
const pipe = getNode<PipeExpression>('PipeExpression')
if (
maybeStartSketchAt?.node.callee.name === 'startSketchAt' &&
pipe.node &&
pipe.node.body.length > 2
) {
const modifiedAst = JSON.parse(JSON.stringify(ast))
const _pipe = getNodeFromPath<PipeExpression>(
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()
}}
>
<primitive object={geo} />
<meshStandardMaterial

View File

@ -5,12 +5,15 @@ import { addNewSketchLn } from '../lang/std/sketch'
import { roundOff } from '../lib/utils'
export const SketchPlane = () => {
const { ast, guiMode, updateAst, programMemory } = useStore((s) => ({
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)
}}
>
<planeGeometry args={[30, 40]} />

View File

@ -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,

View File

@ -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<T extends SketchGroup | ExtrudeGroup>(

View File

@ -48,7 +48,7 @@ export type InternalFnNames =
| 'angledLineOfYLength'
| 'angledLineToY'
| 'startSketchAt'
| 'closee'
| 'close'
| 'angledLineThatIntersects'
export interface ModifyAstBase {

View File

@ -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<StoreState>()(
persist(
(set, get) => ({
@ -159,6 +162,7 @@ export const useStore = create<StoreState>()(
},
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<StoreState>()(
})
}
},
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 })