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) => ({
 | 
			
		||||
    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)
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <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