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