add the ability to edit sketch later
This commit is contained in:
		
							
								
								
									
										50
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								src/App.tsx
									
									
									
									
									
								
							@ -3,7 +3,11 @@ import { Canvas } from '@react-three/fiber'
 | 
			
		||||
import { Allotment } from 'allotment'
 | 
			
		||||
import { OrbitControls, OrthographicCamera } from '@react-three/drei'
 | 
			
		||||
import { lexer } from './lang/tokeniser'
 | 
			
		||||
import { abstractSyntaxTree } from './lang/abstractSyntaxTree'
 | 
			
		||||
import {
 | 
			
		||||
  abstractSyntaxTree,
 | 
			
		||||
  getNodePathFromSourceRange,
 | 
			
		||||
  getNodeFromPath
 | 
			
		||||
} from './lang/abstractSyntaxTree'
 | 
			
		||||
import { executor, processShownObjects, ViewerArtifact } from './lang/executor'
 | 
			
		||||
import { recast } from './lang/recast'
 | 
			
		||||
import { BufferGeometry } from 'three'
 | 
			
		||||
@ -32,7 +36,6 @@ function App() {
 | 
			
		||||
    setSelectionRange,
 | 
			
		||||
    selectionRange,
 | 
			
		||||
    guiMode,
 | 
			
		||||
    setGuiMode,
 | 
			
		||||
    lastGuiMode,
 | 
			
		||||
    removeError,
 | 
			
		||||
    addLog,
 | 
			
		||||
@ -41,6 +44,8 @@ function App() {
 | 
			
		||||
    setAst,
 | 
			
		||||
    formatCode,
 | 
			
		||||
    ast,
 | 
			
		||||
    setError,
 | 
			
		||||
    errorState,
 | 
			
		||||
  } = useStore((s) => ({
 | 
			
		||||
    editorView: s.editorView,
 | 
			
		||||
    setEditorView: s.setEditorView,
 | 
			
		||||
@ -56,6 +61,8 @@ function App() {
 | 
			
		||||
    setAst: s.setAst,
 | 
			
		||||
    lastGuiMode: s.lastGuiMode,
 | 
			
		||||
    formatCode: s.formatCode,
 | 
			
		||||
    setError: s.setError,
 | 
			
		||||
    errorState: s.errorState,
 | 
			
		||||
  }))
 | 
			
		||||
  // const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
 | 
			
		||||
  const onChange = (value: string, viewUpdate: ViewUpdate) => {
 | 
			
		||||
@ -110,8 +117,9 @@ function App() {
 | 
			
		||||
      setGeoArray(geos)
 | 
			
		||||
      removeError()
 | 
			
		||||
      console.log(programMemory)
 | 
			
		||||
      setError()
 | 
			
		||||
    } catch (e: any) {
 | 
			
		||||
      setGuiMode({ mode: 'codeError' })
 | 
			
		||||
      setError('problem')
 | 
			
		||||
      console.log(e)
 | 
			
		||||
      addLog(e)
 | 
			
		||||
    }
 | 
			
		||||
@ -173,7 +181,7 @@ function App() {
 | 
			
		||||
                <AxisIndicator />
 | 
			
		||||
              </Canvas>
 | 
			
		||||
            </div>
 | 
			
		||||
            {guiMode.mode === 'codeError' && (
 | 
			
		||||
            {errorState.isError && (
 | 
			
		||||
              <div className="absolute inset-0 bg-gray-700/20">
 | 
			
		||||
                <pre>
 | 
			
		||||
                  {'last first: \n\n' +
 | 
			
		||||
@ -201,19 +209,39 @@ function Line({
 | 
			
		||||
  sourceRange: [number, number]
 | 
			
		||||
  forceHighlight?: boolean
 | 
			
		||||
}) {
 | 
			
		||||
  const { setHighlightRange, selectionRange } = useStore(
 | 
			
		||||
    ({ setHighlightRange, selectionRange }) => ({
 | 
			
		||||
      setHighlightRange,
 | 
			
		||||
      selectionRange,
 | 
			
		||||
    })
 | 
			
		||||
  )
 | 
			
		||||
  const { setHighlightRange, selectionRange, guiMode, setGuiMode, ast } =
 | 
			
		||||
    useStore(
 | 
			
		||||
      ({ setHighlightRange, selectionRange, guiMode, setGuiMode, ast }) => ({
 | 
			
		||||
        setHighlightRange,
 | 
			
		||||
        selectionRange,
 | 
			
		||||
        guiMode,
 | 
			
		||||
        setGuiMode,
 | 
			
		||||
        ast,
 | 
			
		||||
      })
 | 
			
		||||
    )
 | 
			
		||||
  // This reference will give us direct access to the mesh
 | 
			
		||||
  const ref = useRef<BufferGeometry | undefined>() as any
 | 
			
		||||
  const [hovered, setHover] = useState(false)
 | 
			
		||||
  const [editorCursor, setEditorCursor] = useState(false)
 | 
			
		||||
  const [didSetCanEdit, setDidSetCanEdit] = useState(false)
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const shouldHighlight = isOverlapping(sourceRange, selectionRange)
 | 
			
		||||
    setEditorCursor(shouldHighlight)
 | 
			
		||||
    if (shouldHighlight && guiMode.mode === 'default' && ast) {
 | 
			
		||||
      const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
 | 
			
		||||
      const piper = getNodeFromPath(ast, pathToNode, 'PipeExpression')
 | 
			
		||||
      const axis = piper.type !== 'PipeExpression' ? 'xy'
 | 
			
		||||
        : piper?.body?.[1]?.callee?.name === 'rx' ? 'xz' : 'yz'
 | 
			
		||||
      setGuiMode({ mode: 'canEditSketch', pathToNode, axis })
 | 
			
		||||
      setDidSetCanEdit(true)
 | 
			
		||||
    } else if (
 | 
			
		||||
      !shouldHighlight &&
 | 
			
		||||
      didSetCanEdit &&
 | 
			
		||||
      guiMode.mode === 'canEditSketch'
 | 
			
		||||
    ) {
 | 
			
		||||
      setGuiMode({ mode: 'default' })
 | 
			
		||||
      setDidSetCanEdit(false)
 | 
			
		||||
    }
 | 
			
		||||
  }, [selectionRange, sourceRange])
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@ -257,7 +285,7 @@ function RenderViewerArtifacts({
 | 
			
		||||
    const shouldHighlight = isOverlapping(artifact.sourceRange, selectionRange)
 | 
			
		||||
    setEditorCursor(shouldHighlight)
 | 
			
		||||
  }, [selectionRange, artifact.sourceRange])
 | 
			
		||||
  if (artifact.type === 'geo') {
 | 
			
		||||
  if (artifact.type === 'sketchLine') {
 | 
			
		||||
    const { geo, sourceRange } = artifact
 | 
			
		||||
    return (
 | 
			
		||||
      <Line
 | 
			
		||||
 | 
			
		||||
@ -15,16 +15,50 @@ export const Toolbar = () => {
 | 
			
		||||
              sketchMode: 'selectFace',
 | 
			
		||||
            })
 | 
			
		||||
          }}
 | 
			
		||||
          className="border m-1 px-1 rounded"
 | 
			
		||||
        >
 | 
			
		||||
          Start sketch
 | 
			
		||||
        </button>
 | 
			
		||||
      )}
 | 
			
		||||
      {guiMode.mode === 'sketch' && guiMode.sketchMode === 'points' && (
 | 
			
		||||
        <button>LineTo TODO</button>
 | 
			
		||||
      {guiMode.mode === 'canEditSketch' && (
 | 
			
		||||
        <button
 | 
			
		||||
          onClick={() => {
 | 
			
		||||
            setGuiMode({
 | 
			
		||||
              mode: 'sketch',
 | 
			
		||||
              sketchMode: 'sketchEdit',
 | 
			
		||||
              pathToNode: guiMode.pathToNode,
 | 
			
		||||
              axis: guiMode.axis,
 | 
			
		||||
            })
 | 
			
		||||
          }}
 | 
			
		||||
          className="border m-1 px-1 rounded"
 | 
			
		||||
        >
 | 
			
		||||
          EditSketch
 | 
			
		||||
        </button>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {guiMode.mode !== 'default' && (
 | 
			
		||||
        <button onClick={() => setGuiMode({ mode: 'default' })}>exit</button>
 | 
			
		||||
        <button
 | 
			
		||||
          onClick={() => setGuiMode({ mode: 'default' })}
 | 
			
		||||
          className="border m-1 px-1 rounded"
 | 
			
		||||
        >
 | 
			
		||||
          Exit sketch
 | 
			
		||||
        </button>
 | 
			
		||||
      )}
 | 
			
		||||
      {guiMode.mode === 'sketch' &&
 | 
			
		||||
        (guiMode.sketchMode === 'points' ||
 | 
			
		||||
          guiMode.sketchMode === 'sketchEdit') && (
 | 
			
		||||
          <button
 | 
			
		||||
            className={`border m-1 px-1 rounded ${
 | 
			
		||||
              guiMode.sketchMode === 'points' && 'bg-gray-400'
 | 
			
		||||
            }`}
 | 
			
		||||
            onClick={() => setGuiMode({
 | 
			
		||||
              ...guiMode,
 | 
			
		||||
              sketchMode: guiMode.sketchMode === 'points' ? 'sketchEdit' :'points',
 | 
			
		||||
            })}
 | 
			
		||||
          >
 | 
			
		||||
            LineTo{guiMode.sketchMode === 'points' && '✅'}
 | 
			
		||||
          </button>
 | 
			
		||||
        )}
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -56,9 +56,8 @@ export const BasePlanes = () => {
 | 
			
		||||
 | 
			
		||||
    setGuiMode({
 | 
			
		||||
      mode: 'sketch',
 | 
			
		||||
      sketchMode: 'points',
 | 
			
		||||
      sketchMode: 'sketchEdit',
 | 
			
		||||
      axis,
 | 
			
		||||
      id,
 | 
			
		||||
      pathToNode,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ export const SketchPlane = () => {
 | 
			
		||||
  if (guiMode.mode !== 'sketch') {
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
  if (guiMode.sketchMode !== 'points') {
 | 
			
		||||
  if (guiMode.sketchMode !== 'points' && guiMode.sketchMode !== 'sketchEdit' ) {
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,9 @@ export const SketchPlane = () => {
 | 
			
		||||
        rotation={clickDetectPlaneRotation}
 | 
			
		||||
        name={sketchGridName}
 | 
			
		||||
        onClick={(e) => {
 | 
			
		||||
          if (guiMode.sketchMode !== 'points') {
 | 
			
		||||
            return
 | 
			
		||||
          }
 | 
			
		||||
          const sketchGridIntersection = e.intersections.find(
 | 
			
		||||
            ({ object }) => object.name === sketchGridName
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
@ -1176,7 +1176,8 @@ export function addLine(
 | 
			
		||||
  const dumbyStartend = { start: 0, end: 0 }
 | 
			
		||||
  const sketchExpression = getNodeFromPath(
 | 
			
		||||
    _node,
 | 
			
		||||
    pathToNode
 | 
			
		||||
    pathToNode,
 | 
			
		||||
    'SketchExpression'
 | 
			
		||||
  ) as SketchExpression
 | 
			
		||||
  const line: ExpressionStatement = {
 | 
			
		||||
    type: 'ExpressionStatement',
 | 
			
		||||
@ -1263,8 +1264,13 @@ function debuggerr(tokens: Token[], indexes: number[], msg = ''): string {
 | 
			
		||||
  return debugResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getNodeFromPath(node: Program, path: (string | number)[]) {
 | 
			
		||||
export function getNodeFromPath(
 | 
			
		||||
  node: Program,
 | 
			
		||||
  path: (string | number)[],
 | 
			
		||||
  stopAt: string = ''
 | 
			
		||||
) {
 | 
			
		||||
  let currentNode = node as any
 | 
			
		||||
  let stopAtNode = null
 | 
			
		||||
  let successfulPaths: (string | number)[] = []
 | 
			
		||||
  for (const pathItem of path) {
 | 
			
		||||
    try {
 | 
			
		||||
@ -1272,6 +1278,11 @@ export function getNodeFromPath(node: Program, path: (string | number)[]) {
 | 
			
		||||
        throw new Error('not an object')
 | 
			
		||||
      currentNode = currentNode[pathItem]
 | 
			
		||||
      successfulPaths.push(pathItem)
 | 
			
		||||
      if (currentNode.type === stopAt) {
 | 
			
		||||
        // it will match the deepest node of the type
 | 
			
		||||
        // instead of returning at the first match
 | 
			
		||||
        stopAtNode = currentNode
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        `Could not find path ${pathItem} in node ${JSON.stringify(
 | 
			
		||||
@ -1282,7 +1293,7 @@ export function getNodeFromPath(node: Program, path: (string | number)[]) {
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return currentNode
 | 
			
		||||
  return stopAtNode || currentNode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Path = (string | number)[]
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import fs from 'node:fs'
 | 
			
		||||
import { abstractSyntaxTree } from './abstractSyntaxTree'
 | 
			
		||||
import { lexer } from './tokeniser'
 | 
			
		||||
import { executor, ProgramMemory } from './executor'
 | 
			
		||||
import { Transform } from './sketch'
 | 
			
		||||
import { Transform, SketchGeo } from './sketch'
 | 
			
		||||
 | 
			
		||||
describe('test', () => {
 | 
			
		||||
  it('test assigning two variables, the second summing with the first', () => {
 | 
			
		||||
@ -64,7 +64,7 @@ show(mySketch)
 | 
			
		||||
`
 | 
			
		||||
    const { root, return: _return } = exe(code)
 | 
			
		||||
    expect(
 | 
			
		||||
      root.mySketch.map(
 | 
			
		||||
      root.mySketch.sketch.map(
 | 
			
		||||
        ({ previousPath, firstPath, geo, ...rest }: any) => rest
 | 
			
		||||
      )
 | 
			
		||||
    ).toEqual([
 | 
			
		||||
@ -77,7 +77,7 @@ show(mySketch)
 | 
			
		||||
        sourceRange: [93, 100],
 | 
			
		||||
      },
 | 
			
		||||
    ])
 | 
			
		||||
    expect(root.mySketch[0]).toEqual(root.mySketch[4].firstPath)
 | 
			
		||||
    expect(root.mySketch.sketch[0]).toEqual(root.mySketch.sketch[4].firstPath)
 | 
			
		||||
    // hmm not sure what handle the "show" function
 | 
			
		||||
    expect(_return).toEqual([
 | 
			
		||||
      {
 | 
			
		||||
@ -109,7 +109,7 @@ show(mySketch)
 | 
			
		||||
      // 'show(mySk1)',
 | 
			
		||||
    ].join('\n')
 | 
			
		||||
    const { root } = exe(code)
 | 
			
		||||
    expect(root.mySk1).toHaveLength(4)
 | 
			
		||||
    expect(root.mySk1.sketch).toHaveLength(4)
 | 
			
		||||
    expect(root?.rotated?.type).toBe('transform')
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@ -124,9 +124,7 @@ show(mySketch)
 | 
			
		||||
    const { root } = exe(code)
 | 
			
		||||
    const striptVersion = removeGeoFromSketch(root.mySk1)
 | 
			
		||||
    expect(striptVersion).toEqual({
 | 
			
		||||
      type: 'transform',
 | 
			
		||||
      rotation: [1.5707963267948966, 0, 0],
 | 
			
		||||
      transform: [0, 0, 0],
 | 
			
		||||
      type: 'sketchGeo',
 | 
			
		||||
      sketch: [
 | 
			
		||||
        {
 | 
			
		||||
          type: 'base',
 | 
			
		||||
@ -150,8 +148,38 @@ show(mySketch)
 | 
			
		||||
          sourceRange: [60, 71],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      sourceRange: [77, 86],
 | 
			
		||||
      sourceRange: [13, 73],
 | 
			
		||||
    })
 | 
			
		||||
    // old expect
 | 
			
		||||
    // expect(striptVersion).toEqual({
 | 
			
		||||
    //   type: 'transform',
 | 
			
		||||
    //   rotation: [1.5707963267948966, 0, 0],
 | 
			
		||||
    //   transform: [0, 0, 0],
 | 
			
		||||
    //   sketch: [
 | 
			
		||||
    //     {
 | 
			
		||||
    //       type: 'base',
 | 
			
		||||
    //       from: [0, 0],
 | 
			
		||||
    //       sourceRange: [0, 0],
 | 
			
		||||
    //     },
 | 
			
		||||
    //     {
 | 
			
		||||
    //       type: 'toPoint',
 | 
			
		||||
    //       to: [1, 1],
 | 
			
		||||
    //       sourceRange: [17, 28],
 | 
			
		||||
    //     },
 | 
			
		||||
    //     {
 | 
			
		||||
    //       type: 'toPoint',
 | 
			
		||||
    //       to: [0, 1],
 | 
			
		||||
    //       sourceRange: [36, 57],
 | 
			
		||||
    //       name: 'myPath',
 | 
			
		||||
    //     },
 | 
			
		||||
    //     {
 | 
			
		||||
    //       type: 'toPoint',
 | 
			
		||||
    //       to: [1, 1],
 | 
			
		||||
    //       sourceRange: [60, 71],
 | 
			
		||||
    //     },
 | 
			
		||||
    //   ],
 | 
			
		||||
    //   sourceRange: [77, 86],
 | 
			
		||||
    // })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -166,8 +194,8 @@ function exe(
 | 
			
		||||
  return executor(ast, programMemory)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function removeGeoFromSketch(sketch: Transform): any {
 | 
			
		||||
  if (!Array.isArray(sketch.sketch)) {
 | 
			
		||||
function removeGeoFromSketch(sketch: Transform | SketchGeo): any {
 | 
			
		||||
  if (sketch.type !== 'sketchGeo') {
 | 
			
		||||
    return removeGeoFromSketch(sketch.sketch)
 | 
			
		||||
  }
 | 
			
		||||
  return {
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import {
 | 
			
		||||
  BinaryExpression,
 | 
			
		||||
  PipeExpression,
 | 
			
		||||
} from './abstractSyntaxTree'
 | 
			
		||||
import { Path, Transform, sketchFns } from './sketch'
 | 
			
		||||
import { Path, Transform, SketchGeo, sketchFns } from './sketch'
 | 
			
		||||
import { BufferGeometry } from 'three'
 | 
			
		||||
 | 
			
		||||
export interface ProgramMemory {
 | 
			
		||||
@ -63,7 +63,12 @@ export const executor = (
 | 
			
		||||
            )
 | 
			
		||||
            _sketch = newProgramMemory._sketch
 | 
			
		||||
          }
 | 
			
		||||
          _programMemory.root[variableName] = _sketch
 | 
			
		||||
          const newSketch: SketchGeo = {
 | 
			
		||||
            type: 'sketchGeo',
 | 
			
		||||
            sketch: _sketch,
 | 
			
		||||
            sourceRange: [sketchInit.start, sketchInit.end],
 | 
			
		||||
          }
 | 
			
		||||
          _programMemory.root[variableName] = newSketch
 | 
			
		||||
        } else if (declaration.init.type === 'FunctionExpression') {
 | 
			
		||||
          const fnInit = declaration.init
 | 
			
		||||
 | 
			
		||||
@ -271,9 +276,14 @@ function executePipeBody(
 | 
			
		||||
      _sketch = newProgramMemory._sketch
 | 
			
		||||
    }
 | 
			
		||||
    // _programMemory.root[variableName] = _sketch
 | 
			
		||||
    const newSketch: SketchGeo = {
 | 
			
		||||
      type: 'sketchGeo',
 | 
			
		||||
      sketch: _sketch,
 | 
			
		||||
      sourceRange: [expression.start, expression.end],
 | 
			
		||||
    }
 | 
			
		||||
    return executePipeBody(body, programMemory, expressionIndex + 1, [
 | 
			
		||||
      ...previousResults,
 | 
			
		||||
      _sketch,
 | 
			
		||||
      newSketch,
 | 
			
		||||
    ])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -284,7 +294,7 @@ type SourceRange = [number, number]
 | 
			
		||||
 | 
			
		||||
export type ViewerArtifact =
 | 
			
		||||
  | {
 | 
			
		||||
      type: 'geo'
 | 
			
		||||
      type: 'sketchLine'
 | 
			
		||||
      sourceRange: SourceRange
 | 
			
		||||
      geo: BufferGeometry
 | 
			
		||||
    }
 | 
			
		||||
@ -301,11 +311,11 @@ type PreviousTransforms = {
 | 
			
		||||
 | 
			
		||||
export const processShownObjects = (
 | 
			
		||||
  programMemory: ProgramMemory,
 | 
			
		||||
  geoMeta: Path[] | Transform,
 | 
			
		||||
  geoMeta: SketchGeo | Transform,
 | 
			
		||||
  previousTransforms: PreviousTransforms = []
 | 
			
		||||
): ViewerArtifact[] => {
 | 
			
		||||
  if (Array.isArray(geoMeta)) {
 | 
			
		||||
    return geoMeta.map(({ geo, sourceRange }) => {
 | 
			
		||||
  if (geoMeta?.type === 'sketchGeo') {
 | 
			
		||||
    return geoMeta.sketch.map(({ geo, sourceRange }) => {
 | 
			
		||||
      const newGeo = geo.clone()
 | 
			
		||||
      previousTransforms.forEach(({ rotation, transform }) => {
 | 
			
		||||
        newGeo.rotateX(rotation[0])
 | 
			
		||||
@ -315,7 +325,7 @@ export const processShownObjects = (
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        type: 'geo',
 | 
			
		||||
        type: 'sketchLine',
 | 
			
		||||
        geo: newGeo,
 | 
			
		||||
        sourceRange,
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,13 @@ export interface Transform {
 | 
			
		||||
  type: 'transform'
 | 
			
		||||
  rotation: Rotation3
 | 
			
		||||
  transform: Translate3
 | 
			
		||||
  sketch: Path[] | Transform
 | 
			
		||||
  sketch: SketchGeo | Transform
 | 
			
		||||
  sourceRange: SourceRange
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SketchGeo {
 | 
			
		||||
  type: 'sketchGeo'
 | 
			
		||||
  sketch: Path[]
 | 
			
		||||
  sourceRange: SourceRange
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -213,7 +219,7 @@ function RotateOnAxis(axisMultiplier: [number, number, number]) {
 | 
			
		||||
    programMemory: ProgramMemory,
 | 
			
		||||
    sourceRange: SourceRange,
 | 
			
		||||
    rotationD: number,
 | 
			
		||||
    sketch: Path[] | Transform
 | 
			
		||||
    sketch: SketchGeo | Transform
 | 
			
		||||
  ): Transform => {
 | 
			
		||||
    const rotationR = rotationD * (Math.PI / 180)
 | 
			
		||||
    return {
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,9 @@ import { lexer } from './lang/tokeniser'
 | 
			
		||||
 | 
			
		||||
export type Range = [number, number]
 | 
			
		||||
 | 
			
		||||
type Plane = 'xy' | 'xz' | 'yz'
 | 
			
		||||
type PathToNode = (string | number)[]
 | 
			
		||||
 | 
			
		||||
type GuiModes =
 | 
			
		||||
  | {
 | 
			
		||||
      mode: 'default'
 | 
			
		||||
@ -13,17 +16,25 @@ type GuiModes =
 | 
			
		||||
  | {
 | 
			
		||||
      mode: 'sketch'
 | 
			
		||||
      sketchMode: 'points'
 | 
			
		||||
      axis: 'xy' | 'xz' | 'yz'
 | 
			
		||||
      axis: Plane
 | 
			
		||||
      id?: string
 | 
			
		||||
      pathToNode: (string | number)[]
 | 
			
		||||
      pathToNode: PathToNode
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      mode: 'sketch'
 | 
			
		||||
      sketchMode: 'sketchEdit'
 | 
			
		||||
      axis: Plane
 | 
			
		||||
      pathToNode: PathToNode
 | 
			
		||||
  }
 | 
			
		||||
  | {
 | 
			
		||||
      mode: 'sketch'
 | 
			
		||||
      sketchMode: 'selectFace'
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      mode: 'codeError'
 | 
			
		||||
    }
 | 
			
		||||
      mode: 'canEditSketch'
 | 
			
		||||
      pathToNode: PathToNode
 | 
			
		||||
      axis: Plane
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
interface StoreState {
 | 
			
		||||
  editorView: EditorView | null
 | 
			
		||||
@ -45,6 +56,11 @@ interface StoreState {
 | 
			
		||||
  code: string
 | 
			
		||||
  setCode: (code: string) => void
 | 
			
		||||
  formatCode: () => void
 | 
			
		||||
  errorState: {
 | 
			
		||||
    isError: boolean
 | 
			
		||||
    error: string
 | 
			
		||||
  }
 | 
			
		||||
  setError: (error?: string) => void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useStore = create<StoreState>()((set, get) => ({
 | 
			
		||||
@ -69,19 +85,10 @@ export const useStore = create<StoreState>()((set, get) => ({
 | 
			
		||||
  setGuiMode: (guiMode) => {
 | 
			
		||||
    const lastGuiMode = get().guiMode
 | 
			
		||||
    set({ guiMode })
 | 
			
		||||
    if (guiMode.mode !== 'codeError') {
 | 
			
		||||
      // don't set lastGuiMode to and error state
 | 
			
		||||
      // as the point fo lastGuiMode is to restore the last healthy state
 | 
			
		||||
      // todo maybe rename to lastHealthyGuiMode and remove this comment
 | 
			
		||||
      set({ lastGuiMode })
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  removeError: () => {
 | 
			
		||||
    const lastGuiMode = get().lastGuiMode
 | 
			
		||||
    const currentGuiMode = get().guiMode
 | 
			
		||||
    if (currentGuiMode.mode === 'codeError') {
 | 
			
		||||
      set({ guiMode: lastGuiMode })
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  logs: [],
 | 
			
		||||
  addLog: (log) => {
 | 
			
		||||
@ -108,4 +115,11 @@ export const useStore = create<StoreState>()((set, get) => ({
 | 
			
		||||
    const newCode = recast(ast)
 | 
			
		||||
    set({ code: newCode, ast })
 | 
			
		||||
  },
 | 
			
		||||
  errorState: {
 | 
			
		||||
    isError: false,
 | 
			
		||||
    error: '',
 | 
			
		||||
  },
 | 
			
		||||
  setError: (error = '') => {
 | 
			
		||||
    set({ errorState: { isError: !!error, error } })
 | 
			
		||||
  }
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user