diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 17bbb7740..ade910874 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,7 @@ jobs: - run: yarn install - run: yarn build:wasm:ci - run: yarn simpleserver:ci + - run: npm pkg delete type - run: yarn test:nowatch - run: yarn test:cov - run: yarn test:rust diff --git a/package.json b/package.json index ce31a907f..de9071e7f 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,6 @@ "dependencies": { "@codemirror/lang-javascript": "^6.1.1", "@headlessui/react": "^1.7.13", - "@react-three/drei": "^9.42.0", - "@react-three/fiber": "^8.9.1", "@tauri-apps/api": "^1.3.0", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^13.0.0", @@ -17,6 +15,7 @@ "@types/react-dom": "^18.0.0", "@uiw/react-codemirror": "^4.15.1", "allotment": "^1.17.0", + "crypto-js": "^4.1.1", "http-server": "^14.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -25,8 +24,8 @@ "react-scripts": "5.0.1", "sketch-helpers": "^0.0.2", "swr": "^2.0.4", - "three": "^0.146.0", "toml": "^3.0.0", + "ts-node": "^10.9.1", "typescript": "^4.4.2", "util": "^0.12.5", "uuid": "^9.0.0", @@ -41,13 +40,13 @@ "build:both": "react-scripts build", "build:both:local": "yarn build:wasm && react-scripts build", "test": "react-scripts test", - "test:nowatch": "react-scripts test --watchAll=false", + "test:nowatch": "react-scripts test --watchAll=false --forceExit", "test:rust": "(cd src/wasm-lib && cargo test && cargo clippy)", - "test:cov": "react-scripts test --watchAll=false --coverage=true", + "test:cov": "react-scripts test --watchAll=false --coverage=true --forceExit", "simpleserver:ci": "http-server ./public --cors -p 3000 &", "simpleserver": "http-server ./public --cors -p 3000", "eject": "react-scripts eject", - "fmt": "prettier --write ./src/**/*.{ts,tsx,js}", + "fmt": "prettier --write ./src/**/*.{ts,tsx,js} && prettier --write ./src/**/**/*.{ts,tsx,js}", "remove-importmeta": "sed -i '' 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\"", "remove-importmeta:ci": "sed -i 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\"", "add-missing-import": "echo \"import util from 'util'; if (typeof window !== 'undefined' && !window.TextEncoder) { window.TextEncoder = util.TextEncoder; window.TextDecoder = util.TextDecoder}\" | cat - ./src/wasm-lib/pkg/wasm_lib.js > temp && mv temp ./src/wasm-lib/pkg/wasm_lib.js", @@ -92,8 +91,7 @@ "devDependencies": { "@tauri-apps/cli": "^1.3.1", "@types/crypto-js": "^4.1.1", - "@types/three": "^0.146.0", - "@types/uuid": "^9.0.2", + "@types/uuid": "^9.0.1", "autoprefixer": "^10.4.13", "postcss": "^8.4.19", "prettier": "^2.8.0", diff --git a/src/App.test.tsx b/src/App.test.tsx index 04636a72f..cd373d517 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1,6 +1,5 @@ -import React from 'react' import { render, screen } from '@testing-library/react' -import App from './App' +import { App } from './App' let listener: ((rect: any) => void) | undefined = undefined ;(global as any).ResizeObserver = class ResizeObserver { diff --git a/src/App.tsx b/src/App.tsx index 02081605b..0bea55dce 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,8 @@ -import { useRef, useState, useEffect } from 'react' -import { Canvas } from '@react-three/fiber' +import { useRef, useEffect, useMemo } from 'react' import { Allotment } from 'allotment' -import { OrbitControls, OrthographicCamera } from '@react-three/drei' import { asyncLexer } from './lang/tokeniser' import { abstractSyntaxTree } from './lang/abstractSyntaxTree' -import { executor, ExtrudeGroup, SketchGroup } from './lang/executor' +import { _executor, ExtrudeGroup, SketchGroup } from './lang/executor' import CodeMirror from '@uiw/react-codemirror' import { javascript } from '@codemirror/lang-javascript' import { ViewUpdate } from '@codemirror/view' @@ -12,29 +10,25 @@ import { lineHighlightField, addLineHighlight, } from './editor/highlightextension' -import { useStore } from './useStore' +import { Selections, useStore } from './useStore' import { Toolbar } from './Toolbar' -import { BasePlanes } from './components/BasePlanes' -import { SketchPlane } from './components/SketchPlane' import { Logs } from './components/Logs' -import { AxisIndicator } from './components/AxisIndicator' -import { RenderViewerArtifacts } from './components/RenderViewerArtifacts' import { PanelHeader } from './components/PanelHeader' import { MemoryPanel } from './components/MemoryPanel' import { useHotKeyListener } from './hooks/useHotKeyListener' import { Stream } from './components/Stream' import ModalContainer from 'react-modal-promise' +import { EngineCommandManager } from './lang/std/engineConnection' +import { isOverlap } from './lib/utils' -const OrrthographicCamera = OrthographicCamera as any - -function App() { +export function App() { const cam = useRef() useHotKeyListener() const { editorView, setEditorView, setSelectionRanges, - selectionRanges: selectionRange, + selectionRanges, guiMode, lastGuiMode, addLog, @@ -46,6 +40,13 @@ function App() { setProgramMemory, resetLogs, selectionRangeTypeMap, + setArtifactMap, + engineCommandManager: _engineCommandManager, + setEngineCommandManager, + setHighlightRange, + setCursor2, + sourceRangeMap, + setMediaStream, } = useStore((s) => ({ editorView: s.editorView, setEditorView: s.setEditorView, @@ -63,6 +64,15 @@ function App() { setProgramMemory: s.setProgramMemory, resetLogs: s.resetLogs, selectionRangeTypeMap: s.selectionRangeTypeMap, + setArtifactMap: s.setArtifactNSourceRangeMaps, + engineCommandManager: s.engineCommandManager, + setEngineCommandManager: s.setEngineCommandManager, + setHighlightRange: s.setHighlightRange, + isShiftDown: s.isShiftDown, + setCursor: s.setCursor, + setCursor2: s.setCursor2, + sourceRangeMap: s.sourceRangeMap, + setMediaStream: s.setMediaStream })) // const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => { const onChange = (value: string, viewUpdate: ViewUpdate) => { @@ -78,18 +88,17 @@ function App() { const ranges = viewUpdate.state.selection.ranges const isChange = - ranges.length !== selectionRange.codeBasedSelections.length || + ranges.length !== selectionRanges.codeBasedSelections.length || ranges.some(({ from, to }, i) => { return ( - from !== selectionRange.codeBasedSelections[i].range[0] || - to !== selectionRange.codeBasedSelections[i].range[1] + from !== selectionRanges.codeBasedSelections[i].range[0] || + to !== selectionRanges.codeBasedSelections[i].range[1] ) }) if (!isChange) return - setSelectionRanges({ - otherSelections: [], - codeBasedSelections: ranges.map(({ from, to }, i) => { + const codeBasedSelections: Selections['codeBasedSelections'] = ranges.map( + ({ from, to }) => { if (selectionRangeTypeMap[to]) { return { type: selectionRangeTypeMap[to], @@ -100,15 +109,39 @@ function App() { type: 'default', range: [from, to], } - }), + } + ) + const idBasedSelections = codeBasedSelections + .map(({ type, range }) => { + const hasOverlap = Object.entries(sourceRangeMap).filter( + ([_, sourceRange]) => { + return isOverlap(sourceRange, range) + } + ) + if (hasOverlap.length) { + return { + type, + id: hasOverlap[0][0], + } + } + }) + .filter(Boolean) as any + + _engineCommandManager?.cusorsSelected({ + otherSelections: [], + idBasedSelections, + }) + + setSelectionRanges({ + otherSelections: [], + codeBasedSelections, }) } - const [geoArray, setGeoArray] = useState<(ExtrudeGroup | SketchGroup)[]>([]) + const engineCommandManager = useMemo(() => new EngineCommandManager(setMediaStream), []) useEffect(() => { const asyncWrap = async () => { try { if (!code) { - setGeoArray([]) setAst(null) return } @@ -116,60 +149,86 @@ function App() { const _ast = abstractSyntaxTree(tokens) setAst(_ast) resetLogs() - const programMemory = executor(_ast, { - root: { - log: { - type: 'userVal', - value: (a: any) => { - addLog(a) - }, - __meta: [ - { - pathToNode: [], - sourceRange: [0, 0], + if (_engineCommandManager) { + _engineCommandManager.endSession() + } + engineCommandManager.startNewSession() + setEngineCommandManager(engineCommandManager) + _executor( + _ast, + { + root: { + log: { + type: 'userVal', + value: (a: any) => { + addLog(a) }, - ], - }, - _0: { - type: 'userVal', - value: 0, - __meta: [], - }, - _90: { - type: 'userVal', - value: 90, - __meta: [], - }, - _180: { - type: 'userVal', - value: 180, - __meta: [], - }, - _270: { - type: 'userVal', - value: 270, - __meta: [], + __meta: [ + { + pathToNode: [], + sourceRange: [0, 0], + }, + ], + }, + _0: { + type: 'userVal', + value: 0, + __meta: [], + }, + _90: { + type: 'userVal', + value: 90, + __meta: [], + }, + _180: { + type: 'userVal', + value: 180, + __meta: [], + }, + _270: { + type: 'userVal', + value: 270, + __meta: [], + }, }, + pendingMemory: {}, }, - _sketch: [], - }) - setProgramMemory(programMemory) - const geos = programMemory?.return - ?.map(({ name }: { name: string }) => { - const artifact = programMemory?.root?.[name] - if ( - artifact.type === 'extrudeGroup' || - artifact.type === 'sketchGroup' - ) { - return artifact - } - return null - }) - .filter((a) => a) as (ExtrudeGroup | SketchGroup)[] + engineCommandManager, + { bodyType: 'root' }, + [] + ).then(async (programMemory) => { + const { artifactMap, sourceRangeMap } = + await engineCommandManager.waitForAllCommands() - setGeoArray(geos) - console.log(programMemory) - setError() + setArtifactMap({ artifactMap, sourceRangeMap }) + engineCommandManager.onHover((id) => { + if (!id) { + setHighlightRange([0, 0]) + } else { + const sourceRange = sourceRangeMap[id] + setHighlightRange(sourceRange) + } + }) + engineCommandManager.onClick(({ id, type }) => { + setCursor2({ range: sourceRangeMap[id], type }) + }) + setProgramMemory(programMemory) + const geos = programMemory?.return + ?.map(({ name }: { name: string }) => { + const artifact = programMemory?.root?.[name] + if ( + artifact.type === 'extrudeGroup' || + artifact.type === 'sketchGroup' + ) { + return artifact + } + return null + }) + .filter((a) => a) as (ExtrudeGroup | SketchGroup)[] + + // console.log(programMemory) + setError() + }) } catch (e: any) { setError('problem') console.log(e) @@ -209,52 +268,9 @@ function App() { - - - + + - - - - - - - - - - - - - - {errorState.isError && ( - - - {'last first: \n\n' + - JSON.stringify(lastGuiMode, null, 2) + - '\n\n' + - JSON.stringify(guiMode)} - - - )} - @@ -262,5 +278,3 @@ function App() { ) } - -export default App diff --git a/src/Auth.tsx b/src/Auth.tsx index e616c65d7..0d53efa7f 100644 --- a/src/Auth.tsx +++ b/src/Auth.tsx @@ -1,7 +1,7 @@ import useSWR from 'swr' import fetcher from './lib/fetcher' import withBaseUrl from './lib/withBaseURL' -import App from './App' +import { App } from './App' export const Auth = () => { const { data: user } = useSWR(withBaseUrl('/user'), fetcher) as any diff --git a/src/components/AvailableVarsHelpers.tsx b/src/components/AvailableVarsHelpers.tsx index 2dec7d9be..1454c3fb8 100644 --- a/src/components/AvailableVarsHelpers.tsx +++ b/src/components/AvailableVarsHelpers.tsx @@ -96,11 +96,14 @@ export function useCalc({ newVariableInsertIndex: number setNewVariableName: (a: string) => void } { - const { ast, programMemory, selectionRange } = useStore((s) => ({ - ast: s.ast, - programMemory: s.programMemory, - selectionRange: s.selectionRanges.codeBasedSelections[0].range, - })) + const { ast, programMemory, selectionRange, engineCommandManager } = useStore( + (s) => ({ + ast: s.ast, + programMemory: s.programMemory, + selectionRange: s.selectionRanges.codeBasedSelections[0].range, + engineCommandManager: s.engineCommandManager, + }) + ) const inputRef = useRef(null) const [availableVarInfo, setAvailableVarInfo] = useState< ReturnType @@ -141,6 +144,7 @@ export function useCalc({ }, [ast, programMemory, selectionRange]) useEffect(() => { + if (!engineCommandManager) return try { const code = `const __result__ = ${value}\nshow(__result__)` const ast = abstractSyntaxTree(lexer(code)) @@ -148,18 +152,19 @@ export function useCalc({ availableVarInfo.variables.forEach(({ key, value }) => { _programMem.root[key] = { type: 'userVal', value, __meta: [] } }) - const programMemory = executor(ast, _programMem) - const resultDeclaration = ast.body.find( - (a) => - a.type === 'VariableDeclaration' && - a.declarations?.[0]?.id?.name === '__result__' - ) - const init = - resultDeclaration?.type === 'VariableDeclaration' && - resultDeclaration?.declarations?.[0]?.init - const result = programMemory?.root?.__result__?.value - setCalcResult(typeof result === 'number' ? String(result) : 'NAN') - init && setValueNode(init) + executor(ast, _programMem, engineCommandManager).then((programMemory) => { + const resultDeclaration = ast.body.find( + (a) => + a.type === 'VariableDeclaration' && + a.declarations?.[0]?.id?.name === '__result__' + ) + const init = + resultDeclaration?.type === 'VariableDeclaration' && + resultDeclaration?.declarations?.[0]?.init + const result = programMemory?.root?.__result__?.value + setCalcResult(typeof result === 'number' ? String(result) : 'NAN') + init && setValueNode(init) + }) } catch (e) { setCalcResult('NAN') setValueNode(null) diff --git a/src/components/AxisIndicator.tsx b/src/components/AxisIndicator.tsx deleted file mode 100644 index 849f7ddfe..000000000 --- a/src/components/AxisIndicator.tsx +++ /dev/null @@ -1,16 +0,0 @@ -export const AxisIndicator = () => ( - <> - - - - - - - - - - - - - > -) diff --git a/src/components/BasePlanes.tsx b/src/components/BasePlanes.tsx deleted file mode 100644 index b659ac5b0..000000000 --- a/src/components/BasePlanes.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { useState } from 'react' -import { DoubleSide, Vector3 } from 'three' -import { useStore } from '../useStore' -import { Intersection } from '@react-three/fiber' -import { Text } from '@react-three/drei' -import { addSketchTo } from '../lang/modifyAst' -import { Program } from '../lang/abstractSyntaxTree' -import { Quaternion } from 'three' - -const opacity = 0.1 - -export const BasePlanes = () => { - const [axisIndex, setAxisIndex] = useState(null) - const { setGuiMode, guiMode, ast, updateAst } = useStore( - ({ guiMode, setGuiMode, ast, updateAst }) => ({ - guiMode, - setGuiMode, - ast, - updateAst, - }) - ) - - const onPointerEvent = ({ - intersections, - }: { - intersections: Intersection[] - }) => { - if (!intersections.length) { - setAxisIndex(null) - return - } - let closestIntersection = intersections[0] - intersections.forEach((intersection) => { - if (intersection.distance < closestIntersection.distance) - closestIntersection = intersection - }) - const smallestIndex = Number(closestIntersection.eventObject.name) - setAxisIndex(smallestIndex) - } - const onClick = () => { - if (guiMode.mode !== 'sketch') { - return null - } - if (guiMode.sketchMode !== 'selectFace') { - return null - } - - let _ast: Program = ast - ? ast - : { - type: 'Program', - start: 0, - end: 0, - body: [], - nonCodeMeta: {}, - } - const axis = axisIndex === 0 ? 'xy' : axisIndex === 1 ? 'xz' : 'yz' - const quaternion = new Quaternion() - if (axisIndex === 1) { - quaternion.setFromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2) - } else if (axisIndex === 2) { - quaternion.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / 2) - } - const { modifiedAst, id, pathToNode } = addSketchTo(_ast, axis) - - setGuiMode({ - mode: 'sketch', - sketchMode: 'sketchEdit', - rotation: quaternion.toArray() as [number, number, number, number], - position: [0, 0, 0], - pathToNode, - }) - - updateAst(modifiedAst) - } - if (guiMode.mode !== 'sketch') { - return null - } - if (guiMode.sketchMode !== 'selectFace') { - return null - } - return ( - <> - {Array.from({ length: 3 }).map((_, index) => ( - - - - - {index === 0 ? 'xy' : index === 1 ? 'xz' : 'yz'} - - - {index === 0 ? 'xy' : index === 1 ? 'xz' : 'yz'} - - - ))} - > - ) -} diff --git a/src/components/MemoryPanel.test.tsx b/src/components/MemoryPanel.test.tsx index ead40656f..5c60db4aa 100644 --- a/src/components/MemoryPanel.test.tsx +++ b/src/components/MemoryPanel.test.tsx @@ -1,13 +1,13 @@ import { processMemory } from './MemoryPanel' import { lexer } from '../lang/tokeniser' import { abstractSyntaxTree } from '../lang/abstractSyntaxTree' -import { executor } from '../lang/executor' +import { executor } from '../lib/testHelpers' import { initPromise } from '../lang/rust' beforeAll(() => initPromise) describe('processMemory', () => { - it('should grab the values and remove and geo data', () => { + it('should grab the values and remove and geo data', async () => { const code = ` const myVar = 5 const myFn = (a) => { @@ -28,7 +28,7 @@ describe('processMemory', () => { show(theExtrude, theSketch)` const tokens = lexer(code) const ast = abstractSyntaxTree(tokens) - const programMemory = executor(ast, { + const programMemory = await executor(ast, { root: { log: { type: 'userVal', @@ -38,7 +38,7 @@ describe('processMemory', () => { __meta: [], }, }, - _sketch: [], + pendingMemory: {}, }) const output = processMemory(programMemory) expect(output.myVar).toEqual(5) diff --git a/src/components/RenderViewerArtifacts.tsx b/src/components/RenderViewerArtifacts.tsx deleted file mode 100644 index 59be3392b..000000000 --- a/src/components/RenderViewerArtifacts.tsx +++ /dev/null @@ -1,646 +0,0 @@ -import { useRef, useState, useEffect, useMemo } from 'react' -import { - CallExpression, - ArrayExpression, - PipeExpression, -} from '../lang/abstractSyntaxTree' -import { - getNodePathFromSourceRange, - getNodeFromPath, - getNodeFromPathCurry, -} from '../lang/queryAst' -import { changeSketchArguments } from '../lang/std/sketch' -import { - ExtrudeGroup, - ExtrudeSurface, - SketchGroup, - Path, - Rotation, - Position, - PathToNode, - SourceRange, -} from '../lang/executor' -import { BufferGeometry } from 'three' -import { useStore } from '../useStore' -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 LineEnd({ - geo, - sourceRange, - editorCursor, - rotation, - position, - from, -}: { - geo: BufferGeometry - sourceRange: [number, number] - editorCursor: boolean - rotation: Rotation - position: Position - from: [number, number] -}) { - const ref = useRef() as any - const detectionPlaneRef = useRef() as any - const lastPointerRef = useRef(new Vector3()) - const point2DRef = useRef(new Vector3()) - const [hovered, setHover] = useState(false) - const [isMouseDown, setIsMouseDown] = useState(false) - const baseColor = useConstraintColors(sourceRange) - - const setCursor = useSetCursor(sourceRange, 'line-end') - - const { setHighlightRange, guiMode, ast, updateAst, programMemory } = - useStore((s) => ({ - setHighlightRange: s.setHighlightRange, - guiMode: s.guiMode, - ast: s.ast, - updateAst: s.updateAst, - programMemory: s.programMemory, - })) - const { originalXY } = useMemo(() => { - if (ast) { - const thePath = getNodePathFromSourceRange(ast, sourceRange) - const { node: callExpression } = getNodeFromPath( - ast, - thePath - ) - const [xArg, yArg] = - guiMode.mode === 'sketch' - ? callExpression?.arguments || [] - : (callExpression?.arguments?.[0] as ArrayExpression)?.elements || [] - const x = xArg?.type === 'Literal' ? xArg.value : -1 - const y = yArg?.type === 'Literal' ? yArg.value : -1 - return { - originalXY: [x, y], - } - } - return { - originalXY: [-1, -1], - } - }, [ast]) - - useEffect(() => { - const handleMouseUp = () => { - if (isMouseDown && ast) { - const current2d = point2DRef.current.clone() - const inverseQuaternion = new Quaternion() - if ( - guiMode.mode === 'canEditSketch' || - (guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit') - ) { - inverseQuaternion.set(...guiMode.rotation) - inverseQuaternion.invert() - } - current2d.sub( - new Vector3(...position).applyQuaternion(inverseQuaternion) - ) - let [x, y] = [roundOff(current2d.x, 2), roundOff(current2d.y, 2)] - let theNewPoints: [number, number] = [x, y] - const { modifiedAst } = changeSketchArguments( - ast, - programMemory, - sourceRange, - theNewPoints, - guiMode, - from - ) - if (!(current2d.x === 0 && current2d.y === 0 && current2d.z === 0)) - updateAst(modifiedAst) - ref.current.position.set(...position) - } - setIsMouseDown(false) - } - window.addEventListener('mouseup', handleMouseUp) - return () => { - window.removeEventListener('mouseup', handleMouseUp) - } - }, [isMouseDown]) - - const inEditMode = - guiMode.mode === 'canEditSketch' || - (guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit') - - let clickDetectPlaneQuaternion = new Quaternion() - if (inEditMode) { - clickDetectPlaneQuaternion = new Quaternion(...rotation) - } - - return ( - <> - { - inEditMode && setHover(true) - setHighlightRange(sourceRange) - }} - onPointerOut={(event) => { - setHover(false) - setHighlightRange([0, 0]) - }} - onPointerDown={() => { - inEditMode && setIsMouseDown(true) - setCursor() - }} - > - - - - {isMouseDown && ( - { - const point = a.point - - const transformedPoint = point.clone() - const inverseQuaternion = new Quaternion() - if ( - guiMode.mode === 'canEditSketch' || - (guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit') - ) { - inverseQuaternion.set(...guiMode.rotation) - inverseQuaternion.invert() - } - transformedPoint.applyQuaternion(inverseQuaternion) - point2DRef.current.copy(transformedPoint) - - if ( - lastPointerRef.current.x === 0 && - lastPointerRef.current.y === 0 && - lastPointerRef.current.z === 0 - ) { - lastPointerRef.current.set(point.x, point.y, point.z) - return - } - if (guiMode.mode) - if (ref.current) { - const diff = new Vector3().subVectors( - point.clone().applyQuaternion(inverseQuaternion), - lastPointerRef.current - .clone() - .applyQuaternion(inverseQuaternion) - ) - if (originalXY[0] === -1) { - // x arg is not a literal and should be locked - diff.x = 0 - } - if (originalXY[1] === -1) { - // y arg is not a literal and should be locked - diff.y = 0 - } - ref.current.position.add( - diff.applyQuaternion(inverseQuaternion.invert()) - ) - lastPointerRef.current.copy(point.clone()) - } - }} - > - - - - )} - > - ) -} - -export function RenderViewerArtifacts({ - artifacts, -}: { - artifacts: (ExtrudeGroup | SketchGroup)[] -}) { - useSetAppModeFromCursorLocation(artifacts) - return ( - <> - {artifacts?.map((artifact, i) => ( - - ))} - > - ) -} - -function RenderViewerArtifact({ - artifact, -}: { - artifact: ExtrudeGroup | SketchGroup -}) { - // const { selectionRange, guiMode, ast, setGuiMode } = useStore( - // ({ selectionRange, guiMode, ast, setGuiMode }) => ({ - // selectionRange, - // guiMode, - // ast, - // setGuiMode, - // }) - // ) - // const [editorCursor, setEditorCursor] = useState(false) - // useEffect(() => { - // const shouldHighlight = isOverlapping( - // artifact.__meta.slice(-1)[0].sourceRange, - // selectionRange - // ) - // setEditorCursor(shouldHighlight) - // }, [selectionRange, artifact.__meta]) - - if (artifact.type === 'sketchGroup') { - return ( - <> - {artifact.start && ( - - )} - {artifact.value.map((geoInfo, key) => ( - - ))} - > - ) - } - if (artifact.type === 'extrudeGroup') { - return ( - <> - {artifact.value.map((geoInfo, key) => ( - - ))} - > - ) - } - return null -} - -function WallRender({ - geoInfo, - forceHighlight = false, - rotation, - position, -}: { - geoInfo: ExtrudeSurface - forceHighlight?: boolean - rotation: Rotation - position: Position -}) { - const { setHighlightRange, selectionRanges } = useStore( - ({ setHighlightRange, selectionRanges }) => ({ - setHighlightRange, - selectionRanges, - }) - ) - const onClick = useSetCursor(geoInfo.__geoMeta.sourceRange) - // This reference will give us direct access to the mesh - const ref = useRef() as any - const [hovered, setHover] = useState(false) - - const [editorCursor, setEditorCursor] = useState(false) - useEffect(() => { - const shouldHighlight = selectionRanges.codeBasedSelections.some( - ({ range }) => isOverlap(geoInfo.__geoMeta.sourceRange, range) - ) - setEditorCursor(shouldHighlight) - }, [selectionRanges, geoInfo]) - - return ( - <> - { - setHover(true) - setHighlightRange(geoInfo.__geoMeta.sourceRange) - }} - onPointerOut={(event) => { - setHover(false) - setHighlightRange([0, 0]) - }} - onClick={onClick} - > - - - - > - ) -} - -function PathRender({ - geoInfo, - forceHighlight = false, - rotation, - position, -}: { - geoInfo: Path - forceHighlight?: boolean - rotation: Rotation - position: Position -}) { - const { selectionRanges, updateAstAsync, ast, guiMode } = useStore((s) => ({ - selectionRanges: s.selectionRanges, - updateAstAsync: s.updateAstAsync, - ast: s.ast, - guiMode: s.guiMode, - })) - const [editorCursor, setEditorCursor] = useState(false) - const [editorLineCursor, setEditorLineCursor] = useState(false) - useEffect(() => { - const shouldHighlight = selectionRanges.codeBasedSelections.some( - ({ range }) => isOverlap(geoInfo.__geoMeta.sourceRange, range) - ) - const shouldHighlightLine = selectionRanges.codeBasedSelections.some( - ({ range, type }) => - isOverlap(geoInfo.__geoMeta.sourceRange, range) && type === 'default' - ) - setEditorCursor(shouldHighlight) - setEditorLineCursor(shouldHighlightLine) - }, [selectionRanges, geoInfo]) - return ( - <> - {geoInfo.__geoMeta.geos.map((meta, i) => { - if (meta.type === 'line') - return ( - - ) - if (meta.type === 'lineEnd') - return ( - - ) - if (meta.type === 'sketchBase') - return ( - { - 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') - const pipe = getNode('PipeExpression') - if ( - maybeStartSketchAt?.node.callee.name === 'startSketchAt' && - pipe.node && - pipe.node.body.length > 2 - ) { - const modifiedAst = JSON.parse(JSON.stringify(ast)) - const _pipe = getNodeFromPath( - modifiedAst, - path, - 'PipeExpression' - ) - _pipe.node.body.push( - createCallExpression('close', [createPipeSubstitution()]) - ) - updateAstAsync(modifiedAst) - } - }} - /> - ) - })} - > - ) -} - -function LineRender({ - geo, - sourceRange, - forceHighlight = false, - rotation, - position, - onClick: _onClick = () => {}, -}: { - geo: BufferGeometry - sourceRange: [number, number] - forceHighlight?: boolean - rotation: Rotation - position: Position - onClick?: () => void -}) { - const { setHighlightRange } = useStore((s) => ({ - setHighlightRange: s.setHighlightRange, - })) - const onClick = useSetCursor(sourceRange) - // This reference will give us direct access to the mesh - const ref = useRef() as any - const [hovered, setHover] = useState(false) - - const baseColor = useConstraintColors(sourceRange) - - return ( - <> - { - setHover(true) - setHighlightRange(sourceRange) - }} - onPointerOut={(e) => { - setHover(false) - setHighlightRange([0, 0]) - }} - onClick={() => { - _onClick() - onClick() - }} - > - - - - > - ) -} - -type Artifact = ExtrudeGroup | SketchGroup - -function useSetAppModeFromCursorLocation(artifacts: Artifact[]) { - const { selectionRanges, guiMode, setGuiMode, ast } = useStore( - ({ selectionRanges, guiMode, setGuiMode, ast }) => ({ - selectionRanges, - guiMode, - setGuiMode, - ast, - }) - ) - useEffect(() => { - const artifactsWithinCursorRange: ( - | { - parentType: Artifact['type'] - isParent: true - pathToNode: PathToNode - sourceRange: SourceRange - rotation: Rotation - position: Position - } - | { - parentType: Artifact['type'] - isParent: false - pathToNode: PathToNode - sourceRange: SourceRange - rotation: Rotation - position: Position - } - )[] = [] - artifacts?.forEach((artifact) => { - artifact.value.forEach((geo) => { - if ( - isOverlap( - geo.__geoMeta.sourceRange, - selectionRanges.codeBasedSelections[0].range - ) - ) { - artifactsWithinCursorRange.push({ - parentType: artifact.type, - isParent: false, - pathToNode: geo.__geoMeta.pathToNode, - sourceRange: geo.__geoMeta.sourceRange, - rotation: artifact.rotation, - position: artifact.position, - }) - } - }) - artifact.__meta.forEach((meta) => { - if ( - isOverlap( - meta.sourceRange, - selectionRanges.codeBasedSelections[0].range - ) - ) { - artifactsWithinCursorRange.push({ - parentType: artifact.type, - isParent: true, - pathToNode: meta.pathToNode, - sourceRange: meta.sourceRange, - rotation: artifact.rotation, - position: artifact.position, - }) - } - }) - }) - const parentArtifacts = artifactsWithinCursorRange.filter((a) => a.isParent) - const hasSketchArtifact = artifactsWithinCursorRange.filter( - ({ parentType }) => parentType === 'sketchGroup' - ) - const hasExtrudeArtifact = artifactsWithinCursorRange.filter( - ({ parentType }) => parentType === 'extrudeGroup' - ) - const artifact = parentArtifacts[0] - const shouldHighlight = !!artifact || hasSketchArtifact.length - if ( - (guiMode.mode === 'default' || guiMode.mode === 'canEditSketch') && - ast && - hasSketchArtifact.length - ) { - const pathToNode = getNodePathFromSourceRange( - ast, - hasSketchArtifact[0].sourceRange - ) - const { rotation, position } = hasSketchArtifact[0] - setGuiMode({ mode: 'canEditSketch', pathToNode, rotation, position }) - } else if ( - hasExtrudeArtifact.length && - (guiMode.mode === 'default' || guiMode.mode === 'canEditExtrude') && - ast - ) { - const pathToNode = getNodePathFromSourceRange( - ast, - hasExtrudeArtifact[0].sourceRange - ) - const { rotation, position } = hasExtrudeArtifact[0] - setGuiMode({ mode: 'canEditExtrude', pathToNode, rotation, position }) - } else if ( - !shouldHighlight && - (guiMode.mode === 'canEditExtrude' || guiMode.mode === 'canEditSketch') - ) { - setGuiMode({ mode: 'default' }) - } - }, [artifacts, selectionRanges]) -} - -function useConstraintColors(sourceRange: [number, number]): string { - const { guiMode, ast } = useStore((s) => ({ - guiMode: s.guiMode, - ast: s.ast, - })) - const [baseColor, setBaseColor] = useState('orange') - useEffect(() => { - if (!ast || guiMode.mode !== 'sketch') { - setBaseColor('orange') - return - } - try { - const level = getConstraintLevelFromSourceRange(sourceRange, ast) - if (level === 'free') { - setBaseColor('orange') - } else if (level === 'partial') { - setBaseColor('IndianRed') - } else if (level === 'full') { - setBaseColor('lightgreen') - } - } catch (e) { - setBaseColor('orange') - } - }, [guiMode, ast, sourceRange]) - - return baseColor -} diff --git a/src/components/SketchPlane.tsx b/src/components/SketchPlane.tsx deleted file mode 100644 index 274453c99..000000000 --- a/src/components/SketchPlane.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { useState } from 'react' -import { Selections, useStore } from '../useStore' -import { DoubleSide, Vector3, Quaternion } from 'three' -import { Program } from '../lang/abstractSyntaxTree' -import { addNewSketchLn } from '../lang/std/sketch' -import { roundOff } from '../lib/utils' - -export const SketchPlane = () => { - const { - ast, - guiMode, - programMemory, - updateAstAsync, - setSelectionRanges, - selectionRanges, - isShiftDown, - setCursor, - } = useStore((s) => ({ - guiMode: s.guiMode, - ast: s.ast, - updateAstAsync: s.updateAstAsync, - programMemory: s.programMemory, - setSelectionRanges: s.setSelectionRanges, - selectionRanges: s.selectionRanges, - isShiftDown: s.isShiftDown, - setCursor: s.setCursor, - })) - const [xHover, setXHover] = useState(false) - const [yHover, setYHover] = useState(false) - if (guiMode.mode !== 'sketch') { - return null - } - if (!(guiMode.sketchMode === 'sketchEdit') && !('isTooltip' in guiMode)) { - return null - } - - const sketchGridName = 'sketchGrid' - - let clickDetectQuaternion = new Quaternion(...guiMode.rotation) - - let temp = new Quaternion().setFromAxisAngle( - new Vector3(1, 0, 0), - Math.PI / 2 - ) - let position = guiMode.position - const gridQuaternion = new Quaternion().multiplyQuaternions( - new Quaternion(...guiMode.rotation), - temp - ) - - const onAxisClick = (name: 'y-axis' | 'x-axis') => () => { - const _selectionRanges: Selections = isShiftDown - ? selectionRanges - : { - codeBasedSelections: [ - { - range: [0, 0], - type: 'default', - }, - ], - otherSelections: [], - } - if (!isShiftDown) { - setCursor({ - ..._selectionRanges, - otherSelections: [name], - }) - } - setTimeout(() => { - setSelectionRanges({ - ..._selectionRanges, - otherSelections: [name], - }) - }, 100) - } - - return ( - <> - { - if (!('isTooltip' in guiMode)) { - return - } - const sketchGridIntersection = e.intersections.find( - ({ object }) => object.name === sketchGridName - ) - const inverseQuaternion = clickDetectQuaternion.clone().invert() - let transformedPoint = sketchGridIntersection?.point.clone() - if (transformedPoint) { - transformedPoint.applyQuaternion(inverseQuaternion) - transformedPoint?.sub( - new Vector3(...position).applyQuaternion(inverseQuaternion) - ) - } - - const point = roundy(transformedPoint) - let _ast: Program = ast - ? ast - : { - type: 'Program', - start: 0, - end: 0, - body: [], - nonCodeMeta: {}, - } - const { modifiedAst } = addNewSketchLn({ - node: _ast, - programMemory, - to: [point.x, point.y], - fnName: guiMode.sketchMode, - pathToNode: guiMode.pathToNode, - }) - updateAstAsync(modifiedAst) - }} - > - - - - - !isShiftDown && - setSelectionRanges({ - ...selectionRanges, - otherSelections: [], - }) - } - /> - setXHover(true)} - onPointerOut={() => setXHover(false)} - onClick={onAxisClick('x-axis')} - > - - - - setYHover(true)} - onPointerOut={() => setYHover(false)} - onClick={onAxisClick('y-axis')} - > - - - - > - ) -} - -function roundy({ x, y, z }: any) { - return { - x: roundOff(x, 2), - y: roundOff(y, 2), - z: roundOff(z, 2), - } -} diff --git a/src/components/Stream.tsx b/src/components/Stream.tsx index c685e5179..47f50784b 100644 --- a/src/components/Stream.tsx +++ b/src/components/Stream.tsx @@ -1,9 +1,16 @@ -import { useEffect, useRef } from 'react' +import { MouseEventHandler, useEffect, useRef } from 'react' import { PanelHeader } from '../components/PanelHeader' import { v4 as uuidv4 } from 'uuid' +import { useStore } from '../useStore' +import { throttle } from '../lib/utils' export const Stream = () => { const videoRef = useRef(null) + const cmdId = useRef('') + const { mediaStream, engineCommandManager } = useStore((s) => ({ + mediaStream: s.mediaStream, + engineCommandManager: s.engineCommandManager, + })) useEffect(() => { if ( @@ -11,228 +18,111 @@ export const Stream = () => { typeof RTCPeerConnection === 'undefined' ) return - const url = 'wss://api.dev.kittycad.io/ws/modeling/commands' - const file_id = uuidv4() - let currentCmdId: null | string = null - const [pc, socket] = [new RTCPeerConnection(), new WebSocket(url)] - // Connection opened - socket.addEventListener('open', (event) => { - console.log('Connected to websocket, waiting for ICE servers') - }) + if (!videoRef.current) return + if (!mediaStream) return + videoRef.current.srcObject = mediaStream + }, [mediaStream, engineCommandManager]) - socket.addEventListener('close', (event) => { - console.log('websocket connection closed') - }) + const file_id = uuidv4() - socket.addEventListener('error', (event) => { - console.log('websocket connection error') - }) - - // Listen for messages - socket.addEventListener('message', (event) => { - //console.log('Message from server ', event.data); - if (event.data instanceof Blob) { - const reader = new FileReader() - - reader.onload = () => { - //console.log("Result: " + reader.result); - } - - reader.readAsText(event.data) - } else { - const message = JSON.parse(event.data) - if (message.type === 'SDPAnswer') { - pc.setRemoteDescription(new RTCSessionDescription(message.answer)) - } else if (message.type === 'TrickleIce') { - console.log('got remote trickle ice') - pc.addIceCandidate(message.candidate) - } else if (message.type === 'IceServerInfo') { - console.log('received IceServerInfo') - pc.setConfiguration({ - iceServers: message.ice_servers, - iceTransportPolicy: 'relay', - }) - pc.ontrack = function (event) { - if (videoRef.current) { - videoRef.current.srcObject = event.streams[0] - videoRef.current.muted = true - videoRef.current.autoplay = true - videoRef.current.controls = false - } - } - pc.oniceconnectionstatechange = (e) => - console.log(pc.iceConnectionState) - pc.onicecandidate = (event) => { - if (event.candidate === null) { - console.log('sent SDPOffer') - socket.send( - JSON.stringify({ - type: 'SDPOffer', - offer: pc.localDescription, - }) - ) - } else { - console.log('sending trickle ice candidate') - const { candidate } = event - socket.send( - JSON.stringify({ - type: 'TrickleIce', - candidate: candidate.toJSON(), - }) - ) - } - } - - // Offer to receive 1 video track - pc.addTransceiver('video', { - direction: 'sendrecv', - }) - pc.createOffer() - .then((d) => pc.setLocalDescription(d)) - .then(() => { - console.log('sent SDPOffer begin') - socket.send( - JSON.stringify({ - type: 'SDPOffer', - offer: pc.localDescription, - }) - ) - }) - .catch(console.log) - } - } - }) - - const debounceSocketSend = throttle((message) => { - socket.send(JSON.stringify(message)) - }, 100) - const handleClick = ({ clientX, clientY }: MouseEvent) => { - if (!videoRef.current) return - const { left, top } = videoRef.current.getBoundingClientRect() - const x = clientX - left - const y = clientY - top - console.log('click', x, y) - - if (currentCmdId == null) { - currentCmdId = uuidv4() - - debounceSocketSend({ - type: 'ModelingCmdReq', - cmd: { - CameraDragStart: { - interaction: 'rotate', - window: { - x: x, - y: y, - }, - }, - }, - cmd_id: uuidv4(), - file_id: file_id, - }) - } - } - - const handleMouseUp = ({ clientX, clientY }: MouseEvent) => { - if (!videoRef.current) return - const { left, top } = videoRef.current.getBoundingClientRect() - const x = clientX - left - const y = clientY - top - console.log('click', x, y) - - if (currentCmdId == null) { - return - } - - debounceSocketSend({ - type: 'ModelingCmdReq', - cmd: { - CameraDragEnd: { - interaction: 'rotate', - window: { - x: x, - y: y, - }, + const debounceSocketSend = throttle((message) => { + engineCommandManager?.sendSceneCommand(message) + }, 100) + const handleMouseMove: MouseEventHandler = ({ + clientX, + clientY, + }) => { + if (!videoRef.current) return + if (!cmdId.current) return + const { left, top } = videoRef.current.getBoundingClientRect() + const x = clientX - left + const y = clientY - top + debounceSocketSend({ + type: 'ModelingCmdReq', + cmd: { + CameraDragMove: { + interaction: 'rotate', + window: { + x: x, + y: y, }, }, - cmd_id: uuidv4(), - file_id: file_id, - }) - currentCmdId = null - } - const handleMouseMove = ({ clientX, clientY }: MouseEvent) => { - if (!videoRef.current) return - const { left, top } = videoRef.current.getBoundingClientRect() - const x = clientX - left - const y = clientY - top - if (currentCmdId == null) { - return - } else { - console.log('mouse move', x, y) - debounceSocketSend({ - type: 'ModelingCmdReq', - cmd: { - CameraDragMove: { - interaction: 'rotate', - window: { - x: x, - y: y, - }, - }, + }, + cmd_id: uuidv4(), + file_id: file_id, + }) + } + + const handleMouseDown: MouseEventHandler = ({ + clientX, + clientY, + }) => { + if (!videoRef.current) return + const { left, top } = videoRef.current.getBoundingClientRect() + const x = clientX - left + const y = clientY - top + console.log('click', x, y) + + const newId = uuidv4() + cmdId.current = newId + + engineCommandManager?.sendSceneCommand({ + type: 'ModelingCmdReq', + cmd: { + CameraDragStart: { + interaction: 'rotate', + window: { + x: x, + y: y, }, - cmd_id: uuidv4(), - file_id: file_id, - }) - } - } - if (videoRef.current) { - videoRef.current.addEventListener('mousemove', handleMouseMove) - videoRef.current.addEventListener('mousedown', handleClick) - videoRef.current.addEventListener('mouseup', handleMouseUp) + }, + }, + cmd_id: newId, + file_id, + }) + } + const handleMouseUp: MouseEventHandler = ({ + clientX, + clientY, + }) => { + if (!videoRef.current) return + const { left, top } = videoRef.current.getBoundingClientRect() + const x = clientX - left + const y = clientY - top + + if (cmdId.current == null) { + return } - return () => { - socket.close() - pc.close() - if (!videoRef.current) return - videoRef.current.removeEventListener('mousemove', handleMouseMove) - videoRef.current.removeEventListener('mousedown', handleClick) - videoRef.current.removeEventListener('mouseup', handleMouseUp) - } - }, []) + engineCommandManager?.sendSceneCommand({ + type: 'ModelingCmdReq', + cmd: { + CameraDragEnd: { + interaction: 'rotate', + window: { + x: x, + y: y, + }, + }, + }, + cmd_id: uuidv4(), + file_id: file_id, + }) + cmdId.current = '' + } return ( - + ) } - -function throttle( - func: (...args: any[]) => any, - wait: number -): (...args: any[]) => any { - let timeout: ReturnType | null - let latestArgs: any[] - let latestTimestamp: number - - function later() { - timeout = null - func(...latestArgs) - } - - function throttled(...args: any[]) { - const currentTimestamp = Date.now() - latestArgs = args - - if (!latestTimestamp || currentTimestamp - latestTimestamp >= wait) { - latestTimestamp = currentTimestamp - func(...latestArgs) - } else if (!timeout) { - timeout = setTimeout(later, wait - (currentTimestamp - latestTimestamp)) - } - } - - return throttled -} diff --git a/src/components/Toolbar/Intersect.tsx b/src/components/Toolbar/Intersect.tsx index 863e4f007..1eed0353c 100644 --- a/src/components/Toolbar/Intersect.tsx +++ b/src/components/Toolbar/Intersect.tsx @@ -58,7 +58,6 @@ export const Intersect = () => { selectionRanges.codeBasedSelections?.[1]?.type !== 'line-end' && previousSegment && previousSegment.isParallelAndConstrained - console.log(shouldUsePreviousSegment) const _forcedSelectionRanges: typeof selectionRanges = { ...selectionRanges, diff --git a/src/editor/highlightextension.ts b/src/editor/highlightextension.ts index d8b75f97f..149acba95 100644 --- a/src/editor/highlightextension.ts +++ b/src/editor/highlightextension.ts @@ -15,7 +15,7 @@ export const lineHighlightField = StateField.define({ for (let e of tr.effects) { if (e.is(addLineHighlight)) { lines = Decoration.none - const [from, to] = e.value + const [from, to] = e.value || [0, 0] if (!(from === to && from === 0)) { lines = lines.update({ add: [matchDeco.range(from, to)] }) deco.push(matchDeco.range(from, to)) diff --git a/src/hooks/useSetCursor.ts b/src/hooks/useSetCursor.ts deleted file mode 100644 index 8478bffaa..000000000 --- a/src/hooks/useSetCursor.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useStore, Selection, Selections } from '../useStore' - -export function useSetCursor( - sourceRange: Selection['range'], - type: Selection['type'] = 'default' -) { - const { setCursor, selectionRanges, isShiftDown } = useStore((s) => ({ - setCursor: s.setCursor, - selectionRanges: s.selectionRanges, - isShiftDown: s.isShiftDown, - })) - return () => { - const selections: Selections = { - ...selectionRanges, - codeBasedSelections: isShiftDown - ? [...selectionRanges.codeBasedSelections, { range: sourceRange, type }] - : [{ range: sourceRange, type }], - } - setCursor(selections) - } -} diff --git a/src/lang/artifact.test.ts b/src/lang/artifact.test.ts index f54ad6969..fe164dad8 100644 --- a/src/lang/artifact.test.ts +++ b/src/lang/artifact.test.ts @@ -1,19 +1,20 @@ import { abstractSyntaxTree } from './abstractSyntaxTree' import { lexer } from './tokeniser' -import { executor, SketchGroup, ExtrudeGroup } from './executor' +import { SketchGroup, ExtrudeGroup } from './executor' import { initPromise } from './rust' +import { executor } from '../lib/testHelpers' beforeAll(() => initPromise) describe('testing artifacts', () => { - test('sketch artifacts', () => { + test('sketch artifacts', async () => { const code = ` const mySketch001 = startSketchAt([0, 0]) |> lineTo([-1.59, -1.54], %) |> lineTo([0.46, -5.82], %) |> rx(45, %) show(mySketch001)` - const programMemory = executor(abstractSyntaxTree(lexer(code))) + const programMemory = await executor(abstractSyntaxTree(lexer(code))) const geos = programMemory?.return?.map( (a) => programMemory?.root?.[a.name] ) @@ -26,6 +27,7 @@ show(mySketch001)` to: [0, 0], from: [0, 0], __geoMeta: { + id: '66366561-6465-4734-a463-366330356563', sourceRange: [21, 42], pathToNode: [], geos: ['sketchBase'], @@ -38,6 +40,7 @@ show(mySketch001)` from: [0, 0], __geoMeta: { sourceRange: [48, 73], + id: '30366338-6462-4330-a364-303935626163', pathToNode: [], geos: ['line', 'lineEnd'], }, @@ -48,6 +51,7 @@ show(mySketch001)` from: [-1.59, -1.54], __geoMeta: { sourceRange: [79, 103], + id: '32653334-6331-4231-b162-663334363535', pathToNode: [], geos: ['line', 'lineEnd'], }, @@ -62,7 +66,7 @@ show(mySketch001)` }, ]) }) - test('extrude artifacts', () => { + test('extrude artifacts', async () => { const code = ` const mySketch001 = startSketchAt([0, 0]) |> lineTo([-1.59, -1.54], %) @@ -70,7 +74,7 @@ const mySketch001 = startSketchAt([0, 0]) |> rx(45, %) |> extrude(2, %) show(mySketch001)` - const programMemory = executor(abstractSyntaxTree(lexer(code))) + const programMemory = await executor(abstractSyntaxTree(lexer(code))) const geos = programMemory?.return?.map( (a) => programMemory?.root?.[a.name] ) @@ -87,7 +91,9 @@ show(mySketch001)` 0.8563498075401887, ], __geoMeta: { - geo: 'PlaneGeometry', + id: expect.anything(), + geo: undefined, + refId: '30366338-6462-4330-a364-303935626163', sourceRange: [48, 73], pathToNode: [], }, @@ -102,7 +108,9 @@ show(mySketch001)` 0.4923604609001174, ], __geoMeta: { - geo: 'PlaneGeometry', + id: expect.anything(), + geo: undefined, + refId: '32653334-6331-4231-b162-663334363535', sourceRange: [79, 103], pathToNode: [], }, @@ -118,7 +126,7 @@ show(mySketch001)` }, ]) }) - test('sketch extrude and sketch on one of the faces', () => { + test('sketch extrude and sketch on one of the faces', async () => { const code = ` const sk1 = startSketchAt([0, 0]) |> lineTo([-2.5, 0], %) @@ -138,7 +146,7 @@ const sk2 = startSketchAt([0, 0]) show(theExtrude, sk2)` - const programMemory = executor(abstractSyntaxTree(lexer(code))) + const programMemory = await executor(abstractSyntaxTree(lexer(code))) const geos = programMemory?.return?.map( (a) => programMemory?.root?.[a.name] ) @@ -155,7 +163,9 @@ show(theExtrude, sk2)` 0.9230002039112793, ], __geoMeta: { - geo: 'PlaneGeometry', + id: expect.anything(), // todo figure out why isn't deterministic + geo: undefined, + refId: '36613364-6238-4330-b766-613131633135', sourceRange: [40, 60], pathToNode: [], }, @@ -170,7 +180,9 @@ show(theExtrude, sk2)` -0.5362616571538269, ], __geoMeta: { - geo: 'PlaneGeometry', + id: expect.anything(), + geo: undefined, + refId: '32313832-3531-4933-b839-316634316237', sourceRange: [66, 102], pathToNode: [], }, @@ -186,7 +198,9 @@ show(theExtrude, sk2)` 0.5997895323824204, ], __geoMeta: { - geo: 'PlaneGeometry', + id: expect.anything(), + geo: undefined, + refId: '31356564-3364-4562-a438-653732633238', sourceRange: [108, 127], pathToNode: [], }, @@ -216,7 +230,9 @@ show(theExtrude, sk2)` -0.20351996751370383, ], __geoMeta: { - geo: 'PlaneGeometry', + id: expect.anything(), + geo: undefined, + refId: '31623462-6433-4233-b361-303837663464', sourceRange: [317, 337], pathToNode: [], }, @@ -231,7 +247,9 @@ show(theExtrude, sk2)` -0.5818075544860157, ], __geoMeta: { - geo: 'PlaneGeometry', + id: expect.anything(), + geo: undefined, + refId: '66363230-3430-4961-b831-646363376538', sourceRange: [343, 378], pathToNode: [], }, @@ -247,7 +265,9 @@ show(theExtrude, sk2)` -0.7544557394170275, ], __geoMeta: { - geo: 'PlaneGeometry', + id: expect.anything(), + geo: undefined, + refId: '62366564-3261-4061-b533-623433336531', sourceRange: [384, 403], pathToNode: [], }, @@ -270,14 +290,17 @@ show(theExtrude, sk2)` function removeGeo(arts: (SketchGroup | ExtrudeGroup)[]): any { return arts.map((art) => { - if (art.type === 'extrudeGroup') { + if (!art) { + return {} + } + if (art?.type === 'extrudeGroup') { return { ...art, value: art.value.map((v) => ({ ...v, __geoMeta: { ...v.__geoMeta, - geo: v.__geoMeta.geo.type, + geo: (v?.__geoMeta as any)?.geo?.type, }, })), } diff --git a/src/lang/engine.tsx b/src/lang/engine.tsx deleted file mode 100644 index 19305a27f..000000000 --- a/src/lang/engine.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import { - BoxGeometry, - SphereGeometry, - BufferGeometry, - PlaneGeometry, - Quaternion, - Euler, - CylinderGeometry, -} from 'three' -import { Rotation, Position } from './executor' - -export function baseGeo({ from }: { from: [number, number, number] }) { - const baseSphere = new SphereGeometry(0.25) - baseSphere.translate(from[0], from[1], from[2]) - return baseSphere -} - -function trigCalcs({ - from, - to, -}: { - from: [number, number, number] - to: [number, number, number] -}) { - const sq = (a: number): number => a * a - const centre = [ - (from[0] + to[0]) / 2, - (from[1] + to[1]) / 2, - (from[2] + to[2]) / 2, - ] - const Hypotenuse3d = Math.sqrt( - sq(from[0] - to[0]) + sq(from[1] - to[1]) + sq(from[2] - to[2]) - ) - const ry = Math.atan2(from[2] - to[2], from[0] - to[0]) - const Hypotenuse2d = Math.sqrt(sq(from[0] - to[0]) + sq(from[2] - to[2])) - const rz = - Math.abs(Math.atan((to[1] - from[1]) / Hypotenuse2d)) * - Math.sign(to[1] - from[1]) * - (Math.sign(to[0] - from[0]) || 1) - - const sign = ry === 0 ? 1 : -1 - return { - centre, - Hypotenuse: Hypotenuse3d, - ry, - rz, - sign, - } -} - -export function lineGeo({ - from, - to, -}: { - from: [number, number, number] - to: [number, number, number] -}): { - line: BufferGeometry - tip: BufferGeometry - centre: BufferGeometry -} { - const { - centre, - Hypotenuse: Hypotenuse3d, - ry, - rz, - sign, - } = trigCalcs({ from, to }) - - const lineEndLength = 0.25 - // create BoxGeometry with size [Hypotenuse3d, 0.1, 0.1] centered at center, with rotation of [0, ry, rz] - const lineBody = new BoxGeometry(Hypotenuse3d - lineEndLength, 0.1, 0.1) - const __sign = to[0] === from[0] ? -1 : 1 - lineBody.translate((__sign * lineEndLength) / 2, 0, 0) - lineBody.rotateY(ry) - lineBody.rotateZ(rz) - lineBody.translate(centre[0], centre[1], centre[2]) - - // create line end points with CylinderGeometry at `to` - const lineEnd1 = new CylinderGeometry(0.05, 0.22, lineEndLength + 0.05, 4) - lineEnd1.translate(0, -0.1, 0) - lineEnd1.rotateY(Math.PI / 4) - lineEnd1.rotateZ(rz) - const _sign = to[0] === from[0] ? -sign : sign - lineEnd1.rotateZ((_sign * Math.PI) / 2) - lineEnd1.translate(to[0], to[1], to[2]) - - const centreSphere = new SphereGeometry(0.15) - centreSphere.translate(centre[0], centre[1], centre[2]) - // const lineEnd2 = new SphereGeometry(0.15); - // lineEnd2.translate(from[0], from[1], from[2]) - - return { - line: lineBody, - tip: lineEnd1, - centre: centreSphere, - } -} - -export function sketchBaseGeo({ to }: { to: [number, number, number] }): { - base: BufferGeometry -} { - return { base: new SphereGeometry(0.25).translate(to[0], to[1], to[2]) } -} - -export interface extrudeWallGeo { - line: BufferGeometry - tip: BufferGeometry - centre: BufferGeometry -} - -export function extrudeGeo({ - from, - to, - length, - extrusionDirection = 1, -}: { - from: [number, number, number] - to: [number, number, number] - length: number - extrusionDirection?: number -}): { - geo: BufferGeometry - position: Position - rotation: Rotation -} { - const { - centre, - Hypotenuse: Hypotenuse3d, - ry, - rz, - sign, - } = trigCalcs({ from, to }) - - const face = new PlaneGeometry(Hypotenuse3d, length, 2, 2) - face.rotateX(Math.PI * 0.5) - face.translate(Hypotenuse3d * 0.5, 0, -length * 0.5 * sign) - - face.rotateY(ry) - face.rotateZ(rz) - face.translate(to[0], to[1], to[2]) - - const quat = new Quaternion() - const otherSign = ry === 0 ? 1 : -1 // don't ask questions, it works okay - const euler = new Euler( - (Math.PI * otherSign * extrusionDirection) / 2, - ry, - rz * sign * -otherSign, - 'XYZ' - ) - quat.setFromEuler(euler) - - return { - geo: face, - position: [centre[0], centre[1], centre[2]], - rotation: quat.toArray() as Rotation, - } -} diff --git a/src/lang/executor.test.ts b/src/lang/executor.test.ts index 70f9bc6cd..6e0983ae6 100644 --- a/src/lang/executor.test.ts +++ b/src/lang/executor.test.ts @@ -2,33 +2,34 @@ import fs from 'node:fs' import { abstractSyntaxTree } from './abstractSyntaxTree' import { lexer } from './tokeniser' -import { executor, ProgramMemory, Path, SketchGroup } from './executor' +import { ProgramMemory, Path, SketchGroup } from './executor' import { initPromise } from './rust' +import { executor } from '../lib/testHelpers' beforeAll(() => initPromise) -describe('test', () => { - it('test assigning two variables, the second summing with the first', () => { +describe('test executor', () => { + it('test assigning two variables, the second summing with the first', async () => { const code = `const myVar = 5 const newVar = myVar + 1` - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(5) expect(root.newVar.value).toBe(6) }) - it('test assigning a var with a string', () => { + it('test assigning a var with a string', async () => { const code = `const myVar = "a str"` - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe('a str') }) - it('test assigning a var by cont concatenating two strings string execute', () => { + it('test assigning a var by cont concatenating two strings string execute', async () => { const code = fs.readFileSync( './src/lang/testExamples/variableDeclaration.cado', 'utf-8' ) - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe('a str another str') }) - it('test with function call', () => { + it('test with function call', async () => { const code = ` const myVar = "hello" log(5, myVar)` @@ -44,15 +45,15 @@ log(5, myVar)` ], }, } - const { root } = executor(abstractSyntaxTree(lexer(code)), { + const { root } = await executor(abstractSyntaxTree(lexer(code)), { root: programMemoryOverride, - _sketch: [], + pendingMemory: {}, }) expect(root.myVar.value).toBe('hello') expect(programMemoryOverride.log.value).toHaveBeenCalledWith(5, 'hello') }) - it('fn funcN = () => {} execute', () => { - const { root } = exe( + it('fn funcN = () => {} execute', async () => { + const { root } = await exe( [ 'fn funcN = (a, b) => {', ' return a + b', @@ -64,7 +65,7 @@ log(5, myVar)` expect(root.theVar.value).toBe(60) expect(root.magicNum.value).toBe(69) }) - it('sketch declaration', () => { + it('sketch declaration', async () => { let code = `const mySketch = startSketchAt([0,0]) |> lineTo({to: [0,2], tag: "myPath"}, %) |> lineTo([2,3], %) @@ -72,7 +73,7 @@ log(5, myVar)` // |> close(%) show(mySketch) ` - const { root, return: _return } = exe(code) + const { root, return: _return } = await exe(code) // geo is three js buffer geometry and is very bloated to have in tests const minusGeo = removeGeoFromPaths(root.mySketch.value) expect(minusGeo).toEqual([ @@ -82,6 +83,7 @@ show(mySketch) from: [0, 0], __geoMeta: { sourceRange: [43, 80], + id: '37333036-3033-4432-b530-643030303837', pathToNode: [], geos: ['line', 'lineEnd'], }, @@ -93,6 +95,7 @@ show(mySketch) from: [0, 2], __geoMeta: { sourceRange: [86, 102], + id: '32343136-3330-4134-a462-376437386365', pathToNode: [], geos: ['line', 'lineEnd'], }, @@ -103,6 +106,7 @@ show(mySketch) from: [2, 3], __geoMeta: { sourceRange: [108, 151], + id: '32306132-6130-4138-b832-636363326330', pathToNode: [], geos: ['line', 'lineEnd'], }, @@ -120,16 +124,16 @@ show(mySketch) ]) }) - it('pipe binary expression into call expression', () => { + it('pipe binary expression into call expression', async () => { const code = [ 'fn myFn = (a) => { return a + 1 }', 'const myVar = 5 + 1 |> myFn(%)', ].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(7) }) - it('rotated sketch', () => { + it('rotated sketch', async () => { const code = [ 'const mySk1 = startSketchAt([0,0])', ' |> lineTo([1,1], %)', @@ -137,7 +141,7 @@ show(mySketch) ' |> lineTo([1, 1], %)', 'const rotated = rx(90, mySk1)', ].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.mySk1.value).toHaveLength(3) expect(root?.rotated?.type).toBe('sketchGroup') if ( @@ -154,7 +158,7 @@ show(mySketch) ]) }) - it('execute pipe sketch into call expression', () => { + it('execute pipe sketch into call expression', async () => { const code = [ 'const mySk1 = startSketchAt([0,0])', ' |> lineTo([1,1], %)', @@ -162,7 +166,7 @@ show(mySketch) ' |> lineTo([1,1], %)', ' |> rx(90, %)', ].join('\n') - const { root } = exe(code) + const { root } = await exe(code) const striptVersion = removeGeoFromSketch(root.mySk1 as SketchGroup) expect(striptVersion).toEqual({ type: 'sketchGroup', @@ -171,6 +175,7 @@ show(mySketch) to: [0, 0], from: [0, 0], __geoMeta: { + id: '37663863-3664-4366-a637-623739336334', sourceRange: [14, 34], pathToNode: [], geos: ['sketchBase'], @@ -183,6 +188,7 @@ show(mySketch) from: [0, 0], __geoMeta: { sourceRange: [40, 56], + id: '34356231-3362-4363-b935-393033353034', pathToNode: [], geos: ['line', 'lineEnd'], }, @@ -193,6 +199,7 @@ show(mySketch) from: [1, 1], __geoMeta: { sourceRange: [62, 100], + id: '39623339-3538-4366-b633-356630326639', pathToNode: [], geos: ['line', 'lineEnd'], }, @@ -204,6 +211,7 @@ show(mySketch) from: [0, 1], __geoMeta: { sourceRange: [106, 122], + id: '30636135-6232-4335-b665-366562303161', pathToNode: [], geos: ['line', 'lineEnd'], }, @@ -217,11 +225,11 @@ show(mySketch) ], }) }) - it('execute array expression', () => { + it('execute array expression', async () => { const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join( '\n' ) - const { root } = exe(code) + const { root } = await exe(code) // TODO path to node is probably wrong here, zero indexes are not correct expect(root).toEqual({ three: { @@ -268,12 +276,12 @@ show(mySketch) }, }) }) - it('execute object expression', () => { + it('execute object expression', async () => { const code = [ 'const three = 3', "const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}", ].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.yo).toEqual({ type: 'userVal', value: { aStr: 'str', anum: 2, identifier: 3, binExp: 9 }, @@ -291,11 +299,11 @@ show(mySketch) ], }) }) - it('execute memberExpression', () => { + it('execute memberExpression', async () => { const code = ["const yo = {a: {b: '123'}}", "const myVar = yo.a['b']"].join( '\n' ) - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar).toEqual({ type: 'userVal', value: '123', @@ -316,96 +324,96 @@ show(mySketch) }) describe('testing math operators', () => { - it('it can sum', () => { + it('it can sum', async () => { const code = ['const myVar = 1 + 2'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(3) }) - it('it can subtract', () => { + it('it can subtract', async () => { const code = ['const myVar = 1 - 2'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(-1) }) - it('it can multiply', () => { + it('it can multiply', async () => { const code = ['const myVar = 1 * 2'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(2) }) - it('it can divide', () => { + it('it can divide', async () => { const code = ['const myVar = 1 / 2'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(0.5) }) - it('it can modulus', () => { + it('it can modulus', async () => { const code = ['const myVar = 5 % 2'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(1) }) - it('it can do multiple operations', () => { + it('it can do multiple operations', async () => { const code = ['const myVar = 1 + 2 * 3'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(7) }) - it('big example with parans', () => { + it('big example with parans', async () => { const code = ['const myVar = 1 + 2 * (3 - 4) / -5 + 6'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(7.4) }) - it('with identifier', () => { + it('with identifier', async () => { const code = ['const yo = 6', 'const myVar = yo / 2'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(3) }) - it('with identifier', () => { + it('with identifier', async () => { const code = ['const myVar = 2 * ((2 + 3 ) / 4 + 5)'].join('\n') - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(12.5) }) - it('with callExpression at start', () => { + it('with callExpression at start', async () => { const code = 'const myVar = min(4, 100) + 2' - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(6) }) - it('with callExpression at end', () => { + it('with callExpression at end', async () => { const code = 'const myVar = 2 + min(4, 100)' - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(6) }) - it('with nested callExpression', () => { + it('with nested callExpression', async () => { const code = 'const myVar = 2 + min(100, legLen(5, 3))' - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(6) }) - it('with unaryExpression', () => { + it('with unaryExpression', async () => { const code = 'const myVar = -min(100, 3)' - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(-3) }) - it('with unaryExpression in callExpression', () => { + it('with unaryExpression in callExpression', async () => { const code = 'const myVar = min(-legLen(5, 4), 5)' const code2 = 'const myVar = min(5 , -legLen(5, 4))' - const { root } = exe(code) - const { root: root2 } = exe(code2) + const { root } = await exe(code) + const { root: root2 } = await exe(code2) expect(root.myVar.value).toBe(-3) expect(root.myVar.value).toBe(root2.myVar.value) }) - it('with unaryExpression in ArrayExpression', () => { + it('with unaryExpression in ArrayExpression', async () => { const code = 'const myVar = [1,-legLen(5, 4)]' - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toEqual([1, -3]) }) - it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', () => { + it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => { const code = [ 'const part001 = startSketchAt([0, 0])', '|> line([-2.21, -legLen(5, min(3, 999))], %)', ].join('\n') - const { root } = exe(code) + const { root } = await exe(code) const sketch = removeGeoFromSketch(root.part001 as SketchGroup) // result of `-legLen(5, min(3, 999))` should be -4 const yVal = sketch.value?.[0]?.to?.[1] expect(yVal).toBe(-4) }) - it('test that % substitution feeds down CallExp->ArrExp->UnaryExp->CallExp', () => { + it('test that % substitution feeds down CallExp->ArrExp->UnaryExp->CallExp', async () => { const code = [ `const myVar = 3`, `const part001 = startSketchAt([0, 0])`, @@ -417,7 +425,7 @@ describe('testing math operators', () => { ``, `show(part001)`, ].join('\n') - const { root } = exe(code) + const { root } = await exe(code) const sketch = removeGeoFromSketch(root.part001 as SketchGroup) // expect -legLen(segLen('seg01', %), myVar) to equal -4 setting the y value back to 0 expect(sketch.value?.[1]?.from).toEqual([3, 4]) @@ -426,29 +434,31 @@ describe('testing math operators', () => { `-legLen(segLen('seg01', %), myVar)`, `legLen(segLen('seg01', %), myVar)` ) - const { root: removedUnaryExpRoot } = exe(removedUnaryExp) + const { root: removedUnaryExpRoot } = await exe(removedUnaryExp) const removedUnaryExpRootSketch = removeGeoFromSketch( removedUnaryExpRoot.part001 as SketchGroup ) // without the minus sign, the y value should be 8 expect(removedUnaryExpRootSketch.value?.[1]?.to).toEqual([6, 8]) }) - it('with nested callExpression and binaryExpression', () => { + it('with nested callExpression and binaryExpression', async () => { const code = 'const myVar = 2 + min(100, -1 + legLen(5, 3))' - const { root } = exe(code) + const { root } = await exe(code) expect(root.myVar.value).toBe(5) }) }) // helpers -function exe( +async function exe( code: string, - programMemory: ProgramMemory = { root: {}, _sketch: [] } + programMemory: ProgramMemory = { root: {}, pendingMemory: {} } ) { const tokens = lexer(code) const ast = abstractSyntaxTree(tokens) - return executor(ast, programMemory) + + const result = await executor(ast, programMemory) + return result } function removeGeoFromSketch(sketch: SketchGroup): SketchGroup { diff --git a/src/lang/executor.ts b/src/lang/executor.ts index 5a32c7272..9e6b0882e 100644 --- a/src/lang/executor.ts +++ b/src/lang/executor.ts @@ -12,7 +12,11 @@ import { } from './abstractSyntaxTree' import { InternalFnNames } from './std/stdTypes' import { internalFns } from './std/std' -import { BufferGeometry } from 'three' +import { + EngineCommandManager, + ArtifactMap, + SourceRangeMap, +} from './std/engineConnection' export type SourceRange = [number, number] export type PathToNode = [string | number, string][] // [pathKey, nodeType][] @@ -28,8 +32,8 @@ interface BasePath { to: [number, number] name?: string __geoMeta: { + id: string geos: { - geo: BufferGeometry type: 'line' | 'lineEnd' | 'sketchBase' }[] sourceRange: SourceRange @@ -59,7 +63,8 @@ export interface AngledLineTo extends BasePath { interface GeoMeta { __geoMeta: { - geo: BufferGeometry + id: string + refId?: string sourceRange: SourceRange pathToNode: PathToNode } @@ -69,6 +74,7 @@ export type Path = ToPoint | HorizontalLineTo | AngledLineTo | Base export interface SketchGroup { type: 'sketchGroup' + id: string value: Path[] start?: Base position: Position @@ -102,33 +108,116 @@ export interface UserVal { __meta: Metadata[] } +type MemoryItem = UserVal | SketchGroup | ExtrudeGroup + interface Memory { - [key: string]: UserVal | SketchGroup | ExtrudeGroup // | Memory + [key: string]: MemoryItem +} +interface PendingMemory { + [key: string]: Promise } export interface ProgramMemory { root: Memory + pendingMemory: Partial return?: Identifier[] - _sketch?: Path[] } -export const executor = ( +const addItemToMemory = ( + programMemory: ProgramMemory, + key: string, + value: MemoryItem | Promise +) => { + const _programMemory = programMemory + if (_programMemory.root[key] || _programMemory.pendingMemory[key]) { + throw new Error(`Memory item ${key} already exists`) + } + if (value instanceof Promise) { + _programMemory.pendingMemory[key] = value + value.then((resolvedValue) => { + _programMemory.root[key] = resolvedValue + delete _programMemory.pendingMemory[key] + }) + } else { + _programMemory.root[key] = value + } + return _programMemory +} + +const promisifyMemoryItem = async (obj: MemoryItem) => { + if (obj.value instanceof Promise) { + const resolvedGuy = await obj.value + return { + ...obj, + value: resolvedGuy, + } + } + return obj +} + +const getMemoryItem = async ( + programMemory: ProgramMemory, + key: string +): Promise => { + if (programMemory.root[key]) { + return programMemory.root[key] + } + if (programMemory.pendingMemory[key]) { + return programMemory.pendingMemory[key] as Promise + } + throw new Error(`Memory item ${key} not found`) +} + +export const executor = async ( node: Program, - programMemory: ProgramMemory = { root: {}, _sketch: [] }, + programMemory: ProgramMemory = { root: {}, pendingMemory: {} }, + engineCommandManager: EngineCommandManager, + options: { bodyType: 'root' | 'sketch' | 'block' } = { bodyType: 'root' }, + previousPathToNode: PathToNode = [], + // work around while the gemotry is still be stored on the frontend + // will be removed when the stream UI is added. + tempMapCallback: (a: { + artifactMap: ArtifactMap + sourceRangeMap: SourceRangeMap + }) => void = () => {} +): Promise => { + engineCommandManager.startNewSession() + const _programMemory = await _executor( + node, + programMemory, + engineCommandManager, + options, + previousPathToNode + ) + const { artifactMap, sourceRangeMap } = + await engineCommandManager.waitForAllCommands() + tempMapCallback({ artifactMap, sourceRangeMap }) + + engineCommandManager.endSession() + return _programMemory +} + +export const _executor = async ( + node: Program, + programMemory: ProgramMemory = { root: {}, pendingMemory: {} }, + engineCommandManager: EngineCommandManager, options: { bodyType: 'root' | 'sketch' | 'block' } = { bodyType: 'root' }, previousPathToNode: PathToNode = [] -): ProgramMemory => { - const _programMemory: ProgramMemory = { +): Promise => { + let _programMemory: ProgramMemory = { root: { ...programMemory.root, }, - _sketch: [], + pendingMemory: { + ...programMemory.pendingMemory, + }, return: programMemory.return, } const { body } = node - body.forEach((statement, bodyIndex) => { + const proms: Promise[] = [] + body.forEach(async (statement, bodyIndex) => { if (statement.type === 'VariableDeclaration') { - statement.declarations.forEach((declaration, index) => { + statement.declarations.forEach(async (declaration, index) => { const variableName = declaration.id.name const pathToNode: PathToNode = [ ...previousPathToNode, @@ -150,73 +239,115 @@ export const executor = ( ] if (declaration.init.type === 'PipeExpression') { - const value = getPipeExpressionResult( + const prom = getPipeExpressionResult( declaration.init, _programMemory, + engineCommandManager, pathToNode ) + proms.push(prom) + const value = await prom if (value?.type === 'sketchGroup' || value?.type === 'extrudeGroup') { - _programMemory.root[variableName] = value + _programMemory = addItemToMemory( + _programMemory, + variableName, + value + ) } else { - _programMemory.root[variableName] = { + _programMemory = addItemToMemory(_programMemory, variableName, { type: 'userVal', value, __meta, - } + }) } } else if (declaration.init.type === 'Identifier') { - _programMemory.root[variableName] = { + _programMemory = addItemToMemory(_programMemory, variableName, { type: 'userVal', value: _programMemory.root[declaration.init.name].value, __meta, - } + }) } else if (declaration.init.type === 'Literal') { - _programMemory.root[variableName] = { + _programMemory = addItemToMemory(_programMemory, variableName, { type: 'userVal', value: declaration.init.value, __meta, - } + }) } else if (declaration.init.type === 'BinaryExpression') { - _programMemory.root[variableName] = { - type: 'userVal', - value: getBinaryExpressionResult(declaration.init, _programMemory), - __meta, - } + const prom = getBinaryExpressionResult( + declaration.init, + _programMemory, + engineCommandManager + ) + proms.push(prom) + _programMemory = addItemToMemory( + _programMemory, + variableName, + promisifyMemoryItem({ + type: 'userVal', + value: prom, + __meta, + }) + ) } else if (declaration.init.type === 'UnaryExpression') { - _programMemory.root[variableName] = { - type: 'userVal', - value: getUnaryExpressionResult(declaration.init, _programMemory), - __meta, - } + const prom = getUnaryExpressionResult( + declaration.init, + _programMemory, + engineCommandManager + ) + proms.push(prom) + _programMemory = addItemToMemory( + _programMemory, + variableName, + promisifyMemoryItem({ + type: 'userVal', + value: prom, + __meta, + }) + ) } else if (declaration.init.type === 'ArrayExpression') { - const valueInfo: { value: any; __meta?: Metadata }[] = + const valueInfo: Promise<{ value: any; __meta?: Metadata }>[] = declaration.init.elements.map( - (element): { value: any; __meta?: Metadata } => { + async (element): Promise<{ value: any; __meta?: Metadata }> => { if (element.type === 'Literal') { return { value: element.value, } } else if (element.type === 'BinaryExpression') { + const prom = getBinaryExpressionResult( + element, + _programMemory, + engineCommandManager + ) + proms.push(prom) return { - value: getBinaryExpressionResult(element, _programMemory), + value: await prom, } } else if (element.type === 'PipeExpression') { + const prom = getPipeExpressionResult( + element, + _programMemory, + engineCommandManager, + pathToNode + ) + proms.push(prom) return { - value: getPipeExpressionResult( - element, - _programMemory, - pathToNode - ), + value: await prom, } } else if (element.type === 'Identifier') { - const node = _programMemory.root[element.name] + const node = await getMemoryItem(_programMemory, element.name) return { value: node.value, __meta: node.__meta[node.__meta.length - 1], } } else if (element.type === 'UnaryExpression') { + const prom = getUnaryExpressionResult( + element, + _programMemory, + engineCommandManager + ) + proms.push(prom) return { - value: getUnaryExpressionResult(element, _programMemory), + value: await prom, } } else { throw new Error( @@ -225,73 +356,116 @@ export const executor = ( } } ) - const meta = valueInfo + const awaitedValueInfo = await Promise.all(valueInfo) + const meta = awaitedValueInfo .filter(({ __meta }) => __meta) .map(({ __meta }) => __meta) as Metadata[] - _programMemory.root[variableName] = { + _programMemory = addItemToMemory(_programMemory, variableName, { type: 'userVal', - value: valueInfo.map(({ value }) => value), + value: awaitedValueInfo.map(({ value }) => value), __meta: [...__meta, ...meta], - } + }) } else if (declaration.init.type === 'ObjectExpression') { - _programMemory.root[variableName] = { - type: 'userVal', - value: executeObjectExpression(_programMemory, declaration.init), - __meta, - } + const prom = executeObjectExpression( + _programMemory, + declaration.init, + engineCommandManager + ) + proms.push(prom) + _programMemory = addItemToMemory( + _programMemory, + variableName, + promisifyMemoryItem({ + type: 'userVal', + value: prom, + __meta, + }) + ) } else if (declaration.init.type === 'FunctionExpression') { const fnInit = declaration.init - _programMemory.root[declaration.id.name] = { - type: 'userVal', - value: (...args: any[]) => { - const fnMemory: ProgramMemory = { - root: { - ..._programMemory.root, - }, - _sketch: [], - } - if (args.length > fnInit.params.length) { - throw new Error( - `Too many arguments passed to function ${declaration.id.name}` - ) - } else if (args.length < fnInit.params.length) { - throw new Error( - `Too few arguments passed to function ${declaration.id.name}` - ) - } - fnInit.params.forEach((param, index) => { - fnMemory.root[param.name] = { - type: 'userVal', - value: args[index], - __meta, + _programMemory = addItemToMemory( + _programMemory, + declaration.id.name, + { + type: 'userVal', + value: async (...args: any[]) => { + let fnMemory: ProgramMemory = { + root: { + ..._programMemory.root, + }, + pendingMemory: { + ..._programMemory.pendingMemory, + }, } - }) - return executor(fnInit.body, fnMemory, { bodyType: 'block' }) - .return - }, - __meta, - } + if (args.length > fnInit.params.length) { + throw new Error( + `Too many arguments passed to function ${declaration.id.name}` + ) + } else if (args.length < fnInit.params.length) { + throw new Error( + `Too few arguments passed to function ${declaration.id.name}` + ) + } + fnInit.params.forEach((param, index) => { + fnMemory = addItemToMemory(fnMemory, param.name, { + type: 'userVal', + value: args[index], + __meta, + }) + }) + const prom = _executor( + fnInit.body, + fnMemory, + engineCommandManager, + { + bodyType: 'block', + } + ) + proms.push(prom) + const result = (await prom).return + return result + }, + __meta, + } + ) } else if (declaration.init.type === 'MemberExpression') { - _programMemory.root[variableName] = { - type: 'userVal', - value: getMemberExpressionResult(declaration.init, _programMemory), - __meta, - } + await Promise.all([...proms]) // TODO wait for previous promises, does that makes sense? + const prom = getMemberExpressionResult( + declaration.init, + _programMemory + ) + proms.push(prom) + _programMemory = addItemToMemory( + _programMemory, + variableName, + promisifyMemoryItem({ + type: 'userVal', + value: prom, + __meta, + }) + ) } else if (declaration.init.type === 'CallExpression') { - const result = executeCallExpression( + const prom = executeCallExpression( _programMemory, declaration.init, + engineCommandManager, previousPathToNode ) - _programMemory.root[variableName] = - result?.type === 'sketchGroup' || result?.type === 'extrudeGroup' - ? result - : { - type: 'userVal', - value: result, - __meta, - } + proms.push(prom) + _programMemory = addItemToMemory( + _programMemory, + variableName, + prom.then((a) => { + return a?.type === 'sketchGroup' || a?.type === 'extrudeGroup' + ? a + : { + type: 'userVal', + value: a, + __meta, + } + }) + ) } else { throw new Error( 'Unsupported declaration type: ' + declaration.init.type @@ -306,7 +480,7 @@ export const executor = ( if (arg.type === 'Literal') { return arg.value } else if (arg.type === 'Identifier') { - return _programMemory.root[arg.name].value + return _programMemory.root[arg.name]?.value } }) if ('show' === functionName) { @@ -320,13 +494,17 @@ export const executor = ( } } else if (statement.type === 'ReturnStatement') { if (statement.argument.type === 'BinaryExpression') { - _programMemory.return = getBinaryExpressionResult( + const prom = getBinaryExpressionResult( statement.argument, - _programMemory + _programMemory, + engineCommandManager ) + proms.push(prom) + _programMemory.return = await prom } } }) + await Promise.all(proms) return _programMemory } @@ -342,13 +520,14 @@ function getMemberExpressionResult( const object: any = expression.object.type === 'MemberExpression' ? getMemberExpressionResult(expression.object, programMemory) - : programMemory.root[expression.object.name].value - return object[propertyName] + : programMemory.root[expression.object.name]?.value + return object?.[propertyName] } -function getBinaryExpressionResult( +async function getBinaryExpressionResult( expression: BinaryExpression, programMemory: ProgramMemory, + engineCommandManager: EngineCommandManager, pipeInfo: { isInPipe: boolean previousResults: any[] @@ -366,8 +545,18 @@ function getBinaryExpressionResult( ...pipeInfo, isInPipe: false, } - const left = getBinaryPartResult(expression.left, programMemory, _pipeInfo) - const right = getBinaryPartResult(expression.right, programMemory, _pipeInfo) + const left = await getBinaryPartResult( + expression.left, + programMemory, + engineCommandManager, + _pipeInfo + ) + const right = await getBinaryPartResult( + expression.right, + programMemory, + engineCommandManager, + _pipeInfo + ) if (expression.operator === '+') return left + right if (expression.operator === '-') return left - right if (expression.operator === '*') return left * right @@ -375,9 +564,10 @@ function getBinaryExpressionResult( if (expression.operator === '%') return left % right } -function getBinaryPartResult( +async function getBinaryPartResult( part: BinaryPart, programMemory: ProgramMemory, + engineCommandManager: EngineCommandManager, pipeInfo: { isInPipe: boolean previousResults: any[] @@ -390,7 +580,7 @@ function getBinaryPartResult( expressionIndex: 0, body: [], } -): any { +): Promise { const _pipeInfo = { ...pipeInfo, isInPipe: false, @@ -400,15 +590,30 @@ function getBinaryPartResult( } else if (part.type === 'Identifier') { return programMemory.root[part.name].value } else if (part.type === 'BinaryExpression') { - return getBinaryExpressionResult(part, programMemory, _pipeInfo) + const prom = getBinaryExpressionResult( + part, + programMemory, + engineCommandManager, + _pipeInfo + ) + const result = await prom + return result } else if (part.type === 'CallExpression') { - return executeCallExpression(programMemory, part, [], _pipeInfo) + const result = await executeCallExpression( + programMemory, + part, + engineCommandManager, + [], + _pipeInfo + ) + return result } } -function getUnaryExpressionResult( +async function getUnaryExpressionResult( expression: UnaryExpression, programMemory: ProgramMemory, + engineCommandManager: EngineCommandManager, pipeInfo: { isInPipe: boolean previousResults: any[] @@ -422,50 +627,64 @@ function getUnaryExpressionResult( body: [], } ) { - return -getBinaryPartResult(expression.argument, programMemory, { - ...pipeInfo, - isInPipe: false, - }) + return -(await getBinaryPartResult( + expression.argument, + programMemory, + engineCommandManager, + { + ...pipeInfo, + isInPipe: false, + } + )) } -function getPipeExpressionResult( +async function getPipeExpressionResult( expression: PipeExpression, programMemory: ProgramMemory, + engineCommandManager: EngineCommandManager, previousPathToNode: PathToNode = [] ) { - const executedBody = executePipeBody( + const executedBody = await executePipeBody( expression.body, programMemory, + engineCommandManager, previousPathToNode ) const result = executedBody[executedBody.length - 1] return result } -function executePipeBody( +async function executePipeBody( body: PipeExpression['body'], programMemory: ProgramMemory, + engineCommandManager: EngineCommandManager, previousPathToNode: PathToNode = [], expressionIndex = 0, previousResults: any[] = [] -): any[] { +): Promise { if (expressionIndex === body.length) { return previousResults } const expression = body[expressionIndex] if (expression.type === 'BinaryExpression') { - const result = getBinaryExpressionResult(expression, programMemory) + const result = await getBinaryExpressionResult( + expression, + programMemory, + engineCommandManager + ) return executePipeBody( body, programMemory, + engineCommandManager, previousPathToNode, expressionIndex + 1, [...previousResults, result] ) } else if (expression.type === 'CallExpression') { - return executeCallExpression( + return await executeCallExpression( programMemory, expression, + engineCommandManager, previousPathToNode, { isInPipe: true, @@ -479,9 +698,10 @@ function executePipeBody( throw new Error('Invalid pipe expression') } -function executeObjectExpression( +async function executeObjectExpression( _programMemory: ProgramMemory, objExp: ObjectExpression, + engineCommandManager: EngineCommandManager, pipeInfo: { isInPipe: boolean previousResults: any[] @@ -500,43 +720,67 @@ function executeObjectExpression( isInPipe: false, } const obj: { [key: string]: any } = {} - objExp.properties.forEach((property) => { + const proms: Promise[] = [] + objExp.properties.forEach(async (property) => { if (property.type === 'ObjectProperty') { if (property.value.type === 'Literal') { obj[property.key.name] = property.value.value } else if (property.value.type === 'BinaryExpression') { - obj[property.key.name] = getBinaryExpressionResult( + const prom = getBinaryExpressionResult( property.value, _programMemory, + engineCommandManager, _pipeInfo ) + proms.push(prom) + obj[property.key.name] = await prom } else if (property.value.type === 'PipeExpression') { - obj[property.key.name] = getPipeExpressionResult( + const prom = getPipeExpressionResult( property.value, - _programMemory + _programMemory, + engineCommandManager ) + proms.push(prom) + obj[property.key.name] = await prom } else if (property.value.type === 'Identifier') { - obj[property.key.name] = _programMemory.root[property.value.name].value + obj[property.key.name] = ( + await getMemoryItem(_programMemory, property.value.name) + ).value } else if (property.value.type === 'ObjectExpression') { - obj[property.key.name] = executeObjectExpression( - _programMemory, - property.value - ) - } else if (property.value.type === 'ArrayExpression') { - const result = executeArrayExpression(_programMemory, property.value) - obj[property.key.name] = result - } else if (property.value.type === 'CallExpression') { - obj[property.key.name] = executeCallExpression( + const prom = executeObjectExpression( _programMemory, property.value, + engineCommandManager + ) + proms.push(prom) + obj[property.key.name] = await prom + } else if (property.value.type === 'ArrayExpression') { + const prom = executeArrayExpression( + _programMemory, + property.value, + engineCommandManager + ) + proms.push(prom) + obj[property.key.name] = await prom + } else if (property.value.type === 'CallExpression') { + const prom = executeCallExpression( + _programMemory, + property.value, + engineCommandManager, [], _pipeInfo ) + proms.push(prom) + const result = await prom + obj[property.key.name] = result } else if (property.value.type === 'UnaryExpression') { - obj[property.key.name] = getUnaryExpressionResult( + const prom = getUnaryExpressionResult( property.value, - _programMemory + _programMemory, + engineCommandManager ) + proms.push(prom) + obj[property.key.name] = await prom } else { throw new Error( `Unexpected property type ${property.value.type} in object expression` @@ -548,12 +792,14 @@ function executeObjectExpression( ) } }) + await Promise.all(proms) return obj } -function executeArrayExpression( +async function executeArrayExpression( _programMemory: ProgramMemory, arrExp: ArrayExpression, + engineCommandManager: EngineCommandManager, pipeInfo: { isInPipe: boolean previousResults: any[] @@ -571,36 +817,50 @@ function executeArrayExpression( ...pipeInfo, isInPipe: false, } - return arrExp.elements.map((el) => { - if (el.type === 'Literal') { - return el.value - } else if (el.type === 'Identifier') { - return _programMemory.root?.[el.name]?.value - } else if (el.type === 'BinaryExpression') { - return getBinaryExpressionResult(el, _programMemory, _pipeInfo) - } else if (el.type === 'ObjectExpression') { - return executeObjectExpression(_programMemory, el) - } else if (el.type === 'CallExpression') { - const result: any = executeCallExpression( - _programMemory, - el, - [], - _pipeInfo - ) - return result - } else if (el.type === 'UnaryExpression') { - return getUnaryExpressionResult(el, _programMemory, { - ...pipeInfo, - isInPipe: false, - }) - } - throw new Error('Invalid argument type') - }) + return await Promise.all( + arrExp.elements.map((el) => { + if (el.type === 'Literal') { + return el.value + } else if (el.type === 'Identifier') { + return _programMemory.root?.[el.name]?.value + } else if (el.type === 'BinaryExpression') { + return getBinaryExpressionResult( + el, + _programMemory, + engineCommandManager, + _pipeInfo + ) + } else if (el.type === 'ObjectExpression') { + return executeObjectExpression(_programMemory, el, engineCommandManager) + } else if (el.type === 'CallExpression') { + const result: any = executeCallExpression( + _programMemory, + el, + engineCommandManager, + [], + _pipeInfo + ) + return result + } else if (el.type === 'UnaryExpression') { + return getUnaryExpressionResult( + el, + _programMemory, + engineCommandManager, + { + ...pipeInfo, + isInPipe: false, + } + ) + } + throw new Error('Invalid argument type') + }) + ) } -function executeCallExpression( +async function executeCallExpression( programMemory: ProgramMemory, expression: CallExpression, + engineCommandManager: EngineCommandManager, previousPathToNode: PathToNode = [], pipeInfo: { isInPipe: boolean @@ -627,59 +887,87 @@ function executeCallExpression( ...pipeInfo, isInPipe: false, } - const fnArgs = expression?.arguments?.map((arg) => { - if (arg.type === 'Literal') { - return arg.value - } else if (arg.type === 'Identifier') { - const temp = programMemory.root[arg.name] - return temp?.type === 'userVal' ? temp.value : temp - } else if (arg.type === 'PipeSubstitution') { - return previousResults[expressionIndex - 1] - } else if (arg.type === 'ArrayExpression') { - return executeArrayExpression(programMemory, arg, pipeInfo) - } else if (arg.type === 'CallExpression') { - const result: any = executeCallExpression( - programMemory, - arg, - previousPathToNode, - _pipeInfo - ) - return result - } else if (arg.type === 'ObjectExpression') { - return executeObjectExpression(programMemory, arg, _pipeInfo) - } else if (arg.type === 'UnaryExpression') { - return getUnaryExpressionResult(arg, programMemory, _pipeInfo) - } else if (arg.type === 'BinaryExpression') { - return getBinaryExpressionResult(arg, programMemory, _pipeInfo) - } - throw new Error('Invalid argument type in function call') - }) + const fnArgs = await Promise.all( + expression?.arguments?.map(async (arg) => { + if (arg.type === 'Literal') { + return arg.value + } else if (arg.type === 'Identifier') { + await new Promise((r) => setTimeout(r)) // push into next even loop, but also probably should fix this + const temp = await getMemoryItem(programMemory, arg.name) + return temp?.type === 'userVal' ? temp.value : temp + } else if (arg.type === 'PipeSubstitution') { + return previousResults[expressionIndex - 1] + } else if (arg.type === 'ArrayExpression') { + return await executeArrayExpression( + programMemory, + arg, + engineCommandManager, + pipeInfo + ) + } else if (arg.type === 'CallExpression') { + const result: any = await executeCallExpression( + programMemory, + arg, + engineCommandManager, + previousPathToNode, + _pipeInfo + ) + return result + } else if (arg.type === 'ObjectExpression') { + return await executeObjectExpression( + programMemory, + arg, + engineCommandManager, + _pipeInfo + ) + } else if (arg.type === 'UnaryExpression') { + return getUnaryExpressionResult( + arg, + programMemory, + engineCommandManager, + _pipeInfo + ) + } else if (arg.type === 'BinaryExpression') { + return getBinaryExpressionResult( + arg, + programMemory, + engineCommandManager, + _pipeInfo + ) + } + throw new Error('Invalid argument type in function call') + }) + ) if (functionName in internalFns) { const fnNameWithSketchOrExtrude = functionName as InternalFnNames - const result = internalFns[fnNameWithSketchOrExtrude]( + const result = await internalFns[fnNameWithSketchOrExtrude]( { programMemory, sourceRange: sourceRangeOverride || [expression.start, expression.end], + engineCommandManager, + code: JSON.stringify(expression), }, fnArgs[0], fnArgs[1], fnArgs[2] ) return isInPipe - ? executePipeBody( + ? await executePipeBody( body, programMemory, + engineCommandManager, previousPathToNode, expressionIndex + 1, [...previousResults, result] ) : result } - const result = programMemory.root[functionName].value(...fnArgs) + const result = await programMemory.root[functionName].value(...fnArgs) return isInPipe - ? executePipeBody( + ? await executePipeBody( body, programMemory, + engineCommandManager, previousPathToNode, expressionIndex + 1, [...previousResults, result] diff --git a/src/lang/modifyAst.test.ts b/src/lang/modifyAst.test.ts index c9aef55b9..0f5ae3e6c 100644 --- a/src/lang/modifyAst.test.ts +++ b/src/lang/modifyAst.test.ts @@ -16,7 +16,7 @@ import { import { recast } from './recast' import { lexer } from './tokeniser' import { initPromise } from './rust' -import { executor } from './executor' +import { executor } from '../lib/testHelpers' beforeAll(() => initPromise) @@ -194,9 +194,9 @@ const part001 = startSketchAt([-1.2, 4.83]) const yo = 5 + 6 const yo2 = hmm([identifierGuy + 5]) show(part001)` - it('should move a value into a new variable', () => { + it('should move a value into a new variable', async () => { const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const startIndex = code.indexOf('100 + 100') + 1 const { modifiedAst } = moveValueIntoNewVariable( ast, @@ -208,9 +208,9 @@ show(part001)` expect(newCode).toContain(`const newVar = 100 + 100`) expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) }) - it('should move a value into a new variable', () => { + it('should move a value into a new variable', async () => { const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const startIndex = code.indexOf('2.8') + 1 const { modifiedAst } = moveValueIntoNewVariable( ast, @@ -222,9 +222,9 @@ show(part001)` expect(newCode).toContain(`const newVar = 2.8`) expect(newCode).toContain(`line([newVar, 0], %)`) }) - it('should move a value into a new variable', () => { + it('should move a value into a new variable', async () => { const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const startIndex = code.indexOf('def(') const { modifiedAst } = moveValueIntoNewVariable( ast, @@ -236,9 +236,9 @@ show(part001)` expect(newCode).toContain(`const newVar = def('yo')`) expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) }) - it('should move a value into a new variable', () => { + it('should move a value into a new variable', async () => { const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const startIndex = code.indexOf('jkl(') + 1 const { modifiedAst } = moveValueIntoNewVariable( ast, @@ -250,9 +250,9 @@ show(part001)` expect(newCode).toContain(`const newVar = jkl('yo') + 2`) expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) }) - it('should move a value into a new variable', () => { + it('should move a value into a new variable', async () => { const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const startIndex = code.indexOf('identifierGuy +') + 1 const { modifiedAst } = moveValueIntoNewVariable( ast, diff --git a/src/lang/queryAst.test.ts b/src/lang/queryAst.test.ts index a6958e7ac..557922c3f 100644 --- a/src/lang/queryAst.test.ts +++ b/src/lang/queryAst.test.ts @@ -7,7 +7,7 @@ import { } from './queryAst' import { lexer } from './tokeniser' import { initPromise } from './rust' -import { executor } from './executor' +import { executor } from '../lib/testHelpers' import { createArrayExpression, createCallExpression, @@ -19,7 +19,7 @@ import { recast } from './recast' beforeAll(() => initPromise) describe('findAllPreviousVariables', () => { - it('should find all previous variables', () => { + it('should find all previous variables', async () => { const code = `const baseThick = 1 const armAngle = 60 @@ -38,7 +38,7 @@ const variableBelowShouldNotBeIncluded = 3 show(part001)` const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7 const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const { variables, bodyPath, insertIndex } = findAllPreviousVariables( ast, diff --git a/src/lang/recast.test.ts b/src/lang/recast.test.ts index 42bc87d6f..115487dd9 100644 --- a/src/lang/recast.test.ts +++ b/src/lang/recast.test.ts @@ -64,7 +64,7 @@ log(5, myVar)` const recasted = recast(ast) expect(recasted).toBe(code) }) - it('sketch declaration', () => { + it('recast sketch declaration', () => { let code = `const mySketch = startSketchAt([0, 0]) |> lineTo({ to: [0, 1], tag: "myPath" }, %) |> lineTo([1, 1], %) diff --git a/src/lang/std/engineConnection.ts b/src/lang/std/engineConnection.ts new file mode 100644 index 000000000..0a4bc13d3 --- /dev/null +++ b/src/lang/std/engineConnection.ts @@ -0,0 +1,331 @@ +import { SourceRange } from '../executor' +import { Selections } from '../../useStore' + +interface ResultCommand { + type: 'result' + data: any +} +interface PendingCommand { + type: 'pending' + promise: Promise + resolve: (val: any) => void +} + +export interface ArtifactMap { + [key: string]: ResultCommand | PendingCommand +} +export interface SourceRangeMap { + [key: string]: SourceRange +} + +interface SelectionsArgs { + id: string + type: Selections['codeBasedSelections'][number]['type'] +} + +interface CursorSelectionsArgs { + otherSelections: Selections['otherSelections'] + idBasedSelections: { type: string; id: string }[] +} + +// TODO these types should be in the openApi spec, and therefore in @kittycad/lib +interface MouseStuff { + interaction: 'rotate' + window: { + x: number + y: number + } +} + +type uuid = string +interface XYZ { + x: number + y: number + z: number +} +interface EngineCommand { + type: 'ModelingCmdReq' + cmd: { + StartPath?: {} + MovePathPen?: { + path: uuid + to: XYZ + } + ExtendPath?: { + path: uuid + segment: { + Line: { + end: XYZ + } + } + } + ClosePath?: { + path_id: uuid + } + Extrude?: { + target: uuid + distance: number + cap: boolean + } + CameraDragMove?: MouseStuff + CameraDragStart?: MouseStuff + CameraDragEnd?: MouseStuff + } + cmd_id: uuid + file_id: uuid +} + +export class EngineCommandManager { + artifactMap: ArtifactMap = {} + sourceRangeMap: SourceRangeMap = {} + socket?: WebSocket + pc?: RTCPeerConnection + onHoverCallback: (id?: string) => void = () => {} + onClickCallback: (selection: SelectionsArgs) => void = () => {} + onCursorsSelectedCallback: (selections: CursorSelectionsArgs) => void = + () => {} + constructor(setMediaStream: (stream: MediaStream) => void) { + const url = 'wss://api.dev.kittycad.io/ws/modeling/commands' + this.socket = new WebSocket(url) + this.pc = new RTCPeerConnection() + this.socket.addEventListener('open', (event) => { + console.log('Connected to websocket, waiting for ICE servers') + }) + + this.socket.addEventListener('close', (event) => { + console.log('websocket connection closed') + }) + + this.socket.addEventListener('error', (event) => { + console.log('websocket connection error') + }) + + this?.socket?.addEventListener('message', (event) => { + if (!this.pc || !this.socket) return + + //console.log('Message from server ', event.data); + if (event.data instanceof Blob) { + const reader = new FileReader() + + reader.onload = () => { + //console.log("Result: " + reader.result); + } + + reader.readAsText(event.data) + } else if ( + typeof event.data === 'string' && + event.data.toLocaleLowerCase().startsWith('error') + ) { + console.warn('something went wrong: ', event.data) + } else { + const message = JSON.parse(event.data) + if (message.type === 'SDPAnswer') { + this.pc.setRemoteDescription( + new RTCSessionDescription(message.answer) + ) + } else if (message.type === 'IceServerInfo') { + console.log('received IceServerInfo') + this.pc.setConfiguration({ + iceServers: message.ice_servers, + }) + this.pc.addEventListener('track', (event) => { + console.log('received track', event) + const mediaStream = event.streams[0] + setMediaStream(mediaStream) + }) + + this.pc.addEventListener('connectionstatechange', (e) => + console.log(this?.pc?.iceConnectionState) + ) + this.pc.addEventListener('icecandidate', (event) => { + if (!this.pc || !this.socket) return + if (event.candidate === null) { + console.log('sent SDPOffer') + this.socket.send( + JSON.stringify({ + type: 'SDPOffer', + offer: this.pc.localDescription, + }) + ) + } + }) + + // Offer to receive 1 video track + this.pc.addTransceiver('video', { + direction: 'sendrecv', + }) + this.pc + .createOffer() + .then(async (descriptionInit) => { + await this?.pc?.setLocalDescription(descriptionInit) + console.log('sent SDPOffer begin') + const msg = JSON.stringify({ + type: 'SDPOffer', + offer: this.pc?.localDescription, + }) + this.socket?.send(msg) + }) + .catch(console.log) + } + // TODO talk to the gang about this + // the following message types are made up + // and are placeholders + else if (message.type === 'hover') { + this.onHoverCallback(message.id) + } else if (message.type === 'click') { + this.onClickCallback(message) + } else { + console.log('other message', message) + } + } + }) + } + + startNewSession() { + this.artifactMap = {} + this.sourceRangeMap = {} + + // socket.on('command', ({ id, data }: any) => { + // const command = this.artifactMap[id] + // const geos: any = {} + // if (data.geo) { + // geos.position = data.position + // geos.rotation = data.rotation + // geos.originalId = data.originalId + // try { + // geos.geo = stlLoader.parse(data.geo) + // } catch (e) {} + // } else { + // Object.entries(data).forEach(([key, val]: [string, any]) => { + // let bufferGeometry = new BufferGeometry() + // try { + // bufferGeometry = stlLoader.parse(val) + // } catch (e) { + // console.log('val', val) + // } + // geos[key] = bufferGeometry + // }) + // } + + // if (command && command.type === 'pending') { + // const resolve = command.resolve + // this.artifactMap[id] = { + // type: 'result', + // data: geos, + // } + // resolve({ + // id, + // geo: geos, + // }) + // } else { + // this.artifactMap[id] = { + // type: 'result', + // data: geos, + // } + // } + // }) + } + endSession() { + // this.socket?.close() + // socket.off('command') + } + onHover(callback: (id?: string) => void) { + // It's when the user hovers over a part in the 3d scene, and so the engine should tell the + // frontend about that (with it's id) so that the FE can highlight code associated with that id + this.onHoverCallback = callback + } + onClick(callback: (selection: SelectionsArgs) => void) { + // TODO talk to the gang about this + // It's when the user clicks on a part in the 3d scene, and so the engine should tell the + // frontend about that (with it's id) so that the FE can put the user's cursor on the right + // line of code + this.onClickCallback = callback + } + cusorsSelected(selections: { + otherSelections: Selections['otherSelections'] + idBasedSelections: { type: string; id: string }[] + }) { + // TODO talk to the gang about this + // Really idBasedSelections is the only part that's relevant to the server, but it's when + // the user puts their cursor over a line of code, and there is a engine-id associated with + // it, so we want to tell the engine to change it's color or something + if (this.socket?.readyState === 0) { + console.log('socket not open') + return + } + console.log('sending cursorsSelected') + this.socket?.send( + JSON.stringify({ command: 'cursorsSelected', body: selections }) + ) + } + sendSceneCommand(command: EngineCommand) { + if (this.socket?.readyState === 0) { + console.log('socket not ready') + return + } + this.socket?.send(JSON.stringify(command)) + } + sendModellingCommand({ + id, + params, + range, + command, + }: { + id: string + params: any + range: SourceRange + command: EngineCommand + }): Promise { + if (!this.socket?.OPEN) { + console.log('socket not open') + return new Promise(() => {}) + } + this.sourceRangeMap[id] = range + + // return early if the socket is still in CONNECTING state + if (this.socket?.readyState === 0) { + console.log('socket not ready') + return new Promise(() => {}) + } + console.log('sending command', { + id, + data: params, + command, + }) + this.socket?.send(JSON.stringify(command)) + let resolve: (val: any) => void = () => {} + const promise = new Promise((_resolve, reject) => { + resolve = _resolve + }) + this.artifactMap[id] = { + type: 'pending', + promise, + resolve, + } + return promise + } + commandResult(id: string): Promise { + const command = this.artifactMap[id] + if (!command) { + throw new Error('No command found') + } + if (command.type === 'result') { + return command.data + } + return command.promise + } + async waitForAllCommands(): Promise<{ + artifactMap: ArtifactMap + sourceRangeMap: SourceRangeMap + }> { + const pendingCommands = Object.values(this.artifactMap).filter( + ({ type }) => type === 'pending' + ) as PendingCommand[] + const proms = pendingCommands.map(({ promise }) => promise) + await Promise.all(proms) + return { + artifactMap: this.artifactMap, + sourceRangeMap: this.sourceRangeMap, + } + } +} diff --git a/src/lang/std/extrude.ts b/src/lang/std/extrude.ts index 8d38ae2c1..9cbb9cd99 100644 --- a/src/lang/std/extrude.ts +++ b/src/lang/std/extrude.ts @@ -1,3 +1,4 @@ +import { v4 as uuidv4 } from 'uuid' import { InternalFn } from './stdTypes' import { ExtrudeGroup, @@ -6,68 +7,56 @@ import { Position, Rotation, } from '../executor' -import { Quaternion, Vector3 } from 'three' import { clockwiseSign } from './std' -import { extrudeGeo } from '../engine' +import { generateUuidFromHashSeed } from '../../lib/uuid' export const extrude: InternalFn = ( - { sourceRange }, + { sourceRange, engineCommandManager, code }, length: number, sketchVal: SketchGroup ): ExtrudeGroup => { - const getSketchGeo = (sketchVal: SketchGroup): SketchGroup => { - return sketchVal - } - - const sketch = getSketchGeo(sketchVal) + const sketch = sketchVal const { position, rotation } = sketchVal + const id = generateUuidFromHashSeed( + JSON.stringify({ + code, + sourceRange, + date: { + length, + sketchVal, + }, + }) + ) + const extrudeSurfaces: ExtrudeSurface[] = [] const extrusionDirection = clockwiseSign(sketch.value.map((line) => line.to)) - sketch.value.map((line, index) => { - if (line.type === 'toPoint') { - let from: [number, number] = line.from - const to = line.to - const { - geo, - position: facePosition, - rotation: faceRotation, - } = extrudeGeo({ - from: [from[0], from[1], 0], - to: [to[0], to[1], 0], + engineCommandManager.sendModellingCommand({ + id, + params: [ + { length, - extrusionDirection, - }) - const groupQuaternion = new Quaternion(...rotation) - const currentWallQuat = new Quaternion(...faceRotation) - const unifiedQuit = new Quaternion().multiplyQuaternions( - currentWallQuat, - groupQuaternion.clone().invert() - ) - - const facePositionVector = new Vector3(...facePosition) - facePositionVector.applyQuaternion(groupQuaternion.clone()) - const unifiedPosition = new Vector3().addVectors( - facePositionVector, - new Vector3(...position) - ) - const surface: ExtrudeSurface = { - type: 'extrudePlane', - position: unifiedPosition.toArray() as Position, - rotation: unifiedQuit.toArray() as Rotation, - __geoMeta: { - geo, - sourceRange: line.__geoMeta.sourceRange, - pathToNode: line.__geoMeta.pathToNode, - }, - } - line.name && (surface.name = line.name) - extrudeSurfaces.push(surface) - } + extrusionDirection: extrusionDirection, + }, + ], + range: sourceRange, + command: { + type: 'ModelingCmdReq', + cmd: { + Extrude: { + target: sketch.id, + distance: length, + cap: true, + } + }, + cmd_id: id, + file_id: uuidv4(), + }, }) + return { type: 'extrudeGroup', - value: extrudeSurfaces, + value: extrudeSurfaces, // TODO, this is just an empty array now, should be deleted. height: length, position, rotation, @@ -92,7 +81,7 @@ export const getExtrudeWallTransform: InternalFn = ( position: Position quaternion: Rotation } => { - const path = extrudeGroup.value.find((path) => path.name === pathName) + const path = extrudeGroup?.value.find((path) => path.name === pathName) if (!path) throw new Error(`Could not find path with name ${pathName}`) return { position: path.position, diff --git a/src/lang/std/sketch.test.ts b/src/lang/std/sketch.test.ts index 29e5c8cf3..69d2fd2cd 100644 --- a/src/lang/std/sketch.test.ts +++ b/src/lang/std/sketch.test.ts @@ -9,7 +9,7 @@ import { lexer } from '../tokeniser' import { abstractSyntaxTree } from '../abstractSyntaxTree' import { getNodePathFromSourceRange } from '../queryAst' import { recast } from '../recast' -import { executor } from '../executor' +import { executor } from '../../lib/testHelpers' import { initPromise } from '../rust' beforeAll(() => initPromise) @@ -96,7 +96,7 @@ describe('testing getXComponent', () => { describe('testing changeSketchArguments', () => { const lineToChange = 'lineTo([-1.59, -1.54], %)' const lineAfterChange = 'lineTo([2, 3], %)' - test('changeSketchArguments', () => { + test('changeSketchArguments', async () => { const genCode = (line: string) => ` const mySketch001 = startSketchAt([0, 0]) |> ${line} @@ -106,7 +106,7 @@ show(mySketch001)` const code = genCode(lineToChange) const expectedCode = genCode(lineAfterChange) const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const sourceStart = code.indexOf(lineToChange) const { modifiedAst } = changeSketchArguments( ast, @@ -135,7 +135,7 @@ show(mySketch001)` describe('testing addNewSketchLn', () => { const lineToChange = 'lineTo([-1.59, -1.54], %)' const lineAfterChange = 'lineTo([2, 3], %)' - test('addNewSketchLn', () => { + test('addNewSketchLn', async () => { const code = ` const mySketch001 = startSketchAt([0, 0]) |> rx(45, %) @@ -143,7 +143,7 @@ const mySketch001 = startSketchAt([0, 0]) |> lineTo([0.46, -5.82], %) show(mySketch001)` const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const sourceStart = code.indexOf(lineToChange) const { modifiedAst } = addNewSketchLn({ node: ast, @@ -170,7 +170,7 @@ show(mySketch001)` }) describe('testing addTagForSketchOnFace', () => { - it('needs to be in it', () => { + it('needs to be in it', async () => { const originalLine = 'lineTo([-1.59, -1.54], %)' const genCode = (line: string) => ` const mySketch001 = startSketchAt([0, 0]) @@ -180,7 +180,7 @@ describe('testing addTagForSketchOnFace', () => { show(mySketch001)` const code = genCode(originalLine) const ast = abstractSyntaxTree(lexer(code)) - const programMemory = executor(ast) + const programMemory = await executor(ast) const sourceStart = code.indexOf(originalLine) const sourceRange: [number, number] = [ sourceStart, diff --git a/src/lang/std/sketch.ts b/src/lang/std/sketch.ts index ffde79394..97cc3d8e0 100644 --- a/src/lang/std/sketch.ts +++ b/src/lang/std/sketch.ts @@ -19,9 +19,10 @@ import { getNodeFromPathCurry, getNodePathFromSourceRange, } from '../queryAst' -import { lineGeo, sketchBaseGeo } from '../engine' import { GuiModes, toolTips, TooTip } from '../../useStore' import { splitPathAtPipeExpression } from '../modifyAst' +import { generateUuidFromHashSeed } from '../../lib/uuid' +import { v4 as uuidv4 } from 'uuid' import { SketchLineHelper, @@ -102,9 +103,21 @@ export function createFirstArg( throw new Error('all sketch line types should have been covered') } +type LineData = { + from: [number, number, number] + to: [number, number, number] +} + +function makeId(seed: string | any) { + if (typeof seed === 'string') { + return generateUuidFromHashSeed(seed) + } + return generateUuidFromHashSeed(JSON.stringify(seed)) +} + export const lineTo: SketchLineHelper = { fn: ( - { sourceRange }, + { sourceRange, engineCommandManager, code }, data: | [number, number] | { @@ -118,25 +131,35 @@ export const lineTo: SketchLineHelper = { const sketchGroup = { ...previousSketch } const from = getCoordsFromPaths(sketchGroup, sketchGroup.value.length - 1) const to = 'to' in data ? data.to : data - const geo = lineGeo({ + + const lineData: LineData = { from: [...from, 0], to: [...to, 0], + } + const id = makeId({ + code, + sourceRange, + data, }) + // engineCommandManager.sendModellingCommand({ + // id, + // params: [lineData, previousSketch], + // range: sourceRange, + // }) const currentPath: Path = { type: 'toPoint', to, from, __geoMeta: { sourceRange, + id, pathToNode: [], // TODO geos: [ { type: 'line', - geo: geo.line, }, { type: 'lineEnd', - geo: geo.tip, }, ], }, @@ -217,7 +240,7 @@ export const lineTo: SketchLineHelper = { export const line: SketchLineHelper = { fn: ( - { sourceRange }, + { sourceRange, engineCommandManager, code }, data: | [number, number] | 'default' @@ -239,25 +262,53 @@ export const line: SketchLineHelper = { } const to: [number, number] = [from[0] + args[0], from[1] + args[1]] - const geo = lineGeo({ + const lineData: LineData = { from: [...from, 0], to: [...to, 0], + } + const id = makeId({ + code, + sourceRange, + data, + }) + engineCommandManager.sendModellingCommand({ + id, + params: [lineData, previousSketch], + range: sourceRange, + command: { + type: 'ModelingCmdReq', + cmd: { + ExtendPath: { + path: sketchGroup.id, + segment: { + Line: { + end: { + x: lineData.to[0], + y: lineData.to[1], + z: 0, + }, + }, + }, + }, + }, + cmd_id: id, + file_id: uuidv4(), + } }) const currentPath: Path = { type: 'toPoint', to, from, __geoMeta: { + id, sourceRange, pathToNode: [], // TODO geos: [ { type: 'line', - geo: geo.line, }, { type: 'lineEnd', - geo: geo.tip, }, ], }, @@ -623,7 +674,7 @@ export const yLine: SketchLineHelper = { export const angledLine: SketchLineHelper = { fn: ( - { sourceRange, programMemory }, + { sourceRange, engineCommandManager, code }, data: | [number, number] | { @@ -641,25 +692,34 @@ export const angledLine: SketchLineHelper = { from[0] + length * Math.cos((angle * Math.PI) / 180), from[1] + length * Math.sin((angle * Math.PI) / 180), ] - const geo = lineGeo({ + const lineData: LineData = { from: [...from, 0], to: [...to, 0], + } + const id = makeId({ + code, + sourceRange, + data, }) + // engineCommandManager.sendModellingCommand({ + // id, + // params: [lineData, previousSketch], + // range: sourceRange, + // }) const currentPath: Path = { type: 'toPoint', to, from, __geoMeta: { + id, sourceRange, pathToNode: [], // TODO geos: [ { type: 'line', - geo: geo.line, }, { type: 'lineEnd', - geo: geo.tip, }, ], }, @@ -740,7 +800,7 @@ export const angledLine: SketchLineHelper = { export const angledLineOfXLength: SketchLineHelper = { fn: ( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, data: | [number, number] | { @@ -754,7 +814,7 @@ export const angledLineOfXLength: SketchLineHelper = { const [angle, length, tag] = 'angle' in data ? [data.angle, data.length, data.tag] : data return line.fn( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, { to: getYComponent(angle, length), tag }, previousSketch ) @@ -833,7 +893,7 @@ export const angledLineOfXLength: SketchLineHelper = { export const angledLineOfYLength: SketchLineHelper = { fn: ( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, data: | [number, number] | { @@ -847,7 +907,7 @@ export const angledLineOfYLength: SketchLineHelper = { const [angle, length, tag] = 'angle' in data ? [data.angle, data.length, data.tag] : data return line.fn( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, { to: getXComponent(angle, length), tag }, previousSketch ) @@ -927,7 +987,7 @@ export const angledLineOfYLength: SketchLineHelper = { export const angledLineToX: SketchLineHelper = { fn: ( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, data: | [number, number] | { @@ -948,7 +1008,7 @@ export const angledLineToX: SketchLineHelper = { const yComponent = xComponent * Math.tan((angle * Math.PI) / 180) const yTo = from[1] + yComponent return lineTo.fn( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, { to: [xTo, yTo], tag }, previousSketch ) @@ -1023,7 +1083,7 @@ export const angledLineToX: SketchLineHelper = { export const angledLineToY: SketchLineHelper = { fn: ( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, data: | [number, number] | { @@ -1044,7 +1104,7 @@ export const angledLineToY: SketchLineHelper = { const xComponent = yComponent / Math.tan((angle * Math.PI) / 180) const xTo = from[0] + xComponent return lineTo.fn( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, { to: [xTo, yTo], tag }, previousSketch ) @@ -1120,7 +1180,7 @@ export const angledLineToY: SketchLineHelper = { export const angledLineThatIntersects: SketchLineHelper = { fn: ( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, data: { angle: number intersectTag: string @@ -1145,7 +1205,7 @@ export const angledLineThatIntersects: SketchLineHelper = { line2Angle: data.angle, }) return lineTo.fn( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, { to, tag: data.tag }, previousSketch ) @@ -1513,32 +1573,53 @@ function addTagWithTo( } export const close: InternalFn = ( - { sourceRange }, + { sourceRange, engineCommandManager, code }, sketchGroup: SketchGroup ): SketchGroup => { const from = getCoordsFromPaths(sketchGroup, sketchGroup.value.length - 1) const to = sketchGroup.start ? sketchGroup.start.from : getCoordsFromPaths(sketchGroup, 0) - const geo = lineGeo({ + + const lineData: LineData = { from: [...from, 0], to: [...to, 0], + } + const id = makeId({ + code, + sourceRange, + data: sketchGroup, }) + engineCommandManager.sendModellingCommand({ + id, + params: [lineData], + range: sourceRange, + command: { + type: 'ModelingCmdReq', + cmd: { + ClosePath: { + path_id: sketchGroup.id, + }, + }, + cmd_id: id, + file_id: uuidv4(), + } + }) + const currentPath: Path = { type: 'toPoint', to, from, __geoMeta: { + id, sourceRange, pathToNode: [], // TODO geos: [ { type: 'line', - geo: geo.line, }, { type: 'lineEnd', - geo: geo.tip, }, ], }, @@ -1552,7 +1633,7 @@ export const close: InternalFn = ( } export const startSketchAt: InternalFn = ( - { sourceRange, programMemory }, + { sourceRange, programMemory, engineCommandManager, code }, data: | [number, number] | 'default' @@ -1569,18 +1650,59 @@ export const startSketchAt: InternalFn = ( to = data } - const geo = sketchBaseGeo({ to: [...to, 0] }) + const lineData: { to: [number, number, number] } = { + to: [...to, 0], + } + const id = makeId({ + code, + sourceRange, + data, + }) + const pathId = makeId({ + code, + sourceRange, + data, + isPath: true, + }) + engineCommandManager.sendModellingCommand({ + id: pathId, + params: [lineData], + range: sourceRange, + command: { + type: 'ModelingCmdReq', + cmd: { + StartPath: {}, + }, + cmd_id: pathId, + file_id: uuidv4(), + }, + }) + engineCommandManager.sendSceneCommand({ + type: 'ModelingCmdReq', + cmd: { + MovePathPen: { + path: pathId, + to: { + x: lineData.to[0], + y: lineData.to[1], + z: 0, + }, + }, + }, + cmd_id: id, + file_id: uuidv4(), + }) const currentPath: Path = { type: 'base', to, from: to, __geoMeta: { + id, sourceRange, pathToNode: [], // TODO geos: [ { type: 'sketchBase', - geo: geo.base, }, ], }, @@ -1594,6 +1716,7 @@ export const startSketchAt: InternalFn = ( value: [], position: [0, 0, 0], rotation: [0, 0, 0, 1], + id: pathId, __meta: [ { sourceRange, diff --git a/src/lang/std/sketchConstraints.test.ts b/src/lang/std/sketchConstraints.test.ts index 61668dd71..b72d36a54 100644 --- a/src/lang/std/sketchConstraints.test.ts +++ b/src/lang/std/sketchConstraints.test.ts @@ -1,5 +1,5 @@ import { abstractSyntaxTree } from '../abstractSyntaxTree' -import { executor, SketchGroup } from '../executor' +import { SketchGroup } from '../executor' import { lexer } from '../tokeniser' import { ConstraintType, @@ -10,11 +10,12 @@ import { recast } from '../recast' import { initPromise } from '../rust' import { getSketchSegmentFromSourceRange } from './sketchConstraints' import { Selection } from '../../useStore' +import { executor } from '../../lib/testHelpers' beforeAll(() => initPromise) // testing helper function -function testingSwapSketchFnCall({ +async function testingSwapSketchFnCall({ inputCode, callToSwap, constraintType, @@ -22,10 +23,10 @@ function testingSwapSketchFnCall({ inputCode: string callToSwap: string constraintType: ConstraintType -}): { +}): Promise<{ newCode: string originalRange: [number, number] -} { +}> { const startIndex = inputCode.indexOf(callToSwap) const range: Selection = { type: 'default', @@ -33,7 +34,7 @@ function testingSwapSketchFnCall({ } const tokens = lexer(inputCode) const ast = abstractSyntaxTree(tokens) - const programMemory = executor(ast) + const programMemory = await executor(ast) const selections = { codeBasedSelections: [range], otherSelections: [], @@ -94,10 +95,10 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { `show(part001)`, ] const bigExample = bigExampleArr.join('\n') - it('line with tag converts to xLine', () => { + it('line with tag converts to xLine', async () => { const callToSwap = "line({ to: [-2.04, -0.7], tag: 'abc2' }, %)" const expectedLine = "xLine({ length: -2.04, tag: 'abc2' }, %)" - const { newCode, originalRange } = testingSwapSketchFnCall({ + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap, constraintType: 'horizontal', @@ -106,10 +107,10 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('line w/o tag converts to xLine', () => { + it('line w/o tag converts to xLine', async () => { const callToSwap = 'line([0.73, -0.75], %)' const expectedLine = 'xLine(0.73, %)' - const { newCode, originalRange } = testingSwapSketchFnCall({ + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap, constraintType: 'horizontal', @@ -118,8 +119,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('lineTo with tag converts to xLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('lineTo with tag converts to xLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: "lineTo({ to: [1, 1], tag: 'abc1' }, %)", constraintType: 'horizontal', @@ -129,8 +130,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('lineTo w/o tag converts to xLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('lineTo w/o tag converts to xLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: 'lineTo([2.55, 3.58], %)', constraintType: 'horizontal', @@ -140,8 +141,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLine with tag converts to xLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLine with tag converts to xLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: [ `angledLine({`, @@ -157,8 +158,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLine w/o tag converts to xLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLine w/o tag converts to xLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: 'angledLine([63, 1.38], %)', constraintType: 'horizontal', @@ -168,8 +169,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineOfXLength with tag converts to xLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineOfXLength with tag converts to xLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: [ `angledLineOfXLength({`, @@ -186,8 +187,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineOfXLength w/o tag converts to xLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineOfXLength w/o tag converts to xLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: 'angledLineOfXLength([319, 1.15], %)', constraintType: 'horizontal', @@ -197,8 +198,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineOfYLength with tag converts to yLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineOfYLength with tag converts to yLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: [ `angledLineOfYLength({`, @@ -214,8 +215,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineOfYLength w/o tag converts to yLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineOfYLength w/o tag converts to yLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: 'angledLineOfYLength([50, 1.35], %)', constraintType: 'vertical', @@ -225,8 +226,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineToX with tag converts to xLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineToX with tag converts to xLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: "angledLineToX({ angle: 55, to: -2.89, tag: 'abc6' }, %)", constraintType: 'horizontal', @@ -236,8 +237,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineToX w/o tag converts to xLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineToX w/o tag converts to xLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: 'angledLineToX([291, 6.66], %)', constraintType: 'horizontal', @@ -247,8 +248,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineToY with tag converts to yLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineToY with tag converts to yLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: "angledLineToY({ angle: 330, to: 2.53, tag: 'abc7' }, %)", constraintType: 'vertical', @@ -258,8 +259,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => { // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineToY w/o tag converts to yLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineToY w/o tag converts to yLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: bigExample, callToSwap: 'angledLineToY([228, 2.14], %)', constraintType: 'vertical', @@ -294,8 +295,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari `show(part001)`, ] const varExample = variablesExampleArr.join('\n') - it('line keeps variable when converted to xLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('line keeps variable when converted to xLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: varExample, callToSwap: 'line([lineX, 2.13], %)', constraintType: 'horizontal', @@ -305,8 +306,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('lineTo keeps variable when converted to xLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('lineTo keeps variable when converted to xLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: varExample, callToSwap: 'lineTo([lineToX, 2.85], %)', constraintType: 'horizontal', @@ -316,8 +317,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineOfXLength keeps variable when converted to xLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineOfXLength keeps variable when converted to xLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: varExample, callToSwap: 'angledLineOfXLength([329, angledLineOfXLengthX], %)', constraintType: 'horizontal', @@ -327,8 +328,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineOfYLength keeps variable when converted to yLine', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineOfYLength keeps variable when converted to yLine', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: varExample, callToSwap: 'angledLineOfYLength([222, angledLineOfYLengthY], %)', constraintType: 'vertical', @@ -338,8 +339,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineToX keeps variable when converted to xLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineToX keeps variable when converted to xLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: varExample, callToSwap: 'angledLineToX([330, angledLineToXx], %)', constraintType: 'horizontal', @@ -349,8 +350,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari // new line should start at the same place as the old line expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('angledLineToY keeps variable when converted to yLineTo', () => { - const { newCode, originalRange } = testingSwapSketchFnCall({ + it('angledLineToY keeps variable when converted to yLineTo', async () => { + const { newCode, originalRange } = await testingSwapSketchFnCall({ inputCode: varExample, callToSwap: 'angledLineToY([217, angledLineToYy], %)', constraintType: 'vertical', @@ -361,14 +362,14 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) }) - it('trying to convert angledLineToY to xLineTo should not work because of the variable', () => { + it('trying to convert angledLineToY to xLineTo should not work because of the variable', async () => { const illegalConvert = () => testingSwapSketchFnCall({ inputCode: varExample, callToSwap: 'angledLineToY([217, angledLineToYy], %)', constraintType: 'horizontal', }) - expect(illegalConvert).toThrowError() + await expect(illegalConvert).rejects.toThrowError('no callback helper') }) }) @@ -380,8 +381,8 @@ const part001 = startSketchAt([0, 0.04]) // segment-in-start |> line([2.14, 1.35], %) // normal-segment |> xLine(3.54, %) show(part001)` - it('normal case works', () => { - const programMemory = executor(abstractSyntaxTree(lexer(code))) + it('normal case works', async () => { + const programMemory = await executor(abstractSyntaxTree(lexer(code))) const index = code.indexOf('// normal-segment') - 7 const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( programMemory.root['part001'] as SketchGroup, @@ -393,8 +394,8 @@ show(part001)` from: [3.48, 0.44], }) }) - it('verify it works when the segment is in the `start` property', () => { - const programMemory = executor(abstractSyntaxTree(lexer(code))) + it('verify it works when the segment is in the `start` property', async () => { + const programMemory = await executor(abstractSyntaxTree(lexer(code))) const index = code.indexOf('// segment-in-start') - 7 const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( programMemory.root['part001'] as SketchGroup, diff --git a/src/lang/std/sketchConstraints.ts b/src/lang/std/sketchConstraints.ts index c79fce506..862f6d7f1 100644 --- a/src/lang/std/sketchConstraints.ts +++ b/src/lang/std/sketchConstraints.ts @@ -41,7 +41,7 @@ export const segLen: InternalFn = ( segName: string, sketchGroup: SketchGroup ): number => { - const line = sketchGroup?.value.find((seg) => seg.name === segName) + const line = sketchGroup?.value.find((seg) => seg?.name === segName) // maybe this should throw, but the language doesn't have a way to handle errors yet if (!line) return 0 @@ -64,8 +64,8 @@ export const segAng: InternalFn = ( function segEndFactory(which: 'x' | 'y'): InternalFn { return (_, segName: string, sketchGroup: SketchGroup): number => { const line = - sketchGroup.start?.name === segName - ? sketchGroup.start + sketchGroup?.start?.name === segName + ? sketchGroup?.start : sketchGroup?.value.find((seg) => seg.name === segName) // maybe this should throw, but the language doesn't have a way to handle errors yet if (!line) return 0 diff --git a/src/lang/std/sketchcombos.test.ts b/src/lang/std/sketchcombos.test.ts index bf14be357..d7681fe9c 100644 --- a/src/lang/std/sketchcombos.test.ts +++ b/src/lang/std/sketchcombos.test.ts @@ -10,7 +10,7 @@ import { } from './sketchcombos' import { initPromise } from '../rust' import { Selections, TooTip } from '../../useStore' -import { executor } from '../../lang/executor' +import { executor } from '../../lib/testHelpers' import { recast } from '../../lang/recast' beforeAll(() => initPromise) @@ -196,7 +196,7 @@ const part001 = startSketchAt([0, 0]) |> xLine(segLen('seg01', %), %) // ln-xLineTo-free should convert to xLine |> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine show(part001)` - it('It should transform the ast', () => { + it('It should transform the ast', async () => { const ast = abstractSyntaxTree(lexer(inputScript)) const selectionRanges: Selections['codeBasedSelections'] = inputScript .split('\n') @@ -210,7 +210,7 @@ show(part001)` } }) - const programMemory = executor(ast) + const programMemory = await executor(ast) const transformInfos = getTransformInfos( makeSelections(selectionRanges.slice(1)), ast, @@ -255,7 +255,7 @@ const part001 = startSketchAt([0, 0]) |> angledLineToX([333, myVar3], %) // select for horizontal constraint 10 |> angledLineToY([301, myVar], %) // select for vertical constraint 10 show(part001)` - it('It should transform horizontal lines the ast', () => { + it('It should transform horizontal lines the ast', async () => { const expectModifiedScript = `const myVar = 2 const myVar2 = 12 const myVar3 = -10 @@ -295,7 +295,7 @@ show(part001)` } }) - const programMemory = executor(ast) + const programMemory = await executor(ast) const transformInfos = getTransformInfos( makeSelections(selectionRanges), ast, @@ -312,7 +312,7 @@ show(part001)` const newCode = recast(newAst) expect(newCode).toBe(expectModifiedScript) }) - it('It should transform vertical lines the ast', () => { + it('It should transform vertical lines the ast', async () => { const expectModifiedScript = `const myVar = 2 const myVar2 = 12 const myVar3 = -10 @@ -352,7 +352,7 @@ show(part001)` } }) - const programMemory = executor(ast) + const programMemory = await executor(ast) const transformInfos = getTransformInfos( makeSelections(selectionRanges), ast, @@ -381,13 +381,13 @@ const part001 = startSketchAt([0, 0]) |> line([myVar, 0.01], %) // xRelative |> line([0.7, myVar], %) // yRelative show(part001)` - it('testing for free to horizontal and vertical distance', () => { - const expectedHorizontalCode = helperThing( + it('testing for free to horizontal and vertical distance', async () => { + const expectedHorizontalCode = await helperThing( inputScript, ['// base selection', '// free'], 'setHorzDistance' ) - const expectedVerticalCode = helperThing( + const expectedVerticalCode = await helperThing( inputScript, ['// base selection', '// free'], 'setVertDistance' @@ -399,8 +399,8 @@ show(part001)` `lineTo([1.21, segEndY('seg01', %) + 2.92], %) // free` ) }) - it('testing for xRelative to vertical distance', () => { - const expectedCode = helperThing( + it('testing for xRelative to vertical distance', async () => { + const expectedCode = await helperThing( inputScript, ['// base selection', '// xRelative'], 'setVertDistance' @@ -410,8 +410,8 @@ show(part001)` segEndY('seg01', %) + 2.93 ], %) // xRelative`) }) - it('testing for yRelative to horizontal distance', () => { - const expectedCode = helperThing( + it('testing for yRelative to horizontal distance', async () => { + const expectedCode = await helperThing( inputScript, ['// base selection', '// yRelative'], 'setHorzDistance' @@ -424,11 +424,11 @@ show(part001)` }) }) -function helperThing( +async function helperThing( inputScript: string, linesOfInterest: string[], constraint: ConstraintType -): string { +): Promise { const ast = abstractSyntaxTree(lexer(inputScript)) const selectionRanges: Selections['codeBasedSelections'] = inputScript .split('\n') @@ -444,7 +444,7 @@ function helperThing( } }) - const programMemory = executor(ast) + const programMemory = await executor(ast) const transformInfos = getTransformInfos( makeSelections(selectionRanges.slice(1)), ast, diff --git a/src/lang/std/std.test.ts b/src/lang/std/std.test.ts index 3f1015509..c7de38201 100644 --- a/src/lang/std/std.test.ts +++ b/src/lang/std/std.test.ts @@ -1,12 +1,12 @@ import { abstractSyntaxTree } from '../abstractSyntaxTree' -import { executor } from '../executor' +import { executor } from '../../lib/testHelpers' import { lexer } from '../tokeniser' import { initPromise } from '../rust' beforeAll(() => initPromise) describe('testing angledLineThatIntersects', () => { - it('angledLineThatIntersects should intersect with another line', () => { + it('angledLineThatIntersects should intersect with another line', async () => { const code = (offset: string) => `const part001 = startSketchAt([0, 0]) |> lineTo({to:[2, 2], tag: "yo"}, %) |> lineTo([3, 1], %) @@ -18,9 +18,11 @@ describe('testing angledLineThatIntersects', () => { }, %) const intersect = segEndX('yo2', part001) show(part001)` - const { root } = executor(abstractSyntaxTree(lexer(code('-1')))) + const { root } = await executor(abstractSyntaxTree(lexer(code('-1')))) expect(root.intersect.value).toBe(1 + Math.sqrt(2)) - const { root: noOffset } = executor(abstractSyntaxTree(lexer(code('0')))) + const { root: noOffset } = await executor( + abstractSyntaxTree(lexer(code('0'))) + ) expect(noOffset.intersect.value).toBeCloseTo(1) }) }) diff --git a/src/lang/std/std.ts b/src/lang/std/std.ts index 89f72871b..8d8503149 100644 --- a/src/lang/std/std.ts +++ b/src/lang/std/std.ts @@ -24,62 +24,61 @@ import { lastSegX, lastSegY, } from './sketchConstraints' -import { extrude, getExtrudeWallTransform } from './extrude' -import { Quaternion, Vector3 } from 'three' +import { getExtrudeWallTransform, extrude } from './extrude' import { SketchGroup, ExtrudeGroup, Position, Rotation } from '../executor' import { InternalFn, InternalFnNames, InternalFirstArg } from './stdTypes' -const transform: InternalFn = ( - { sourceRange }: InternalFirstArg, - transformInfo: { - position: Position - quaternion: Rotation - }, - sketch: T -): T => { - const quaternionToApply = new Quaternion(...transformInfo.quaternion) - const newQuaternion = new Quaternion(...sketch.rotation).multiply( - quaternionToApply.invert() - ) +// const transform: InternalFn = ( +// { sourceRange }: InternalFirstArg, +// transformInfo: { +// position: Position +// quaternion: Rotation +// }, +// sketch: T +// ): T => { +// const quaternionToApply = new Quaternion(...transformInfo?.quaternion) +// const newQuaternion = new Quaternion(...sketch.rotation).multiply( +// quaternionToApply.invert() +// ) - const oldPosition = new Vector3(...sketch.position) - const newPosition = oldPosition - .applyQuaternion(quaternionToApply) - .add(new Vector3(...transformInfo.position)) - return { - ...sketch, - position: newPosition.toArray(), - rotation: newQuaternion.toArray(), - __meta: [ - ...sketch.__meta, - { - sourceRange, - pathToNode: [], // TODO - }, - ], - } -} +// const oldPosition = new Vector3(...sketch?.position) +// const newPosition = oldPosition +// .applyQuaternion(quaternionToApply) +// .add(new Vector3(...transformInfo?.position)) +// return { +// ...sketch, +// position: newPosition.toArray(), +// rotation: newQuaternion.toArray(), +// __meta: [ +// ...sketch.__meta, +// { +// sourceRange, +// pathToNode: [], // TODO +// }, +// ], +// } +// } -const translate: InternalFn = ( - { sourceRange }: InternalFirstArg, - vec3: [number, number, number], - sketch: T -): T => { - const oldPosition = new Vector3(...sketch.position) - const newPosition = oldPosition.add(new Vector3(...vec3)) - return { - ...sketch, - position: newPosition.toArray(), - __meta: [ - ...sketch.__meta, - { - sourceRange, - pathToNode: [], // TODO - }, - ], - } -} +// const translate: InternalFn = ( +// { sourceRange }: InternalFirstArg, +// vec3: [number, number, number], +// sketch: T +// ): T => { +// const oldPosition = new Vector3(...sketch.position) +// const newPosition = oldPosition.add(new Vector3(...vec3)) +// return { +// ...sketch, +// position: newPosition.toArray(), +// __meta: [ +// ...sketch.__meta, +// { +// sourceRange, +// pathToNode: [], // TODO +// }, +// ], +// } +// } const min: InternalFn = (_, a: number, b: number): number => Math.min(a, b) @@ -95,12 +94,13 @@ const legAngY: InternalFn = (_, hypotenuse: number, leg: number): number => (Math.asin(Math.min(leg, hypotenuse) / hypotenuse) * 180) / Math.PI export const internalFns: { [key in InternalFnNames]: InternalFn } = { - rx: rotateOnAxis([1, 0, 0]), - ry: rotateOnAxis([0, 1, 0]), - rz: rotateOnAxis([0, 0, 1]), + // TODO - re-enable these + // rx: rotateOnAxis([1, 0, 0]), + // ry: rotateOnAxis([0, 1, 0]), + // rz: rotateOnAxis([0, 0, 1]), extrude, - translate, - transform, + // translate, + // transform, getExtrudeWallTransform, min, legLen, @@ -130,35 +130,35 @@ export const internalFns: { [key in InternalFnNames]: InternalFn } = { close, } -function rotateOnAxis( - axisMultiplier: [number, number, number] -): InternalFn { - return ({ sourceRange }, rotationD: number, sketch: T): T => { - const rotationR = rotationD * (Math.PI / 180) - const rotateVec = new Vector3(...axisMultiplier) - const quaternion = new Quaternion() - quaternion.setFromAxisAngle(rotateVec, rotationR) +// function rotateOnAxis( +// axisMultiplier: [number, number, number] +// ): InternalFn { +// return ({ sourceRange }, rotationD: number, sketch: T): T => { +// const rotationR = rotationD * (Math.PI / 180) +// const rotateVec = new Vector3(...axisMultiplier) +// const quaternion = new Quaternion() +// quaternion.setFromAxisAngle(rotateVec, rotationR) - const position = new Vector3(...sketch.position) - .applyQuaternion(quaternion) - .toArray() +// const position = new Vector3(...sketch.position) +// .applyQuaternion(quaternion) +// .toArray() - const existingQuat = new Quaternion(...sketch.rotation) - const rotation = quaternion.multiply(existingQuat).toArray() - return { - ...sketch, - rotation, - position, - __meta: [ - ...sketch.__meta, - { - sourceRange, - pathToNode: [], // TODO - }, - ], - } - } -} +// const existingQuat = new Quaternion(...sketch.rotation) +// const rotation = quaternion.multiply(existingQuat).toArray() +// return { +// ...sketch, +// rotation, +// position, +// __meta: [ +// ...sketch.__meta, +// { +// sourceRange, +// pathToNode: [], // TODO +// }, +// ], +// } +// } +// } export function clockwiseSign(points: [number, number][]): number { let sum = 0 diff --git a/src/lang/std/stdTypes.ts b/src/lang/std/stdTypes.ts index fb715372d..67f6f7870 100644 --- a/src/lang/std/stdTypes.ts +++ b/src/lang/std/stdTypes.ts @@ -2,11 +2,14 @@ import { ProgramMemory, Path, SourceRange } from '../executor' import { Program, Value } from '../abstractSyntaxTree' import { TooTip } from '../../useStore' import { PathToNode } from '../executor' +import { EngineCommandManager } from './engineConnection' export interface InternalFirstArg { programMemory: ProgramMemory name?: string sourceRange: SourceRange + engineCommandManager: EngineCommandManager + code: string } export interface PathReturn { @@ -17,9 +20,13 @@ export interface PathReturn { export type InternalFn = (internals: InternalFirstArg, ...args: any[]) => any export type InternalFnNames = + // TODO re-enable these + // | 'translate' + // | 'transform' + // | 'rx' + // | 'ry' + // | 'rz' | 'extrude' - | 'translate' - | 'transform' | 'getExtrudeWallTransform' | 'min' | 'legLen' @@ -33,9 +40,6 @@ export type InternalFnNames = | 'segAng' | 'angleToMatchLengthX' | 'angleToMatchLengthY' - | 'rx' - | 'ry' - | 'rz' | 'lineTo' | 'yLineTo' | 'xLineTo' diff --git a/src/lib/testHelpers.ts b/src/lib/testHelpers.ts new file mode 100644 index 000000000..1ec7b5097 --- /dev/null +++ b/src/lib/testHelpers.ts @@ -0,0 +1,14 @@ +import { Program } from '../lang/abstractSyntaxTree' +import { ProgramMemory, _executor } from '../lang/executor' +import { EngineCommandManager } from '../lang/std/engineConnection' + +export async function executor( + ast: Program, + pm: ProgramMemory = { root: {}, pendingMemory: {} } +): Promise { + const engineCommandManager = new EngineCommandManager(() => {}) + engineCommandManager.startNewSession() + const programMemory = await _executor(ast, pm, engineCommandManager) + await engineCommandManager.waitForAllCommands() + return programMemory +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 5cc87ac19..0de56df87 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -27,3 +27,31 @@ export function normaliseAngle(angle: number): number { const result = ((angle % 360) + 360) % 360 return result > 180 ? result - 360 : result } + +export function throttle( + func: (...args: any[]) => any, + wait: number +): (...args: any[]) => any { + let timeout: ReturnType | null + let latestArgs: any[] + let latestTimestamp: number + + function later() { + timeout = null + func(...latestArgs) + } + + function throttled(...args: any[]) { + const currentTimestamp = Date.now() + latestArgs = args + + if (!latestTimestamp || currentTimestamp - latestTimestamp >= wait) { + latestTimestamp = currentTimestamp + func(...latestArgs) + } else if (!timeout) { + timeout = setTimeout(later, wait - (currentTimestamp - latestTimestamp)) + } + } + + return throttled +} diff --git a/src/lib/uuid.test.ts b/src/lib/uuid.test.ts new file mode 100644 index 000000000..7c97650bd --- /dev/null +++ b/src/lib/uuid.test.ts @@ -0,0 +1,9 @@ +import { generateUuidFromHashSeed } from './uuid' + +describe('generateUuidFromHashSeed', () => { + it('generates a UUID from a hash seed', () => { + const inputString = 'Hello, World!' + const uuid = generateUuidFromHashSeed(inputString) + expect(uuid).toEqual('64666664-3630-4231-a262-326264356230') + }) +}) diff --git a/src/lib/uuid.ts b/src/lib/uuid.ts new file mode 100644 index 000000000..784049086 --- /dev/null +++ b/src/lib/uuid.ts @@ -0,0 +1,10 @@ +import { v4 as uuidv4 } from 'uuid' +import { SHA256 } from 'crypto-js' + +export function generateUuidFromHashSeed(seed: string): string { + const hashedSeed = SHA256(seed).toString() + const uuid = uuidv4({ + random: hashedSeed.split('').map((c: string) => c.charCodeAt(0)), + }) + return uuid +} diff --git a/src/setupTests.ts b/src/setupTests.ts index 52aaef1d2..cdc6b90e6 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -3,3 +3,5 @@ // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom import '@testing-library/jest-dom' + +import 'setimmediate' diff --git a/src/useStore.ts b/src/useStore.ts index 291c0efc0..a250d7aeb 100644 --- a/src/useStore.ts +++ b/src/useStore.ts @@ -14,6 +14,12 @@ import { recast } from './lang/recast' import { asyncLexer } from './lang/tokeniser' import { EditorSelection } from '@codemirror/state' import { BaseDirectory } from '@tauri-apps/api/fs' +import { ArtifactMap, SourceRangeMap, EngineCommandManager } from './lang/std/engineConnection' +// import { +// ArtifactMap, +// SourceRangeMap, +// EngineCommandManager, +// } from './lang/std/engineConnection' export type Selection = { type: 'default' | 'line-end' | 'line-mid' @@ -100,6 +106,7 @@ export interface StoreState { highlightRange: [number, number] setHighlightRange: (range: Selection['range']) => void setCursor: (selections: Selections) => void + setCursor2: (a: Selection) => void selectionRanges: Selections selectionRangeTypeMap: { [key: number]: Selection['type'] } setSelectionRanges: (range: Selections) => void @@ -131,6 +138,16 @@ export interface StoreState { setProgramMemory: (programMemory: ProgramMemory) => void isShiftDown: boolean setIsShiftDown: (isShiftDown: boolean) => void + artifactMap: ArtifactMap + sourceRangeMap: SourceRangeMap + setArtifactNSourceRangeMaps: (a: { + artifactMap: ArtifactMap + sourceRangeMap: SourceRangeMap + }) => void + engineCommandManager?: EngineCommandManager + setEngineCommandManager: (engineCommandManager: EngineCommandManager) => void + mediaStream?: MediaStream + setMediaStream: (mediaStream: MediaStream) => void // tauri specific app settings defaultDir: DefaultDir @@ -168,8 +185,10 @@ export const useStore = create()( const selectionRangeTypeMap: { [key: number]: Selection['type'] } = {} set({ selectionRangeTypeMap }) selections.codeBasedSelections.forEach(({ range, type }) => { - ranges.push(EditorSelection.cursor(range[1])) - selectionRangeTypeMap[range[1]] = type + if (range?.[1]) { + ranges.push(EditorSelection.cursor(range[1])) + selectionRangeTypeMap[range[1]] = type + } }) setTimeout(() => { editorView.dispatch({ @@ -180,6 +199,16 @@ export const useStore = create()( }) }) }, + setCursor2: (codeSelections) => { + const currestSelections = get().selectionRanges + const selections: Selections = { + ...currestSelections, + codeBasedSelections: get().isShiftDown + ? [...currestSelections.codeBasedSelections, codeSelections] + : [codeSelections], + } + get().setCursor(selections) + }, selectionRangeTypeMap: {}, selectionRanges: { otherSelections: [], @@ -261,11 +290,17 @@ export const useStore = create()( setError: (error = '') => { set({ errorState: { isError: !!error, error } }) }, - programMemory: { root: {}, _sketch: [] }, + programMemory: { root: {}, pendingMemory: {} }, setProgramMemory: (programMemory) => set({ programMemory }), isShiftDown: false, setIsShiftDown: (isShiftDown) => set({ isShiftDown }), - + artifactMap: {}, + sourceRangeMap: {}, + setArtifactNSourceRangeMaps: (maps) => set({ ...maps }), + setEngineCommandManager: (engineCommandManager) => + set({ engineCommandManager }), + setMediaStream: (mediaStream) => set({ mediaStream }), + // tauri specific app settings defaultDir: { dir: '', diff --git a/yarn.lock b/yarn.lock index c6aae348d..4bd43f204 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1233,7 +1233,7 @@ core-js-pure "^3.25.1" regenerator-runtime "^0.13.10" -"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.6", "@babel/runtime@^7.18.9", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.18.9", "@babel/runtime@^7.9.2": version "7.20.1" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== @@ -1319,33 +1319,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@chevrotain/cst-dts-gen@10.4.1": - version "10.4.1" - resolved "https://registry.yarnpkg.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.4.1.tgz#b5b5656d6121dd6cf21675ffc19641057c005c44" - integrity sha512-wNDw9Rh6dPJKH275er8nijuDIpTcG2GjQANjnG8RaeGkZ3JN99+u6HRtnjKhjoi4NY9rg+udHChHQSskZtlkPw== - dependencies: - "@chevrotain/gast" "10.4.1" - "@chevrotain/types" "10.4.1" - lodash "4.17.21" - -"@chevrotain/gast@10.4.1": - version "10.4.1" - resolved "https://registry.yarnpkg.com/@chevrotain/gast/-/gast-10.4.1.tgz#5725a7939ffde52c0f702e38ce9c4b910acb6715" - integrity sha512-HRv66QVbmC7eb/ppwsPCfNH4oZ/VV+thuMZILm7A7W6Q5M0tqiZv0ecdiB8hydmPO8je0aSrXEOCcaA6fuXc3Q== - dependencies: - "@chevrotain/types" "10.4.1" - lodash "4.17.21" - -"@chevrotain/types@10.4.1": - version "10.4.1" - resolved "https://registry.yarnpkg.com/@chevrotain/types/-/types-10.4.1.tgz#0663800a3ef949eb512fce5be808d29128351738" - integrity sha512-J8iyZNn/RGYWSyNJdGd3QI01gKFUx4mCSM0+vEqmIw9TXFlxj1IsHteXFahtezSHjgMtBTqWn6hb2YxCLjpHVg== - -"@chevrotain/utils@10.4.1": - version "10.4.1" - resolved "https://registry.yarnpkg.com/@chevrotain/utils/-/utils-10.4.1.tgz#42b9e0d222b24395b50d88917d20934ae77f6892" - integrity sha512-vPIgzES8QhHMchb5UaQ4V/c9xmoaECN+4EXpuhWE+pu3LXJUUtAwDn/SEKFgtyiRo269Hxv3b0NbPlQfH0jeVA== - "@codemirror/autocomplete@^6.0.0": version "6.3.3" resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.3.3.tgz#f9dba421b9a0d83ecf89a06def37a96e6afb593a" @@ -1433,6 +1406,13 @@ style-mod "^4.0.0" w3c-keyname "^2.2.4" +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@csstools/normalize.css@*": version "12.0.0" resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.0.0.tgz#a9583a75c3f150667771f30b60d9f059473e62c4" @@ -1859,6 +1839,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" @@ -1877,6 +1862,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" @@ -1965,90 +1958,6 @@ schema-utils "^3.0.0" source-map "^0.7.3" -"@react-spring/animated@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.5.5.tgz#d3bfd0f62ed13a337463a55d2c93bb23c15bbf3e" - integrity sha512-glzViz7syQ3CE6BQOwAyr75cgh0qsihm5lkaf24I0DfU63cMm/3+br299UEYkuaHNmfDfM414uktiPlZCNJbQA== - dependencies: - "@react-spring/shared" "~9.5.5" - "@react-spring/types" "~9.5.5" - -"@react-spring/core@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.5.5.tgz#1d8a4c64630ee26b2295361e1eedfd716a85b4ae" - integrity sha512-shaJYb3iX18Au6gkk8ahaF0qx0LpS0Yd+ajb4asBaAQf6WPGuEdJsbsNSgei1/O13JyEATsJl20lkjeslJPMYA== - dependencies: - "@react-spring/animated" "~9.5.5" - "@react-spring/rafz" "~9.5.5" - "@react-spring/shared" "~9.5.5" - "@react-spring/types" "~9.5.5" - -"@react-spring/rafz@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.5.5.tgz#62a49c5e294104b79db2a8afdf4f3a274c7f44ca" - integrity sha512-F/CLwB0d10jL6My5vgzRQxCNY2RNyDJZedRBK7FsngdCmzoq3V4OqqNc/9voJb9qRC2wd55oGXUeXv2eIaFmsw== - -"@react-spring/shared@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.5.5.tgz#9be0b391d546e3e184a24ecbaf40acbaeab7fc73" - integrity sha512-YwW70Pa/YXPOwTutExHZmMQSHcNC90kJOnNR4G4mCDNV99hE98jWkIPDOsgqbYx3amIglcFPiYKMaQuGdr8dyQ== - dependencies: - "@react-spring/rafz" "~9.5.5" - "@react-spring/types" "~9.5.5" - -"@react-spring/three@^9.3.1": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/three/-/three-9.5.5.tgz#c6fbee977007d1980406db20a28ac3f5dc2ce153" - integrity sha512-9kTIaSceqFIl5EIrdwM7Z53o5I+9BGNVzbp4oZZYMao+GMAWOosnlQdDG5GeqNsIqfW9fZCEquGqagfKAxftcA== - dependencies: - "@react-spring/animated" "~9.5.5" - "@react-spring/core" "~9.5.5" - "@react-spring/shared" "~9.5.5" - "@react-spring/types" "~9.5.5" - -"@react-spring/types@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.5.5.tgz#c8e94f1b9232ca7cb9d860ea67762ec401b1de14" - integrity sha512-7I/qY8H7Enwasxr4jU6WmtNK+RZ4Z/XvSlDvjXFVe7ii1x0MoSlkw6pD7xuac8qrHQRm9BTcbZNyeeKApYsvCg== - -"@react-three/drei@^9.42.0": - version "9.42.0" - resolved "https://registry.yarnpkg.com/@react-three/drei/-/drei-9.42.0.tgz#0c81ebdf3bd48d87edddc42a0007a0db3aaf48f5" - integrity sha512-rVVQl+GICK1ny1KkA2Qo3zdnNPeGCobuaNjS4rbIeTELumsVB9s5au76OcjrW5kSpaKxNeQNQQ5NTKme5xvIHw== - dependencies: - "@babel/runtime" "^7.11.2" - "@react-spring/three" "^9.3.1" - "@use-gesture/react" "^10.2.0" - detect-gpu "^4.0.36" - glsl-noise "^0.0.0" - lodash.clamp "^4.0.3" - lodash.omit "^4.5.0" - lodash.pick "^4.4.0" - meshline "^2.0.4" - react-composer "^5.0.3" - react-merge-refs "^1.1.0" - stats.js "^0.17.0" - suspend-react "^0.0.8" - three-mesh-bvh "^0.5.15" - three-stdlib "^2.18.1" - troika-three-text "^0.46.4" - utility-types "^3.10.0" - zustand "^3.5.13" - -"@react-three/fiber@^8.9.1": - version "8.9.1" - resolved "https://registry.yarnpkg.com/@react-three/fiber/-/fiber-8.9.1.tgz#54e278148ae1c301a4b516936bfce0d9240a7292" - integrity sha512-xRMO9RGp0DkxSFu5BmmkjCxJ4r0dEpLobtxXdZwI0h2rZZaCnkPM5zThRN8xaZNbZhzRSVICeNOFaZltr9xFyQ== - dependencies: - "@babel/runtime" "^7.17.8" - "@types/react-reconciler" "^0.26.7" - its-fine "^1.0.6" - react-reconciler "^0.27.0" - react-use-measure "^2.1.1" - scheduler "^0.21.0" - suspend-react "^0.0.8" - zustand "^3.7.1" - "@rollup/plugin-babel@^5.2.0": version "5.3.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" @@ -2343,6 +2252,26 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + "@types/aria-query@^4.2.0": version "4.2.2" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" @@ -2562,11 +2491,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc" integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg== -"@types/offscreencanvas@^2019.6.4": - version "2019.7.0" - resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz#e4a932069db47bb3eabeb0b305502d01586fa90d" - integrity sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg== - "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -2604,20 +2528,6 @@ dependencies: "@types/react" "*" -"@types/react-reconciler@^0.26.7": - version "0.26.7" - resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.26.7.tgz#0c4643f30821ae057e401b0d9037e03e8e9b2a36" - integrity sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ== - dependencies: - "@types/react" "*" - -"@types/react-reconciler@^0.28.0": - version "0.28.0" - resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.28.0.tgz#513acbed173140e958c909041ca14eb40412077f" - integrity sha512-5cjk9ottZAj7eaTsqzPUIlrVbh3hBAO2YaEL1rkjHKB3xNAId7oU8GhzvAX+gfmlfoxTwJnBjPxEHyxkEA1Ffg== - dependencies: - "@types/react" "*" - "@types/react@*", "@types/react@^18.0.0": version "18.0.25" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.25.tgz#8b1dcd7e56fe7315535a4af25435e0bb55c8ae44" @@ -2683,27 +2593,15 @@ dependencies: "@types/jest" "*" -"@types/three@^0.146.0": - version "0.146.0" - resolved "https://registry.yarnpkg.com/@types/three/-/three-0.146.0.tgz#83813ba0d2fff6bdc6d7fda3a77993a932bba45f" - integrity sha512-75AgysUrIvTCB054eQa2pDVFurfeFW8CrMQjpzjt3yHBfuuknoSvvsESd/3EhQxPrz9si3+P0wiDUVsWUlljfA== - dependencies: - "@types/webxr" "*" - "@types/trusted-types@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== -"@types/uuid@^9.0.2": - version "9.0.2" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.2.tgz#ede1d1b1e451548d44919dc226253e32a6952c4b" - integrity sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ== - -"@types/webxr@*": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.0.tgz#aae1cef3210d88fd4204f8c33385a0bbc4da07c9" - integrity sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA== +"@types/uuid@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" + integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== "@types/ws@^8.5.1": version "8.5.3" @@ -2846,18 +2744,6 @@ "@uiw/codemirror-extensions-basic-setup" "4.15.1" codemirror "^6.0.0" -"@use-gesture/core@10.2.22": - version "10.2.22" - resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.2.22.tgz#90bd543b042e6c3a40a69ce8c2c38ecb67f43a2f" - integrity sha512-Ek0JZFYfk+hicLmoG094gm3YOuDMBNckHb988e59YOZoAkETT8dQSzT+g3QkSHSiP1m5wFXAGPSgxvOuwvGKHQ== - -"@use-gesture/react@^10.2.0": - version "10.2.22" - resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.2.22.tgz#2b8a39fe9bab83d73b112c8a5248b95099672c84" - integrity sha512-ECo7ig16SxBE06ENIURO1woKEB6TC8qY3a0rugJjQ2f1o0Tj28xS/eYNyJuqzQB5YT0q5IrF7ZFpbx1p/5ohYA== - dependencies: - "@use-gesture/core" "10.2.22" - "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -2979,11 +2865,6 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@webgpu/glslang@^0.0.15": - version "0.0.15" - resolved "https://registry.yarnpkg.com/@webgpu/glslang/-/glslang-0.0.15.tgz#f5ccaf6015241e6175f4b90906b053f88483d1f2" - integrity sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q== - "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -3047,6 +2928,11 @@ acorn-walk@^7.0.0, acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^7.0.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -3062,6 +2948,11 @@ acorn@^8.2.4, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== +acorn@^8.4.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + address@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -3222,6 +3113,11 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + arg@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" @@ -3534,13 +3430,6 @@ bfj@^7.0.2: hoopy "^0.1.4" tryer "^1.0.1" -bidi-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bidi-js/-/bidi-js-1.0.2.tgz#1a497a762c2ddea377429d2649c9ce0f8a91527f" - integrity sha512-rzSy/k7WdX5zOyeHHCOixGXbCHkyogkxPKL2r8QtzHmVQDiWCXUWa18bLdMWT9CYMLOYTjWpTHawuev2ouYJVw== - dependencies: - require-from-string "^2.0.2" - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -3773,18 +3662,6 @@ check-types@^11.1.1: resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f" integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== -chevrotain@^10.1.2: - version "10.4.1" - resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-10.4.1.tgz#85571fa23551c1fa440ce62f0a83159616cfc520" - integrity sha512-1y4vnssauVmrrP5MBaJ6DZvsv3BpXLlKVNK5S52fTGQHqg09qxMDBAz0wZbb04Ovc1pBCA4obcCjOlRioIV+cA== - dependencies: - "@chevrotain/cst-dts-gen" "10.4.1" - "@chevrotain/gast" "10.4.1" - "@chevrotain/types" "10.4.1" - "@chevrotain/utils" "10.4.1" - lodash "4.17.21" - regexp-to-ast "0.5.0" - chokidar@^3.4.2, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -4069,6 +3946,11 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + crelt@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94" @@ -4090,6 +3972,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-js@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" + integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== + crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -4311,11 +4198,6 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -debounce@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" - integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== - debug@2.6.9, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -4449,13 +4331,6 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-gpu@^4.0.36: - version "4.0.49" - resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-4.0.49.tgz#a604b971c3cd1431be21a9842117dc495d82a982" - integrity sha512-o/iuS6Pz2+wOcVm3A47cJM3O8XwPecEyHRc7jTthNL2E1ZcqthLj2GKv4fQ5Zl20L9rm/G39DuPvBV2gS8F6Pg== - dependencies: - webgl-constants "^1.1.1" - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -4498,6 +4373,11 @@ diff-sequences@^29.2.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.2.0.tgz#4c55b5b40706c7b5d2c5c75999a50c56d214e8f6" integrity sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -4629,11 +4509,6 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -draco3d@^1.4.1: - version "1.5.5" - resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.5.5.tgz#6bf4bbdd65950e6153e991cb0dcb8a10323f610e" - integrity sha512-JVuNV0EJzD3LBYhGyIXJLeBID/EVtmFO1ZNhAYflTgiMiAJlbhXQmRRda/azjc8MRVMHh0gqGhiqHUo5dIXM8Q== - duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -5264,11 +5139,6 @@ fbjs@^3.0.0, fbjs@^3.0.1: setimmediate "^1.0.5" ua-parser-js "^0.7.30" -fflate@^0.6.9: - version "0.6.10" - resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.6.10.tgz#5f40f9659205936a2d18abf88b2e7781662b6d43" - integrity sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg== - file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -5610,11 +5480,6 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -glsl-noise@^0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/glsl-noise/-/glsl-noise-0.0.0.tgz#367745f3a33382c0eeec4cb54b7e99cfc1d7670b" - integrity sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w== - gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -6322,13 +6187,6 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -its-fine@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/its-fine/-/its-fine-1.0.6.tgz#087b14d71137816dab676d8b57c35a6cd5d2b021" - integrity sha512-VZJZPwVT2kxe5KQv+TxCjojfLiUIut8zXDNLTxcM7gJ/xQ/bSPk5M0neZ+j3myy45KKkltY1mm1jyJgx3Fxsdg== - dependencies: - "@types/react-reconciler" "^0.28.0" - jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -7027,11 +6885,6 @@ klona@^2.0.4, klona@^2.0.5: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== -ktx-parse@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/ktx-parse/-/ktx-parse-0.4.5.tgz#79905e22281a9d3e602b2ff522df1ee7d1813aa6" - integrity sha512-MK3FOody4TXbFf8Yqv7EBbySw7aPvEcPX++Ipt6Sox+/YMFvR5xaTyhfNSk1AEmMy+RYIw81ctN4IMxCB8OAlg== - language-subtag-registry@~0.3.2: version "0.3.22" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" @@ -7116,7 +6969,7 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.clamp@^4.0.0, lodash.clamp@^4.0.3: +lodash.clamp@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash.clamp/-/lodash.clamp-4.0.3.tgz#5c24bedeeeef0753560dc2b4cb4671f90a6ddfaa" integrity sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg== @@ -7151,16 +7004,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.omit@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" - integrity sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg== - -lodash.pick@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q== - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -7171,16 +7014,16 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - lodash@^4.17.13: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7228,6 +7071,11 @@ make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -7272,11 +7120,6 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -meshline@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/meshline/-/meshline-2.0.4.tgz#39c7bcf36b503397642f2312e6211f2a8ecf75c5" - integrity sha512-Jh6DJl/zLqA4xsKvGv5950jr2ukyXQE1wgxs8u94cImHrvL6soVIggqjP+2hVHZXGYaKnWszhtjuCbKNeQyYiw== - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -7411,11 +7254,6 @@ mkdirp@~0.5.1: dependencies: minimist "^1.2.5" -mmd-parser@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mmd-parser/-/mmd-parser-1.0.4.tgz#87cc05782cb5974ca854f0303fc5147bc9d690e7" - integrity sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -7698,14 +7536,6 @@ opener@^1.5.1: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -opentype.js@^1.3.3: - version "1.3.4" - resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-1.3.4.tgz#1c0e72e46288473cc4a4c6a2dc60fd7fe6020d77" - integrity sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw== - dependencies: - string.prototype.codepointat "^0.2.1" - tiny-inflate "^1.0.3" - optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -8504,11 +8334,6 @@ postcss@^8.4.18, postcss@^8.4.19: picocolors "^1.0.0" source-map-js "^1.0.2" -potpack@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14" - integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ== - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -8600,7 +8425,7 @@ prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.6.0, prop-types@^15.8.1: +prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8710,13 +8535,6 @@ react-base16-styling@^0.6.0: lodash.flow "^3.3.0" pure-color "^1.2.0" -react-composer@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/react-composer/-/react-composer-5.0.3.tgz#7beb9513da5e8687f4f434ea1333ef36a4f3091b" - integrity sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA== - dependencies: - prop-types "^15.6.0" - react-dev-utils@^12.0.1: version "12.0.1" resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73" @@ -8790,24 +8608,11 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-merge-refs@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" - integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ== - react-modal-promise@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/react-modal-promise/-/react-modal-promise-1.0.2.tgz#122620b7f19eec73683affadfa77c543d88edc40" integrity sha512-dqT618ROhG8qh1+O6EZkia5ELw3zaZWGpMX2YfEH4bgwYENPuFonqKw1W70LFx3K/SCZvVBcD6UYEI12yzYXzg== -react-reconciler@^0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.27.0.tgz#360124fdf2d76447c7491ee5f0e04503ed9acf5b" - integrity sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.21.0" - react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" @@ -8877,13 +8682,6 @@ react-textarea-autosize@^8.3.2: use-composed-ref "^1.3.0" use-latest "^1.2.1" -react-use-measure@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.1.tgz#5824537f4ee01c9469c45d5f7a8446177c6cc4ba" - integrity sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig== - dependencies: - debounce "^1.2.1" - react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -8988,11 +8786,6 @@ regex-parser@^2.2.11: resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== -regexp-to-ast@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz#56c73856bee5e1fef7f73a00f1473452ab712a24" - integrity sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw== - regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" @@ -9239,13 +9032,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@^0.21.0: - version "0.21.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" - integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ== - dependencies: - loose-envify "^1.1.0" - scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -9566,11 +9352,6 @@ stackframe@^1.3.4: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== -stats.js@^0.17.0: - version "0.17.0" - resolved "https://registry.yarnpkg.com/stats.js/-/stats.js-0.17.0.tgz#b1c3dc46d94498b578b7fd3985b81ace7131cc7d" - integrity sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw== - statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -9620,11 +9401,6 @@ string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.codepointat@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc" - integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg== - string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" @@ -9801,11 +9577,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -suspend-react@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/suspend-react/-/suspend-react-0.0.8.tgz#b0740c1386b4eb652f17affe4339915ee268bd31" - integrity sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg== - svg-parser@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" @@ -9993,33 +9764,6 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -three-mesh-bvh@^0.5.15: - version "0.5.18" - resolved "https://registry.yarnpkg.com/three-mesh-bvh/-/three-mesh-bvh-0.5.18.tgz#e884bf7f23356b2e9de99be7a0dcd6f9d72b4860" - integrity sha512-lJQkt4A+pfHMf8Pbyqm5UiIBoVtp3cuy5rrTpuhIaJlbAobJW3/uQxJVZKiHaGi1Bs+5Svb+T8xIS17EqjG2ZA== - -three-stdlib@^2.18.1: - version "2.19.0" - resolved "https://registry.yarnpkg.com/three-stdlib/-/three-stdlib-2.19.0.tgz#22b73fe07b2524548b486e551737db4b17586beb" - integrity sha512-ImKJXIlmx/iYH3U2CeLiYA2V60UqhpwLE2ErOME+FrD3Xodk7oQU5N9IZGKQzGjnl3hOfLmLZ5BPJrotmttBjg== - dependencies: - "@babel/runtime" "^7.16.7" - "@types/offscreencanvas" "^2019.6.4" - "@webgpu/glslang" "^0.0.15" - chevrotain "^10.1.2" - draco3d "^1.4.1" - fflate "^0.6.9" - ktx-parse "^0.4.5" - mmd-parser "^1.0.4" - opentype.js "^1.3.3" - potpack "^1.0.1" - zstddec "^0.0.2" - -three@^0.146.0: - version "0.146.0" - resolved "https://registry.yarnpkg.com/three/-/three-0.146.0.tgz#fd80f0d128ab4bb821a02191ae241e4e6326f17a" - integrity sha512-1lvNfLezN6OJ9NaFAhfX4sm5e9YCzHtaRgZ1+B4C+Hv6TibRMsuBAM5/wVKzxjpYIlMymvgsHEFrrigEfXnb2A== - throat@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" @@ -10030,11 +9774,6 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== -tiny-inflate@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" - integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== - tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -10091,31 +9830,30 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -troika-three-text@^0.46.4: - version "0.46.4" - resolved "https://registry.yarnpkg.com/troika-three-text/-/troika-three-text-0.46.4.tgz#77627ac2ac4765d5248c857a8b42f82c25f2d034" - integrity sha512-Qsv0HhUKTZgSmAJs5wvO7YlBoJSP9TGPLmrg+K9pbQq4lseQdcevbno/WI38bwJBZ/qS56hvfqEzY0zUEFzDIw== - dependencies: - bidi-js "^1.0.2" - troika-three-utils "^0.46.0" - troika-worker-utils "^0.46.0" - webgl-sdf-generator "1.1.1" - -troika-three-utils@^0.46.0: - version "0.46.0" - resolved "https://registry.yarnpkg.com/troika-three-utils/-/troika-three-utils-0.46.0.tgz#6d97a9bf08f2260285edf2bb0be6328dd3d50eec" - integrity sha512-llHyrXAcwzr0bpg80GxsIp73N7FuImm4WCrKDJkAqcAsWmE5pfP9+Qzw+oMWK1P/AdHQ79eOrOl9NjyW4aOw0w== - -troika-worker-utils@^0.46.0: - version "0.46.0" - resolved "https://registry.yarnpkg.com/troika-worker-utils/-/troika-worker-utils-0.46.0.tgz#1b698090af78b51a27e03881c90237a2e648d6c4" - integrity sha512-bzOx5f2ZBxkFhXtIvDJlLn2AI3bzCkGVbCndl/2dL5QZrwHEKl45OEIilCxYQQWJG1rEbOD9O80tMjoYjw19OA== - tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tsconfig-paths@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" @@ -10395,11 +10133,6 @@ utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -utility-types@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" - integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -10415,6 +10148,11 @@ uuid@^9.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + v8-to-istanbul@^8.1.0: version "8.1.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" @@ -10482,16 +10220,6 @@ web-vitals@^2.1.0: resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c" integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg== -webgl-constants@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/webgl-constants/-/webgl-constants-1.1.1.tgz#f9633ee87fea56647a60b9ce735cbdfb891c6855" - integrity sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg== - -webgl-sdf-generator@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz#3e1b422b3d87cd3cc77f2602c9db63bc0f6accbd" - integrity sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA== - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -11005,21 +10733,16 @@ yarn@^1.22.19: resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.19.tgz#4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" integrity sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ== +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zstddec@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.0.2.tgz#57e2f28dd1ff56b750e07d158a43f0611ad9eeb4" - integrity sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA== - -zustand@^3.5.13, zustand@^3.7.1: - version "3.7.2" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.7.2.tgz#7b44c4f4a5bfd7a8296a3957b13e1c346f42514d" - integrity sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA== - zustand@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.1.4.tgz#b0286da4cc9edd35e91c96414fa54bfa4652a54d"
- {'last first: \n\n' + - JSON.stringify(lastGuiMode, null, 2) + - '\n\n' + - JSON.stringify(guiMode)} -