Add UI for closing the sketch loop (#87)
This commit is contained in:
@ -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
|
||||
|
@ -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]} />
|
||||
|
@ -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,
|
||||
|
@ -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>(
|
||||
|
@ -48,7 +48,7 @@ export type InternalFnNames =
|
||||
| 'angledLineOfYLength'
|
||||
| 'angledLineToY'
|
||||
| 'startSketchAt'
|
||||
| 'closee'
|
||||
| 'close'
|
||||
| 'angledLineThatIntersects'
|
||||
|
||||
export interface ModifyAstBase {
|
||||
|
@ -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 })
|
||||
|
Reference in New Issue
Block a user