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 { useRef, useState, useEffect, useMemo } from 'react'
import { CallExpression, ArrayExpression } from '../lang/abstractSyntaxTree' import {
import { getNodePathFromSourceRange, getNodeFromPath } from '../lang/queryAst' CallExpression,
ArrayExpression,
PipeExpression,
} from '../lang/abstractSyntaxTree'
import {
getNodePathFromSourceRange,
getNodeFromPath,
getNodeFromPathCurry,
} from '../lang/queryAst'
import { changeSketchArguments } from '../lang/std/sketch' import { changeSketchArguments } from '../lang/std/sketch'
import { import {
ExtrudeGroup, ExtrudeGroup,
@ -18,6 +26,7 @@ import { isOverlap, roundOff } from '../lib/utils'
import { Vector3, DoubleSide, Quaternion } from 'three' import { Vector3, DoubleSide, Quaternion } from 'three'
import { useSetCursor } from '../hooks/useSetCursor' import { useSetCursor } from '../hooks/useSetCursor'
import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos' import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos'
import { createCallExpression, createPipeSubstitution } from '../lang/modifyAst'
function MovingSphere({ function MovingSphere({
geo, geo,
@ -352,8 +361,11 @@ function PathRender({
rotation: Rotation rotation: Rotation
position: Position position: Position
}) { }) {
const { selectionRanges } = useStore(({ selectionRanges }) => ({ const { selectionRanges, updateAstAsync, ast, guiMode } = useStore((s) => ({
selectionRanges, selectionRanges: s.selectionRanges,
updateAstAsync: s.updateAstAsync,
ast: s.ast,
guiMode: s.guiMode,
})) }))
const [editorCursor, setEditorCursor] = useState(false) const [editorCursor, setEditorCursor] = useState(false)
useEffect(() => { useEffect(() => {
@ -397,6 +409,37 @@ function PathRender({
forceHighlight={forceHighlight || editorCursor} forceHighlight={forceHighlight || editorCursor}
rotation={rotation} rotation={rotation}
position={position} 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, forceHighlight = false,
rotation, rotation,
position, position,
onClick: _onClick = () => {},
}: { }: {
geo: BufferGeometry geo: BufferGeometry
sourceRange: [number, number] sourceRange: [number, number]
forceHighlight?: boolean forceHighlight?: boolean
rotation: Rotation rotation: Rotation
position: Position position: Position
onClick?: () => void
}) { }) {
const { setHighlightRange, guiMode, ast } = useStore((s) => ({ const { setHighlightRange, guiMode, ast } = useStore((s) => ({
setHighlightRange: s.setHighlightRange, setHighlightRange: s.setHighlightRange,
@ -457,7 +502,10 @@ function LineRender({
setHover(false) setHover(false)
setHighlightRange([0, 0]) setHighlightRange([0, 0])
}} }}
onClick={onClick} onClick={() => {
_onClick()
onClick()
}}
> >
<primitive object={geo} /> <primitive object={geo} />
<meshStandardMaterial <meshStandardMaterial

View File

@ -5,12 +5,15 @@ import { addNewSketchLn } from '../lang/std/sketch'
import { roundOff } from '../lib/utils' import { roundOff } from '../lib/utils'
export const SketchPlane = () => { export const SketchPlane = () => {
const { ast, guiMode, updateAst, programMemory } = useStore((s) => ({ const { ast, guiMode, updateAst, programMemory, updateAstAsync } = useStore(
guiMode: s.guiMode, (s) => ({
ast: s.ast, guiMode: s.guiMode,
updateAst: s.updateAst, ast: s.ast,
programMemory: s.programMemory, updateAst: s.updateAst,
})) updateAstAsync: s.updateAstAsync,
programMemory: s.programMemory,
})
)
if (guiMode.mode !== 'sketch') { if (guiMode.mode !== 'sketch') {
return null return null
} }
@ -71,7 +74,7 @@ export const SketchPlane = () => {
fnName: guiMode.sketchMode, fnName: guiMode.sketchMode,
pathToNode: guiMode.pathToNode, pathToNode: guiMode.pathToNode,
}) })
updateAst(modifiedAst) updateAstAsync(modifiedAst)
}} }}
> >
<planeGeometry args={[30, 40]} /> <planeGeometry args={[30, 40]} />

View File

@ -1479,12 +1479,14 @@ function addTagWithTo(
} }
} }
export const closee: InternalFn = ( export const close: InternalFn = (
{ sourceRange, programMemory }, { sourceRange },
sketchGroup: SketchGroup sketchGroup: SketchGroup
): SketchGroup => { ): SketchGroup => {
const from = getCoordsFromPaths(sketchGroup, sketchGroup.value.length - 1) 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({ const geo = lineGeo({
from: [...from, 0], from: [...from, 0],
to: [...to, 0], to: [...to, 0],
@ -1509,7 +1511,7 @@ export const closee: InternalFn = (
}, },
} }
const newValue = [...sketchGroup.value] const newValue = [...sketchGroup.value]
newValue[0] = currentPath newValue.push(currentPath)
return { return {
...sketchGroup, ...sketchGroup,
value: newValue, value: newValue,

View File

@ -10,7 +10,7 @@ import {
angledLineToX, angledLineToX,
angledLineOfYLength, angledLineOfYLength,
angledLineToY, angledLineToY,
closee, close,
startSketchAt, startSketchAt,
angledLineThatIntersects, angledLineThatIntersects,
} from './sketch' } from './sketch'
@ -127,7 +127,7 @@ export const internalFns: { [key in InternalFnNames]: InternalFn } = {
angledLineToY: angledLineToY.fn, angledLineToY: angledLineToY.fn,
angledLineThatIntersects: angledLineThatIntersects.fn, angledLineThatIntersects: angledLineThatIntersects.fn,
startSketchAt, startSketchAt,
closee, close,
} }
function rotateOnAxis<T extends SketchGroup | ExtrudeGroup>( function rotateOnAxis<T extends SketchGroup | ExtrudeGroup>(

View File

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

View File

@ -93,6 +93,7 @@ interface StoreState {
ast: Program | null ast: Program | null
setAst: (ast: Program | null) => void setAst: (ast: Program | null) => void
updateAst: (ast: Program, focusPath?: PathToNode) => void updateAst: (ast: Program, focusPath?: PathToNode) => void
updateAstAsync: (ast: Program, focusPath?: PathToNode) => void
code: string code: string
setCode: (code: string) => void setCode: (code: string) => void
formatCode: () => void formatCode: () => void
@ -107,6 +108,8 @@ interface StoreState {
setIsShiftDown: (isShiftDown: boolean) => void setIsShiftDown: (isShiftDown: boolean) => void
} }
let pendingAstUpdates: number[] = []
export const useStore = create<StoreState>()( export const useStore = create<StoreState>()(
persist( persist(
(set, get) => ({ (set, get) => ({
@ -159,6 +162,7 @@ export const useStore = create<StoreState>()(
}, },
updateAst: async (ast, focusPath) => { updateAst: async (ast, focusPath) => {
const newCode = recast(ast) const newCode = recast(ast)
console.log('running update Ast', ast)
const astWithUpdatedSource = abstractSyntaxTree( const astWithUpdatedSource = abstractSyntaxTree(
await asyncLexer(newCode) 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: '', code: '',
setCode: (code) => { setCode: (code) => {
set({ code }) set({ code })