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 { 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
|
||||||
|
@ -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]} />
|
||||||
|
@ -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,
|
||||||
|
@ -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>(
|
||||||
|
@ -48,7 +48,7 @@ export type InternalFnNames =
|
|||||||
| 'angledLineOfYLength'
|
| 'angledLineOfYLength'
|
||||||
| 'angledLineToY'
|
| 'angledLineToY'
|
||||||
| 'startSketchAt'
|
| 'startSketchAt'
|
||||||
| 'closee'
|
| 'close'
|
||||||
| 'angledLineThatIntersects'
|
| 'angledLineThatIntersects'
|
||||||
|
|
||||||
export interface ModifyAstBase {
|
export interface ModifyAstBase {
|
||||||
|
@ -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 })
|
||||||
|
Reference in New Issue
Block a user