asyncronise executor (#115)

* Intital async of executor

The execture now sends websocket message instead of calling functions
directly from the engine, When it does so it holds onto the id.
The engine is still returning geo/polys directly but I'm working make it
so that the UI doesn't need to know about that, so that we can switch
over the streaming ui.

Things left to do:
- it is still making both direct function calls and websockets, and the former should be removed.
- It does highlighting of segments and sourceRanges not through websockets and that needs to be fixed.
- Tests have not been adjusted for these changes.
- Selecting the head of a segment is not working correctly again yet.

* Rough engine prep changes (#135)

* rough changes for engine prep

* mouse movements working again

* connect to engine for startsketch, line, close and extrude
This commit is contained in:
Kurt Hutten
2023-06-22 16:43:33 +10:00
committed by GitHub
parent dd3117cf03
commit 2d3c73d46a
39 changed files with 1798 additions and 2443 deletions

View File

@ -15,6 +15,7 @@ jobs:
- run: yarn install - run: yarn install
- run: yarn build:wasm:ci - run: yarn build:wasm:ci
- run: yarn simpleserver:ci - run: yarn simpleserver:ci
- run: npm pkg delete type
- run: yarn test:nowatch - run: yarn test:nowatch
- run: yarn test:cov - run: yarn test:cov
- run: yarn test:rust - run: yarn test:rust

View File

@ -5,8 +5,6 @@
"dependencies": { "dependencies": {
"@codemirror/lang-javascript": "^6.1.1", "@codemirror/lang-javascript": "^6.1.1",
"@headlessui/react": "^1.7.13", "@headlessui/react": "^1.7.13",
"@react-three/drei": "^9.42.0",
"@react-three/fiber": "^8.9.1",
"@tauri-apps/api": "^1.3.0", "@tauri-apps/api": "^1.3.0",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0", "@testing-library/react": "^13.0.0",
@ -17,6 +15,7 @@
"@types/react-dom": "^18.0.0", "@types/react-dom": "^18.0.0",
"@uiw/react-codemirror": "^4.15.1", "@uiw/react-codemirror": "^4.15.1",
"allotment": "^1.17.0", "allotment": "^1.17.0",
"crypto-js": "^4.1.1",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -25,8 +24,8 @@
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sketch-helpers": "^0.0.2", "sketch-helpers": "^0.0.2",
"swr": "^2.0.4", "swr": "^2.0.4",
"three": "^0.146.0",
"toml": "^3.0.0", "toml": "^3.0.0",
"ts-node": "^10.9.1",
"typescript": "^4.4.2", "typescript": "^4.4.2",
"util": "^0.12.5", "util": "^0.12.5",
"uuid": "^9.0.0", "uuid": "^9.0.0",
@ -41,13 +40,13 @@
"build:both": "react-scripts build", "build:both": "react-scripts build",
"build:both:local": "yarn build:wasm && react-scripts build", "build:both:local": "yarn build:wasm && react-scripts build",
"test": "react-scripts test", "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: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:ci": "http-server ./public --cors -p 3000 &",
"simpleserver": "http-server ./public --cors -p 3000", "simpleserver": "http-server ./public --cors -p 3000",
"eject": "react-scripts eject", "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": "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\"", "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", "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": { "devDependencies": {
"@tauri-apps/cli": "^1.3.1", "@tauri-apps/cli": "^1.3.1",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"@types/three": "^0.146.0", "@types/uuid": "^9.0.1",
"@types/uuid": "^9.0.2",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"postcss": "^8.4.19", "postcss": "^8.4.19",
"prettier": "^2.8.0", "prettier": "^2.8.0",

View File

@ -1,6 +1,5 @@
import React from 'react'
import { render, screen } from '@testing-library/react' import { render, screen } from '@testing-library/react'
import App from './App' import { App } from './App'
let listener: ((rect: any) => void) | undefined = undefined let listener: ((rect: any) => void) | undefined = undefined
;(global as any).ResizeObserver = class ResizeObserver { ;(global as any).ResizeObserver = class ResizeObserver {

View File

@ -1,10 +1,8 @@
import { useRef, useState, useEffect } from 'react' import { useRef, useEffect, useMemo } from 'react'
import { Canvas } from '@react-three/fiber'
import { Allotment } from 'allotment' import { Allotment } from 'allotment'
import { OrbitControls, OrthographicCamera } from '@react-three/drei'
import { asyncLexer } from './lang/tokeniser' import { asyncLexer } from './lang/tokeniser'
import { abstractSyntaxTree } from './lang/abstractSyntaxTree' 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 CodeMirror from '@uiw/react-codemirror'
import { javascript } from '@codemirror/lang-javascript' import { javascript } from '@codemirror/lang-javascript'
import { ViewUpdate } from '@codemirror/view' import { ViewUpdate } from '@codemirror/view'
@ -12,29 +10,25 @@ import {
lineHighlightField, lineHighlightField,
addLineHighlight, addLineHighlight,
} from './editor/highlightextension' } from './editor/highlightextension'
import { useStore } from './useStore' import { Selections, useStore } from './useStore'
import { Toolbar } from './Toolbar' import { Toolbar } from './Toolbar'
import { BasePlanes } from './components/BasePlanes'
import { SketchPlane } from './components/SketchPlane'
import { Logs } from './components/Logs' import { Logs } from './components/Logs'
import { AxisIndicator } from './components/AxisIndicator'
import { RenderViewerArtifacts } from './components/RenderViewerArtifacts'
import { PanelHeader } from './components/PanelHeader' import { PanelHeader } from './components/PanelHeader'
import { MemoryPanel } from './components/MemoryPanel' import { MemoryPanel } from './components/MemoryPanel'
import { useHotKeyListener } from './hooks/useHotKeyListener' import { useHotKeyListener } from './hooks/useHotKeyListener'
import { Stream } from './components/Stream' import { Stream } from './components/Stream'
import ModalContainer from 'react-modal-promise' import ModalContainer from 'react-modal-promise'
import { EngineCommandManager } from './lang/std/engineConnection'
import { isOverlap } from './lib/utils'
const OrrthographicCamera = OrthographicCamera as any export function App() {
function App() {
const cam = useRef() const cam = useRef()
useHotKeyListener() useHotKeyListener()
const { const {
editorView, editorView,
setEditorView, setEditorView,
setSelectionRanges, setSelectionRanges,
selectionRanges: selectionRange, selectionRanges,
guiMode, guiMode,
lastGuiMode, lastGuiMode,
addLog, addLog,
@ -46,6 +40,13 @@ function App() {
setProgramMemory, setProgramMemory,
resetLogs, resetLogs,
selectionRangeTypeMap, selectionRangeTypeMap,
setArtifactMap,
engineCommandManager: _engineCommandManager,
setEngineCommandManager,
setHighlightRange,
setCursor2,
sourceRangeMap,
setMediaStream,
} = useStore((s) => ({ } = useStore((s) => ({
editorView: s.editorView, editorView: s.editorView,
setEditorView: s.setEditorView, setEditorView: s.setEditorView,
@ -63,6 +64,15 @@ function App() {
setProgramMemory: s.setProgramMemory, setProgramMemory: s.setProgramMemory,
resetLogs: s.resetLogs, resetLogs: s.resetLogs,
selectionRangeTypeMap: s.selectionRangeTypeMap, 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 = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
const onChange = (value: string, viewUpdate: ViewUpdate) => { const onChange = (value: string, viewUpdate: ViewUpdate) => {
@ -78,18 +88,17 @@ function App() {
const ranges = viewUpdate.state.selection.ranges const ranges = viewUpdate.state.selection.ranges
const isChange = const isChange =
ranges.length !== selectionRange.codeBasedSelections.length || ranges.length !== selectionRanges.codeBasedSelections.length ||
ranges.some(({ from, to }, i) => { ranges.some(({ from, to }, i) => {
return ( return (
from !== selectionRange.codeBasedSelections[i].range[0] || from !== selectionRanges.codeBasedSelections[i].range[0] ||
to !== selectionRange.codeBasedSelections[i].range[1] to !== selectionRanges.codeBasedSelections[i].range[1]
) )
}) })
if (!isChange) return if (!isChange) return
setSelectionRanges({ const codeBasedSelections: Selections['codeBasedSelections'] = ranges.map(
otherSelections: [], ({ from, to }) => {
codeBasedSelections: ranges.map(({ from, to }, i) => {
if (selectionRangeTypeMap[to]) { if (selectionRangeTypeMap[to]) {
return { return {
type: selectionRangeTypeMap[to], type: selectionRangeTypeMap[to],
@ -100,15 +109,39 @@ function App() {
type: 'default', type: 'default',
range: [from, to], 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(() => { useEffect(() => {
const asyncWrap = async () => { const asyncWrap = async () => {
try { try {
if (!code) { if (!code) {
setGeoArray([])
setAst(null) setAst(null)
return return
} }
@ -116,7 +149,14 @@ function App() {
const _ast = abstractSyntaxTree(tokens) const _ast = abstractSyntaxTree(tokens)
setAst(_ast) setAst(_ast)
resetLogs() resetLogs()
const programMemory = executor(_ast, { if (_engineCommandManager) {
_engineCommandManager.endSession()
}
engineCommandManager.startNewSession()
setEngineCommandManager(engineCommandManager)
_executor(
_ast,
{
root: { root: {
log: { log: {
type: 'userVal', type: 'userVal',
@ -151,7 +191,26 @@ function App() {
__meta: [], __meta: [],
}, },
}, },
_sketch: [], pendingMemory: {},
},
engineCommandManager,
{ bodyType: 'root' },
[]
).then(async (programMemory) => {
const { artifactMap, sourceRangeMap } =
await engineCommandManager.waitForAllCommands()
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) setProgramMemory(programMemory)
const geos = programMemory?.return const geos = programMemory?.return
@ -167,9 +226,9 @@ function App() {
}) })
.filter((a) => a) as (ExtrudeGroup | SketchGroup)[] .filter((a) => a) as (ExtrudeGroup | SketchGroup)[]
setGeoArray(geos) // console.log(programMemory)
console.log(programMemory)
setError() setError()
})
} catch (e: any) { } catch (e: any) {
setError('problem') setError('problem')
console.log(e) console.log(e)
@ -209,52 +268,9 @@ function App() {
<MemoryPanel /> <MemoryPanel />
<Logs /> <Logs />
</Allotment> </Allotment>
<Allotment vertical defaultSizes={[1, 400]} minSize={20}> <Allotment vertical defaultSizes={[40, 400]} minSize={20}>
<div className="h-full"> <div>
<PanelHeader title="Drafting Board" />
<Toolbar /> <Toolbar />
<div className="border h-full border-gray-300 relative">
<div className="absolute inset-0">
<Canvas>
<OrbitControls
enableDamping={false}
enablePan
enableRotate={
!(
guiMode.mode === 'canEditSketch' ||
guiMode.mode === 'sketch'
)
}
enableZoom
reverseOrbit={false}
/>
<OrrthographicCamera
ref={cam}
makeDefault
position={[0, 0, 1000]}
zoom={100}
rotation={[0, 0, 0]}
far={2000}
/>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<RenderViewerArtifacts artifacts={geoArray} />
<BasePlanes />
<SketchPlane />
<AxisIndicator />
</Canvas>
</div>
{errorState.isError && (
<div className="absolute inset-0 bg-gray-700/20">
<pre>
{'last first: \n\n' +
JSON.stringify(lastGuiMode, null, 2) +
'\n\n' +
JSON.stringify(guiMode)}
</pre>
</div>
)}
</div>
</div> </div>
<Stream /> <Stream />
</Allotment> </Allotment>
@ -262,5 +278,3 @@ function App() {
</div> </div>
) )
} }
export default App

View File

@ -1,7 +1,7 @@
import useSWR from 'swr' import useSWR from 'swr'
import fetcher from './lib/fetcher' import fetcher from './lib/fetcher'
import withBaseUrl from './lib/withBaseURL' import withBaseUrl from './lib/withBaseURL'
import App from './App' import { App } from './App'
export const Auth = () => { export const Auth = () => {
const { data: user } = useSWR(withBaseUrl('/user'), fetcher) as any const { data: user } = useSWR(withBaseUrl('/user'), fetcher) as any

View File

@ -96,11 +96,14 @@ export function useCalc({
newVariableInsertIndex: number newVariableInsertIndex: number
setNewVariableName: (a: string) => void setNewVariableName: (a: string) => void
} { } {
const { ast, programMemory, selectionRange } = useStore((s) => ({ const { ast, programMemory, selectionRange, engineCommandManager } = useStore(
(s) => ({
ast: s.ast, ast: s.ast,
programMemory: s.programMemory, programMemory: s.programMemory,
selectionRange: s.selectionRanges.codeBasedSelections[0].range, selectionRange: s.selectionRanges.codeBasedSelections[0].range,
})) engineCommandManager: s.engineCommandManager,
})
)
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
const [availableVarInfo, setAvailableVarInfo] = useState< const [availableVarInfo, setAvailableVarInfo] = useState<
ReturnType<typeof findAllPreviousVariables> ReturnType<typeof findAllPreviousVariables>
@ -141,6 +144,7 @@ export function useCalc({
}, [ast, programMemory, selectionRange]) }, [ast, programMemory, selectionRange])
useEffect(() => { useEffect(() => {
if (!engineCommandManager) return
try { try {
const code = `const __result__ = ${value}\nshow(__result__)` const code = `const __result__ = ${value}\nshow(__result__)`
const ast = abstractSyntaxTree(lexer(code)) const ast = abstractSyntaxTree(lexer(code))
@ -148,7 +152,7 @@ export function useCalc({
availableVarInfo.variables.forEach(({ key, value }) => { availableVarInfo.variables.forEach(({ key, value }) => {
_programMem.root[key] = { type: 'userVal', value, __meta: [] } _programMem.root[key] = { type: 'userVal', value, __meta: [] }
}) })
const programMemory = executor(ast, _programMem) executor(ast, _programMem, engineCommandManager).then((programMemory) => {
const resultDeclaration = ast.body.find( const resultDeclaration = ast.body.find(
(a) => (a) =>
a.type === 'VariableDeclaration' && a.type === 'VariableDeclaration' &&
@ -160,6 +164,7 @@ export function useCalc({
const result = programMemory?.root?.__result__?.value const result = programMemory?.root?.__result__?.value
setCalcResult(typeof result === 'number' ? String(result) : 'NAN') setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
init && setValueNode(init) init && setValueNode(init)
})
} catch (e) { } catch (e) {
setCalcResult('NAN') setCalcResult('NAN')
setValueNode(null) setValueNode(null)

View File

@ -1,16 +0,0 @@
export const AxisIndicator = () => (
<>
<mesh position={[0.5, 0, 0]}>
<boxBufferGeometry args={[1, 0.05, 0.05]} />
<meshStandardMaterial color="red" />
</mesh>
<mesh position={[0, 0.5, 0]}>
<boxBufferGeometry args={[0.05, 1, 0.05]} />
<meshStandardMaterial color="blue" />
</mesh>
<mesh position={[0, 0, 0.5]}>
<boxBufferGeometry args={[0.05, 0.05, 1]} />
<meshStandardMaterial color="green" />
</mesh>
</>
)

View File

@ -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 | number>(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) => (
<mesh
key={index}
rotation-x={index === 1 ? -Math.PI / 2 : 0}
rotation-y={index === 2 ? -Math.PI / 2 : 0}
onPointerMove={onPointerEvent}
onPointerOut={onPointerEvent}
onClick={onClick}
name={`${index}`}
>
<planeGeometry args={[5, 5]} />
<meshStandardMaterial
color="blue"
side={DoubleSide}
transparent
opacity={opacity + (axisIndex === index ? 0.3 : 0)}
/>
<Text
fontSize={1}
color="#555"
position={[1, 1, 0.01]}
font={'/roboto.woff'}
>
{index === 0 ? 'xy' : index === 1 ? 'xz' : 'yz'}
</Text>
<Text
fontSize={1}
color="#555"
position={[1, 1, -0.01]}
font={'/roboto.woff'}
>
{index === 0 ? 'xy' : index === 1 ? 'xz' : 'yz'}
</Text>
</mesh>
))}
</>
)
}

View File

@ -1,13 +1,13 @@
import { processMemory } from './MemoryPanel' import { processMemory } from './MemoryPanel'
import { lexer } from '../lang/tokeniser' import { lexer } from '../lang/tokeniser'
import { abstractSyntaxTree } from '../lang/abstractSyntaxTree' import { abstractSyntaxTree } from '../lang/abstractSyntaxTree'
import { executor } from '../lang/executor' import { executor } from '../lib/testHelpers'
import { initPromise } from '../lang/rust' import { initPromise } from '../lang/rust'
beforeAll(() => initPromise) beforeAll(() => initPromise)
describe('processMemory', () => { 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 code = `
const myVar = 5 const myVar = 5
const myFn = (a) => { const myFn = (a) => {
@ -28,7 +28,7 @@ describe('processMemory', () => {
show(theExtrude, theSketch)` show(theExtrude, theSketch)`
const tokens = lexer(code) const tokens = lexer(code)
const ast = abstractSyntaxTree(tokens) const ast = abstractSyntaxTree(tokens)
const programMemory = executor(ast, { const programMemory = await executor(ast, {
root: { root: {
log: { log: {
type: 'userVal', type: 'userVal',
@ -38,7 +38,7 @@ describe('processMemory', () => {
__meta: [], __meta: [],
}, },
}, },
_sketch: [], pendingMemory: {},
}) })
const output = processMemory(programMemory) const output = processMemory(programMemory)
expect(output.myVar).toEqual(5) expect(output.myVar).toEqual(5)

View File

@ -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<BufferGeometry | undefined>() as any
const detectionPlaneRef = useRef<BufferGeometry | undefined>() as any
const lastPointerRef = useRef<Vector3>(new Vector3())
const point2DRef = useRef<Vector3>(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<CallExpression>(
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 (
<>
<mesh
position={position}
quaternion={rotation}
ref={ref}
onPointerOver={(event) => {
inEditMode && setHover(true)
setHighlightRange(sourceRange)
}}
onPointerOut={(event) => {
setHover(false)
setHighlightRange([0, 0])
}}
onPointerDown={() => {
inEditMode && setIsMouseDown(true)
setCursor()
}}
>
<primitive object={geo} scale={hovered ? 2 : 1} />
<meshStandardMaterial
color={hovered ? 'hotpink' : editorCursor ? 'skyblue' : baseColor}
/>
</mesh>
{isMouseDown && (
<mesh
quaternion={clickDetectPlaneQuaternion}
onPointerMove={(a) => {
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())
}
}}
>
<planeGeometry args={[50, 50]} ref={detectionPlaneRef} />
<meshStandardMaterial
side={DoubleSide}
color="blue"
transparent
opacity={0}
/>
</mesh>
)}
</>
)
}
export function RenderViewerArtifacts({
artifacts,
}: {
artifacts: (ExtrudeGroup | SketchGroup)[]
}) {
useSetAppModeFromCursorLocation(artifacts)
return (
<>
{artifacts?.map((artifact, i) => (
<RenderViewerArtifact key={i} artifact={artifact} />
))}
</>
)
}
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 && (
<PathRender
geoInfo={artifact.start}
forceHighlight={false}
rotation={artifact.rotation}
position={artifact.position}
/>
)}
{artifact.value.map((geoInfo, key) => (
<PathRender
geoInfo={geoInfo}
key={key}
forceHighlight={false}
rotation={artifact.rotation}
position={artifact.position}
/>
))}
</>
)
}
if (artifact.type === 'extrudeGroup') {
return (
<>
{artifact.value.map((geoInfo, key) => (
<WallRender
geoInfo={geoInfo}
key={key}
forceHighlight={false}
rotation={artifact.rotation}
position={artifact.position}
/>
))}
</>
)
}
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<BufferGeometry | undefined>() 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 (
<>
<mesh
quaternion={rotation}
position={position}
ref={ref}
onPointerOver={(event) => {
setHover(true)
setHighlightRange(geoInfo.__geoMeta.sourceRange)
}}
onPointerOut={(event) => {
setHover(false)
setHighlightRange([0, 0])
}}
onClick={onClick}
>
<primitive object={geoInfo.__geoMeta.geo} />
<meshStandardMaterial
side={DoubleSide}
color={
hovered
? 'hotpink'
: forceHighlight || editorCursor
? 'skyblue'
: 'orange'
}
/>
</mesh>
</>
)
}
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 (
<LineRender
key={i}
geo={meta.geo}
sourceRange={geoInfo.__geoMeta.sourceRange}
forceHighlight={editorLineCursor}
rotation={rotation}
position={position}
/>
)
if (meta.type === 'lineEnd')
return (
<LineEnd
key={i}
geo={meta.geo}
from={geoInfo.from}
sourceRange={geoInfo.__geoMeta.sourceRange}
editorCursor={forceHighlight || editorCursor}
rotation={rotation}
position={position}
/>
)
if (meta.type === 'sketchBase')
return (
<LineRender
key={i}
geo={meta.geo}
sourceRange={geoInfo.__geoMeta.sourceRange}
forceHighlight={forceHighlight || editorLineCursor}
rotation={rotation}
position={position}
onClick={() => {
if (
!ast ||
!(guiMode.mode === 'sketch' && guiMode.sketchMode === 'line')
)
return
const path = getNodePathFromSourceRange(
ast,
geoInfo.__geoMeta.sourceRange
)
const getNode = getNodeFromPathCurry(ast, path)
const maybeStartSketchAt =
getNode<CallExpression>('CallExpression')
const pipe = getNode<PipeExpression>('PipeExpression')
if (
maybeStartSketchAt?.node.callee.name === 'startSketchAt' &&
pipe.node &&
pipe.node.body.length > 2
) {
const modifiedAst = JSON.parse(JSON.stringify(ast))
const _pipe = getNodeFromPath<PipeExpression>(
modifiedAst,
path,
'PipeExpression'
)
_pipe.node.body.push(
createCallExpression('close', [createPipeSubstitution()])
)
updateAstAsync(modifiedAst)
}
}}
/>
)
})}
</>
)
}
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<BufferGeometry | undefined>() as any
const [hovered, setHover] = useState(false)
const baseColor = useConstraintColors(sourceRange)
return (
<>
<mesh
quaternion={rotation}
position={position}
ref={ref}
onPointerOver={(e) => {
setHover(true)
setHighlightRange(sourceRange)
}}
onPointerOut={(e) => {
setHover(false)
setHighlightRange([0, 0])
}}
onClick={() => {
_onClick()
onClick()
}}
>
<primitive object={geo} />
<meshStandardMaterial
color={hovered ? 'hotpink' : forceHighlight ? 'skyblue' : baseColor}
/>
</mesh>
</>
)
}
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
}

View File

@ -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 (
<>
<mesh
quaternion={clickDetectQuaternion}
position={position}
name={sketchGridName}
onPointerDown={(e) => {
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)
}}
>
<planeGeometry args={[30, 40]} />
<meshStandardMaterial
color="blue"
side={DoubleSide}
opacity={0}
transparent
/>
</mesh>
<gridHelper
args={[50, 50, 'blue', 'hotpink']}
quaternion={gridQuaternion}
position={position}
onClick={() =>
!isShiftDown &&
setSelectionRanges({
...selectionRanges,
otherSelections: [],
})
}
/>
<mesh
onPointerOver={() => setXHover(true)}
onPointerOut={() => setXHover(false)}
onClick={onAxisClick('x-axis')}
>
<boxGeometry args={[50, 0.2, 0.05]} />
<meshStandardMaterial
color={
selectionRanges.otherSelections.includes('x-axis')
? 'skyblue'
: xHover
? '#FF5555'
: '#FF1111'
}
/>
</mesh>
<mesh
onPointerOver={() => setYHover(true)}
onPointerOut={() => setYHover(false)}
onClick={onAxisClick('y-axis')}
>
<boxGeometry args={[0.2, 50, 0.05]} />
<meshStandardMaterial
color={
selectionRanges.otherSelections.includes('y-axis')
? 'skyblue'
: yHover
? '#5555FF'
: '#1111FF'
}
/>
</mesh>
</>
)
}
function roundy({ x, y, z }: any) {
return {
x: roundOff(x, 2),
y: roundOff(y, 2),
z: roundOff(z, 2),
}
}

View File

@ -1,9 +1,16 @@
import { useEffect, useRef } from 'react' import { MouseEventHandler, useEffect, useRef } from 'react'
import { PanelHeader } from '../components/PanelHeader' import { PanelHeader } from '../components/PanelHeader'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { useStore } from '../useStore'
import { throttle } from '../lib/utils'
export const Stream = () => { export const Stream = () => {
const videoRef = useRef<HTMLVideoElement>(null) const videoRef = useRef<HTMLVideoElement>(null)
const cmdId = useRef('')
const { mediaStream, engineCommandManager } = useStore((s) => ({
mediaStream: s.mediaStream,
engineCommandManager: s.engineCommandManager,
}))
useEffect(() => { useEffect(() => {
if ( if (
@ -11,164 +18,25 @@ export const Stream = () => {
typeof RTCPeerConnection === 'undefined' typeof RTCPeerConnection === 'undefined'
) )
return return
const url = 'wss://api.dev.kittycad.io/ws/modeling/commands' if (!videoRef.current) return
if (!mediaStream) return
videoRef.current.srcObject = mediaStream
}, [mediaStream, engineCommandManager])
const file_id = uuidv4() 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')
})
socket.addEventListener('close', (event) => {
console.log('websocket connection closed')
})
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) => { const debounceSocketSend = throttle((message) => {
socket.send(JSON.stringify(message)) engineCommandManager?.sendSceneCommand(message)
}, 100) }, 100)
const handleClick = ({ clientX, clientY }: MouseEvent) => { const handleMouseMove: MouseEventHandler<HTMLVideoElement> = ({
clientX,
clientY,
}) => {
if (!videoRef.current) return if (!videoRef.current) return
if (!cmdId.current) return
const { left, top } = videoRef.current.getBoundingClientRect() const { left, top } = videoRef.current.getBoundingClientRect()
const x = clientX - left const x = clientX - left
const y = clientY - top 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,
},
},
},
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({ debounceSocketSend({
type: 'ModelingCmdReq', type: 'ModelingCmdReq',
cmd: { cmd: {
@ -184,55 +52,77 @@ export const Stream = () => {
file_id: file_id, file_id: file_id,
}) })
} }
const handleMouseDown: MouseEventHandler<HTMLVideoElement> = ({
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: newId,
file_id,
})
} }
if (videoRef.current) { const handleMouseUp: MouseEventHandler<HTMLVideoElement> = ({
videoRef.current.addEventListener('mousemove', handleMouseMove) clientX,
videoRef.current.addEventListener('mousedown', handleClick) clientY,
videoRef.current.addEventListener('mouseup', handleMouseUp) }) => {
if (!videoRef.current) return
const { left, top } = videoRef.current.getBoundingClientRect()
const x = clientX - left
const y = clientY - top
if (cmdId.current == null) {
return
} }
return () => { engineCommandManager?.sendSceneCommand({
socket.close() type: 'ModelingCmdReq',
pc.close() cmd: {
if (!videoRef.current) return CameraDragEnd: {
videoRef.current.removeEventListener('mousemove', handleMouseMove) interaction: 'rotate',
videoRef.current.removeEventListener('mousedown', handleClick) window: {
videoRef.current.removeEventListener('mouseup', handleMouseUp) x: x,
y: y,
},
},
},
cmd_id: uuidv4(),
file_id: file_id,
})
cmdId.current = ''
} }
}, [])
return ( return (
<div> <div>
<PanelHeader title="Stream" /> <PanelHeader title="Stream" />
<video ref={videoRef} /> <video
ref={videoRef}
muted
autoPlay
controls={false}
onMouseMove={handleMouseMove}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
/>
</div> </div>
) )
} }
function throttle(
func: (...args: any[]) => any,
wait: number
): (...args: any[]) => any {
let timeout: ReturnType<typeof setTimeout> | 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
}

View File

@ -58,7 +58,6 @@ export const Intersect = () => {
selectionRanges.codeBasedSelections?.[1]?.type !== 'line-end' && selectionRanges.codeBasedSelections?.[1]?.type !== 'line-end' &&
previousSegment && previousSegment &&
previousSegment.isParallelAndConstrained previousSegment.isParallelAndConstrained
console.log(shouldUsePreviousSegment)
const _forcedSelectionRanges: typeof selectionRanges = { const _forcedSelectionRanges: typeof selectionRanges = {
...selectionRanges, ...selectionRanges,

View File

@ -15,7 +15,7 @@ export const lineHighlightField = StateField.define({
for (let e of tr.effects) { for (let e of tr.effects) {
if (e.is(addLineHighlight)) { if (e.is(addLineHighlight)) {
lines = Decoration.none lines = Decoration.none
const [from, to] = e.value const [from, to] = e.value || [0, 0]
if (!(from === to && from === 0)) { if (!(from === to && from === 0)) {
lines = lines.update({ add: [matchDeco.range(from, to)] }) lines = lines.update({ add: [matchDeco.range(from, to)] })
deco.push(matchDeco.range(from, to)) deco.push(matchDeco.range(from, to))

View File

@ -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)
}
}

View File

@ -1,19 +1,20 @@
import { abstractSyntaxTree } from './abstractSyntaxTree' import { abstractSyntaxTree } from './abstractSyntaxTree'
import { lexer } from './tokeniser' import { lexer } from './tokeniser'
import { executor, SketchGroup, ExtrudeGroup } from './executor' import { SketchGroup, ExtrudeGroup } from './executor'
import { initPromise } from './rust' import { initPromise } from './rust'
import { executor } from '../lib/testHelpers'
beforeAll(() => initPromise) beforeAll(() => initPromise)
describe('testing artifacts', () => { describe('testing artifacts', () => {
test('sketch artifacts', () => { test('sketch artifacts', async () => {
const code = ` const code = `
const mySketch001 = startSketchAt([0, 0]) const mySketch001 = startSketchAt([0, 0])
|> lineTo([-1.59, -1.54], %) |> lineTo([-1.59, -1.54], %)
|> lineTo([0.46, -5.82], %) |> lineTo([0.46, -5.82], %)
|> rx(45, %) |> rx(45, %)
show(mySketch001)` show(mySketch001)`
const programMemory = executor(abstractSyntaxTree(lexer(code))) const programMemory = await executor(abstractSyntaxTree(lexer(code)))
const geos = programMemory?.return?.map( const geos = programMemory?.return?.map(
(a) => programMemory?.root?.[a.name] (a) => programMemory?.root?.[a.name]
) )
@ -26,6 +27,7 @@ show(mySketch001)`
to: [0, 0], to: [0, 0],
from: [0, 0], from: [0, 0],
__geoMeta: { __geoMeta: {
id: '66366561-6465-4734-a463-366330356563',
sourceRange: [21, 42], sourceRange: [21, 42],
pathToNode: [], pathToNode: [],
geos: ['sketchBase'], geos: ['sketchBase'],
@ -38,6 +40,7 @@ show(mySketch001)`
from: [0, 0], from: [0, 0],
__geoMeta: { __geoMeta: {
sourceRange: [48, 73], sourceRange: [48, 73],
id: '30366338-6462-4330-a364-303935626163',
pathToNode: [], pathToNode: [],
geos: ['line', 'lineEnd'], geos: ['line', 'lineEnd'],
}, },
@ -48,6 +51,7 @@ show(mySketch001)`
from: [-1.59, -1.54], from: [-1.59, -1.54],
__geoMeta: { __geoMeta: {
sourceRange: [79, 103], sourceRange: [79, 103],
id: '32653334-6331-4231-b162-663334363535',
pathToNode: [], pathToNode: [],
geos: ['line', 'lineEnd'], geos: ['line', 'lineEnd'],
}, },
@ -62,7 +66,7 @@ show(mySketch001)`
}, },
]) ])
}) })
test('extrude artifacts', () => { test('extrude artifacts', async () => {
const code = ` const code = `
const mySketch001 = startSketchAt([0, 0]) const mySketch001 = startSketchAt([0, 0])
|> lineTo([-1.59, -1.54], %) |> lineTo([-1.59, -1.54], %)
@ -70,7 +74,7 @@ const mySketch001 = startSketchAt([0, 0])
|> rx(45, %) |> rx(45, %)
|> extrude(2, %) |> extrude(2, %)
show(mySketch001)` show(mySketch001)`
const programMemory = executor(abstractSyntaxTree(lexer(code))) const programMemory = await executor(abstractSyntaxTree(lexer(code)))
const geos = programMemory?.return?.map( const geos = programMemory?.return?.map(
(a) => programMemory?.root?.[a.name] (a) => programMemory?.root?.[a.name]
) )
@ -87,7 +91,9 @@ show(mySketch001)`
0.8563498075401887, 0.8563498075401887,
], ],
__geoMeta: { __geoMeta: {
geo: 'PlaneGeometry', id: expect.anything(),
geo: undefined,
refId: '30366338-6462-4330-a364-303935626163',
sourceRange: [48, 73], sourceRange: [48, 73],
pathToNode: [], pathToNode: [],
}, },
@ -102,7 +108,9 @@ show(mySketch001)`
0.4923604609001174, 0.4923604609001174,
], ],
__geoMeta: { __geoMeta: {
geo: 'PlaneGeometry', id: expect.anything(),
geo: undefined,
refId: '32653334-6331-4231-b162-663334363535',
sourceRange: [79, 103], sourceRange: [79, 103],
pathToNode: [], 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 code = `
const sk1 = startSketchAt([0, 0]) const sk1 = startSketchAt([0, 0])
|> lineTo([-2.5, 0], %) |> lineTo([-2.5, 0], %)
@ -138,7 +146,7 @@ const sk2 = startSketchAt([0, 0])
show(theExtrude, sk2)` show(theExtrude, sk2)`
const programMemory = executor(abstractSyntaxTree(lexer(code))) const programMemory = await executor(abstractSyntaxTree(lexer(code)))
const geos = programMemory?.return?.map( const geos = programMemory?.return?.map(
(a) => programMemory?.root?.[a.name] (a) => programMemory?.root?.[a.name]
) )
@ -155,7 +163,9 @@ show(theExtrude, sk2)`
0.9230002039112793, 0.9230002039112793,
], ],
__geoMeta: { __geoMeta: {
geo: 'PlaneGeometry', id: expect.anything(), // todo figure out why isn't deterministic
geo: undefined,
refId: '36613364-6238-4330-b766-613131633135',
sourceRange: [40, 60], sourceRange: [40, 60],
pathToNode: [], pathToNode: [],
}, },
@ -170,7 +180,9 @@ show(theExtrude, sk2)`
-0.5362616571538269, -0.5362616571538269,
], ],
__geoMeta: { __geoMeta: {
geo: 'PlaneGeometry', id: expect.anything(),
geo: undefined,
refId: '32313832-3531-4933-b839-316634316237',
sourceRange: [66, 102], sourceRange: [66, 102],
pathToNode: [], pathToNode: [],
}, },
@ -186,7 +198,9 @@ show(theExtrude, sk2)`
0.5997895323824204, 0.5997895323824204,
], ],
__geoMeta: { __geoMeta: {
geo: 'PlaneGeometry', id: expect.anything(),
geo: undefined,
refId: '31356564-3364-4562-a438-653732633238',
sourceRange: [108, 127], sourceRange: [108, 127],
pathToNode: [], pathToNode: [],
}, },
@ -216,7 +230,9 @@ show(theExtrude, sk2)`
-0.20351996751370383, -0.20351996751370383,
], ],
__geoMeta: { __geoMeta: {
geo: 'PlaneGeometry', id: expect.anything(),
geo: undefined,
refId: '31623462-6433-4233-b361-303837663464',
sourceRange: [317, 337], sourceRange: [317, 337],
pathToNode: [], pathToNode: [],
}, },
@ -231,7 +247,9 @@ show(theExtrude, sk2)`
-0.5818075544860157, -0.5818075544860157,
], ],
__geoMeta: { __geoMeta: {
geo: 'PlaneGeometry', id: expect.anything(),
geo: undefined,
refId: '66363230-3430-4961-b831-646363376538',
sourceRange: [343, 378], sourceRange: [343, 378],
pathToNode: [], pathToNode: [],
}, },
@ -247,7 +265,9 @@ show(theExtrude, sk2)`
-0.7544557394170275, -0.7544557394170275,
], ],
__geoMeta: { __geoMeta: {
geo: 'PlaneGeometry', id: expect.anything(),
geo: undefined,
refId: '62366564-3261-4061-b533-623433336531',
sourceRange: [384, 403], sourceRange: [384, 403],
pathToNode: [], pathToNode: [],
}, },
@ -270,14 +290,17 @@ show(theExtrude, sk2)`
function removeGeo(arts: (SketchGroup | ExtrudeGroup)[]): any { function removeGeo(arts: (SketchGroup | ExtrudeGroup)[]): any {
return arts.map((art) => { return arts.map((art) => {
if (art.type === 'extrudeGroup') { if (!art) {
return {}
}
if (art?.type === 'extrudeGroup') {
return { return {
...art, ...art,
value: art.value.map((v) => ({ value: art.value.map((v) => ({
...v, ...v,
__geoMeta: { __geoMeta: {
...v.__geoMeta, ...v.__geoMeta,
geo: v.__geoMeta.geo.type, geo: (v?.__geoMeta as any)?.geo?.type,
}, },
})), })),
} }

View File

@ -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,
}
}

View File

@ -2,33 +2,34 @@ import fs from 'node:fs'
import { abstractSyntaxTree } from './abstractSyntaxTree' import { abstractSyntaxTree } from './abstractSyntaxTree'
import { lexer } from './tokeniser' import { lexer } from './tokeniser'
import { executor, ProgramMemory, Path, SketchGroup } from './executor' import { ProgramMemory, Path, SketchGroup } from './executor'
import { initPromise } from './rust' import { initPromise } from './rust'
import { executor } from '../lib/testHelpers'
beforeAll(() => initPromise) beforeAll(() => initPromise)
describe('test', () => { describe('test executor', () => {
it('test assigning two variables, the second summing with the first', () => { it('test assigning two variables, the second summing with the first', async () => {
const code = `const myVar = 5 const code = `const myVar = 5
const newVar = myVar + 1` const newVar = myVar + 1`
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(5) expect(root.myVar.value).toBe(5)
expect(root.newVar.value).toBe(6) 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 code = `const myVar = "a str"`
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe('a str') 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( const code = fs.readFileSync(
'./src/lang/testExamples/variableDeclaration.cado', './src/lang/testExamples/variableDeclaration.cado',
'utf-8' 'utf-8'
) )
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe('a str another str') expect(root.myVar.value).toBe('a str another str')
}) })
it('test with function call', () => { it('test with function call', async () => {
const code = ` const code = `
const myVar = "hello" const myVar = "hello"
log(5, myVar)` 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, root: programMemoryOverride,
_sketch: [], pendingMemory: {},
}) })
expect(root.myVar.value).toBe('hello') expect(root.myVar.value).toBe('hello')
expect(programMemoryOverride.log.value).toHaveBeenCalledWith(5, 'hello') expect(programMemoryOverride.log.value).toHaveBeenCalledWith(5, 'hello')
}) })
it('fn funcN = () => {} execute', () => { it('fn funcN = () => {} execute', async () => {
const { root } = exe( const { root } = await exe(
[ [
'fn funcN = (a, b) => {', 'fn funcN = (a, b) => {',
' return a + b', ' return a + b',
@ -64,7 +65,7 @@ log(5, myVar)`
expect(root.theVar.value).toBe(60) expect(root.theVar.value).toBe(60)
expect(root.magicNum.value).toBe(69) expect(root.magicNum.value).toBe(69)
}) })
it('sketch declaration', () => { it('sketch declaration', async () => {
let code = `const mySketch = startSketchAt([0,0]) let code = `const mySketch = startSketchAt([0,0])
|> lineTo({to: [0,2], tag: "myPath"}, %) |> lineTo({to: [0,2], tag: "myPath"}, %)
|> lineTo([2,3], %) |> lineTo([2,3], %)
@ -72,7 +73,7 @@ log(5, myVar)`
// |> close(%) // |> close(%)
show(mySketch) 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 // geo is three js buffer geometry and is very bloated to have in tests
const minusGeo = removeGeoFromPaths(root.mySketch.value) const minusGeo = removeGeoFromPaths(root.mySketch.value)
expect(minusGeo).toEqual([ expect(minusGeo).toEqual([
@ -82,6 +83,7 @@ show(mySketch)
from: [0, 0], from: [0, 0],
__geoMeta: { __geoMeta: {
sourceRange: [43, 80], sourceRange: [43, 80],
id: '37333036-3033-4432-b530-643030303837',
pathToNode: [], pathToNode: [],
geos: ['line', 'lineEnd'], geos: ['line', 'lineEnd'],
}, },
@ -93,6 +95,7 @@ show(mySketch)
from: [0, 2], from: [0, 2],
__geoMeta: { __geoMeta: {
sourceRange: [86, 102], sourceRange: [86, 102],
id: '32343136-3330-4134-a462-376437386365',
pathToNode: [], pathToNode: [],
geos: ['line', 'lineEnd'], geos: ['line', 'lineEnd'],
}, },
@ -103,6 +106,7 @@ show(mySketch)
from: [2, 3], from: [2, 3],
__geoMeta: { __geoMeta: {
sourceRange: [108, 151], sourceRange: [108, 151],
id: '32306132-6130-4138-b832-636363326330',
pathToNode: [], pathToNode: [],
geos: ['line', 'lineEnd'], 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 = [ const code = [
'fn myFn = (a) => { return a + 1 }', 'fn myFn = (a) => { return a + 1 }',
'const myVar = 5 + 1 |> myFn(%)', 'const myVar = 5 + 1 |> myFn(%)',
].join('\n') ].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(7) expect(root.myVar.value).toBe(7)
}) })
it('rotated sketch', () => { it('rotated sketch', async () => {
const code = [ const code = [
'const mySk1 = startSketchAt([0,0])', 'const mySk1 = startSketchAt([0,0])',
' |> lineTo([1,1], %)', ' |> lineTo([1,1], %)',
@ -137,7 +141,7 @@ show(mySketch)
' |> lineTo([1, 1], %)', ' |> lineTo([1, 1], %)',
'const rotated = rx(90, mySk1)', 'const rotated = rx(90, mySk1)',
].join('\n') ].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.mySk1.value).toHaveLength(3) expect(root.mySk1.value).toHaveLength(3)
expect(root?.rotated?.type).toBe('sketchGroup') expect(root?.rotated?.type).toBe('sketchGroup')
if ( 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 code = [
'const mySk1 = startSketchAt([0,0])', 'const mySk1 = startSketchAt([0,0])',
' |> lineTo([1,1], %)', ' |> lineTo([1,1], %)',
@ -162,7 +166,7 @@ show(mySketch)
' |> lineTo([1,1], %)', ' |> lineTo([1,1], %)',
' |> rx(90, %)', ' |> rx(90, %)',
].join('\n') ].join('\n')
const { root } = exe(code) const { root } = await exe(code)
const striptVersion = removeGeoFromSketch(root.mySk1 as SketchGroup) const striptVersion = removeGeoFromSketch(root.mySk1 as SketchGroup)
expect(striptVersion).toEqual({ expect(striptVersion).toEqual({
type: 'sketchGroup', type: 'sketchGroup',
@ -171,6 +175,7 @@ show(mySketch)
to: [0, 0], to: [0, 0],
from: [0, 0], from: [0, 0],
__geoMeta: { __geoMeta: {
id: '37663863-3664-4366-a637-623739336334',
sourceRange: [14, 34], sourceRange: [14, 34],
pathToNode: [], pathToNode: [],
geos: ['sketchBase'], geos: ['sketchBase'],
@ -183,6 +188,7 @@ show(mySketch)
from: [0, 0], from: [0, 0],
__geoMeta: { __geoMeta: {
sourceRange: [40, 56], sourceRange: [40, 56],
id: '34356231-3362-4363-b935-393033353034',
pathToNode: [], pathToNode: [],
geos: ['line', 'lineEnd'], geos: ['line', 'lineEnd'],
}, },
@ -193,6 +199,7 @@ show(mySketch)
from: [1, 1], from: [1, 1],
__geoMeta: { __geoMeta: {
sourceRange: [62, 100], sourceRange: [62, 100],
id: '39623339-3538-4366-b633-356630326639',
pathToNode: [], pathToNode: [],
geos: ['line', 'lineEnd'], geos: ['line', 'lineEnd'],
}, },
@ -204,6 +211,7 @@ show(mySketch)
from: [0, 1], from: [0, 1],
__geoMeta: { __geoMeta: {
sourceRange: [106, 122], sourceRange: [106, 122],
id: '30636135-6232-4335-b665-366562303161',
pathToNode: [], pathToNode: [],
geos: ['line', 'lineEnd'], 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( const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
'\n' '\n'
) )
const { root } = exe(code) const { root } = await exe(code)
// TODO path to node is probably wrong here, zero indexes are not correct // TODO path to node is probably wrong here, zero indexes are not correct
expect(root).toEqual({ expect(root).toEqual({
three: { three: {
@ -268,12 +276,12 @@ show(mySketch)
}, },
}) })
}) })
it('execute object expression', () => { it('execute object expression', async () => {
const code = [ const code = [
'const three = 3', 'const three = 3',
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}", "const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
].join('\n') ].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.yo).toEqual({ expect(root.yo).toEqual({
type: 'userVal', type: 'userVal',
value: { aStr: 'str', anum: 2, identifier: 3, binExp: 9 }, 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( const code = ["const yo = {a: {b: '123'}}", "const myVar = yo.a['b']"].join(
'\n' '\n'
) )
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar).toEqual({ expect(root.myVar).toEqual({
type: 'userVal', type: 'userVal',
value: '123', value: '123',
@ -316,96 +324,96 @@ show(mySketch)
}) })
describe('testing math operators', () => { describe('testing math operators', () => {
it('it can sum', () => { it('it can sum', async () => {
const code = ['const myVar = 1 + 2'].join('\n') const code = ['const myVar = 1 + 2'].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(3) expect(root.myVar.value).toBe(3)
}) })
it('it can subtract', () => { it('it can subtract', async () => {
const code = ['const myVar = 1 - 2'].join('\n') const code = ['const myVar = 1 - 2'].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(-1) expect(root.myVar.value).toBe(-1)
}) })
it('it can multiply', () => { it('it can multiply', async () => {
const code = ['const myVar = 1 * 2'].join('\n') const code = ['const myVar = 1 * 2'].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(2) expect(root.myVar.value).toBe(2)
}) })
it('it can divide', () => { it('it can divide', async () => {
const code = ['const myVar = 1 / 2'].join('\n') const code = ['const myVar = 1 / 2'].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(0.5) 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 code = ['const myVar = 5 % 2'].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(1) 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 code = ['const myVar = 1 + 2 * 3'].join('\n')
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(7) 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 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) 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 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) 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 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) 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 code = 'const myVar = min(4, 100) + 2'
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(6) 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 code = 'const myVar = 2 + min(4, 100)'
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(6) 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 code = 'const myVar = 2 + min(100, legLen(5, 3))'
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(6) expect(root.myVar.value).toBe(6)
}) })
it('with unaryExpression', () => { it('with unaryExpression', async () => {
const code = 'const myVar = -min(100, 3)' const code = 'const myVar = -min(100, 3)'
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toBe(-3) 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 code = 'const myVar = min(-legLen(5, 4), 5)'
const code2 = 'const myVar = min(5 , -legLen(5, 4))' const code2 = 'const myVar = min(5 , -legLen(5, 4))'
const { root } = exe(code) const { root } = await exe(code)
const { root: root2 } = exe(code2) const { root: root2 } = await exe(code2)
expect(root.myVar.value).toBe(-3) expect(root.myVar.value).toBe(-3)
expect(root.myVar.value).toBe(root2.myVar.value) 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 code = 'const myVar = [1,-legLen(5, 4)]'
const { root } = exe(code) const { root } = await exe(code)
expect(root.myVar.value).toEqual([1, -3]) 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 code = [
'const part001 = startSketchAt([0, 0])', 'const part001 = startSketchAt([0, 0])',
'|> line([-2.21, -legLen(5, min(3, 999))], %)', '|> line([-2.21, -legLen(5, min(3, 999))], %)',
].join('\n') ].join('\n')
const { root } = exe(code) const { root } = await exe(code)
const sketch = removeGeoFromSketch(root.part001 as SketchGroup) const sketch = removeGeoFromSketch(root.part001 as SketchGroup)
// result of `-legLen(5, min(3, 999))` should be -4 // result of `-legLen(5, min(3, 999))` should be -4
const yVal = sketch.value?.[0]?.to?.[1] const yVal = sketch.value?.[0]?.to?.[1]
expect(yVal).toBe(-4) 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 code = [
`const myVar = 3`, `const myVar = 3`,
`const part001 = startSketchAt([0, 0])`, `const part001 = startSketchAt([0, 0])`,
@ -417,7 +425,7 @@ describe('testing math operators', () => {
``, ``,
`show(part001)`, `show(part001)`,
].join('\n') ].join('\n')
const { root } = exe(code) const { root } = await exe(code)
const sketch = removeGeoFromSketch(root.part001 as SketchGroup) const sketch = removeGeoFromSketch(root.part001 as SketchGroup)
// expect -legLen(segLen('seg01', %), myVar) to equal -4 setting the y value back to 0 // expect -legLen(segLen('seg01', %), myVar) to equal -4 setting the y value back to 0
expect(sketch.value?.[1]?.from).toEqual([3, 4]) expect(sketch.value?.[1]?.from).toEqual([3, 4])
@ -426,29 +434,31 @@ describe('testing math operators', () => {
`-legLen(segLen('seg01', %), myVar)`, `-legLen(segLen('seg01', %), myVar)`,
`legLen(segLen('seg01', %), myVar)` `legLen(segLen('seg01', %), myVar)`
) )
const { root: removedUnaryExpRoot } = exe(removedUnaryExp) const { root: removedUnaryExpRoot } = await exe(removedUnaryExp)
const removedUnaryExpRootSketch = removeGeoFromSketch( const removedUnaryExpRootSketch = removeGeoFromSketch(
removedUnaryExpRoot.part001 as SketchGroup removedUnaryExpRoot.part001 as SketchGroup
) )
// without the minus sign, the y value should be 8 // without the minus sign, the y value should be 8
expect(removedUnaryExpRootSketch.value?.[1]?.to).toEqual([6, 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 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) expect(root.myVar.value).toBe(5)
}) })
}) })
// helpers // helpers
function exe( async function exe(
code: string, code: string,
programMemory: ProgramMemory = { root: {}, _sketch: [] } programMemory: ProgramMemory = { root: {}, pendingMemory: {} }
) { ) {
const tokens = lexer(code) const tokens = lexer(code)
const ast = abstractSyntaxTree(tokens) const ast = abstractSyntaxTree(tokens)
return executor(ast, programMemory)
const result = await executor(ast, programMemory)
return result
} }
function removeGeoFromSketch(sketch: SketchGroup): SketchGroup { function removeGeoFromSketch(sketch: SketchGroup): SketchGroup {

View File

@ -12,7 +12,11 @@ import {
} from './abstractSyntaxTree' } from './abstractSyntaxTree'
import { InternalFnNames } from './std/stdTypes' import { InternalFnNames } from './std/stdTypes'
import { internalFns } from './std/std' import { internalFns } from './std/std'
import { BufferGeometry } from 'three' import {
EngineCommandManager,
ArtifactMap,
SourceRangeMap,
} from './std/engineConnection'
export type SourceRange = [number, number] export type SourceRange = [number, number]
export type PathToNode = [string | number, string][] // [pathKey, nodeType][] export type PathToNode = [string | number, string][] // [pathKey, nodeType][]
@ -28,8 +32,8 @@ interface BasePath {
to: [number, number] to: [number, number]
name?: string name?: string
__geoMeta: { __geoMeta: {
id: string
geos: { geos: {
geo: BufferGeometry
type: 'line' | 'lineEnd' | 'sketchBase' type: 'line' | 'lineEnd' | 'sketchBase'
}[] }[]
sourceRange: SourceRange sourceRange: SourceRange
@ -59,7 +63,8 @@ export interface AngledLineTo extends BasePath {
interface GeoMeta { interface GeoMeta {
__geoMeta: { __geoMeta: {
geo: BufferGeometry id: string
refId?: string
sourceRange: SourceRange sourceRange: SourceRange
pathToNode: PathToNode pathToNode: PathToNode
} }
@ -69,6 +74,7 @@ export type Path = ToPoint | HorizontalLineTo | AngledLineTo | Base
export interface SketchGroup { export interface SketchGroup {
type: 'sketchGroup' type: 'sketchGroup'
id: string
value: Path[] value: Path[]
start?: Base start?: Base
position: Position position: Position
@ -102,33 +108,116 @@ export interface UserVal {
__meta: Metadata[] __meta: Metadata[]
} }
type MemoryItem = UserVal | SketchGroup | ExtrudeGroup
interface Memory { interface Memory {
[key: string]: UserVal | SketchGroup | ExtrudeGroup // | Memory [key: string]: MemoryItem
}
interface PendingMemory {
[key: string]: Promise<MemoryItem>
} }
export interface ProgramMemory { export interface ProgramMemory {
root: Memory root: Memory
pendingMemory: Partial<PendingMemory>
return?: Identifier[] return?: Identifier[]
_sketch?: Path[]
} }
export const executor = ( const addItemToMemory = (
programMemory: ProgramMemory,
key: string,
value: MemoryItem | Promise<MemoryItem>
) => {
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<MemoryItem> => {
if (programMemory.root[key]) {
return programMemory.root[key]
}
if (programMemory.pendingMemory[key]) {
return programMemory.pendingMemory[key] as Promise<MemoryItem>
}
throw new Error(`Memory item ${key} not found`)
}
export const executor = async (
node: Program, 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<ProgramMemory> => {
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' }, options: { bodyType: 'root' | 'sketch' | 'block' } = { bodyType: 'root' },
previousPathToNode: PathToNode = [] previousPathToNode: PathToNode = []
): ProgramMemory => { ): Promise<ProgramMemory> => {
const _programMemory: ProgramMemory = { let _programMemory: ProgramMemory = {
root: { root: {
...programMemory.root, ...programMemory.root,
}, },
_sketch: [], pendingMemory: {
...programMemory.pendingMemory,
},
return: programMemory.return, return: programMemory.return,
} }
const { body } = node const { body } = node
body.forEach((statement, bodyIndex) => { const proms: Promise<any>[] = []
body.forEach(async (statement, bodyIndex) => {
if (statement.type === 'VariableDeclaration') { if (statement.type === 'VariableDeclaration') {
statement.declarations.forEach((declaration, index) => { statement.declarations.forEach(async (declaration, index) => {
const variableName = declaration.id.name const variableName = declaration.id.name
const pathToNode: PathToNode = [ const pathToNode: PathToNode = [
...previousPathToNode, ...previousPathToNode,
@ -150,73 +239,115 @@ export const executor = (
] ]
if (declaration.init.type === 'PipeExpression') { if (declaration.init.type === 'PipeExpression') {
const value = getPipeExpressionResult( const prom = getPipeExpressionResult(
declaration.init, declaration.init,
_programMemory, _programMemory,
engineCommandManager,
pathToNode pathToNode
) )
proms.push(prom)
const value = await prom
if (value?.type === 'sketchGroup' || value?.type === 'extrudeGroup') { if (value?.type === 'sketchGroup' || value?.type === 'extrudeGroup') {
_programMemory.root[variableName] = value _programMemory = addItemToMemory(
_programMemory,
variableName,
value
)
} else { } else {
_programMemory.root[variableName] = { _programMemory = addItemToMemory(_programMemory, variableName, {
type: 'userVal', type: 'userVal',
value, value,
__meta, __meta,
} })
} }
} else if (declaration.init.type === 'Identifier') { } else if (declaration.init.type === 'Identifier') {
_programMemory.root[variableName] = { _programMemory = addItemToMemory(_programMemory, variableName, {
type: 'userVal', type: 'userVal',
value: _programMemory.root[declaration.init.name].value, value: _programMemory.root[declaration.init.name].value,
__meta, __meta,
} })
} else if (declaration.init.type === 'Literal') { } else if (declaration.init.type === 'Literal') {
_programMemory.root[variableName] = { _programMemory = addItemToMemory(_programMemory, variableName, {
type: 'userVal', type: 'userVal',
value: declaration.init.value, value: declaration.init.value,
__meta, __meta,
} })
} else if (declaration.init.type === 'BinaryExpression') { } else if (declaration.init.type === 'BinaryExpression') {
_programMemory.root[variableName] = { const prom = getBinaryExpressionResult(
declaration.init,
_programMemory,
engineCommandManager
)
proms.push(prom)
_programMemory = addItemToMemory(
_programMemory,
variableName,
promisifyMemoryItem({
type: 'userVal', type: 'userVal',
value: getBinaryExpressionResult(declaration.init, _programMemory), value: prom,
__meta, __meta,
} })
)
} else if (declaration.init.type === 'UnaryExpression') { } else if (declaration.init.type === 'UnaryExpression') {
_programMemory.root[variableName] = { const prom = getUnaryExpressionResult(
declaration.init,
_programMemory,
engineCommandManager
)
proms.push(prom)
_programMemory = addItemToMemory(
_programMemory,
variableName,
promisifyMemoryItem({
type: 'userVal', type: 'userVal',
value: getUnaryExpressionResult(declaration.init, _programMemory), value: prom,
__meta, __meta,
} })
)
} else if (declaration.init.type === 'ArrayExpression') { } else if (declaration.init.type === 'ArrayExpression') {
const valueInfo: { value: any; __meta?: Metadata }[] = const valueInfo: Promise<{ value: any; __meta?: Metadata }>[] =
declaration.init.elements.map( declaration.init.elements.map(
(element): { value: any; __meta?: Metadata } => { async (element): Promise<{ value: any; __meta?: Metadata }> => {
if (element.type === 'Literal') { if (element.type === 'Literal') {
return { return {
value: element.value, value: element.value,
} }
} else if (element.type === 'BinaryExpression') { } else if (element.type === 'BinaryExpression') {
return { const prom = getBinaryExpressionResult(
value: getBinaryExpressionResult(element, _programMemory),
}
} else if (element.type === 'PipeExpression') {
return {
value: getPipeExpressionResult(
element, element,
_programMemory, _programMemory,
engineCommandManager
)
proms.push(prom)
return {
value: await prom,
}
} else if (element.type === 'PipeExpression') {
const prom = getPipeExpressionResult(
element,
_programMemory,
engineCommandManager,
pathToNode pathToNode
), )
proms.push(prom)
return {
value: await prom,
} }
} else if (element.type === 'Identifier') { } else if (element.type === 'Identifier') {
const node = _programMemory.root[element.name] const node = await getMemoryItem(_programMemory, element.name)
return { return {
value: node.value, value: node.value,
__meta: node.__meta[node.__meta.length - 1], __meta: node.__meta[node.__meta.length - 1],
} }
} else if (element.type === 'UnaryExpression') { } else if (element.type === 'UnaryExpression') {
const prom = getUnaryExpressionResult(
element,
_programMemory,
engineCommandManager
)
proms.push(prom)
return { return {
value: getUnaryExpressionResult(element, _programMemory), value: await prom,
} }
} else { } else {
throw new Error( throw new Error(
@ -225,31 +356,47 @@ export const executor = (
} }
} }
) )
const meta = valueInfo const awaitedValueInfo = await Promise.all(valueInfo)
const meta = awaitedValueInfo
.filter(({ __meta }) => __meta) .filter(({ __meta }) => __meta)
.map(({ __meta }) => __meta) as Metadata[] .map(({ __meta }) => __meta) as Metadata[]
_programMemory.root[variableName] = { _programMemory = addItemToMemory(_programMemory, variableName, {
type: 'userVal', type: 'userVal',
value: valueInfo.map(({ value }) => value), value: awaitedValueInfo.map(({ value }) => value),
__meta: [...__meta, ...meta], __meta: [...__meta, ...meta],
} })
} else if (declaration.init.type === 'ObjectExpression') { } else if (declaration.init.type === 'ObjectExpression') {
_programMemory.root[variableName] = { const prom = executeObjectExpression(
_programMemory,
declaration.init,
engineCommandManager
)
proms.push(prom)
_programMemory = addItemToMemory(
_programMemory,
variableName,
promisifyMemoryItem({
type: 'userVal', type: 'userVal',
value: executeObjectExpression(_programMemory, declaration.init), value: prom,
__meta, __meta,
} })
)
} else if (declaration.init.type === 'FunctionExpression') { } else if (declaration.init.type === 'FunctionExpression') {
const fnInit = declaration.init const fnInit = declaration.init
_programMemory.root[declaration.id.name] = { _programMemory = addItemToMemory(
_programMemory,
declaration.id.name,
{
type: 'userVal', type: 'userVal',
value: (...args: any[]) => { value: async (...args: any[]) => {
const fnMemory: ProgramMemory = { let fnMemory: ProgramMemory = {
root: { root: {
..._programMemory.root, ..._programMemory.root,
}, },
_sketch: [], pendingMemory: {
..._programMemory.pendingMemory,
},
} }
if (args.length > fnInit.params.length) { if (args.length > fnInit.params.length) {
throw new Error( throw new Error(
@ -261,37 +408,64 @@ export const executor = (
) )
} }
fnInit.params.forEach((param, index) => { fnInit.params.forEach((param, index) => {
fnMemory.root[param.name] = { fnMemory = addItemToMemory(fnMemory, param.name, {
type: 'userVal', type: 'userVal',
value: args[index], value: args[index],
__meta, __meta,
}
}) })
return executor(fnInit.body, fnMemory, { bodyType: 'block' }) })
.return const prom = _executor(
fnInit.body,
fnMemory,
engineCommandManager,
{
bodyType: 'block',
}
)
proms.push(prom)
const result = (await prom).return
return result
}, },
__meta, __meta,
} }
)
} else if (declaration.init.type === 'MemberExpression') { } else if (declaration.init.type === 'MemberExpression') {
_programMemory.root[variableName] = { 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', type: 'userVal',
value: getMemberExpressionResult(declaration.init, _programMemory), value: prom,
__meta, __meta,
} })
)
} else if (declaration.init.type === 'CallExpression') { } else if (declaration.init.type === 'CallExpression') {
const result = executeCallExpression( const prom = executeCallExpression(
_programMemory, _programMemory,
declaration.init, declaration.init,
engineCommandManager,
previousPathToNode previousPathToNode
) )
_programMemory.root[variableName] = proms.push(prom)
result?.type === 'sketchGroup' || result?.type === 'extrudeGroup' _programMemory = addItemToMemory(
? result _programMemory,
variableName,
prom.then((a) => {
return a?.type === 'sketchGroup' || a?.type === 'extrudeGroup'
? a
: { : {
type: 'userVal', type: 'userVal',
value: result, value: a,
__meta, __meta,
} }
})
)
} else { } else {
throw new Error( throw new Error(
'Unsupported declaration type: ' + declaration.init.type 'Unsupported declaration type: ' + declaration.init.type
@ -306,7 +480,7 @@ export const executor = (
if (arg.type === 'Literal') { if (arg.type === 'Literal') {
return arg.value return arg.value
} else if (arg.type === 'Identifier') { } else if (arg.type === 'Identifier') {
return _programMemory.root[arg.name].value return _programMemory.root[arg.name]?.value
} }
}) })
if ('show' === functionName) { if ('show' === functionName) {
@ -320,13 +494,17 @@ export const executor = (
} }
} else if (statement.type === 'ReturnStatement') { } else if (statement.type === 'ReturnStatement') {
if (statement.argument.type === 'BinaryExpression') { if (statement.argument.type === 'BinaryExpression') {
_programMemory.return = getBinaryExpressionResult( const prom = getBinaryExpressionResult(
statement.argument, statement.argument,
_programMemory _programMemory,
engineCommandManager
) )
proms.push(prom)
_programMemory.return = await prom
} }
} }
}) })
await Promise.all(proms)
return _programMemory return _programMemory
} }
@ -342,13 +520,14 @@ function getMemberExpressionResult(
const object: any = const object: any =
expression.object.type === 'MemberExpression' expression.object.type === 'MemberExpression'
? getMemberExpressionResult(expression.object, programMemory) ? getMemberExpressionResult(expression.object, programMemory)
: programMemory.root[expression.object.name].value : programMemory.root[expression.object.name]?.value
return object[propertyName] return object?.[propertyName]
} }
function getBinaryExpressionResult( async function getBinaryExpressionResult(
expression: BinaryExpression, expression: BinaryExpression,
programMemory: ProgramMemory, programMemory: ProgramMemory,
engineCommandManager: EngineCommandManager,
pipeInfo: { pipeInfo: {
isInPipe: boolean isInPipe: boolean
previousResults: any[] previousResults: any[]
@ -366,8 +545,18 @@ function getBinaryExpressionResult(
...pipeInfo, ...pipeInfo,
isInPipe: false, isInPipe: false,
} }
const left = getBinaryPartResult(expression.left, programMemory, _pipeInfo) const left = await getBinaryPartResult(
const right = getBinaryPartResult(expression.right, programMemory, _pipeInfo) 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 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 if (expression.operator === '%') return left % right
} }
function getBinaryPartResult( async function getBinaryPartResult(
part: BinaryPart, part: BinaryPart,
programMemory: ProgramMemory, programMemory: ProgramMemory,
engineCommandManager: EngineCommandManager,
pipeInfo: { pipeInfo: {
isInPipe: boolean isInPipe: boolean
previousResults: any[] previousResults: any[]
@ -390,7 +580,7 @@ function getBinaryPartResult(
expressionIndex: 0, expressionIndex: 0,
body: [], body: [],
} }
): any { ): Promise<any> {
const _pipeInfo = { const _pipeInfo = {
...pipeInfo, ...pipeInfo,
isInPipe: false, isInPipe: false,
@ -400,15 +590,30 @@ function getBinaryPartResult(
} else if (part.type === 'Identifier') { } else if (part.type === 'Identifier') {
return programMemory.root[part.name].value return programMemory.root[part.name].value
} else if (part.type === 'BinaryExpression') { } 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') { } 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, expression: UnaryExpression,
programMemory: ProgramMemory, programMemory: ProgramMemory,
engineCommandManager: EngineCommandManager,
pipeInfo: { pipeInfo: {
isInPipe: boolean isInPipe: boolean
previousResults: any[] previousResults: any[]
@ -422,50 +627,64 @@ function getUnaryExpressionResult(
body: [], body: [],
} }
) { ) {
return -getBinaryPartResult(expression.argument, programMemory, { return -(await getBinaryPartResult(
expression.argument,
programMemory,
engineCommandManager,
{
...pipeInfo, ...pipeInfo,
isInPipe: false, isInPipe: false,
}) }
))
} }
function getPipeExpressionResult( async function getPipeExpressionResult(
expression: PipeExpression, expression: PipeExpression,
programMemory: ProgramMemory, programMemory: ProgramMemory,
engineCommandManager: EngineCommandManager,
previousPathToNode: PathToNode = [] previousPathToNode: PathToNode = []
) { ) {
const executedBody = executePipeBody( const executedBody = await executePipeBody(
expression.body, expression.body,
programMemory, programMemory,
engineCommandManager,
previousPathToNode previousPathToNode
) )
const result = executedBody[executedBody.length - 1] const result = executedBody[executedBody.length - 1]
return result return result
} }
function executePipeBody( async function executePipeBody(
body: PipeExpression['body'], body: PipeExpression['body'],
programMemory: ProgramMemory, programMemory: ProgramMemory,
engineCommandManager: EngineCommandManager,
previousPathToNode: PathToNode = [], previousPathToNode: PathToNode = [],
expressionIndex = 0, expressionIndex = 0,
previousResults: any[] = [] previousResults: any[] = []
): any[] { ): Promise<any[]> {
if (expressionIndex === body.length) { if (expressionIndex === body.length) {
return previousResults return previousResults
} }
const expression = body[expressionIndex] const expression = body[expressionIndex]
if (expression.type === 'BinaryExpression') { if (expression.type === 'BinaryExpression') {
const result = getBinaryExpressionResult(expression, programMemory) const result = await getBinaryExpressionResult(
expression,
programMemory,
engineCommandManager
)
return executePipeBody( return executePipeBody(
body, body,
programMemory, programMemory,
engineCommandManager,
previousPathToNode, previousPathToNode,
expressionIndex + 1, expressionIndex + 1,
[...previousResults, result] [...previousResults, result]
) )
} else if (expression.type === 'CallExpression') { } else if (expression.type === 'CallExpression') {
return executeCallExpression( return await executeCallExpression(
programMemory, programMemory,
expression, expression,
engineCommandManager,
previousPathToNode, previousPathToNode,
{ {
isInPipe: true, isInPipe: true,
@ -479,9 +698,10 @@ function executePipeBody(
throw new Error('Invalid pipe expression') throw new Error('Invalid pipe expression')
} }
function executeObjectExpression( async function executeObjectExpression(
_programMemory: ProgramMemory, _programMemory: ProgramMemory,
objExp: ObjectExpression, objExp: ObjectExpression,
engineCommandManager: EngineCommandManager,
pipeInfo: { pipeInfo: {
isInPipe: boolean isInPipe: boolean
previousResults: any[] previousResults: any[]
@ -500,43 +720,67 @@ function executeObjectExpression(
isInPipe: false, isInPipe: false,
} }
const obj: { [key: string]: any } = {} const obj: { [key: string]: any } = {}
objExp.properties.forEach((property) => { const proms: Promise<any>[] = []
objExp.properties.forEach(async (property) => {
if (property.type === 'ObjectProperty') { if (property.type === 'ObjectProperty') {
if (property.value.type === 'Literal') { if (property.value.type === 'Literal') {
obj[property.key.name] = property.value.value obj[property.key.name] = property.value.value
} else if (property.value.type === 'BinaryExpression') { } else if (property.value.type === 'BinaryExpression') {
obj[property.key.name] = getBinaryExpressionResult( const prom = getBinaryExpressionResult(
property.value, property.value,
_programMemory, _programMemory,
engineCommandManager,
_pipeInfo _pipeInfo
) )
proms.push(prom)
obj[property.key.name] = await prom
} else if (property.value.type === 'PipeExpression') { } else if (property.value.type === 'PipeExpression') {
obj[property.key.name] = getPipeExpressionResult( const prom = getPipeExpressionResult(
property.value, property.value,
_programMemory _programMemory,
engineCommandManager
) )
proms.push(prom)
obj[property.key.name] = await prom
} else if (property.value.type === 'Identifier') { } 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') { } else if (property.value.type === 'ObjectExpression') {
obj[property.key.name] = executeObjectExpression( const prom = 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(
_programMemory, _programMemory,
property.value, 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 _pipeInfo
) )
proms.push(prom)
const result = await prom
obj[property.key.name] = result
} else if (property.value.type === 'UnaryExpression') { } else if (property.value.type === 'UnaryExpression') {
obj[property.key.name] = getUnaryExpressionResult( const prom = getUnaryExpressionResult(
property.value, property.value,
_programMemory _programMemory,
engineCommandManager
) )
proms.push(prom)
obj[property.key.name] = await prom
} else { } else {
throw new Error( throw new Error(
`Unexpected property type ${property.value.type} in object expression` `Unexpected property type ${property.value.type} in object expression`
@ -548,12 +792,14 @@ function executeObjectExpression(
) )
} }
}) })
await Promise.all(proms)
return obj return obj
} }
function executeArrayExpression( async function executeArrayExpression(
_programMemory: ProgramMemory, _programMemory: ProgramMemory,
arrExp: ArrayExpression, arrExp: ArrayExpression,
engineCommandManager: EngineCommandManager,
pipeInfo: { pipeInfo: {
isInPipe: boolean isInPipe: boolean
previousResults: any[] previousResults: any[]
@ -571,36 +817,50 @@ function executeArrayExpression(
...pipeInfo, ...pipeInfo,
isInPipe: false, isInPipe: false,
} }
return arrExp.elements.map((el) => { return await Promise.all(
arrExp.elements.map((el) => {
if (el.type === 'Literal') { if (el.type === 'Literal') {
return el.value return el.value
} else if (el.type === 'Identifier') { } else if (el.type === 'Identifier') {
return _programMemory.root?.[el.name]?.value return _programMemory.root?.[el.name]?.value
} else if (el.type === 'BinaryExpression') { } else if (el.type === 'BinaryExpression') {
return getBinaryExpressionResult(el, _programMemory, _pipeInfo) return getBinaryExpressionResult(
el,
_programMemory,
engineCommandManager,
_pipeInfo
)
} else if (el.type === 'ObjectExpression') { } else if (el.type === 'ObjectExpression') {
return executeObjectExpression(_programMemory, el) return executeObjectExpression(_programMemory, el, engineCommandManager)
} else if (el.type === 'CallExpression') { } else if (el.type === 'CallExpression') {
const result: any = executeCallExpression( const result: any = executeCallExpression(
_programMemory, _programMemory,
el, el,
engineCommandManager,
[], [],
_pipeInfo _pipeInfo
) )
return result return result
} else if (el.type === 'UnaryExpression') { } else if (el.type === 'UnaryExpression') {
return getUnaryExpressionResult(el, _programMemory, { return getUnaryExpressionResult(
el,
_programMemory,
engineCommandManager,
{
...pipeInfo, ...pipeInfo,
isInPipe: false, isInPipe: false,
}) }
)
} }
throw new Error('Invalid argument type') throw new Error('Invalid argument type')
}) })
)
} }
function executeCallExpression( async function executeCallExpression(
programMemory: ProgramMemory, programMemory: ProgramMemory,
expression: CallExpression, expression: CallExpression,
engineCommandManager: EngineCommandManager,
previousPathToNode: PathToNode = [], previousPathToNode: PathToNode = [],
pipeInfo: { pipeInfo: {
isInPipe: boolean isInPipe: boolean
@ -627,59 +887,87 @@ function executeCallExpression(
...pipeInfo, ...pipeInfo,
isInPipe: false, isInPipe: false,
} }
const fnArgs = expression?.arguments?.map((arg) => { const fnArgs = await Promise.all(
expression?.arguments?.map(async (arg) => {
if (arg.type === 'Literal') { if (arg.type === 'Literal') {
return arg.value return arg.value
} else if (arg.type === 'Identifier') { } else if (arg.type === 'Identifier') {
const temp = programMemory.root[arg.name] 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 return temp?.type === 'userVal' ? temp.value : temp
} else if (arg.type === 'PipeSubstitution') { } else if (arg.type === 'PipeSubstitution') {
return previousResults[expressionIndex - 1] return previousResults[expressionIndex - 1]
} else if (arg.type === 'ArrayExpression') { } else if (arg.type === 'ArrayExpression') {
return executeArrayExpression(programMemory, arg, pipeInfo) return await executeArrayExpression(
} else if (arg.type === 'CallExpression') {
const result: any = executeCallExpression(
programMemory, programMemory,
arg, arg,
engineCommandManager,
pipeInfo
)
} else if (arg.type === 'CallExpression') {
const result: any = await executeCallExpression(
programMemory,
arg,
engineCommandManager,
previousPathToNode, previousPathToNode,
_pipeInfo _pipeInfo
) )
return result return result
} else if (arg.type === 'ObjectExpression') { } else if (arg.type === 'ObjectExpression') {
return executeObjectExpression(programMemory, arg, _pipeInfo) return await executeObjectExpression(
programMemory,
arg,
engineCommandManager,
_pipeInfo
)
} else if (arg.type === 'UnaryExpression') { } else if (arg.type === 'UnaryExpression') {
return getUnaryExpressionResult(arg, programMemory, _pipeInfo) return getUnaryExpressionResult(
arg,
programMemory,
engineCommandManager,
_pipeInfo
)
} else if (arg.type === 'BinaryExpression') { } else if (arg.type === 'BinaryExpression') {
return getBinaryExpressionResult(arg, programMemory, _pipeInfo) return getBinaryExpressionResult(
arg,
programMemory,
engineCommandManager,
_pipeInfo
)
} }
throw new Error('Invalid argument type in function call') throw new Error('Invalid argument type in function call')
}) })
)
if (functionName in internalFns) { if (functionName in internalFns) {
const fnNameWithSketchOrExtrude = functionName as InternalFnNames const fnNameWithSketchOrExtrude = functionName as InternalFnNames
const result = internalFns[fnNameWithSketchOrExtrude]( const result = await internalFns[fnNameWithSketchOrExtrude](
{ {
programMemory, programMemory,
sourceRange: sourceRangeOverride || [expression.start, expression.end], sourceRange: sourceRangeOverride || [expression.start, expression.end],
engineCommandManager,
code: JSON.stringify(expression),
}, },
fnArgs[0], fnArgs[0],
fnArgs[1], fnArgs[1],
fnArgs[2] fnArgs[2]
) )
return isInPipe return isInPipe
? executePipeBody( ? await executePipeBody(
body, body,
programMemory, programMemory,
engineCommandManager,
previousPathToNode, previousPathToNode,
expressionIndex + 1, expressionIndex + 1,
[...previousResults, result] [...previousResults, result]
) )
: result : result
} }
const result = programMemory.root[functionName].value(...fnArgs) const result = await programMemory.root[functionName].value(...fnArgs)
return isInPipe return isInPipe
? executePipeBody( ? await executePipeBody(
body, body,
programMemory, programMemory,
engineCommandManager,
previousPathToNode, previousPathToNode,
expressionIndex + 1, expressionIndex + 1,
[...previousResults, result] [...previousResults, result]

View File

@ -16,7 +16,7 @@ import {
import { recast } from './recast' import { recast } from './recast'
import { lexer } from './tokeniser' import { lexer } from './tokeniser'
import { initPromise } from './rust' import { initPromise } from './rust'
import { executor } from './executor' import { executor } from '../lib/testHelpers'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -194,9 +194,9 @@ const part001 = startSketchAt([-1.2, 4.83])
const yo = 5 + 6 const yo = 5 + 6
const yo2 = hmm([identifierGuy + 5]) const yo2 = hmm([identifierGuy + 5])
show(part001)` 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 ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const startIndex = code.indexOf('100 + 100') + 1 const startIndex = code.indexOf('100 + 100') + 1
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
ast, ast,
@ -208,9 +208,9 @@ show(part001)`
expect(newCode).toContain(`const newVar = 100 + 100`) expect(newCode).toContain(`const newVar = 100 + 100`)
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) 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 ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const startIndex = code.indexOf('2.8') + 1 const startIndex = code.indexOf('2.8') + 1
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
ast, ast,
@ -222,9 +222,9 @@ show(part001)`
expect(newCode).toContain(`const newVar = 2.8`) expect(newCode).toContain(`const newVar = 2.8`)
expect(newCode).toContain(`line([newVar, 0], %)`) 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 ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const startIndex = code.indexOf('def(') const startIndex = code.indexOf('def(')
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
ast, ast,
@ -236,9 +236,9 @@ show(part001)`
expect(newCode).toContain(`const newVar = def('yo')`) expect(newCode).toContain(`const newVar = def('yo')`)
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) 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 ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const startIndex = code.indexOf('jkl(') + 1 const startIndex = code.indexOf('jkl(') + 1
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
ast, ast,
@ -250,9 +250,9 @@ show(part001)`
expect(newCode).toContain(`const newVar = jkl('yo') + 2`) expect(newCode).toContain(`const newVar = jkl('yo') + 2`)
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) 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 ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const startIndex = code.indexOf('identifierGuy +') + 1 const startIndex = code.indexOf('identifierGuy +') + 1
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
ast, ast,

View File

@ -7,7 +7,7 @@ import {
} from './queryAst' } from './queryAst'
import { lexer } from './tokeniser' import { lexer } from './tokeniser'
import { initPromise } from './rust' import { initPromise } from './rust'
import { executor } from './executor' import { executor } from '../lib/testHelpers'
import { import {
createArrayExpression, createArrayExpression,
createCallExpression, createCallExpression,
@ -19,7 +19,7 @@ import { recast } from './recast'
beforeAll(() => initPromise) beforeAll(() => initPromise)
describe('findAllPreviousVariables', () => { describe('findAllPreviousVariables', () => {
it('should find all previous variables', () => { it('should find all previous variables', async () => {
const code = `const baseThick = 1 const code = `const baseThick = 1
const armAngle = 60 const armAngle = 60
@ -38,7 +38,7 @@ const variableBelowShouldNotBeIncluded = 3
show(part001)` show(part001)`
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7 const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
const ast = abstractSyntaxTree(lexer(code)) const ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const { variables, bodyPath, insertIndex } = findAllPreviousVariables( const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
ast, ast,

View File

@ -64,7 +64,7 @@ log(5, myVar)`
const recasted = recast(ast) const recasted = recast(ast)
expect(recasted).toBe(code) expect(recasted).toBe(code)
}) })
it('sketch declaration', () => { it('recast sketch declaration', () => {
let code = `const mySketch = startSketchAt([0, 0]) let code = `const mySketch = startSketchAt([0, 0])
|> lineTo({ to: [0, 1], tag: "myPath" }, %) |> lineTo({ to: [0, 1], tag: "myPath" }, %)
|> lineTo([1, 1], %) |> lineTo([1, 1], %)

View File

@ -0,0 +1,331 @@
import { SourceRange } from '../executor'
import { Selections } from '../../useStore'
interface ResultCommand {
type: 'result'
data: any
}
interface PendingCommand {
type: 'pending'
promise: Promise<any>
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<any> {
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<any> {
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,
}
}
}

View File

@ -1,3 +1,4 @@
import { v4 as uuidv4 } from 'uuid'
import { InternalFn } from './stdTypes' import { InternalFn } from './stdTypes'
import { import {
ExtrudeGroup, ExtrudeGroup,
@ -6,68 +7,56 @@ import {
Position, Position,
Rotation, Rotation,
} from '../executor' } from '../executor'
import { Quaternion, Vector3 } from 'three'
import { clockwiseSign } from './std' import { clockwiseSign } from './std'
import { extrudeGeo } from '../engine' import { generateUuidFromHashSeed } from '../../lib/uuid'
export const extrude: InternalFn = ( export const extrude: InternalFn = (
{ sourceRange }, { sourceRange, engineCommandManager, code },
length: number, length: number,
sketchVal: SketchGroup sketchVal: SketchGroup
): ExtrudeGroup => { ): ExtrudeGroup => {
const getSketchGeo = (sketchVal: SketchGroup): SketchGroup => { const sketch = sketchVal
return sketchVal
}
const sketch = getSketchGeo(sketchVal)
const { position, rotation } = sketchVal const { position, rotation } = sketchVal
const id = generateUuidFromHashSeed(
JSON.stringify({
code,
sourceRange,
date: {
length,
sketchVal,
},
})
)
const extrudeSurfaces: ExtrudeSurface[] = [] const extrudeSurfaces: ExtrudeSurface[] = []
const extrusionDirection = clockwiseSign(sketch.value.map((line) => line.to)) const extrusionDirection = clockwiseSign(sketch.value.map((line) => line.to))
sketch.value.map((line, index) => { engineCommandManager.sendModellingCommand({
if (line.type === 'toPoint') { id,
let from: [number, number] = line.from params: [
const to = line.to {
const {
geo,
position: facePosition,
rotation: faceRotation,
} = extrudeGeo({
from: [from[0], from[1], 0],
to: [to[0], to[1], 0],
length, length,
extrusionDirection, extrusionDirection: 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,
}, },
],
range: sourceRange,
command: {
type: 'ModelingCmdReq',
cmd: {
Extrude: {
target: sketch.id,
distance: length,
cap: true,
} }
line.name && (surface.name = line.name) },
extrudeSurfaces.push(surface) cmd_id: id,
} file_id: uuidv4(),
},
}) })
return { return {
type: 'extrudeGroup', type: 'extrudeGroup',
value: extrudeSurfaces, value: extrudeSurfaces, // TODO, this is just an empty array now, should be deleted.
height: length, height: length,
position, position,
rotation, rotation,
@ -92,7 +81,7 @@ export const getExtrudeWallTransform: InternalFn = (
position: Position position: Position
quaternion: Rotation 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}`) if (!path) throw new Error(`Could not find path with name ${pathName}`)
return { return {
position: path.position, position: path.position,

View File

@ -9,7 +9,7 @@ import { lexer } from '../tokeniser'
import { abstractSyntaxTree } from '../abstractSyntaxTree' import { abstractSyntaxTree } from '../abstractSyntaxTree'
import { getNodePathFromSourceRange } from '../queryAst' import { getNodePathFromSourceRange } from '../queryAst'
import { recast } from '../recast' import { recast } from '../recast'
import { executor } from '../executor' import { executor } from '../../lib/testHelpers'
import { initPromise } from '../rust' import { initPromise } from '../rust'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -96,7 +96,7 @@ describe('testing getXComponent', () => {
describe('testing changeSketchArguments', () => { describe('testing changeSketchArguments', () => {
const lineToChange = 'lineTo([-1.59, -1.54], %)' const lineToChange = 'lineTo([-1.59, -1.54], %)'
const lineAfterChange = 'lineTo([2, 3], %)' const lineAfterChange = 'lineTo([2, 3], %)'
test('changeSketchArguments', () => { test('changeSketchArguments', async () => {
const genCode = (line: string) => ` const genCode = (line: string) => `
const mySketch001 = startSketchAt([0, 0]) const mySketch001 = startSketchAt([0, 0])
|> ${line} |> ${line}
@ -106,7 +106,7 @@ show(mySketch001)`
const code = genCode(lineToChange) const code = genCode(lineToChange)
const expectedCode = genCode(lineAfterChange) const expectedCode = genCode(lineAfterChange)
const ast = abstractSyntaxTree(lexer(code)) const ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const sourceStart = code.indexOf(lineToChange) const sourceStart = code.indexOf(lineToChange)
const { modifiedAst } = changeSketchArguments( const { modifiedAst } = changeSketchArguments(
ast, ast,
@ -135,7 +135,7 @@ show(mySketch001)`
describe('testing addNewSketchLn', () => { describe('testing addNewSketchLn', () => {
const lineToChange = 'lineTo([-1.59, -1.54], %)' const lineToChange = 'lineTo([-1.59, -1.54], %)'
const lineAfterChange = 'lineTo([2, 3], %)' const lineAfterChange = 'lineTo([2, 3], %)'
test('addNewSketchLn', () => { test('addNewSketchLn', async () => {
const code = ` const code = `
const mySketch001 = startSketchAt([0, 0]) const mySketch001 = startSketchAt([0, 0])
|> rx(45, %) |> rx(45, %)
@ -143,7 +143,7 @@ const mySketch001 = startSketchAt([0, 0])
|> lineTo([0.46, -5.82], %) |> lineTo([0.46, -5.82], %)
show(mySketch001)` show(mySketch001)`
const ast = abstractSyntaxTree(lexer(code)) const ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const sourceStart = code.indexOf(lineToChange) const sourceStart = code.indexOf(lineToChange)
const { modifiedAst } = addNewSketchLn({ const { modifiedAst } = addNewSketchLn({
node: ast, node: ast,
@ -170,7 +170,7 @@ show(mySketch001)`
}) })
describe('testing addTagForSketchOnFace', () => { describe('testing addTagForSketchOnFace', () => {
it('needs to be in it', () => { it('needs to be in it', async () => {
const originalLine = 'lineTo([-1.59, -1.54], %)' const originalLine = 'lineTo([-1.59, -1.54], %)'
const genCode = (line: string) => ` const genCode = (line: string) => `
const mySketch001 = startSketchAt([0, 0]) const mySketch001 = startSketchAt([0, 0])
@ -180,7 +180,7 @@ describe('testing addTagForSketchOnFace', () => {
show(mySketch001)` show(mySketch001)`
const code = genCode(originalLine) const code = genCode(originalLine)
const ast = abstractSyntaxTree(lexer(code)) const ast = abstractSyntaxTree(lexer(code))
const programMemory = executor(ast) const programMemory = await executor(ast)
const sourceStart = code.indexOf(originalLine) const sourceStart = code.indexOf(originalLine)
const sourceRange: [number, number] = [ const sourceRange: [number, number] = [
sourceStart, sourceStart,

View File

@ -19,9 +19,10 @@ import {
getNodeFromPathCurry, getNodeFromPathCurry,
getNodePathFromSourceRange, getNodePathFromSourceRange,
} from '../queryAst' } from '../queryAst'
import { lineGeo, sketchBaseGeo } from '../engine'
import { GuiModes, toolTips, TooTip } from '../../useStore' import { GuiModes, toolTips, TooTip } from '../../useStore'
import { splitPathAtPipeExpression } from '../modifyAst' import { splitPathAtPipeExpression } from '../modifyAst'
import { generateUuidFromHashSeed } from '../../lib/uuid'
import { v4 as uuidv4 } from 'uuid'
import { import {
SketchLineHelper, SketchLineHelper,
@ -102,9 +103,21 @@ export function createFirstArg(
throw new Error('all sketch line types should have been covered') 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 = { export const lineTo: SketchLineHelper = {
fn: ( fn: (
{ sourceRange }, { sourceRange, engineCommandManager, code },
data: data:
| [number, number] | [number, number]
| { | {
@ -118,25 +131,35 @@ export const lineTo: SketchLineHelper = {
const sketchGroup = { ...previousSketch } const sketchGroup = { ...previousSketch }
const from = getCoordsFromPaths(sketchGroup, sketchGroup.value.length - 1) const from = getCoordsFromPaths(sketchGroup, sketchGroup.value.length - 1)
const to = 'to' in data ? data.to : data const to = 'to' in data ? data.to : data
const geo = lineGeo({
const lineData: LineData = {
from: [...from, 0], from: [...from, 0],
to: [...to, 0], to: [...to, 0],
}
const id = makeId({
code,
sourceRange,
data,
}) })
// engineCommandManager.sendModellingCommand({
// id,
// params: [lineData, previousSketch],
// range: sourceRange,
// })
const currentPath: Path = { const currentPath: Path = {
type: 'toPoint', type: 'toPoint',
to, to,
from, from,
__geoMeta: { __geoMeta: {
sourceRange, sourceRange,
id,
pathToNode: [], // TODO pathToNode: [], // TODO
geos: [ geos: [
{ {
type: 'line', type: 'line',
geo: geo.line,
}, },
{ {
type: 'lineEnd', type: 'lineEnd',
geo: geo.tip,
}, },
], ],
}, },
@ -217,7 +240,7 @@ export const lineTo: SketchLineHelper = {
export const line: SketchLineHelper = { export const line: SketchLineHelper = {
fn: ( fn: (
{ sourceRange }, { sourceRange, engineCommandManager, code },
data: data:
| [number, number] | [number, number]
| 'default' | 'default'
@ -239,25 +262,53 @@ export const line: SketchLineHelper = {
} }
const to: [number, number] = [from[0] + args[0], from[1] + args[1]] const to: [number, number] = [from[0] + args[0], from[1] + args[1]]
const geo = lineGeo({ const lineData: LineData = {
from: [...from, 0], from: [...from, 0],
to: [...to, 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 = { const currentPath: Path = {
type: 'toPoint', type: 'toPoint',
to, to,
from, from,
__geoMeta: { __geoMeta: {
id,
sourceRange, sourceRange,
pathToNode: [], // TODO pathToNode: [], // TODO
geos: [ geos: [
{ {
type: 'line', type: 'line',
geo: geo.line,
}, },
{ {
type: 'lineEnd', type: 'lineEnd',
geo: geo.tip,
}, },
], ],
}, },
@ -623,7 +674,7 @@ export const yLine: SketchLineHelper = {
export const angledLine: SketchLineHelper = { export const angledLine: SketchLineHelper = {
fn: ( fn: (
{ sourceRange, programMemory }, { sourceRange, engineCommandManager, code },
data: data:
| [number, number] | [number, number]
| { | {
@ -641,25 +692,34 @@ export const angledLine: SketchLineHelper = {
from[0] + length * Math.cos((angle * Math.PI) / 180), from[0] + length * Math.cos((angle * Math.PI) / 180),
from[1] + length * Math.sin((angle * Math.PI) / 180), from[1] + length * Math.sin((angle * Math.PI) / 180),
] ]
const geo = lineGeo({ const lineData: LineData = {
from: [...from, 0], from: [...from, 0],
to: [...to, 0], to: [...to, 0],
}
const id = makeId({
code,
sourceRange,
data,
}) })
// engineCommandManager.sendModellingCommand({
// id,
// params: [lineData, previousSketch],
// range: sourceRange,
// })
const currentPath: Path = { const currentPath: Path = {
type: 'toPoint', type: 'toPoint',
to, to,
from, from,
__geoMeta: { __geoMeta: {
id,
sourceRange, sourceRange,
pathToNode: [], // TODO pathToNode: [], // TODO
geos: [ geos: [
{ {
type: 'line', type: 'line',
geo: geo.line,
}, },
{ {
type: 'lineEnd', type: 'lineEnd',
geo: geo.tip,
}, },
], ],
}, },
@ -740,7 +800,7 @@ export const angledLine: SketchLineHelper = {
export const angledLineOfXLength: SketchLineHelper = { export const angledLineOfXLength: SketchLineHelper = {
fn: ( fn: (
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
data: data:
| [number, number] | [number, number]
| { | {
@ -754,7 +814,7 @@ export const angledLineOfXLength: SketchLineHelper = {
const [angle, length, tag] = const [angle, length, tag] =
'angle' in data ? [data.angle, data.length, data.tag] : data 'angle' in data ? [data.angle, data.length, data.tag] : data
return line.fn( return line.fn(
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
{ to: getYComponent(angle, length), tag }, { to: getYComponent(angle, length), tag },
previousSketch previousSketch
) )
@ -833,7 +893,7 @@ export const angledLineOfXLength: SketchLineHelper = {
export const angledLineOfYLength: SketchLineHelper = { export const angledLineOfYLength: SketchLineHelper = {
fn: ( fn: (
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
data: data:
| [number, number] | [number, number]
| { | {
@ -847,7 +907,7 @@ export const angledLineOfYLength: SketchLineHelper = {
const [angle, length, tag] = const [angle, length, tag] =
'angle' in data ? [data.angle, data.length, data.tag] : data 'angle' in data ? [data.angle, data.length, data.tag] : data
return line.fn( return line.fn(
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
{ to: getXComponent(angle, length), tag }, { to: getXComponent(angle, length), tag },
previousSketch previousSketch
) )
@ -927,7 +987,7 @@ export const angledLineOfYLength: SketchLineHelper = {
export const angledLineToX: SketchLineHelper = { export const angledLineToX: SketchLineHelper = {
fn: ( fn: (
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
data: data:
| [number, number] | [number, number]
| { | {
@ -948,7 +1008,7 @@ export const angledLineToX: SketchLineHelper = {
const yComponent = xComponent * Math.tan((angle * Math.PI) / 180) const yComponent = xComponent * Math.tan((angle * Math.PI) / 180)
const yTo = from[1] + yComponent const yTo = from[1] + yComponent
return lineTo.fn( return lineTo.fn(
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
{ to: [xTo, yTo], tag }, { to: [xTo, yTo], tag },
previousSketch previousSketch
) )
@ -1023,7 +1083,7 @@ export const angledLineToX: SketchLineHelper = {
export const angledLineToY: SketchLineHelper = { export const angledLineToY: SketchLineHelper = {
fn: ( fn: (
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
data: data:
| [number, number] | [number, number]
| { | {
@ -1044,7 +1104,7 @@ export const angledLineToY: SketchLineHelper = {
const xComponent = yComponent / Math.tan((angle * Math.PI) / 180) const xComponent = yComponent / Math.tan((angle * Math.PI) / 180)
const xTo = from[0] + xComponent const xTo = from[0] + xComponent
return lineTo.fn( return lineTo.fn(
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
{ to: [xTo, yTo], tag }, { to: [xTo, yTo], tag },
previousSketch previousSketch
) )
@ -1120,7 +1180,7 @@ export const angledLineToY: SketchLineHelper = {
export const angledLineThatIntersects: SketchLineHelper = { export const angledLineThatIntersects: SketchLineHelper = {
fn: ( fn: (
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
data: { data: {
angle: number angle: number
intersectTag: string intersectTag: string
@ -1145,7 +1205,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
line2Angle: data.angle, line2Angle: data.angle,
}) })
return lineTo.fn( return lineTo.fn(
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
{ to, tag: data.tag }, { to, tag: data.tag },
previousSketch previousSketch
) )
@ -1513,32 +1573,53 @@ function addTagWithTo(
} }
export const close: InternalFn = ( export const close: InternalFn = (
{ sourceRange }, { sourceRange, engineCommandManager, code },
sketchGroup: SketchGroup sketchGroup: SketchGroup
): SketchGroup => { ): SketchGroup => {
const from = getCoordsFromPaths(sketchGroup, sketchGroup.value.length - 1) const from = getCoordsFromPaths(sketchGroup, sketchGroup.value.length - 1)
const to = sketchGroup.start const to = sketchGroup.start
? sketchGroup.start.from ? sketchGroup.start.from
: getCoordsFromPaths(sketchGroup, 0) : getCoordsFromPaths(sketchGroup, 0)
const geo = lineGeo({
const lineData: LineData = {
from: [...from, 0], from: [...from, 0],
to: [...to, 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 = { const currentPath: Path = {
type: 'toPoint', type: 'toPoint',
to, to,
from, from,
__geoMeta: { __geoMeta: {
id,
sourceRange, sourceRange,
pathToNode: [], // TODO pathToNode: [], // TODO
geos: [ geos: [
{ {
type: 'line', type: 'line',
geo: geo.line,
}, },
{ {
type: 'lineEnd', type: 'lineEnd',
geo: geo.tip,
}, },
], ],
}, },
@ -1552,7 +1633,7 @@ export const close: InternalFn = (
} }
export const startSketchAt: InternalFn = ( export const startSketchAt: InternalFn = (
{ sourceRange, programMemory }, { sourceRange, programMemory, engineCommandManager, code },
data: data:
| [number, number] | [number, number]
| 'default' | 'default'
@ -1569,18 +1650,59 @@ export const startSketchAt: InternalFn = (
to = data 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 = { const currentPath: Path = {
type: 'base', type: 'base',
to, to,
from: to, from: to,
__geoMeta: { __geoMeta: {
id,
sourceRange, sourceRange,
pathToNode: [], // TODO pathToNode: [], // TODO
geos: [ geos: [
{ {
type: 'sketchBase', type: 'sketchBase',
geo: geo.base,
}, },
], ],
}, },
@ -1594,6 +1716,7 @@ export const startSketchAt: InternalFn = (
value: [], value: [],
position: [0, 0, 0], position: [0, 0, 0],
rotation: [0, 0, 0, 1], rotation: [0, 0, 0, 1],
id: pathId,
__meta: [ __meta: [
{ {
sourceRange, sourceRange,

View File

@ -1,5 +1,5 @@
import { abstractSyntaxTree } from '../abstractSyntaxTree' import { abstractSyntaxTree } from '../abstractSyntaxTree'
import { executor, SketchGroup } from '../executor' import { SketchGroup } from '../executor'
import { lexer } from '../tokeniser' import { lexer } from '../tokeniser'
import { import {
ConstraintType, ConstraintType,
@ -10,11 +10,12 @@ import { recast } from '../recast'
import { initPromise } from '../rust' import { initPromise } from '../rust'
import { getSketchSegmentFromSourceRange } from './sketchConstraints' import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { Selection } from '../../useStore' import { Selection } from '../../useStore'
import { executor } from '../../lib/testHelpers'
beforeAll(() => initPromise) beforeAll(() => initPromise)
// testing helper function // testing helper function
function testingSwapSketchFnCall({ async function testingSwapSketchFnCall({
inputCode, inputCode,
callToSwap, callToSwap,
constraintType, constraintType,
@ -22,10 +23,10 @@ function testingSwapSketchFnCall({
inputCode: string inputCode: string
callToSwap: string callToSwap: string
constraintType: ConstraintType constraintType: ConstraintType
}): { }): Promise<{
newCode: string newCode: string
originalRange: [number, number] originalRange: [number, number]
} { }> {
const startIndex = inputCode.indexOf(callToSwap) const startIndex = inputCode.indexOf(callToSwap)
const range: Selection = { const range: Selection = {
type: 'default', type: 'default',
@ -33,7 +34,7 @@ function testingSwapSketchFnCall({
} }
const tokens = lexer(inputCode) const tokens = lexer(inputCode)
const ast = abstractSyntaxTree(tokens) const ast = abstractSyntaxTree(tokens)
const programMemory = executor(ast) const programMemory = await executor(ast)
const selections = { const selections = {
codeBasedSelections: [range], codeBasedSelections: [range],
otherSelections: [], otherSelections: [],
@ -94,10 +95,10 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => {
`show(part001)`, `show(part001)`,
] ]
const bigExample = bigExampleArr.join('\n') 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 callToSwap = "line({ to: [-2.04, -0.7], tag: 'abc2' }, %)"
const expectedLine = "xLine({ length: -2.04, tag: 'abc2' }, %)" const expectedLine = "xLine({ length: -2.04, tag: 'abc2' }, %)"
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap, callToSwap,
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) 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 callToSwap = 'line([0.73, -0.75], %)'
const expectedLine = 'xLine(0.73, %)' const expectedLine = 'xLine(0.73, %)'
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap, callToSwap,
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('lineTo with tag converts to xLineTo', () => { it('lineTo with tag converts to xLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: "lineTo({ to: [1, 1], tag: 'abc1' }, %)", callToSwap: "lineTo({ to: [1, 1], tag: 'abc1' }, %)",
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('lineTo w/o tag converts to xLineTo', () => { it('lineTo w/o tag converts to xLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: 'lineTo([2.55, 3.58], %)', callToSwap: 'lineTo([2.55, 3.58], %)',
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLine with tag converts to xLine', () => { it('angledLine with tag converts to xLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: [ callToSwap: [
`angledLine({`, `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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLine w/o tag converts to xLine', () => { it('angledLine w/o tag converts to xLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: 'angledLine([63, 1.38], %)', callToSwap: 'angledLine([63, 1.38], %)',
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineOfXLength with tag converts to xLine', () => { it('angledLineOfXLength with tag converts to xLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: [ callToSwap: [
`angledLineOfXLength({`, `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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineOfXLength w/o tag converts to xLine', () => { it('angledLineOfXLength w/o tag converts to xLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: 'angledLineOfXLength([319, 1.15], %)', callToSwap: 'angledLineOfXLength([319, 1.15], %)',
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineOfYLength with tag converts to yLine', () => { it('angledLineOfYLength with tag converts to yLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: [ callToSwap: [
`angledLineOfYLength({`, `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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineOfYLength w/o tag converts to yLine', () => { it('angledLineOfYLength w/o tag converts to yLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: 'angledLineOfYLength([50, 1.35], %)', callToSwap: 'angledLineOfYLength([50, 1.35], %)',
constraintType: 'vertical', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineToX with tag converts to xLineTo', () => { it('angledLineToX with tag converts to xLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: "angledLineToX({ angle: 55, to: -2.89, tag: 'abc6' }, %)", callToSwap: "angledLineToX({ angle: 55, to: -2.89, tag: 'abc6' }, %)",
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineToX w/o tag converts to xLineTo', () => { it('angledLineToX w/o tag converts to xLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: 'angledLineToX([291, 6.66], %)', callToSwap: 'angledLineToX([291, 6.66], %)',
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineToY with tag converts to yLineTo', () => { it('angledLineToY with tag converts to yLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: "angledLineToY({ angle: 330, to: 2.53, tag: 'abc7' }, %)", callToSwap: "angledLineToY({ angle: 330, to: 2.53, tag: 'abc7' }, %)",
constraintType: 'vertical', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineToY w/o tag converts to yLineTo', () => { it('angledLineToY w/o tag converts to yLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: bigExample, inputCode: bigExample,
callToSwap: 'angledLineToY([228, 2.14], %)', callToSwap: 'angledLineToY([228, 2.14], %)',
constraintType: 'vertical', constraintType: 'vertical',
@ -294,8 +295,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari
`show(part001)`, `show(part001)`,
] ]
const varExample = variablesExampleArr.join('\n') const varExample = variablesExampleArr.join('\n')
it('line keeps variable when converted to xLine', () => { it('line keeps variable when converted to xLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: varExample, inputCode: varExample,
callToSwap: 'line([lineX, 2.13], %)', callToSwap: 'line([lineX, 2.13], %)',
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('lineTo keeps variable when converted to xLineTo', () => { it('lineTo keeps variable when converted to xLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: varExample, inputCode: varExample,
callToSwap: 'lineTo([lineToX, 2.85], %)', callToSwap: 'lineTo([lineToX, 2.85], %)',
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineOfXLength keeps variable when converted to xLine', () => { it('angledLineOfXLength keeps variable when converted to xLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: varExample, inputCode: varExample,
callToSwap: 'angledLineOfXLength([329, angledLineOfXLengthX], %)', callToSwap: 'angledLineOfXLength([329, angledLineOfXLengthX], %)',
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineOfYLength keeps variable when converted to yLine', () => { it('angledLineOfYLength keeps variable when converted to yLine', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: varExample, inputCode: varExample,
callToSwap: 'angledLineOfYLength([222, angledLineOfYLengthY], %)', callToSwap: 'angledLineOfYLength([222, angledLineOfYLengthY], %)',
constraintType: 'vertical', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineToX keeps variable when converted to xLineTo', () => { it('angledLineToX keeps variable when converted to xLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: varExample, inputCode: varExample,
callToSwap: 'angledLineToX([330, angledLineToXx], %)', callToSwap: 'angledLineToX([330, angledLineToXx], %)',
constraintType: 'horizontal', 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 // new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine)) expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
}) })
it('angledLineToY keeps variable when converted to yLineTo', () => { it('angledLineToY keeps variable when converted to yLineTo', async () => {
const { newCode, originalRange } = testingSwapSketchFnCall({ const { newCode, originalRange } = await testingSwapSketchFnCall({
inputCode: varExample, inputCode: varExample,
callToSwap: 'angledLineToY([217, angledLineToYy], %)', callToSwap: 'angledLineToY([217, angledLineToYy], %)',
constraintType: 'vertical', 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)) 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 = () => const illegalConvert = () =>
testingSwapSketchFnCall({ testingSwapSketchFnCall({
inputCode: varExample, inputCode: varExample,
callToSwap: 'angledLineToY([217, angledLineToYy], %)', callToSwap: 'angledLineToY([217, angledLineToYy], %)',
constraintType: 'horizontal', 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 |> line([2.14, 1.35], %) // normal-segment
|> xLine(3.54, %) |> xLine(3.54, %)
show(part001)` show(part001)`
it('normal case works', () => { it('normal case works', async () => {
const programMemory = executor(abstractSyntaxTree(lexer(code))) const programMemory = await executor(abstractSyntaxTree(lexer(code)))
const index = code.indexOf('// normal-segment') - 7 const index = code.indexOf('// normal-segment') - 7
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
programMemory.root['part001'] as SketchGroup, programMemory.root['part001'] as SketchGroup,
@ -393,8 +394,8 @@ show(part001)`
from: [3.48, 0.44], from: [3.48, 0.44],
}) })
}) })
it('verify it works when the segment is in the `start` property', () => { it('verify it works when the segment is in the `start` property', async () => {
const programMemory = executor(abstractSyntaxTree(lexer(code))) const programMemory = await executor(abstractSyntaxTree(lexer(code)))
const index = code.indexOf('// segment-in-start') - 7 const index = code.indexOf('// segment-in-start') - 7
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
programMemory.root['part001'] as SketchGroup, programMemory.root['part001'] as SketchGroup,

View File

@ -41,7 +41,7 @@ export const segLen: InternalFn = (
segName: string, segName: string,
sketchGroup: SketchGroup sketchGroup: SketchGroup
): number => { ): 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 // maybe this should throw, but the language doesn't have a way to handle errors yet
if (!line) return 0 if (!line) return 0
@ -64,8 +64,8 @@ export const segAng: InternalFn = (
function segEndFactory(which: 'x' | 'y'): InternalFn { function segEndFactory(which: 'x' | 'y'): InternalFn {
return (_, segName: string, sketchGroup: SketchGroup): number => { return (_, segName: string, sketchGroup: SketchGroup): number => {
const line = const line =
sketchGroup.start?.name === segName sketchGroup?.start?.name === segName
? sketchGroup.start ? sketchGroup?.start
: sketchGroup?.value.find((seg) => seg.name === segName) : sketchGroup?.value.find((seg) => seg.name === segName)
// maybe this should throw, but the language doesn't have a way to handle errors yet // maybe this should throw, but the language doesn't have a way to handle errors yet
if (!line) return 0 if (!line) return 0

View File

@ -10,7 +10,7 @@ import {
} from './sketchcombos' } from './sketchcombos'
import { initPromise } from '../rust' import { initPromise } from '../rust'
import { Selections, TooTip } from '../../useStore' import { Selections, TooTip } from '../../useStore'
import { executor } from '../../lang/executor' import { executor } from '../../lib/testHelpers'
import { recast } from '../../lang/recast' import { recast } from '../../lang/recast'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -196,7 +196,7 @@ const part001 = startSketchAt([0, 0])
|> xLine(segLen('seg01', %), %) // ln-xLineTo-free should convert to xLine |> xLine(segLen('seg01', %), %) // ln-xLineTo-free should convert to xLine
|> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine |> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine
show(part001)` show(part001)`
it('It should transform the ast', () => { it('It should transform the ast', async () => {
const ast = abstractSyntaxTree(lexer(inputScript)) const ast = abstractSyntaxTree(lexer(inputScript))
const selectionRanges: Selections['codeBasedSelections'] = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
@ -210,7 +210,7 @@ show(part001)`
} }
}) })
const programMemory = executor(ast) const programMemory = await executor(ast)
const transformInfos = getTransformInfos( const transformInfos = getTransformInfos(
makeSelections(selectionRanges.slice(1)), makeSelections(selectionRanges.slice(1)),
ast, ast,
@ -255,7 +255,7 @@ const part001 = startSketchAt([0, 0])
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10 |> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|> angledLineToY([301, myVar], %) // select for vertical constraint 10 |> angledLineToY([301, myVar], %) // select for vertical constraint 10
show(part001)` 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 expectModifiedScript = `const myVar = 2
const myVar2 = 12 const myVar2 = 12
const myVar3 = -10 const myVar3 = -10
@ -295,7 +295,7 @@ show(part001)`
} }
}) })
const programMemory = executor(ast) const programMemory = await executor(ast)
const transformInfos = getTransformInfos( const transformInfos = getTransformInfos(
makeSelections(selectionRanges), makeSelections(selectionRanges),
ast, ast,
@ -312,7 +312,7 @@ show(part001)`
const newCode = recast(newAst) const newCode = recast(newAst)
expect(newCode).toBe(expectModifiedScript) 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 expectModifiedScript = `const myVar = 2
const myVar2 = 12 const myVar2 = 12
const myVar3 = -10 const myVar3 = -10
@ -352,7 +352,7 @@ show(part001)`
} }
}) })
const programMemory = executor(ast) const programMemory = await executor(ast)
const transformInfos = getTransformInfos( const transformInfos = getTransformInfos(
makeSelections(selectionRanges), makeSelections(selectionRanges),
ast, ast,
@ -381,13 +381,13 @@ const part001 = startSketchAt([0, 0])
|> line([myVar, 0.01], %) // xRelative |> line([myVar, 0.01], %) // xRelative
|> line([0.7, myVar], %) // yRelative |> line([0.7, myVar], %) // yRelative
show(part001)` show(part001)`
it('testing for free to horizontal and vertical distance', () => { it('testing for free to horizontal and vertical distance', async () => {
const expectedHorizontalCode = helperThing( const expectedHorizontalCode = await helperThing(
inputScript, inputScript,
['// base selection', '// free'], ['// base selection', '// free'],
'setHorzDistance' 'setHorzDistance'
) )
const expectedVerticalCode = helperThing( const expectedVerticalCode = await helperThing(
inputScript, inputScript,
['// base selection', '// free'], ['// base selection', '// free'],
'setVertDistance' 'setVertDistance'
@ -399,8 +399,8 @@ show(part001)`
`lineTo([1.21, segEndY('seg01', %) + 2.92], %) // free` `lineTo([1.21, segEndY('seg01', %) + 2.92], %) // free`
) )
}) })
it('testing for xRelative to vertical distance', () => { it('testing for xRelative to vertical distance', async () => {
const expectedCode = helperThing( const expectedCode = await helperThing(
inputScript, inputScript,
['// base selection', '// xRelative'], ['// base selection', '// xRelative'],
'setVertDistance' 'setVertDistance'
@ -410,8 +410,8 @@ show(part001)`
segEndY('seg01', %) + 2.93 segEndY('seg01', %) + 2.93
], %) // xRelative`) ], %) // xRelative`)
}) })
it('testing for yRelative to horizontal distance', () => { it('testing for yRelative to horizontal distance', async () => {
const expectedCode = helperThing( const expectedCode = await helperThing(
inputScript, inputScript,
['// base selection', '// yRelative'], ['// base selection', '// yRelative'],
'setHorzDistance' 'setHorzDistance'
@ -424,11 +424,11 @@ show(part001)`
}) })
}) })
function helperThing( async function helperThing(
inputScript: string, inputScript: string,
linesOfInterest: string[], linesOfInterest: string[],
constraint: ConstraintType constraint: ConstraintType
): string { ): Promise<string> {
const ast = abstractSyntaxTree(lexer(inputScript)) const ast = abstractSyntaxTree(lexer(inputScript))
const selectionRanges: Selections['codeBasedSelections'] = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
@ -444,7 +444,7 @@ function helperThing(
} }
}) })
const programMemory = executor(ast) const programMemory = await executor(ast)
const transformInfos = getTransformInfos( const transformInfos = getTransformInfos(
makeSelections(selectionRanges.slice(1)), makeSelections(selectionRanges.slice(1)),
ast, ast,

View File

@ -1,12 +1,12 @@
import { abstractSyntaxTree } from '../abstractSyntaxTree' import { abstractSyntaxTree } from '../abstractSyntaxTree'
import { executor } from '../executor' import { executor } from '../../lib/testHelpers'
import { lexer } from '../tokeniser' import { lexer } from '../tokeniser'
import { initPromise } from '../rust' import { initPromise } from '../rust'
beforeAll(() => initPromise) beforeAll(() => initPromise)
describe('testing angledLineThatIntersects', () => { 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]) const code = (offset: string) => `const part001 = startSketchAt([0, 0])
|> lineTo({to:[2, 2], tag: "yo"}, %) |> lineTo({to:[2, 2], tag: "yo"}, %)
|> lineTo([3, 1], %) |> lineTo([3, 1], %)
@ -18,9 +18,11 @@ describe('testing angledLineThatIntersects', () => {
}, %) }, %)
const intersect = segEndX('yo2', part001) const intersect = segEndX('yo2', part001)
show(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)) 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) expect(noOffset.intersect.value).toBeCloseTo(1)
}) })
}) })

View File

@ -24,62 +24,61 @@ import {
lastSegX, lastSegX,
lastSegY, lastSegY,
} from './sketchConstraints' } from './sketchConstraints'
import { extrude, getExtrudeWallTransform } from './extrude' import { getExtrudeWallTransform, extrude } from './extrude'
import { Quaternion, Vector3 } from 'three'
import { SketchGroup, ExtrudeGroup, Position, Rotation } from '../executor' import { SketchGroup, ExtrudeGroup, Position, Rotation } from '../executor'
import { InternalFn, InternalFnNames, InternalFirstArg } from './stdTypes' import { InternalFn, InternalFnNames, InternalFirstArg } from './stdTypes'
const transform: InternalFn = <T extends SketchGroup | ExtrudeGroup>( // const transform: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
{ sourceRange }: InternalFirstArg, // { sourceRange }: InternalFirstArg,
transformInfo: { // transformInfo: {
position: Position // position: Position
quaternion: Rotation // quaternion: Rotation
}, // },
sketch: T // sketch: T
): T => { // ): T => {
const quaternionToApply = new Quaternion(...transformInfo.quaternion) // const quaternionToApply = new Quaternion(...transformInfo?.quaternion)
const newQuaternion = new Quaternion(...sketch.rotation).multiply( // const newQuaternion = new Quaternion(...sketch.rotation).multiply(
quaternionToApply.invert() // quaternionToApply.invert()
) // )
const oldPosition = new Vector3(...sketch.position) // const oldPosition = new Vector3(...sketch?.position)
const newPosition = oldPosition // const newPosition = oldPosition
.applyQuaternion(quaternionToApply) // .applyQuaternion(quaternionToApply)
.add(new Vector3(...transformInfo.position)) // .add(new Vector3(...transformInfo?.position))
return { // return {
...sketch, // ...sketch,
position: newPosition.toArray(), // position: newPosition.toArray(),
rotation: newQuaternion.toArray(), // rotation: newQuaternion.toArray(),
__meta: [ // __meta: [
...sketch.__meta, // ...sketch.__meta,
{ // {
sourceRange, // sourceRange,
pathToNode: [], // TODO // pathToNode: [], // TODO
}, // },
], // ],
} // }
} // }
const translate: InternalFn = <T extends SketchGroup | ExtrudeGroup>( // const translate: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
{ sourceRange }: InternalFirstArg, // { sourceRange }: InternalFirstArg,
vec3: [number, number, number], // vec3: [number, number, number],
sketch: T // sketch: T
): T => { // ): T => {
const oldPosition = new Vector3(...sketch.position) // const oldPosition = new Vector3(...sketch.position)
const newPosition = oldPosition.add(new Vector3(...vec3)) // const newPosition = oldPosition.add(new Vector3(...vec3))
return { // return {
...sketch, // ...sketch,
position: newPosition.toArray(), // position: newPosition.toArray(),
__meta: [ // __meta: [
...sketch.__meta, // ...sketch.__meta,
{ // {
sourceRange, // sourceRange,
pathToNode: [], // TODO // pathToNode: [], // TODO
}, // },
], // ],
} // }
} // }
const min: InternalFn = (_, a: number, b: number): number => Math.min(a, b) 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 (Math.asin(Math.min(leg, hypotenuse) / hypotenuse) * 180) / Math.PI
export const internalFns: { [key in InternalFnNames]: InternalFn } = { export const internalFns: { [key in InternalFnNames]: InternalFn } = {
rx: rotateOnAxis([1, 0, 0]), // TODO - re-enable these
ry: rotateOnAxis([0, 1, 0]), // rx: rotateOnAxis([1, 0, 0]),
rz: rotateOnAxis([0, 0, 1]), // ry: rotateOnAxis([0, 1, 0]),
// rz: rotateOnAxis([0, 0, 1]),
extrude, extrude,
translate, // translate,
transform, // transform,
getExtrudeWallTransform, getExtrudeWallTransform,
min, min,
legLen, legLen,
@ -130,35 +130,35 @@ export const internalFns: { [key in InternalFnNames]: InternalFn } = {
close, close,
} }
function rotateOnAxis<T extends SketchGroup | ExtrudeGroup>( // function rotateOnAxis<T extends SketchGroup | ExtrudeGroup>(
axisMultiplier: [number, number, number] // axisMultiplier: [number, number, number]
): InternalFn { // ): InternalFn {
return ({ sourceRange }, rotationD: number, sketch: T): T => { // return ({ sourceRange }, rotationD: number, sketch: T): T => {
const rotationR = rotationD * (Math.PI / 180) // const rotationR = rotationD * (Math.PI / 180)
const rotateVec = new Vector3(...axisMultiplier) // const rotateVec = new Vector3(...axisMultiplier)
const quaternion = new Quaternion() // const quaternion = new Quaternion()
quaternion.setFromAxisAngle(rotateVec, rotationR) // quaternion.setFromAxisAngle(rotateVec, rotationR)
const position = new Vector3(...sketch.position) // const position = new Vector3(...sketch.position)
.applyQuaternion(quaternion) // .applyQuaternion(quaternion)
.toArray() // .toArray()
const existingQuat = new Quaternion(...sketch.rotation) // const existingQuat = new Quaternion(...sketch.rotation)
const rotation = quaternion.multiply(existingQuat).toArray() // const rotation = quaternion.multiply(existingQuat).toArray()
return { // return {
...sketch, // ...sketch,
rotation, // rotation,
position, // position,
__meta: [ // __meta: [
...sketch.__meta, // ...sketch.__meta,
{ // {
sourceRange, // sourceRange,
pathToNode: [], // TODO // pathToNode: [], // TODO
}, // },
], // ],
} // }
} // }
} // }
export function clockwiseSign(points: [number, number][]): number { export function clockwiseSign(points: [number, number][]): number {
let sum = 0 let sum = 0

View File

@ -2,11 +2,14 @@ import { ProgramMemory, Path, SourceRange } from '../executor'
import { Program, Value } from '../abstractSyntaxTree' import { Program, Value } from '../abstractSyntaxTree'
import { TooTip } from '../../useStore' import { TooTip } from '../../useStore'
import { PathToNode } from '../executor' import { PathToNode } from '../executor'
import { EngineCommandManager } from './engineConnection'
export interface InternalFirstArg { export interface InternalFirstArg {
programMemory: ProgramMemory programMemory: ProgramMemory
name?: string name?: string
sourceRange: SourceRange sourceRange: SourceRange
engineCommandManager: EngineCommandManager
code: string
} }
export interface PathReturn { export interface PathReturn {
@ -17,9 +20,13 @@ export interface PathReturn {
export type InternalFn = (internals: InternalFirstArg, ...args: any[]) => any export type InternalFn = (internals: InternalFirstArg, ...args: any[]) => any
export type InternalFnNames = export type InternalFnNames =
// TODO re-enable these
// | 'translate'
// | 'transform'
// | 'rx'
// | 'ry'
// | 'rz'
| 'extrude' | 'extrude'
| 'translate'
| 'transform'
| 'getExtrudeWallTransform' | 'getExtrudeWallTransform'
| 'min' | 'min'
| 'legLen' | 'legLen'
@ -33,9 +40,6 @@ export type InternalFnNames =
| 'segAng' | 'segAng'
| 'angleToMatchLengthX' | 'angleToMatchLengthX'
| 'angleToMatchLengthY' | 'angleToMatchLengthY'
| 'rx'
| 'ry'
| 'rz'
| 'lineTo' | 'lineTo'
| 'yLineTo' | 'yLineTo'
| 'xLineTo' | 'xLineTo'

14
src/lib/testHelpers.ts Normal file
View File

@ -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<ProgramMemory> {
const engineCommandManager = new EngineCommandManager(() => {})
engineCommandManager.startNewSession()
const programMemory = await _executor(ast, pm, engineCommandManager)
await engineCommandManager.waitForAllCommands()
return programMemory
}

View File

@ -27,3 +27,31 @@ export function normaliseAngle(angle: number): number {
const result = ((angle % 360) + 360) % 360 const result = ((angle % 360) + 360) % 360
return result > 180 ? result - 360 : result return result > 180 ? result - 360 : result
} }
export function throttle(
func: (...args: any[]) => any,
wait: number
): (...args: any[]) => any {
let timeout: ReturnType<typeof setTimeout> | 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
}

9
src/lib/uuid.test.ts Normal file
View File

@ -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')
})
})

10
src/lib/uuid.ts Normal file
View File

@ -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
}

View File

@ -3,3 +3,5 @@
// expect(element).toHaveTextContent(/react/i) // expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom // learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom' import '@testing-library/jest-dom'
import 'setimmediate'

View File

@ -14,6 +14,12 @@ import { recast } from './lang/recast'
import { asyncLexer } from './lang/tokeniser' import { asyncLexer } from './lang/tokeniser'
import { EditorSelection } from '@codemirror/state' import { EditorSelection } from '@codemirror/state'
import { BaseDirectory } from '@tauri-apps/api/fs' 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 = { export type Selection = {
type: 'default' | 'line-end' | 'line-mid' type: 'default' | 'line-end' | 'line-mid'
@ -100,6 +106,7 @@ export interface StoreState {
highlightRange: [number, number] highlightRange: [number, number]
setHighlightRange: (range: Selection['range']) => void setHighlightRange: (range: Selection['range']) => void
setCursor: (selections: Selections) => void setCursor: (selections: Selections) => void
setCursor2: (a: Selection) => void
selectionRanges: Selections selectionRanges: Selections
selectionRangeTypeMap: { [key: number]: Selection['type'] } selectionRangeTypeMap: { [key: number]: Selection['type'] }
setSelectionRanges: (range: Selections) => void setSelectionRanges: (range: Selections) => void
@ -131,6 +138,16 @@ export interface StoreState {
setProgramMemory: (programMemory: ProgramMemory) => void setProgramMemory: (programMemory: ProgramMemory) => void
isShiftDown: boolean isShiftDown: boolean
setIsShiftDown: (isShiftDown: boolean) => void 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 // tauri specific app settings
defaultDir: DefaultDir defaultDir: DefaultDir
@ -168,8 +185,10 @@ export const useStore = create<StoreState>()(
const selectionRangeTypeMap: { [key: number]: Selection['type'] } = {} const selectionRangeTypeMap: { [key: number]: Selection['type'] } = {}
set({ selectionRangeTypeMap }) set({ selectionRangeTypeMap })
selections.codeBasedSelections.forEach(({ range, type }) => { selections.codeBasedSelections.forEach(({ range, type }) => {
if (range?.[1]) {
ranges.push(EditorSelection.cursor(range[1])) ranges.push(EditorSelection.cursor(range[1]))
selectionRangeTypeMap[range[1]] = type selectionRangeTypeMap[range[1]] = type
}
}) })
setTimeout(() => { setTimeout(() => {
editorView.dispatch({ editorView.dispatch({
@ -180,6 +199,16 @@ export const useStore = create<StoreState>()(
}) })
}) })
}, },
setCursor2: (codeSelections) => {
const currestSelections = get().selectionRanges
const selections: Selections = {
...currestSelections,
codeBasedSelections: get().isShiftDown
? [...currestSelections.codeBasedSelections, codeSelections]
: [codeSelections],
}
get().setCursor(selections)
},
selectionRangeTypeMap: {}, selectionRangeTypeMap: {},
selectionRanges: { selectionRanges: {
otherSelections: [], otherSelections: [],
@ -261,10 +290,16 @@ export const useStore = create<StoreState>()(
setError: (error = '') => { setError: (error = '') => {
set({ errorState: { isError: !!error, error } }) set({ errorState: { isError: !!error, error } })
}, },
programMemory: { root: {}, _sketch: [] }, programMemory: { root: {}, pendingMemory: {} },
setProgramMemory: (programMemory) => set({ programMemory }), setProgramMemory: (programMemory) => set({ programMemory }),
isShiftDown: false, isShiftDown: false,
setIsShiftDown: (isShiftDown) => set({ isShiftDown }), setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
artifactMap: {},
sourceRangeMap: {},
setArtifactNSourceRangeMaps: (maps) => set({ ...maps }),
setEngineCommandManager: (engineCommandManager) =>
set({ engineCommandManager }),
setMediaStream: (mediaStream) => set({ mediaStream }),
// tauri specific app settings // tauri specific app settings
defaultDir: { defaultDir: {

509
yarn.lock
View File

@ -1233,7 +1233,7 @@
core-js-pure "^3.25.1" core-js-pure "^3.25.1"
regenerator-runtime "^0.13.10" 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" version "7.20.1"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9"
integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==
@ -1319,33 +1319,6 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== 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": "@codemirror/autocomplete@^6.0.0":
version "6.3.3" version "6.3.3"
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.3.3.tgz#f9dba421b9a0d83ecf89a06def37a96e6afb593a" resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.3.3.tgz#f9dba421b9a0d83ecf89a06def37a96e6afb593a"
@ -1433,6 +1406,13 @@
style-mod "^4.0.0" style-mod "^4.0.0"
w3c-keyname "^2.2.4" 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@*": "@csstools/normalize.css@*":
version "12.0.0" version "12.0.0"
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.0.0.tgz#a9583a75c3f150667771f30b60d9f059473e62c4" 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" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== 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": "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" 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" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 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": "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.17" version "0.3.17"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
@ -1965,90 +1958,6 @@
schema-utils "^3.0.0" schema-utils "^3.0.0"
source-map "^0.7.3" 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": "@rollup/plugin-babel@^5.2.0":
version "5.3.1" version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" 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" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== 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": "@types/aria-query@^4.2.0":
version "4.2.2" version "4.2.2"
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" 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" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc"
integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg== 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": "@types/parse-json@^4.0.0":
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@ -2604,20 +2528,6 @@
dependencies: dependencies:
"@types/react" "*" "@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": "@types/react@*", "@types/react@^18.0.0":
version "18.0.25" version "18.0.25"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.25.tgz#8b1dcd7e56fe7315535a4af25435e0bb55c8ae44" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.25.tgz#8b1dcd7e56fe7315535a4af25435e0bb55c8ae44"
@ -2683,27 +2593,15 @@
dependencies: dependencies:
"@types/jest" "*" "@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": "@types/trusted-types@^2.0.2":
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
"@types/uuid@^9.0.2": "@types/uuid@^9.0.1":
version "9.0.2" version "9.0.1"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.2.tgz#ede1d1b1e451548d44919dc226253e32a6952c4b" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6"
integrity sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ== integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==
"@types/webxr@*":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.0.tgz#aae1cef3210d88fd4204f8c33385a0bbc4da07c9"
integrity sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==
"@types/ws@^8.5.1": "@types/ws@^8.5.1":
version "8.5.3" version "8.5.3"
@ -2846,18 +2744,6 @@
"@uiw/codemirror-extensions-basic-setup" "4.15.1" "@uiw/codemirror-extensions-basic-setup" "4.15.1"
codemirror "^6.0.0" 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": "@webassemblyjs/ast@1.11.1":
version "1.11.1" version "1.11.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
@ -2979,11 +2865,6 @@
"@webassemblyjs/ast" "1.11.1" "@webassemblyjs/ast" "1.11.1"
"@xtuc/long" "4.2.2" "@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": "@xtuc/ieee754@^1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" 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" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== 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: acorn@^7.0.0:
version "7.4.1" version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" 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" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== 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: address@^1.0.1:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" 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" normalize-path "^3.0.0"
picomatch "^2.0.4" 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: arg@^5.0.2:
version "5.0.2" version "5.0.2"
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
@ -3534,13 +3430,6 @@ bfj@^7.0.2:
hoopy "^0.1.4" hoopy "^0.1.4"
tryer "^1.0.1" 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: big.js@^5.2.2:
version "5.2.2" version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" 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" resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f"
integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== 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: chokidar@^3.4.2, chokidar@^3.5.3:
version "3.5.3" version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 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" path-type "^4.0.0"
yaml "^1.10.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: crelt@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94" 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" shebang-command "^2.0.0"
which "^2.0.1" 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: crypto-random-string@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" 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-mimetype "^2.3.0"
whatwg-url "^8.0.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: debug@2.6.9, debug@^2.6.0, debug@^2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 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" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== 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: detect-newline@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" 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" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.2.0.tgz#4c55b5b40706c7b5d2c5c75999a50c56d214e8f6"
integrity sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw== 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: dir-glob@^3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" 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" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== 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: duplexer@^0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" 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" setimmediate "^1.0.5"
ua-parser-js "^0.7.30" 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: file-entry-cache@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" 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" merge2 "^1.4.1"
slash "^3.0.0" 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: gopd@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" 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" html-escaper "^2.0.0"
istanbul-lib-report "^3.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: jake@^10.8.5:
version "10.8.5" version "10.8.5"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" 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" resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== 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: language-subtag-registry@~0.3.2:
version "0.3.22" version "0.3.22"
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" 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: dependencies:
p-locate "^5.0.0" p-locate "^5.0.0"
lodash.clamp@^4.0.0, lodash.clamp@^4.0.3: lodash.clamp@^4.0.0:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash.clamp/-/lodash.clamp-4.0.3.tgz#5c24bedeeeef0753560dc2b4cb4671f90a6ddfaa" resolved "https://registry.yarnpkg.com/lodash.clamp/-/lodash.clamp-4.0.3.tgz#5c24bedeeeef0753560dc2b4cb4671f90a6ddfaa"
integrity sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg== 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" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 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: lodash.sortby@^4.7.0:
version "4.7.0" version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" 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" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= 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: lodash@^4.17.13:
version "4.17.15" version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== 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: loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@ -7228,6 +7071,11 @@ make-dir@^3.0.2:
dependencies: dependencies:
semver "^6.0.0" 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: makeerror@1.0.x:
version "1.0.11" version "1.0.11"
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" 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" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 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: methods@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@ -7411,11 +7254,6 @@ mkdirp@~0.5.1:
dependencies: dependencies:
minimist "^1.2.5" 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: ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 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" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== 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: optionator@^0.8.1:
version "0.8.3" version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" 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" picocolors "^1.0.0"
source-map-js "^1.0.2" 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: prelude-ls@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" 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" kleur "^3.0.3"
sisteransi "^1.0.5" sisteransi "^1.0.5"
prop-types@^15.6.0, prop-types@^15.8.1: prop-types@^15.8.1:
version "15.8.1" version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -8710,13 +8535,6 @@ react-base16-styling@^0.6.0:
lodash.flow "^3.3.0" lodash.flow "^3.3.0"
pure-color "^1.2.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: react-dev-utils@^12.0.1:
version "12.0.1" version "12.0.1"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73" 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" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== 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: react-modal-promise@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/react-modal-promise/-/react-modal-promise-1.0.2.tgz#122620b7f19eec73683affadfa77c543d88edc40" resolved "https://registry.yarnpkg.com/react-modal-promise/-/react-modal-promise-1.0.2.tgz#122620b7f19eec73683affadfa77c543d88edc40"
integrity sha512-dqT618ROhG8qh1+O6EZkia5ELw3zaZWGpMX2YfEH4bgwYENPuFonqKw1W70LFx3K/SCZvVBcD6UYEI12yzYXzg== 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: react-refresh@^0.11.0:
version "0.11.0" version "0.11.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" 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-composed-ref "^1.3.0"
use-latest "^1.2.1" 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: react@^18.2.0:
version "18.2.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" 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" resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58"
integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== 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: regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3:
version "1.4.3" version "1.4.3"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" 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: dependencies:
xmlchars "^2.2.0" 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: scheduler@^0.23.0:
version "0.23.0" version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" 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" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== 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: statuses@2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" 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" is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1" 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: string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.7:
version "4.0.7" version "4.0.7"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" 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" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 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: svg-parser@^2.0.2:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" 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" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 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: throat@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" 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" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== 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: tmpl@1.0.x:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" 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" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== 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: tryer@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== 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: tsconfig-paths@^3.14.1:
version "3.14.1" version "3.14.1"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" 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" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= 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: utils-merge@1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 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" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== 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: v8-to-istanbul@^8.1.0:
version "8.1.1" version "8.1.1"
resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" 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" resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c"
integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg== 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: webidl-conversions@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" 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" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.19.tgz#4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
integrity sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ== 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: yocto-queue@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 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: zustand@^4.1.4:
version "4.1.4" version "4.1.4"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.1.4.tgz#b0286da4cc9edd35e91c96414fa54bfa4652a54d" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.1.4.tgz#b0286da4cc9edd35e91c96414fa54bfa4652a54d"