massive overhall to how program memory works and how artifacts their metada are rendered
This commit is contained in:
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"geos"
|
||||||
|
]
|
||||||
|
}
|
44
src/App.tsx
44
src/App.tsx
@ -4,7 +4,7 @@ import { Allotment } from 'allotment'
|
|||||||
import { OrbitControls, OrthographicCamera } from '@react-three/drei'
|
import { OrbitControls, OrthographicCamera } from '@react-three/drei'
|
||||||
import { lexer } from './lang/tokeniser'
|
import { lexer } from './lang/tokeniser'
|
||||||
import { abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
import { abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
||||||
import { executor, processShownObjects, ViewerArtifact } from './lang/executor'
|
import { executor, ExtrudeGroup, SketchGroup } from './lang/executor'
|
||||||
import { recast } from './lang/recast'
|
import { recast } from './lang/recast'
|
||||||
import CodeMirror from '@uiw/react-codemirror'
|
import CodeMirror from '@uiw/react-codemirror'
|
||||||
import { javascript } from '@codemirror/lang-javascript'
|
import { javascript } from '@codemirror/lang-javascript'
|
||||||
@ -78,7 +78,7 @@ function App() {
|
|||||||
if (isNoChange) return
|
if (isNoChange) return
|
||||||
setSelectionRange([range.from, range.to])
|
setSelectionRange([range.from, range.to])
|
||||||
}
|
}
|
||||||
const [geoArray, setGeoArray] = useState<ViewerArtifact[]>([])
|
const [geoArray, setGeoArray] = useState<(ExtrudeGroup | SketchGroup)[]>([])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
if (!code) {
|
if (!code) {
|
||||||
@ -92,26 +92,44 @@ function App() {
|
|||||||
setAst(_ast)
|
setAst(_ast)
|
||||||
const programMemory = executor(_ast, {
|
const programMemory = executor(_ast, {
|
||||||
root: {
|
root: {
|
||||||
log: (a: any) => {
|
log: {
|
||||||
|
type: 'userVal',
|
||||||
|
value: (a: any) => {
|
||||||
|
console.log('raw log', a)
|
||||||
let b = a
|
let b = a
|
||||||
if (Array.isArray(a)) {
|
if (Array.isArray(a)) {
|
||||||
b = a.map(({ geo, ...rest }) => rest)
|
b = a.map(({ geo, __geoMeta, ...rest }) => rest)
|
||||||
b = JSON.stringify(b, null, 2)
|
b = JSON.stringify(b, null, 2)
|
||||||
} else if (typeof a === 'object') {
|
} else if (typeof a === 'object') {
|
||||||
b = JSON.stringify(a, null, 2)
|
const { geo, __geoMeta, ...rest } = a
|
||||||
|
b = JSON.stringify(rest, null, 2)
|
||||||
}
|
}
|
||||||
addLog(b)
|
addLog(b)
|
||||||
},
|
},
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
pathToNode: [],
|
||||||
|
sourceRange: [0, 0],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
_sketch: [],
|
_sketch: [],
|
||||||
})
|
})
|
||||||
setProgramMemory(programMemory)
|
setProgramMemory(programMemory)
|
||||||
const geos: ViewerArtifact[] =
|
const geos = programMemory?.return
|
||||||
programMemory?.return?.flatMap(
|
?.map(({ name }: { name: string }) => {
|
||||||
({ name }: { name: string }) =>
|
const artifact = programMemory?.root?.[name]
|
||||||
processShownObjects(programMemory, programMemory?.root?.[name]) ||
|
if (
|
||||||
[]
|
artifact.type === 'extrudeGroup' ||
|
||||||
) || []
|
artifact.type === 'sketchGroup'
|
||||||
|
) {
|
||||||
|
return artifact
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
.filter((a) => a) as (ExtrudeGroup | SketchGroup)[]
|
||||||
|
|
||||||
setGeoArray(geos)
|
setGeoArray(geos)
|
||||||
removeError()
|
removeError()
|
||||||
console.log(programMemory)
|
console.log(programMemory)
|
||||||
@ -177,9 +195,7 @@ function App() {
|
|||||||
/>
|
/>
|
||||||
<ambientLight />
|
<ambientLight />
|
||||||
<pointLight position={[10, 10, 10]} />
|
<pointLight position={[10, 10, 10]} />
|
||||||
{geoArray.map((artifact, index) => (
|
<RenderViewerArtifacts artifacts={geoArray} />
|
||||||
<RenderViewerArtifacts artifact={artifact} key={index} />
|
|
||||||
))}
|
|
||||||
<BasePlanes />
|
<BasePlanes />
|
||||||
<SketchPlane />
|
<SketchPlane />
|
||||||
<AxisIndicator />
|
<AxisIndicator />
|
||||||
|
@ -34,7 +34,7 @@ export const Toolbar = () => {
|
|||||||
mode: 'sketch',
|
mode: 'sketch',
|
||||||
sketchMode: 'sketchEdit',
|
sketchMode: 'sketchEdit',
|
||||||
pathToNode: guiMode.pathToNode,
|
pathToNode: guiMode.pathToNode,
|
||||||
quaternion: guiMode.quaternion,
|
rotation: guiMode.rotation,
|
||||||
position: guiMode.position,
|
position: guiMode.position,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
@ -65,7 +65,7 @@ export const BasePlanes = () => {
|
|||||||
setGuiMode({
|
setGuiMode({
|
||||||
mode: 'sketch',
|
mode: 'sketch',
|
||||||
sketchMode: 'sketchEdit',
|
sketchMode: 'sketchEdit',
|
||||||
quaternion,
|
rotation: quaternion.toArray() as [number, number, number, number],
|
||||||
position: [0, 0, 0],
|
position: [0, 0, 0],
|
||||||
pathToNode,
|
pathToNode,
|
||||||
})
|
})
|
||||||
|
@ -3,110 +3,22 @@ import {
|
|||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
VariableDeclarator,
|
|
||||||
} from '../lang/abstractSyntaxTree'
|
} from '../lang/abstractSyntaxTree'
|
||||||
import { changeArguments } from '../lang/modifyAst'
|
import { changeArguments } from '../lang/modifyAst'
|
||||||
import { ViewerArtifact } from '../lang/executor'
|
import {
|
||||||
|
ExtrudeGroup,
|
||||||
|
ExtrudeSurface,
|
||||||
|
SketchGroup,
|
||||||
|
Path,
|
||||||
|
Rotation,
|
||||||
|
Position,
|
||||||
|
} from '../lang/executor'
|
||||||
import { BufferGeometry } from 'three'
|
import { BufferGeometry } from 'three'
|
||||||
import { useStore } from '../useStore'
|
import { useStore } from '../useStore'
|
||||||
import { isOverlapping } from '../lib/utils'
|
import { isOverlapping } from '../lib/utils'
|
||||||
import { LineGeos } from '../lang/engine'
|
import { Vector3, DoubleSide, Quaternion } from 'three'
|
||||||
import { Vector3, DoubleSide, Quaternion, Vector2 } from 'three'
|
|
||||||
import { combineTransformsAlt } from '../lang/sketch'
|
|
||||||
import { useSetCursor } from '../hooks/useSetCursor'
|
import { useSetCursor } from '../hooks/useSetCursor'
|
||||||
|
|
||||||
function SketchLine({
|
|
||||||
geo,
|
|
||||||
sourceRange,
|
|
||||||
forceHighlight = false,
|
|
||||||
}: {
|
|
||||||
geo: LineGeos
|
|
||||||
sourceRange: [number, number]
|
|
||||||
forceHighlight?: boolean
|
|
||||||
}) {
|
|
||||||
const { setHighlightRange } = useStore(({ setHighlightRange }) => ({
|
|
||||||
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)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<mesh
|
|
||||||
ref={ref}
|
|
||||||
onPointerOver={(event) => {
|
|
||||||
setHover(true)
|
|
||||||
setHighlightRange(sourceRange)
|
|
||||||
}}
|
|
||||||
onPointerOut={(event) => {
|
|
||||||
setHover(false)
|
|
||||||
setHighlightRange([0, 0])
|
|
||||||
}}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
<primitive object={geo.line} />
|
|
||||||
<meshStandardMaterial
|
|
||||||
color={hovered ? 'hotpink' : forceHighlight ? 'skyblue' : 'orange'}
|
|
||||||
/>
|
|
||||||
</mesh>
|
|
||||||
<MovingSphere
|
|
||||||
geo={geo.tip}
|
|
||||||
sourceRange={sourceRange}
|
|
||||||
editorCursor={forceHighlight}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExtrudeWall({
|
|
||||||
geo,
|
|
||||||
sourceRange,
|
|
||||||
forceHighlight = false,
|
|
||||||
}: {
|
|
||||||
geo: BufferGeometry
|
|
||||||
sourceRange: [number, number]
|
|
||||||
forceHighlight?: boolean
|
|
||||||
}) {
|
|
||||||
const { setHighlightRange } = useStore(
|
|
||||||
({ setHighlightRange, selectionRange, guiMode, setGuiMode, ast }) => ({
|
|
||||||
setHighlightRange,
|
|
||||||
selectionRange,
|
|
||||||
guiMode,
|
|
||||||
setGuiMode,
|
|
||||||
ast,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
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)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<mesh
|
|
||||||
ref={ref}
|
|
||||||
onPointerOver={(event) => {
|
|
||||||
setHover(true)
|
|
||||||
setHighlightRange(sourceRange)
|
|
||||||
}}
|
|
||||||
onPointerOut={(event) => {
|
|
||||||
setHover(false)
|
|
||||||
setHighlightRange([0, 0])
|
|
||||||
}}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
<primitive object={geo} />
|
|
||||||
<meshStandardMaterial
|
|
||||||
side={DoubleSide}
|
|
||||||
color={hovered ? 'hotpink' : forceHighlight ? 'skyblue' : 'orange'}
|
|
||||||
/>
|
|
||||||
</mesh>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const roundOff = (num: number, places: number): number => {
|
const roundOff = (num: number, places: number): number => {
|
||||||
const x = Math.pow(10, places)
|
const x = Math.pow(10, places)
|
||||||
return Math.round(num * x) / x
|
return Math.round(num * x) / x
|
||||||
@ -116,15 +28,19 @@ function MovingSphere({
|
|||||||
geo,
|
geo,
|
||||||
sourceRange,
|
sourceRange,
|
||||||
editorCursor,
|
editorCursor,
|
||||||
|
rotation,
|
||||||
|
position,
|
||||||
}: {
|
}: {
|
||||||
geo: BufferGeometry
|
geo: BufferGeometry
|
||||||
sourceRange: [number, number]
|
sourceRange: [number, number]
|
||||||
editorCursor: boolean
|
editorCursor: boolean
|
||||||
|
rotation: Rotation
|
||||||
|
position: Position
|
||||||
}) {
|
}) {
|
||||||
const ref = useRef<BufferGeometry | undefined>() as any
|
const ref = useRef<BufferGeometry | undefined>() as any
|
||||||
const detectionPlaneRef = useRef<BufferGeometry | undefined>() as any
|
const detectionPlaneRef = useRef<BufferGeometry | undefined>() as any
|
||||||
const lastPointerRef = useRef<Vector3>(new Vector3())
|
const lastPointerRef = useRef<Vector3>(new Vector3())
|
||||||
const point2DRef = useRef<Vector2>(new Vector2())
|
const point2DRef = useRef<Vector3>(new Vector3())
|
||||||
const [hovered, setHover] = useState(false)
|
const [hovered, setHover] = useState(false)
|
||||||
const [isMouseDown, setIsMouseDown] = useState(false)
|
const [isMouseDown, setIsMouseDown] = useState(false)
|
||||||
|
|
||||||
@ -154,14 +70,22 @@ function MovingSphere({
|
|||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
if (isMouseDown && ast) {
|
if (isMouseDown && ast) {
|
||||||
const thePath = getNodePathFromSourceRange(ast, sourceRange)
|
const thePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
let [x, y] = [
|
const yo = point2DRef.current.clone()
|
||||||
roundOff(point2DRef.current.x, 2),
|
const inverseQuaternion = new Quaternion()
|
||||||
roundOff(point2DRef.current.y, 2),
|
if (
|
||||||
]
|
guiMode.mode === 'canEditSketch' ||
|
||||||
|
(guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit')
|
||||||
|
) {
|
||||||
|
inverseQuaternion.set(...guiMode.rotation)
|
||||||
|
inverseQuaternion.invert()
|
||||||
|
}
|
||||||
|
yo.sub(new Vector3(...position).applyQuaternion(inverseQuaternion))
|
||||||
|
let [x, y] = [roundOff(yo.x, 2), roundOff(yo.y, 2)]
|
||||||
let theNewPoints: [number, number] = [x, y]
|
let theNewPoints: [number, number] = [x, y]
|
||||||
const { modifiedAst } = changeArguments(ast, thePath, theNewPoints)
|
const { modifiedAst } = changeArguments(ast, thePath, theNewPoints)
|
||||||
updateAst(modifiedAst)
|
updateAst(modifiedAst)
|
||||||
ref.current.position.set(0, 0, 0)
|
console.log('reset position')
|
||||||
|
ref.current.position.set(...position)
|
||||||
}
|
}
|
||||||
setIsMouseDown(false)
|
setIsMouseDown(false)
|
||||||
}
|
}
|
||||||
@ -169,31 +93,32 @@ function MovingSphere({
|
|||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('mouseup', handleMouseUp)
|
window.removeEventListener('mouseup', handleMouseUp)
|
||||||
}
|
}
|
||||||
}, [isMouseDown, ast])
|
}, [isMouseDown])
|
||||||
|
|
||||||
let clickDetectPlaneQuaternion = new Quaternion()
|
const inEditMode =
|
||||||
let position = new Vector3(0, 0, 0)
|
|
||||||
if (
|
|
||||||
guiMode.mode === 'canEditSketch' ||
|
guiMode.mode === 'canEditSketch' ||
|
||||||
(guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit')
|
(guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit')
|
||||||
) {
|
|
||||||
clickDetectPlaneQuaternion = guiMode.quaternion.clone()
|
let clickDetectPlaneQuaternion = new Quaternion()
|
||||||
position = new Vector3(...guiMode.position)
|
if (inEditMode) {
|
||||||
|
clickDetectPlaneQuaternion = new Quaternion(...rotation)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<mesh
|
<mesh
|
||||||
|
position={position}
|
||||||
|
quaternion={rotation}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onPointerOver={(event) => {
|
onPointerOver={(event) => {
|
||||||
setHover(true)
|
inEditMode && setHover(true)
|
||||||
setHighlightRange(sourceRange)
|
setHighlightRange(sourceRange)
|
||||||
}}
|
}}
|
||||||
onPointerOut={(event) => {
|
onPointerOut={(event) => {
|
||||||
setHover(false)
|
setHover(false)
|
||||||
setHighlightRange([0, 0])
|
setHighlightRange([0, 0])
|
||||||
}}
|
}}
|
||||||
onPointerDown={() => setIsMouseDown(true)}
|
onPointerDown={() => inEditMode && setIsMouseDown(true)}
|
||||||
>
|
>
|
||||||
<primitive object={geo} scale={hovered ? 2 : 1} />
|
<primitive object={geo} scale={hovered ? 2 : 1} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
@ -202,7 +127,6 @@ function MovingSphere({
|
|||||||
</mesh>
|
</mesh>
|
||||||
{isMouseDown && (
|
{isMouseDown && (
|
||||||
<mesh
|
<mesh
|
||||||
position={position}
|
|
||||||
quaternion={clickDetectPlaneQuaternion}
|
quaternion={clickDetectPlaneQuaternion}
|
||||||
onPointerMove={(a) => {
|
onPointerMove={(a) => {
|
||||||
const point = a.point
|
const point = a.point
|
||||||
@ -213,13 +137,11 @@ function MovingSphere({
|
|||||||
guiMode.mode === 'canEditSketch' ||
|
guiMode.mode === 'canEditSketch' ||
|
||||||
(guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit')
|
(guiMode.mode === 'sketch' && guiMode.sketchMode === 'sketchEdit')
|
||||||
) {
|
) {
|
||||||
inverseQuaternion.copy(guiMode.quaternion.clone().invert())
|
inverseQuaternion.set(...guiMode.rotation)
|
||||||
|
inverseQuaternion.invert()
|
||||||
}
|
}
|
||||||
transformedPoint.applyQuaternion(inverseQuaternion)
|
transformedPoint.applyQuaternion(inverseQuaternion)
|
||||||
transformedPoint.sub(
|
point2DRef.current.copy(transformedPoint)
|
||||||
position.clone().applyQuaternion(inverseQuaternion)
|
|
||||||
)
|
|
||||||
point2DRef.current.set(transformedPoint.x, transformedPoint.y)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
lastPointerRef.current.x === 0 &&
|
lastPointerRef.current.x === 0 &&
|
||||||
@ -248,7 +170,7 @@ function MovingSphere({
|
|||||||
ref.current.position.add(
|
ref.current.position.add(
|
||||||
diff.applyQuaternion(inverseQuaternion.invert())
|
diff.applyQuaternion(inverseQuaternion.invert())
|
||||||
)
|
)
|
||||||
lastPointerRef.current.set(point.x, point.y, point.z)
|
lastPointerRef.current.copy(point.clone())
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -266,86 +188,258 @@ function MovingSphere({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function RenderViewerArtifacts({
|
export function RenderViewerArtifacts({
|
||||||
artifact,
|
artifacts,
|
||||||
forceHighlight = false,
|
|
||||||
}: {
|
}: {
|
||||||
artifact: ViewerArtifact
|
artifacts: (ExtrudeGroup | SketchGroup)[]
|
||||||
forceHighlight?: boolean
|
|
||||||
}) {
|
}) {
|
||||||
const { selectionRange, guiMode, ast, setGuiMode, programMemory } = useStore(
|
return (
|
||||||
({ selectionRange, guiMode, ast, setGuiMode, programMemory }) => ({
|
<>
|
||||||
|
{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,
|
selectionRange,
|
||||||
guiMode,
|
guiMode,
|
||||||
ast,
|
ast,
|
||||||
setGuiMode,
|
setGuiMode,
|
||||||
programMemory,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
const [editorCursor, setEditorCursor] = useState(false)
|
const [editorCursor, setEditorCursor] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldHighlight = isOverlapping(artifact.sourceRange, selectionRange)
|
const shouldHighlight = isOverlapping(
|
||||||
setEditorCursor(shouldHighlight && artifact.type !== 'sketch')
|
artifact.__meta.slice(-1)[0].sourceRange,
|
||||||
}, [selectionRange, artifact.sourceRange])
|
selectionRange
|
||||||
|
)
|
||||||
|
setEditorCursor(shouldHighlight)
|
||||||
|
}, [selectionRange, artifact.__meta])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldHighlight = isOverlapping(artifact.sourceRange, selectionRange)
|
const shouldHighlight = artifact.__meta.some((aMeta) =>
|
||||||
|
isOverlapping(aMeta.sourceRange, selectionRange)
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
shouldHighlight &&
|
shouldHighlight &&
|
||||||
(guiMode.mode === 'default' || guiMode.mode === 'canEditSketch') &&
|
(guiMode.mode === 'default' || guiMode.mode === 'canEditSketch') &&
|
||||||
artifact.type === 'sketch' &&
|
ast &&
|
||||||
ast
|
artifact.type === 'sketchGroup'
|
||||||
) {
|
) {
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, artifact.sourceRange)
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
const varDec: VariableDeclarator = getNodeFromPath(
|
|
||||||
ast,
|
ast,
|
||||||
pathToNode,
|
artifact.__meta[0].sourceRange
|
||||||
'VariableDeclarator'
|
|
||||||
)
|
)
|
||||||
const varName = varDec?.id?.name
|
const { rotation, position } = artifact
|
||||||
const { quaternion, position } = combineTransformsAlt(
|
setGuiMode({ mode: 'canEditSketch', pathToNode, rotation, position })
|
||||||
programMemory.root[varName]
|
|
||||||
)
|
|
||||||
setGuiMode({ mode: 'canEditSketch', pathToNode, quaternion, position })
|
|
||||||
} else if (
|
} else if (
|
||||||
!shouldHighlight &&
|
!shouldHighlight &&
|
||||||
guiMode.mode === 'canEditSketch' &&
|
guiMode.mode === 'canEditSketch' &&
|
||||||
artifact.type === 'sketch'
|
artifact.type === 'sketchGroup'
|
||||||
) {
|
) {
|
||||||
setGuiMode({ mode: 'default' })
|
setGuiMode({ mode: 'default' })
|
||||||
}
|
}
|
||||||
}, [selectionRange, artifact.sourceRange, ast, guiMode.mode, setGuiMode])
|
}, [selectionRange, artifact, ast, guiMode.mode, setGuiMode])
|
||||||
if (artifact.type === 'sketchLine') {
|
|
||||||
const { geo, sourceRange } = artifact
|
if (artifact.type === 'sketchGroup') {
|
||||||
return (
|
|
||||||
<SketchLine
|
|
||||||
geo={geo}
|
|
||||||
sourceRange={sourceRange}
|
|
||||||
forceHighlight={forceHighlight || editorCursor}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (artifact.type === 'sketchBase') {
|
|
||||||
console.log('BASE TODO')
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (artifact.type === 'extrudeWall') {
|
|
||||||
return (
|
|
||||||
<ExtrudeWall
|
|
||||||
geo={artifact.geo}
|
|
||||||
sourceRange={artifact.sourceRange}
|
|
||||||
forceHighlight={forceHighlight || editorCursor}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{artifact.children.map((artifact, index) => (
|
{artifact.value.map((geoInfo, key) => (
|
||||||
<RenderViewerArtifacts
|
<PathRender
|
||||||
artifact={artifact}
|
geoInfo={geoInfo}
|
||||||
key={index}
|
key={key}
|
||||||
forceHighlight={forceHighlight || editorCursor}
|
forceHighlight={editorCursor}
|
||||||
|
rotation={artifact.rotation}
|
||||||
|
position={artifact.position}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (artifact.type === 'extrudeGroup') {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{artifact.value.map((geoInfo, key) => (
|
||||||
|
<WallRender
|
||||||
|
geoInfo={geoInfo}
|
||||||
|
key={key}
|
||||||
|
forceHighlight={editorCursor}
|
||||||
|
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, selectionRange } = useStore(
|
||||||
|
({ setHighlightRange, selectionRange }) => ({
|
||||||
|
setHighlightRange,
|
||||||
|
selectionRange,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
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 = isOverlapping(
|
||||||
|
geoInfo.__geoMeta.sourceRange,
|
||||||
|
selectionRange
|
||||||
|
)
|
||||||
|
setEditorCursor(shouldHighlight)
|
||||||
|
}, [selectionRange, 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 { selectionRange } = useStore(({ selectionRange }) => ({
|
||||||
|
selectionRange,
|
||||||
|
}))
|
||||||
|
const [editorCursor, setEditorCursor] = useState(false)
|
||||||
|
useEffect(() => {
|
||||||
|
const shouldHighlight = isOverlapping(
|
||||||
|
geoInfo.__geoMeta.sourceRange,
|
||||||
|
selectionRange
|
||||||
|
)
|
||||||
|
setEditorCursor(shouldHighlight)
|
||||||
|
}, [selectionRange, geoInfo])
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{geoInfo.__geoMeta.geos.map((meta, i) => {
|
||||||
|
if (meta.type === 'line') {
|
||||||
|
return (
|
||||||
|
<LineRender
|
||||||
|
key={i}
|
||||||
|
geo={meta.geo}
|
||||||
|
sourceRange={geoInfo.__geoMeta.sourceRange}
|
||||||
|
forceHighlight={forceHighlight || editorCursor}
|
||||||
|
rotation={rotation}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (meta.type === 'lineEnd') {
|
||||||
|
return (
|
||||||
|
<MovingSphere
|
||||||
|
key={i}
|
||||||
|
geo={meta.geo}
|
||||||
|
sourceRange={geoInfo.__geoMeta.sourceRange}
|
||||||
|
editorCursor={forceHighlight || editorCursor}
|
||||||
|
rotation={rotation}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function LineRender({
|
||||||
|
geo,
|
||||||
|
sourceRange,
|
||||||
|
forceHighlight = false,
|
||||||
|
rotation,
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
geo: BufferGeometry
|
||||||
|
sourceRange: [number, number]
|
||||||
|
forceHighlight?: boolean
|
||||||
|
rotation: Rotation
|
||||||
|
position: Position
|
||||||
|
}) {
|
||||||
|
const { setHighlightRange } = useStore(({ setHighlightRange }) => ({
|
||||||
|
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)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<mesh
|
||||||
|
quaternion={rotation}
|
||||||
|
position={position}
|
||||||
|
ref={ref}
|
||||||
|
onPointerOver={(event) => {
|
||||||
|
setHover(true)
|
||||||
|
setHighlightRange(sourceRange)
|
||||||
|
}}
|
||||||
|
onPointerOut={(event) => {
|
||||||
|
setHover(false)
|
||||||
|
setHighlightRange([0, 0])
|
||||||
|
}}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<primitive object={geo} />
|
||||||
|
<meshStandardMaterial
|
||||||
|
color={hovered ? 'hotpink' : forceHighlight ? 'skyblue' : 'orange'}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -20,7 +20,7 @@ export const SketchPlane = () => {
|
|||||||
|
|
||||||
const sketchGridName = 'sketchGrid'
|
const sketchGridName = 'sketchGrid'
|
||||||
|
|
||||||
let clickDetectQuaternion = guiMode.quaternion.clone()
|
let clickDetectQuaternion = new Quaternion(...guiMode.rotation)
|
||||||
|
|
||||||
let temp = new Quaternion().setFromAxisAngle(
|
let temp = new Quaternion().setFromAxisAngle(
|
||||||
new Vector3(1, 0, 0),
|
new Vector3(1, 0, 0),
|
||||||
@ -28,7 +28,7 @@ export const SketchPlane = () => {
|
|||||||
)
|
)
|
||||||
let position = guiMode.position
|
let position = guiMode.position
|
||||||
const gridQuaternion = new Quaternion().multiplyQuaternions(
|
const gridQuaternion = new Quaternion().multiplyQuaternions(
|
||||||
guiMode.quaternion,
|
new Quaternion(...guiMode.rotation),
|
||||||
temp
|
temp
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,8 +47,12 @@ export const SketchPlane = () => {
|
|||||||
)
|
)
|
||||||
const inverseQuaternion = clickDetectQuaternion.clone().invert()
|
const inverseQuaternion = clickDetectQuaternion.clone().invert()
|
||||||
let transformedPoint = sketchGridIntersection?.point.clone()
|
let transformedPoint = sketchGridIntersection?.point.clone()
|
||||||
if (transformedPoint)
|
if (transformedPoint) {
|
||||||
transformedPoint.applyQuaternion(inverseQuaternion)
|
transformedPoint.applyQuaternion(inverseQuaternion)
|
||||||
|
transformedPoint?.sub(
|
||||||
|
new Vector3(...position).applyQuaternion(inverseQuaternion)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const point = roundy(transformedPoint)
|
const point = roundy(transformedPoint)
|
||||||
let _ast: Program = ast
|
let _ast: Program = ast
|
||||||
|
@ -453,7 +453,7 @@ export interface Literal extends GeneralStatement {
|
|||||||
raw: string
|
raw: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Identifier extends GeneralStatement {
|
export interface Identifier extends GeneralStatement {
|
||||||
type: 'Identifier'
|
type: 'Identifier'
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { abstractSyntaxTree } from './abstractSyntaxTree'
|
import { abstractSyntaxTree } from './abstractSyntaxTree'
|
||||||
import { lexer } from './tokeniser'
|
import { lexer } from './tokeniser'
|
||||||
import { executor, ViewerArtifact, processShownObjects } from './executor'
|
import { executor, SketchGroup, ExtrudeGroup } from './executor'
|
||||||
|
|
||||||
describe('findClosingBrace', () => {
|
describe('testing artifacts', () => {
|
||||||
test('finds the closing brace', () => {
|
test('sketch artifacts', () => {
|
||||||
const code = `
|
const code = `
|
||||||
sketch mySketch001 {
|
sketch mySketch001 {
|
||||||
lineTo(-1.59, -1.54)
|
lineTo(-1.59, -1.54)
|
||||||
@ -12,34 +12,86 @@ sketch mySketch001 {
|
|||||||
|> rx(45, %)
|
|> rx(45, %)
|
||||||
show(mySketch001)`
|
show(mySketch001)`
|
||||||
const programMemory = executor(abstractSyntaxTree(lexer(code)))
|
const programMemory = executor(abstractSyntaxTree(lexer(code)))
|
||||||
const geos: ViewerArtifact[] =
|
const geos = programMemory?.return?.map(
|
||||||
programMemory?.return?.flatMap(
|
(a) => programMemory?.root?.[a.name]
|
||||||
({ name }: { name: string }) =>
|
)
|
||||||
processShownObjects(programMemory, programMemory?.root?.[name]) || []
|
const artifactsWithoutGeos = removeGeo(geos as any)
|
||||||
) || []
|
expect(artifactsWithoutGeos).toEqual([
|
||||||
const artifactsWithouGeos = removeGeo(geos)
|
|
||||||
expect(artifactsWithouGeos).toEqual([
|
|
||||||
{
|
{
|
||||||
type: 'parent',
|
type: 'sketchGroup',
|
||||||
sourceRange: [74, 83],
|
value: [
|
||||||
children: [
|
|
||||||
{
|
{
|
||||||
type: 'sketch',
|
type: 'toPoint',
|
||||||
sourceRange: [20, 68],
|
to: [-1.59, -1.54],
|
||||||
children: [
|
from: [0, 0],
|
||||||
{
|
__geoMeta: {
|
||||||
type: 'sketchBase',
|
|
||||||
sourceRange: [0, 0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'sketchLine',
|
|
||||||
sourceRange: [24, 44],
|
sourceRange: [24, 44],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'sketchLine',
|
type: 'toPoint',
|
||||||
|
to: [0.46, -5.82],
|
||||||
|
from: [-1.59, -1.54],
|
||||||
|
__geoMeta: {
|
||||||
sourceRange: [47, 66],
|
sourceRange: [47, 66],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
position: [0, 0, 0],
|
||||||
|
rotation: [0.3826834323650898, 0, 0, 0.9238795325112867],
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
sourceRange: [20, 68],
|
||||||
|
pathToNode: ['body', 0, 'declarations', 0, 'init', 0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRange: [74, 83],
|
||||||
|
pathToNode: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
test('extrude artifacts', () => {
|
||||||
|
const code = `
|
||||||
|
sketch mySketch001 {
|
||||||
|
lineTo(-1.59, -1.54)
|
||||||
|
lineTo(0.46, -5.82)
|
||||||
|
}
|
||||||
|
|> rx(45, %)
|
||||||
|
|> extrude(2, %)
|
||||||
|
show(mySketch001)`
|
||||||
|
const programMemory = executor(abstractSyntaxTree(lexer(code)))
|
||||||
|
const geos = programMemory?.return?.map(
|
||||||
|
(a) => programMemory?.root?.[a.name]
|
||||||
|
)
|
||||||
|
const artifactsWithoutGeos = removeGeo(geos as any)
|
||||||
|
expect(artifactsWithoutGeos).toEqual([
|
||||||
|
{
|
||||||
|
type: 'extrudeGroup',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'extrudePlane',
|
||||||
|
position: [0, 0, 0],
|
||||||
|
rotation: [0.3826834323650898, 0, 0, 0.9238795325112867],
|
||||||
|
__geoMeta: {
|
||||||
|
geo: 'PlaneGeometry',
|
||||||
|
sourceRange: [47, 66],
|
||||||
|
pathToNode: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
height: 2,
|
||||||
|
position: [0, 0, 0],
|
||||||
|
rotation: [0.3826834323650898, 0, 0, 0.9238795325112867],
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
sourceRange: [89, 102],
|
||||||
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -47,24 +99,29 @@ show(mySketch001)`
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function removeGeo(arts: ViewerArtifact[]): any {
|
function removeGeo(arts: (SketchGroup | ExtrudeGroup)[]): any {
|
||||||
return arts.map((art) => {
|
return arts.map((art) => {
|
||||||
if (art.type === 'sketchLine' || art.type === 'sketchBase') {
|
if (art.type === 'extrudeGroup') {
|
||||||
const { geo, ...rest } = art
|
|
||||||
return rest
|
|
||||||
}
|
|
||||||
if (art.type === 'parent') {
|
|
||||||
return {
|
return {
|
||||||
...art,
|
...art,
|
||||||
children: removeGeo(art.children),
|
value: art.value.map((v) => ({
|
||||||
|
...v,
|
||||||
|
__geoMeta: {
|
||||||
|
...v.__geoMeta,
|
||||||
|
geo: v.__geoMeta.geo.type,
|
||||||
|
},
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (art.type === 'sketch') {
|
|
||||||
return {
|
return {
|
||||||
...art,
|
...art,
|
||||||
children: removeGeo(art.children),
|
value: art.value.map((v) => ({
|
||||||
|
...v,
|
||||||
|
__geoMeta: {
|
||||||
|
...v.__geoMeta,
|
||||||
|
geos: v.__geoMeta.geos.map((g) => g.type),
|
||||||
|
},
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return art
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -44,19 +44,17 @@ function trigCalcs({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LineGeos {
|
|
||||||
line: BufferGeometry
|
|
||||||
tip: BufferGeometry
|
|
||||||
centre: BufferGeometry
|
|
||||||
}
|
|
||||||
|
|
||||||
export function lineGeo({
|
export function lineGeo({
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
}: {
|
}: {
|
||||||
from: [number, number, number]
|
from: [number, number, number]
|
||||||
to: [number, number, number]
|
to: [number, number, number]
|
||||||
}): LineGeos {
|
}): {
|
||||||
|
line: BufferGeometry
|
||||||
|
tip: BufferGeometry
|
||||||
|
centre: BufferGeometry
|
||||||
|
} {
|
||||||
const {
|
const {
|
||||||
centre,
|
centre,
|
||||||
Hypotenuse: Hypotenuse3d,
|
Hypotenuse: Hypotenuse3d,
|
||||||
|
@ -2,21 +2,20 @@ import fs from 'node:fs'
|
|||||||
|
|
||||||
import { abstractSyntaxTree } from './abstractSyntaxTree'
|
import { abstractSyntaxTree } from './abstractSyntaxTree'
|
||||||
import { lexer } from './tokeniser'
|
import { lexer } from './tokeniser'
|
||||||
import { executor, ProgramMemory } from './executor'
|
import { executor, ProgramMemory, Path, SketchGroup } from './executor'
|
||||||
import { Transform, SketchGeo } from './sketch'
|
|
||||||
|
|
||||||
describe('test', () => {
|
describe('test', () => {
|
||||||
it('test assigning two variables, the second summing with the first', () => {
|
it('test assigning two variables, the second summing with the first', () => {
|
||||||
const code = `const myVar = 5
|
const code = `const myVar = 5
|
||||||
const newVar = myVar + 1`
|
const newVar = myVar + 1`
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
expect(root.myVar).toBe(5)
|
expect(root.myVar.value).toBe(5)
|
||||||
expect(root.newVar).toBe(6)
|
expect(root.newVar.value).toBe(6)
|
||||||
})
|
})
|
||||||
it('test assigning a var with a string', () => {
|
it('test assigning a var with a string', () => {
|
||||||
const code = `const myVar = "a str"`
|
const code = `const myVar = "a str"`
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
expect(root.myVar).toBe('a str')
|
expect(root.myVar.value).toBe('a str')
|
||||||
})
|
})
|
||||||
it('test assigning a var by cont concatenating two strings string execute', () => {
|
it('test assigning a var by cont concatenating two strings string execute', () => {
|
||||||
const code = fs.readFileSync(
|
const code = fs.readFileSync(
|
||||||
@ -24,21 +23,30 @@ const newVar = myVar + 1`
|
|||||||
'utf-8'
|
'utf-8'
|
||||||
)
|
)
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
expect(root.myVar).toBe('a str another str')
|
expect(root.myVar.value).toBe('a str another str')
|
||||||
})
|
})
|
||||||
it('test with function call', () => {
|
it('test with function call', () => {
|
||||||
const code = `
|
const code = `
|
||||||
const myVar = "hello"
|
const myVar = "hello"
|
||||||
log(5, myVar)`
|
log(5, myVar)`
|
||||||
const programMemoryOverride = {
|
const programMemoryOverride: ProgramMemory['root'] = {
|
||||||
log: jest.fn(),
|
log: {
|
||||||
|
type: 'userVal',
|
||||||
|
value: jest.fn(),
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
sourceRange: [0, 0],
|
||||||
|
pathToNode: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
const { root } = executor(abstractSyntaxTree(lexer(code)), {
|
const { root } = executor(abstractSyntaxTree(lexer(code)), {
|
||||||
root: programMemoryOverride,
|
root: programMemoryOverride,
|
||||||
_sketch: [],
|
_sketch: [],
|
||||||
})
|
})
|
||||||
expect(root.myVar).toBe('hello')
|
expect(root.myVar.value).toBe('hello')
|
||||||
expect(programMemoryOverride.log).toHaveBeenCalledWith(5, 'hello')
|
expect(programMemoryOverride.log.value).toHaveBeenCalledWith(5, 'hello')
|
||||||
})
|
})
|
||||||
it('fn funcN = () => {} execute', () => {
|
it('fn funcN = () => {} execute', () => {
|
||||||
const { root } = exe(
|
const { root } = exe(
|
||||||
@ -50,39 +58,71 @@ log(5, myVar)`
|
|||||||
'const magicNum = funcN(9, theVar)',
|
'const magicNum = funcN(9, theVar)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
)
|
)
|
||||||
expect(root.theVar).toBe(60)
|
expect(root.theVar.value).toBe(60)
|
||||||
expect(root.magicNum).toBe(69)
|
expect(root.magicNum.value).toBe(69)
|
||||||
})
|
})
|
||||||
it('sketch declaration', () => {
|
it('sketch declaration', () => {
|
||||||
let code = `sketch mySketch {
|
let code = `sketch mySketch {
|
||||||
path myPath = lineTo(0,1)
|
path myPath = lineTo(0,2)
|
||||||
lineTo(1,1)
|
lineTo(2,3)
|
||||||
path rightPath = lineTo(1,0)
|
path rightPath = lineTo(5,-1)
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
show(mySketch)
|
show(mySketch)
|
||||||
`
|
`
|
||||||
const { root, return: _return } = exe(code)
|
const { root, return: _return } = exe(code)
|
||||||
expect(
|
// geo is three js buffer geometry and is very bloated to have in tests
|
||||||
root.mySketch.sketch.map(
|
const minusGeo = removeGeoFromPaths(root.mySketch.value)
|
||||||
({ previousPath, firstPath, geo, ...rest }: any) => rest
|
expect(minusGeo).toEqual([
|
||||||
)
|
|
||||||
).toEqual([
|
|
||||||
{ type: 'base', from: [0, 0], sourceRange: [0, 0] },
|
|
||||||
{ type: 'toPoint', to: [0, 1], sourceRange: [25, 45], name: 'myPath' },
|
|
||||||
{ type: 'toPoint', to: [1, 1], sourceRange: [48, 59] },
|
|
||||||
{ type: 'toPoint', to: [1, 0], sourceRange: [67, 90], name: 'rightPath' },
|
|
||||||
{
|
{
|
||||||
type: 'close',
|
type: 'toPoint',
|
||||||
sourceRange: [93, 100],
|
to: [0, 2],
|
||||||
|
from: [5, -1],
|
||||||
|
__geoMeta: {
|
||||||
|
sourceRange: [25, 45],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
|
name: 'myPath',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'toPoint',
|
||||||
|
to: [2, 3],
|
||||||
|
from: [0, 2],
|
||||||
|
__geoMeta: {
|
||||||
|
sourceRange: [48, 59],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'toPoint',
|
||||||
|
to: [5, -1],
|
||||||
|
from: [2, 3],
|
||||||
|
__geoMeta: {
|
||||||
|
sourceRange: [67, 91],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
|
name: 'rightPath',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'toPoint',
|
||||||
|
from: [5, -1],
|
||||||
|
to: [0, 2],
|
||||||
|
__geoMeta: {
|
||||||
|
sourceRange: [94, 101],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
// expect(root.mySketch.sketch[0]).toEqual(root.mySketch.sketch[4].firstPath)
|
// expect(root.mySketch.sketch[0]).toEqual(root.mySketch.sketch[4].firstPath)
|
||||||
expect(_return).toEqual([
|
expect(_return).toEqual([
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 108,
|
start: 109,
|
||||||
end: 116,
|
end: 117,
|
||||||
name: 'mySketch',
|
name: 'mySketch',
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
@ -94,7 +134,7 @@ show(mySketch)
|
|||||||
'const myVar = 5 + 1 |> myFn(%)',
|
'const myVar = 5 + 1 |> myFn(%)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
expect(root.myVar).toBe(7)
|
expect(root.myVar.value).toBe(7)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('rotated sketch', () => {
|
it('rotated sketch', () => {
|
||||||
@ -108,8 +148,20 @@ show(mySketch)
|
|||||||
// 'show(mySk1)',
|
// 'show(mySk1)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
expect(root.mySk1.sketch).toHaveLength(4)
|
expect(root.mySk1.value).toHaveLength(3)
|
||||||
expect(root?.rotated?.type).toBe('transform')
|
expect(root?.rotated?.type).toBe('sketchGroup')
|
||||||
|
if (
|
||||||
|
root?.mySk1?.type !== 'sketchGroup' ||
|
||||||
|
root?.rotated?.type !== 'sketchGroup'
|
||||||
|
)
|
||||||
|
throw new Error('not a sketch group')
|
||||||
|
expect(root.mySk1.rotation).toEqual([0, 0, 0, 1])
|
||||||
|
expect(root.rotated.rotation.map((a) => a.toFixed(4))).toEqual([
|
||||||
|
'0.7071',
|
||||||
|
'0.0000',
|
||||||
|
'0.0000',
|
||||||
|
'0.7071',
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('execute pipe sketch into call expression', () => {
|
it('execute pipe sketch into call expression', () => {
|
||||||
@ -121,73 +173,87 @@ show(mySketch)
|
|||||||
'} |> rx(90, %)',
|
'} |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
const striptVersion = removeGeoFromSketch(root.mySk1)
|
const striptVersion = removeGeoFromSketch(root.mySk1 as SketchGroup)
|
||||||
expect(striptVersion).toEqual({
|
expect(striptVersion).toEqual({
|
||||||
type: 'sketchGeo',
|
type: 'sketchGroup',
|
||||||
sketch: [
|
value: [
|
||||||
{
|
|
||||||
type: 'base',
|
|
||||||
from: [0, 0],
|
|
||||||
sourceRange: [0, 0],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'toPoint',
|
type: 'toPoint',
|
||||||
to: [1, 1],
|
to: [1, 1],
|
||||||
|
from: [0, 0],
|
||||||
|
__geoMeta: {
|
||||||
sourceRange: [17, 28],
|
sourceRange: [17, 28],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'toPoint',
|
type: 'toPoint',
|
||||||
to: [0, 1],
|
to: [0, 1],
|
||||||
|
from: [1, 1],
|
||||||
|
__geoMeta: {
|
||||||
sourceRange: [36, 57],
|
sourceRange: [36, 57],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
name: 'myPath',
|
name: 'myPath',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'toPoint',
|
type: 'toPoint',
|
||||||
to: [1, 1],
|
to: [1, 1],
|
||||||
|
from: [0, 1],
|
||||||
|
__geoMeta: {
|
||||||
sourceRange: [60, 71],
|
sourceRange: [60, 71],
|
||||||
|
pathToNode: [],
|
||||||
|
geos: ['line', 'lineEnd'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
position: [0, 0, 0],
|
||||||
|
rotation: [0.7071067811865475, 0, 0, 0.7071067811865476],
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
sourceRange: [13, 73],
|
sourceRange: [13, 73],
|
||||||
|
pathToNode: ['body', 0, 'declarations', 0, 'init', 0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRange: [77, 86],
|
||||||
|
pathToNode: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
// old expect
|
|
||||||
// expect(striptVersion).toEqual({
|
|
||||||
// type: 'transform',
|
|
||||||
// rotation: [1.5707963267948966, 0, 0],
|
|
||||||
// transform: [0, 0, 0],
|
|
||||||
// sketch: [
|
|
||||||
// {
|
|
||||||
// type: 'base',
|
|
||||||
// from: [0, 0],
|
|
||||||
// sourceRange: [0, 0],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: 'toPoint',
|
|
||||||
// to: [1, 1],
|
|
||||||
// sourceRange: [17, 28],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: 'toPoint',
|
|
||||||
// to: [0, 1],
|
|
||||||
// sourceRange: [36, 57],
|
|
||||||
// name: 'myPath',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: 'toPoint',
|
|
||||||
// to: [1, 1],
|
|
||||||
// sourceRange: [60, 71],
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// sourceRange: [77, 86],
|
|
||||||
// })
|
|
||||||
})
|
})
|
||||||
it('execute array expression', () => {
|
it('execute array expression', () => {
|
||||||
const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
|
const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
|
||||||
'\n'
|
'\n'
|
||||||
)
|
)
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
|
// TODO path to node is probably wrong here, zero indexes are not correct
|
||||||
expect(root).toEqual({
|
expect(root).toEqual({
|
||||||
three: 3,
|
three: {
|
||||||
yo: [1, '2', 3, 9],
|
type: 'userVal',
|
||||||
|
value: 3,
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
pathToNode: ['body', 0, 'declarations', 0, 'init'],
|
||||||
|
sourceRange: [14, 15],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
yo: {
|
||||||
|
type: 'userVal',
|
||||||
|
value: [1, '2', 3, 9],
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
pathToNode: ['body', 1, 'declarations', 0, 'init'],
|
||||||
|
sourceRange: [27, 49],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pathToNode: ['body', 0, 'declarations', 0, 'init'],
|
||||||
|
sourceRange: [14, 15],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('execute object expression', () => {
|
it('execute object expression', () => {
|
||||||
@ -196,14 +262,20 @@ show(mySketch)
|
|||||||
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
expect(root).toEqual({
|
expect(root.yo).toEqual({
|
||||||
three: 3,
|
type: 'userVal',
|
||||||
yo: {
|
value: {
|
||||||
aStr: 'str',
|
aStr: 'str',
|
||||||
anum: 2,
|
anum: 2,
|
||||||
identifier: 3,
|
identifier: 3,
|
||||||
binExp: 9,
|
binExp: 9,
|
||||||
},
|
},
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
pathToNode: ['body', 1, 'declarations', 0, 'init'],
|
||||||
|
sourceRange: [27, 83],
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('execute memberExpression', () => {
|
it('execute memberExpression', () => {
|
||||||
@ -211,11 +283,15 @@ show(mySketch)
|
|||||||
'\n'
|
'\n'
|
||||||
)
|
)
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
expect(root).toEqual({
|
expect(root.myVar).toEqual({
|
||||||
yo: {
|
type: 'userVal',
|
||||||
a: { b: '123' },
|
value: '123',
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
pathToNode: ['body', 1, 'declarations', 0, 'init'],
|
||||||
|
sourceRange: [41, 50],
|
||||||
},
|
},
|
||||||
myVar: '123',
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -231,15 +307,22 @@ function exe(
|
|||||||
return executor(ast, programMemory)
|
return executor(ast, programMemory)
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeGeoFromSketch(sketch: Transform | SketchGeo): any {
|
function removeGeoFromSketch(sketch: SketchGroup): any {
|
||||||
if (sketch.type !== 'sketchGeo' && sketch.type === 'transform') {
|
|
||||||
return removeGeoFromSketch(sketch.sketch as any) // TODO fix type
|
|
||||||
}
|
|
||||||
if (sketch.type === 'sketchGeo') {
|
|
||||||
return {
|
return {
|
||||||
...sketch,
|
...sketch,
|
||||||
sketch: sketch.sketch.map(({ geo, previousPath, ...rest }: any) => rest),
|
value: removeGeoFromPaths(sketch.value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('not a sketch')
|
|
||||||
|
function removeGeoFromPaths(paths: Path[]): any[] {
|
||||||
|
return paths.map((path: Path) => {
|
||||||
|
const newGeos = path?.__geoMeta?.geos.map((geo) => geo.type)
|
||||||
|
return {
|
||||||
|
...path,
|
||||||
|
__geoMeta: {
|
||||||
|
...path.__geoMeta,
|
||||||
|
geos: newGeos,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -5,21 +5,108 @@ import {
|
|||||||
PipeExpression,
|
PipeExpression,
|
||||||
ObjectExpression,
|
ObjectExpression,
|
||||||
MemberExpression,
|
MemberExpression,
|
||||||
|
Identifier,
|
||||||
} from './abstractSyntaxTree'
|
} from './abstractSyntaxTree'
|
||||||
import { Path, Transform, SketchGeo, sketchFns, ExtrudeGeo } from './sketch'
|
import { sketchFns } from './sketch'
|
||||||
import { BufferGeometry, Quaternion, Vector3 } from 'three'
|
import { BufferGeometry } from 'three'
|
||||||
import { LineGeos } from './engine'
|
|
||||||
|
export type SourceRange = [number, number]
|
||||||
|
export type PathToNode = (string | number)[]
|
||||||
|
export type Metadata = {
|
||||||
|
sourceRange: SourceRange
|
||||||
|
pathToNode: PathToNode
|
||||||
|
}
|
||||||
|
export type Position = [number, number, number]
|
||||||
|
export type Rotation = [number, number, number, number]
|
||||||
|
|
||||||
|
interface BasePath {
|
||||||
|
from: [number, number]
|
||||||
|
to: [number, number]
|
||||||
|
name?: string
|
||||||
|
__geoMeta: {
|
||||||
|
geos: {
|
||||||
|
geo: BufferGeometry
|
||||||
|
type: 'line' | 'lineEnd'
|
||||||
|
}[]
|
||||||
|
sourceRange: SourceRange
|
||||||
|
pathToNode: PathToNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToPoint extends BasePath {
|
||||||
|
type: 'toPoint'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HorizontalLineTo extends BasePath {
|
||||||
|
type: 'horizontalLineTo'
|
||||||
|
x: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AngledLineTo extends BasePath {
|
||||||
|
type: 'angledLineTo'
|
||||||
|
angle: number
|
||||||
|
x?: number
|
||||||
|
y?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GeoMeta {
|
||||||
|
__geoMeta: {
|
||||||
|
geo: BufferGeometry
|
||||||
|
sourceRange: SourceRange
|
||||||
|
pathToNode: PathToNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Path = ToPoint | HorizontalLineTo | AngledLineTo
|
||||||
|
|
||||||
|
export interface SketchGroup {
|
||||||
|
type: 'sketchGroup'
|
||||||
|
value: Path[]
|
||||||
|
position: Position
|
||||||
|
rotation: Rotation
|
||||||
|
__meta: Metadata[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtrudePlane {
|
||||||
|
type: 'extrudePlane'
|
||||||
|
position: Position
|
||||||
|
rotation: Rotation
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExtrudeSurface = GeoMeta &
|
||||||
|
ExtrudePlane /* | ExtrudeRadius | ExtrudeSpline */
|
||||||
|
|
||||||
|
export interface ExtrudeGroup {
|
||||||
|
type: 'extrudeGroup'
|
||||||
|
value: ExtrudeSurface[]
|
||||||
|
height: number
|
||||||
|
position: Position
|
||||||
|
rotation: Rotation
|
||||||
|
__meta: Metadata[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** UserVal not produced by one of our internal functions */
|
||||||
|
export interface UserVal {
|
||||||
|
type: 'userVal'
|
||||||
|
value: any
|
||||||
|
__meta: Metadata[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Memory {
|
||||||
|
[key: string]: UserVal | SketchGroup | ExtrudeGroup // | Memory
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProgramMemory {
|
export interface ProgramMemory {
|
||||||
root: { [key: string]: any }
|
root: Memory
|
||||||
return?: any
|
return?: Identifier[]
|
||||||
_sketch: Path[]
|
_sketch?: Path[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const executor = (
|
export const executor = (
|
||||||
node: Program,
|
node: Program,
|
||||||
programMemory: ProgramMemory = { root: {}, _sketch: [] },
|
programMemory: ProgramMemory = { root: {}, _sketch: [] },
|
||||||
options: { bodyType: 'root' | 'sketch' | 'block' } = { bodyType: 'root' }
|
options: { bodyType: 'root' | 'sketch' | 'block' } = { bodyType: 'root' },
|
||||||
|
previousPathToNode: PathToNode = []
|
||||||
): ProgramMemory => {
|
): ProgramMemory => {
|
||||||
const _programMemory: ProgramMemory = {
|
const _programMemory: ProgramMemory = {
|
||||||
root: {
|
root: {
|
||||||
@ -29,33 +116,82 @@ export const executor = (
|
|||||||
return: programMemory.return,
|
return: programMemory.return,
|
||||||
}
|
}
|
||||||
const { body } = node
|
const { body } = node
|
||||||
body.forEach((statement) => {
|
body.forEach((statement, bodyIndex) => {
|
||||||
if (statement.type === 'VariableDeclaration') {
|
if (statement.type === 'VariableDeclaration') {
|
||||||
statement.declarations.forEach((declaration) => {
|
statement.declarations.forEach((declaration, index) => {
|
||||||
const variableName = declaration.id.name
|
const variableName = declaration.id.name
|
||||||
|
const pathToNode = [
|
||||||
|
...previousPathToNode,
|
||||||
|
'body',
|
||||||
|
bodyIndex,
|
||||||
|
'declarations',
|
||||||
|
index,
|
||||||
|
'init',
|
||||||
|
]
|
||||||
|
const sourceRange: SourceRange = [
|
||||||
|
declaration.init.start,
|
||||||
|
declaration.init.end,
|
||||||
|
]
|
||||||
|
const __meta: Metadata[] = [
|
||||||
|
{
|
||||||
|
pathToNode,
|
||||||
|
sourceRange,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
if (declaration.init.type === 'PipeExpression') {
|
if (declaration.init.type === 'PipeExpression') {
|
||||||
_programMemory.root[variableName] = getPipeExpressionResult(
|
const value = getPipeExpressionResult(
|
||||||
declaration.init,
|
declaration.init,
|
||||||
_programMemory
|
_programMemory,
|
||||||
|
pathToNode
|
||||||
)
|
)
|
||||||
|
if (value?.type === 'sketchGroup' || value?.type === 'extrudeGroup') {
|
||||||
|
_programMemory.root[variableName] = value
|
||||||
|
} else {
|
||||||
|
_programMemory.root[variableName] = {
|
||||||
|
type: 'userVal',
|
||||||
|
value,
|
||||||
|
__meta,
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (declaration.init.type === 'Literal') {
|
} else if (declaration.init.type === 'Literal') {
|
||||||
_programMemory.root[variableName] = declaration.init.value
|
_programMemory.root[variableName] = {
|
||||||
|
type: 'userVal',
|
||||||
|
value: declaration.init.value,
|
||||||
|
__meta,
|
||||||
|
}
|
||||||
} else if (declaration.init.type === 'BinaryExpression') {
|
} else if (declaration.init.type === 'BinaryExpression') {
|
||||||
_programMemory.root[variableName] = getBinaryExpressionResult(
|
_programMemory.root[variableName] = {
|
||||||
declaration.init,
|
type: 'userVal',
|
||||||
_programMemory
|
value: getBinaryExpressionResult(declaration.init, _programMemory),
|
||||||
)
|
__meta,
|
||||||
|
}
|
||||||
} else if (declaration.init.type === 'ArrayExpression') {
|
} else if (declaration.init.type === 'ArrayExpression') {
|
||||||
_programMemory.root[variableName] = declaration.init.elements.map(
|
const valueInfo: { value: any; __meta?: Metadata }[] =
|
||||||
(element) => {
|
declaration.init.elements.map(
|
||||||
|
(element): { value: any; __meta?: Metadata } => {
|
||||||
if (element.type === 'Literal') {
|
if (element.type === 'Literal') {
|
||||||
return element.value
|
return {
|
||||||
|
value: element.value,
|
||||||
|
}
|
||||||
} else if (element.type === 'BinaryExpression') {
|
} else if (element.type === 'BinaryExpression') {
|
||||||
return getBinaryExpressionResult(element, _programMemory)
|
return {
|
||||||
|
value: getBinaryExpressionResult(element, _programMemory),
|
||||||
|
}
|
||||||
} else if (element.type === 'PipeExpression') {
|
} else if (element.type === 'PipeExpression') {
|
||||||
return getPipeExpressionResult(element, _programMemory)
|
return {
|
||||||
|
value: getPipeExpressionResult(
|
||||||
|
element,
|
||||||
|
_programMemory,
|
||||||
|
pathToNode
|
||||||
|
),
|
||||||
|
}
|
||||||
} else if (element.type === 'Identifier') {
|
} else if (element.type === 'Identifier') {
|
||||||
return _programMemory.root[element.name]
|
const node = _programMemory.root[element.name]
|
||||||
|
return {
|
||||||
|
value: node.value,
|
||||||
|
__meta: node.__meta[node.__meta.length - 1],
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected element type ${element.type} in array expression`
|
`Unexpected element type ${element.type} in array expression`
|
||||||
@ -63,9 +199,20 @@ export const executor = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
const meta = valueInfo
|
||||||
|
.filter(({ __meta }) => __meta)
|
||||||
|
.map(({ __meta }) => __meta) as Metadata[]
|
||||||
|
_programMemory.root[variableName] = {
|
||||||
|
type: 'userVal',
|
||||||
|
value: valueInfo.map(({ value }) => value),
|
||||||
|
__meta: [...__meta, ...meta],
|
||||||
|
}
|
||||||
} else if (declaration.init.type === 'ObjectExpression') {
|
} else if (declaration.init.type === 'ObjectExpression') {
|
||||||
const obj = executeObjectExpression(_programMemory, declaration.init)
|
_programMemory.root[variableName] = {
|
||||||
_programMemory.root[variableName] = obj
|
type: 'userVal',
|
||||||
|
value: executeObjectExpression(_programMemory, declaration.init),
|
||||||
|
__meta,
|
||||||
|
}
|
||||||
} else if (declaration.init.type === 'SketchExpression') {
|
} else if (declaration.init.type === 'SketchExpression') {
|
||||||
const sketchInit = declaration.init
|
const sketchInit = declaration.init
|
||||||
const fnMemory: ProgramMemory = {
|
const fnMemory: ProgramMemory = {
|
||||||
@ -77,26 +224,20 @@ export const executor = (
|
|||||||
let { _sketch } = executor(sketchInit.body, fnMemory, {
|
let { _sketch } = executor(sketchInit.body, fnMemory, {
|
||||||
bodyType: 'sketch',
|
bodyType: 'sketch',
|
||||||
})
|
})
|
||||||
if (_sketch.length === 0) {
|
const newSketch: SketchGroup = {
|
||||||
const { programMemory: newProgramMemory } = sketchFns.base(
|
type: 'sketchGroup',
|
||||||
fnMemory,
|
value: _sketch || [],
|
||||||
'',
|
position: [0, 0, 0],
|
||||||
[0, 0],
|
rotation: [0, 0, 0, 1], //x,y,z,w
|
||||||
0,
|
__meta,
|
||||||
0
|
|
||||||
)
|
|
||||||
_sketch = newProgramMemory._sketch
|
|
||||||
}
|
|
||||||
const newSketch: SketchGeo = {
|
|
||||||
type: 'sketchGeo',
|
|
||||||
sketch: _sketch,
|
|
||||||
sourceRange: [sketchInit.start, sketchInit.end],
|
|
||||||
}
|
}
|
||||||
_programMemory.root[variableName] = newSketch
|
_programMemory.root[variableName] = newSketch
|
||||||
} else if (declaration.init.type === 'FunctionExpression') {
|
} else if (declaration.init.type === 'FunctionExpression') {
|
||||||
const fnInit = declaration.init
|
const fnInit = declaration.init
|
||||||
|
|
||||||
_programMemory.root[declaration.id.name] = (...args: any[]) => {
|
_programMemory.root[declaration.id.name] = {
|
||||||
|
type: 'userVal',
|
||||||
|
value: (...args: any[]) => {
|
||||||
const fnMemory: ProgramMemory = {
|
const fnMemory: ProgramMemory = {
|
||||||
root: {
|
root: {
|
||||||
..._programMemory.root,
|
..._programMemory.root,
|
||||||
@ -113,28 +254,36 @@ export const executor = (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
fnInit.params.forEach((param, index) => {
|
fnInit.params.forEach((param, index) => {
|
||||||
fnMemory.root[param.name] = args[index]
|
fnMemory.root[param.name] = {
|
||||||
|
type: 'userVal',
|
||||||
|
value: args[index],
|
||||||
|
__meta,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return executor(fnInit.body, fnMemory, { bodyType: 'block' }).return
|
return executor(fnInit.body, fnMemory, { bodyType: 'block' })
|
||||||
|
.return
|
||||||
|
},
|
||||||
|
__meta,
|
||||||
}
|
}
|
||||||
} else if (declaration.init.type === 'MemberExpression') {
|
} else if (declaration.init.type === 'MemberExpression') {
|
||||||
_programMemory.root[variableName] = getMemberExpressionResult(
|
_programMemory.root[variableName] = {
|
||||||
declaration.init,
|
type: 'userVal',
|
||||||
_programMemory
|
value: getMemberExpressionResult(declaration.init, _programMemory),
|
||||||
)
|
__meta,
|
||||||
|
}
|
||||||
} else if (declaration.init.type === 'CallExpression') {
|
} else if (declaration.init.type === 'CallExpression') {
|
||||||
const functionName = declaration.init.callee.name
|
const functionName = declaration.init.callee.name
|
||||||
const fnArgs = declaration.init.arguments.map((arg) => {
|
const fnArgs = declaration.init.arguments.map((arg) => {
|
||||||
if (arg.type === 'Literal') {
|
if (arg.type === 'Literal') {
|
||||||
return arg.value
|
return arg.value
|
||||||
} else if (arg.type === 'Identifier') {
|
} else if (arg.type === 'Identifier') {
|
||||||
return _programMemory.root[arg.name]
|
return _programMemory.root[arg.name].value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (
|
if (
|
||||||
'lineTo' === functionName ||
|
'lineTo' === functionName ||
|
||||||
'close' === functionName ||
|
'close' === functionName // ||
|
||||||
'base' === functionName
|
// 'base' === functionName
|
||||||
) {
|
) {
|
||||||
if (options.bodyType !== 'sketch') {
|
if (options.bodyType !== 'sketch') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -148,7 +297,11 @@ export const executor = (
|
|||||||
...fnArgs
|
...fnArgs
|
||||||
)
|
)
|
||||||
_programMemory._sketch = result.programMemory._sketch
|
_programMemory._sketch = result.programMemory._sketch
|
||||||
_programMemory.root[variableName] = result.currentPath
|
_programMemory.root[variableName] = {
|
||||||
|
type: 'userVal',
|
||||||
|
value: result.currentPath,
|
||||||
|
__meta,
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
'rx' === functionName ||
|
'rx' === functionName ||
|
||||||
'ry' === functionName ||
|
'ry' === functionName ||
|
||||||
@ -162,9 +315,9 @@ export const executor = (
|
|||||||
_programMemory,
|
_programMemory,
|
||||||
[declaration.start, declaration.end],
|
[declaration.start, declaration.end],
|
||||||
fnArgs[0],
|
fnArgs[0],
|
||||||
sketchVal
|
sketchVal as any // todo memory redo
|
||||||
)
|
)
|
||||||
_programMemory.root[variableName] = result
|
_programMemory.root[variableName] = result as any // todo memory redo
|
||||||
} else if (functionName === 'extrude') {
|
} else if (functionName === 'extrude') {
|
||||||
const sketch = declaration.init.arguments[1]
|
const sketch = declaration.init.arguments[1]
|
||||||
if (sketch.type !== 'Identifier')
|
if (sketch.type !== 'Identifier')
|
||||||
@ -175,9 +328,9 @@ export const executor = (
|
|||||||
'yo',
|
'yo',
|
||||||
[declaration.start, declaration.end],
|
[declaration.start, declaration.end],
|
||||||
fnArgs[0],
|
fnArgs[0],
|
||||||
sketchVal
|
sketchVal as any // todo memory redo
|
||||||
)
|
)
|
||||||
_programMemory.root[variableName] = result
|
_programMemory.root[variableName] = result as any // todo memory redo
|
||||||
} else if (functionName === 'translate') {
|
} else if (functionName === 'translate') {
|
||||||
const sketch = declaration.init.arguments[1]
|
const sketch = declaration.init.arguments[1]
|
||||||
if (sketch.type !== 'Identifier')
|
if (sketch.type !== 'Identifier')
|
||||||
@ -187,13 +340,15 @@ export const executor = (
|
|||||||
_programMemory,
|
_programMemory,
|
||||||
[declaration.start, declaration.end],
|
[declaration.start, declaration.end],
|
||||||
fnArgs[0],
|
fnArgs[0],
|
||||||
sketchVal
|
sketchVal as any // todo memory redo
|
||||||
)
|
)
|
||||||
_programMemory.root[variableName] = result
|
_programMemory.root[variableName] = result as any // todo memory redo
|
||||||
} else {
|
} else {
|
||||||
_programMemory.root[variableName] = _programMemory.root[
|
_programMemory.root[variableName] = {
|
||||||
functionName
|
type: 'userVal',
|
||||||
](...fnArgs)
|
value: _programMemory.root[functionName].value(...fnArgs),
|
||||||
|
__meta,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -209,13 +364,13 @@ export const executor = (
|
|||||||
if (arg.type === 'Literal') {
|
if (arg.type === 'Literal') {
|
||||||
return arg.value
|
return arg.value
|
||||||
} else if (arg.type === 'Identifier') {
|
} else if (arg.type === 'Identifier') {
|
||||||
return _programMemory.root[arg.name]
|
return _programMemory.root[arg.name].value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (
|
if (
|
||||||
'lineTo' === functionName ||
|
'lineTo' === functionName ||
|
||||||
'close' === functionName ||
|
'close' === functionName
|
||||||
'base' === functionName
|
// || 'base' === functionName
|
||||||
) {
|
) {
|
||||||
if (options.bodyType !== 'sketch') {
|
if (options.bodyType !== 'sketch') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -228,14 +383,14 @@ export const executor = (
|
|||||||
[statement.start, statement.end],
|
[statement.start, statement.end],
|
||||||
...args
|
...args
|
||||||
)
|
)
|
||||||
_programMemory._sketch = [...result.programMemory._sketch]
|
_programMemory._sketch = [...(result.programMemory._sketch || [])]
|
||||||
} else if ('show' === functionName) {
|
} else if ('show' === functionName) {
|
||||||
if (options.bodyType !== 'root') {
|
if (options.bodyType !== 'root') {
|
||||||
throw new Error(`Cannot call ${functionName} outside of a root`)
|
throw new Error(`Cannot call ${functionName} outside of a root`)
|
||||||
}
|
}
|
||||||
_programMemory.return = expression.arguments
|
_programMemory.return = expression.arguments as any // todo memory redo
|
||||||
} else {
|
} else {
|
||||||
_programMemory.root[functionName](...args)
|
_programMemory.root[functionName].value(...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (statement.type === 'ReturnStatement') {
|
} else if (statement.type === 'ReturnStatement') {
|
||||||
@ -262,7 +417,7 @@ function getMemberExpressionResult(
|
|||||||
const object: any =
|
const object: any =
|
||||||
expression.object.type === 'MemberExpression'
|
expression.object.type === 'MemberExpression'
|
||||||
? getMemberExpressionResult(expression.object, programMemory)
|
? getMemberExpressionResult(expression.object, programMemory)
|
||||||
: programMemory.root[expression.object.name]
|
: programMemory.root[expression.object.name].value
|
||||||
return object[propertyName]
|
return object[propertyName]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +429,7 @@ function getBinaryExpressionResult(
|
|||||||
if (part.type === 'Literal') {
|
if (part.type === 'Literal') {
|
||||||
return part.value
|
return part.value
|
||||||
} else if (part.type === 'Identifier') {
|
} else if (part.type === 'Identifier') {
|
||||||
return programMemory.root[part.name]
|
return programMemory.root[part.name].value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const left = getVal(expression.left)
|
const left = getVal(expression.left)
|
||||||
@ -284,9 +439,14 @@ function getBinaryExpressionResult(
|
|||||||
|
|
||||||
function getPipeExpressionResult(
|
function getPipeExpressionResult(
|
||||||
expression: PipeExpression,
|
expression: PipeExpression,
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory,
|
||||||
|
previousPathToNode: PathToNode = []
|
||||||
) {
|
) {
|
||||||
const executedBody = executePipeBody(expression.body, programMemory)
|
const executedBody = executePipeBody(
|
||||||
|
expression.body,
|
||||||
|
programMemory,
|
||||||
|
previousPathToNode
|
||||||
|
)
|
||||||
const result = executedBody[executedBody.length - 1]
|
const result = executedBody[executedBody.length - 1]
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -294,6 +454,7 @@ function getPipeExpressionResult(
|
|||||||
function executePipeBody(
|
function executePipeBody(
|
||||||
body: PipeExpression['body'],
|
body: PipeExpression['body'],
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
|
previousPathToNode: PathToNode = [],
|
||||||
expressionIndex = 0,
|
expressionIndex = 0,
|
||||||
previousResults: any[] = []
|
previousResults: any[] = []
|
||||||
): any[] {
|
): any[] {
|
||||||
@ -303,10 +464,13 @@ function executePipeBody(
|
|||||||
const expression = body[expressionIndex]
|
const expression = body[expressionIndex]
|
||||||
if (expression.type === 'BinaryExpression') {
|
if (expression.type === 'BinaryExpression') {
|
||||||
const result = getBinaryExpressionResult(expression, programMemory)
|
const result = getBinaryExpressionResult(expression, programMemory)
|
||||||
return executePipeBody(body, programMemory, expressionIndex + 1, [
|
return executePipeBody(
|
||||||
...previousResults,
|
body,
|
||||||
result,
|
programMemory,
|
||||||
])
|
previousPathToNode,
|
||||||
|
expressionIndex + 1,
|
||||||
|
[...previousResults, result]
|
||||||
|
)
|
||||||
} else if (expression.type === 'CallExpression') {
|
} else if (expression.type === 'CallExpression') {
|
||||||
const functionName = expression.callee.name
|
const functionName = expression.callee.name
|
||||||
const fnArgs = expression.arguments.map((arg) => {
|
const fnArgs = expression.arguments.map((arg) => {
|
||||||
@ -341,10 +505,13 @@ function executePipeBody(
|
|||||||
fnArgs[0],
|
fnArgs[0],
|
||||||
fnArgs[1]
|
fnArgs[1]
|
||||||
)
|
)
|
||||||
return executePipeBody(body, programMemory, expressionIndex + 1, [
|
return executePipeBody(
|
||||||
...previousResults,
|
body,
|
||||||
result,
|
programMemory,
|
||||||
])
|
previousPathToNode,
|
||||||
|
expressionIndex + 1,
|
||||||
|
[...previousResults, result]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (functionName === 'extrude') {
|
if (functionName === 'extrude') {
|
||||||
const result = sketchFns[functionName](
|
const result = sketchFns[functionName](
|
||||||
@ -354,10 +521,13 @@ function executePipeBody(
|
|||||||
fnArgs[0],
|
fnArgs[0],
|
||||||
fnArgs[1]
|
fnArgs[1]
|
||||||
)
|
)
|
||||||
return executePipeBody(body, programMemory, expressionIndex + 1, [
|
return executePipeBody(
|
||||||
...previousResults,
|
body,
|
||||||
result,
|
programMemory,
|
||||||
])
|
previousPathToNode,
|
||||||
|
expressionIndex + 1,
|
||||||
|
[...previousResults, result]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (functionName === 'translate') {
|
if (functionName === 'translate') {
|
||||||
const result = sketchFns[functionName](
|
const result = sketchFns[functionName](
|
||||||
@ -366,16 +536,22 @@ function executePipeBody(
|
|||||||
fnArgs[0],
|
fnArgs[0],
|
||||||
fnArgs[1]
|
fnArgs[1]
|
||||||
)
|
)
|
||||||
return executePipeBody(body, programMemory, expressionIndex + 1, [
|
return executePipeBody(
|
||||||
...previousResults,
|
body,
|
||||||
result,
|
programMemory,
|
||||||
])
|
previousPathToNode,
|
||||||
|
expressionIndex + 1,
|
||||||
|
[...previousResults, result]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const result = programMemory.root[functionName](...fnArgs)
|
const result = programMemory.root[functionName].value(...fnArgs)
|
||||||
return executePipeBody(body, programMemory, expressionIndex + 1, [
|
return executePipeBody(
|
||||||
...previousResults,
|
body,
|
||||||
result,
|
programMemory,
|
||||||
])
|
previousPathToNode,
|
||||||
|
expressionIndex + 1,
|
||||||
|
[...previousResults, result]
|
||||||
|
)
|
||||||
} else if (expression.type === 'SketchExpression') {
|
} else if (expression.type === 'SketchExpression') {
|
||||||
const sketchBody = expression.body
|
const sketchBody = expression.body
|
||||||
const fnMemory: ProgramMemory = {
|
const fnMemory: ProgramMemory = {
|
||||||
@ -387,26 +563,25 @@ function executePipeBody(
|
|||||||
let { _sketch } = executor(sketchBody, fnMemory, {
|
let { _sketch } = executor(sketchBody, fnMemory, {
|
||||||
bodyType: 'sketch',
|
bodyType: 'sketch',
|
||||||
})
|
})
|
||||||
if (_sketch.length === 0) {
|
const newSketch: SketchGroup = {
|
||||||
const { programMemory: newProgramMemory } = sketchFns.base(
|
type: 'sketchGroup',
|
||||||
fnMemory,
|
value: _sketch || [],
|
||||||
'',
|
position: [0, 0, 0],
|
||||||
[0, 0],
|
rotation: [0, 0, 0, 1], //x,y,z,w
|
||||||
0,
|
__meta: [
|
||||||
0
|
{
|
||||||
)
|
|
||||||
_sketch = newProgramMemory._sketch
|
|
||||||
}
|
|
||||||
// _programMemory.root[variableName] = _sketch
|
|
||||||
const newSketch: SketchGeo = {
|
|
||||||
type: 'sketchGeo',
|
|
||||||
sketch: _sketch,
|
|
||||||
sourceRange: [expression.start, expression.end],
|
sourceRange: [expression.start, expression.end],
|
||||||
|
pathToNode: [...previousPathToNode, expressionIndex],
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
return executePipeBody(body, programMemory, expressionIndex + 1, [
|
return executePipeBody(
|
||||||
...previousResults,
|
body,
|
||||||
newSketch,
|
programMemory,
|
||||||
])
|
previousPathToNode,
|
||||||
|
expressionIndex + 1,
|
||||||
|
[...previousResults, newSketch]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Invalid pipe expression')
|
throw new Error('Invalid pipe expression')
|
||||||
@ -432,7 +607,7 @@ function executeObjectExpression(
|
|||||||
_programMemory
|
_programMemory
|
||||||
)
|
)
|
||||||
} else if (property.value.type === 'Identifier') {
|
} else if (property.value.type === 'Identifier') {
|
||||||
obj[property.key.name] = _programMemory.root[property.value.name]
|
obj[property.key.name] = _programMemory.root[property.value.name].value
|
||||||
} else if (property.value.type === 'ObjectExpression') {
|
} else if (property.value.type === 'ObjectExpression') {
|
||||||
obj[property.key.name] = executeObjectExpression(
|
obj[property.key.name] = executeObjectExpression(
|
||||||
_programMemory,
|
_programMemory,
|
||||||
@ -451,125 +626,3 @@ function executeObjectExpression(
|
|||||||
})
|
})
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
type SourceRange = [number, number]
|
|
||||||
|
|
||||||
export type ViewerArtifact =
|
|
||||||
| {
|
|
||||||
type: 'sketchLine'
|
|
||||||
sourceRange: SourceRange
|
|
||||||
geo: LineGeos
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'sketchBase'
|
|
||||||
sourceRange: SourceRange
|
|
||||||
geo: BufferGeometry
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'extrudeWall'
|
|
||||||
sourceRange: SourceRange
|
|
||||||
geo: BufferGeometry
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'parent'
|
|
||||||
sourceRange: SourceRange
|
|
||||||
children: ViewerArtifact[]
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'sketch'
|
|
||||||
sourceRange: SourceRange
|
|
||||||
children: ViewerArtifact[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type PreviousTransforms = {
|
|
||||||
rotation: Quaternion
|
|
||||||
transform: [number, number, number]
|
|
||||||
}[]
|
|
||||||
|
|
||||||
export const processShownObjects = (
|
|
||||||
programMemory: ProgramMemory,
|
|
||||||
geoMeta: SketchGeo | ExtrudeGeo | Transform,
|
|
||||||
previousTransforms: PreviousTransforms = []
|
|
||||||
): ViewerArtifact[] => {
|
|
||||||
if (geoMeta?.type === 'sketchGeo') {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
type: 'sketch',
|
|
||||||
sourceRange: geoMeta.sourceRange,
|
|
||||||
children: geoMeta.sketch.map(({ geo, sourceRange, type }) => {
|
|
||||||
if (type === 'toPoint') {
|
|
||||||
const newGeo: LineGeos = {
|
|
||||||
line: geo.line.clone(),
|
|
||||||
tip: geo.tip.clone(),
|
|
||||||
centre: geo.centre.clone(),
|
|
||||||
}
|
|
||||||
let rotationQuaternion = new Quaternion()
|
|
||||||
let position = new Vector3(0, 0, 0)
|
|
||||||
previousTransforms.forEach(({ rotation, transform }) => {
|
|
||||||
const newQuant = rotation.clone()
|
|
||||||
newQuant.multiply(rotationQuaternion)
|
|
||||||
rotationQuaternion.copy(newQuant)
|
|
||||||
position.applyQuaternion(rotation)
|
|
||||||
position.add(new Vector3(...transform))
|
|
||||||
})
|
|
||||||
Object.values(newGeo).forEach((geoItem: BufferGeometry) => {
|
|
||||||
geoItem.applyQuaternion(rotationQuaternion.clone())
|
|
||||||
const position_ = position.clone()
|
|
||||||
geoItem.translate(position_.x, position_.y, position_.z)
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
type: 'sketchLine',
|
|
||||||
geo: newGeo,
|
|
||||||
sourceRange,
|
|
||||||
}
|
|
||||||
} else if (type === 'base') {
|
|
||||||
const newGeo: BufferGeometry = geo.clone()
|
|
||||||
const rotationQuaternion = new Quaternion()
|
|
||||||
let position = new Vector3(0, 0, 0)
|
|
||||||
// todo don't think this is right
|
|
||||||
previousTransforms.forEach(({ rotation, transform }) => {
|
|
||||||
newGeo.applyQuaternion(rotationQuaternion)
|
|
||||||
newGeo.translate(position.x, position.y, position.z)
|
|
||||||
})
|
|
||||||
newGeo.applyQuaternion(rotationQuaternion)
|
|
||||||
newGeo.translate(position.x, position.y, position.z)
|
|
||||||
return {
|
|
||||||
type: 'sketchBase',
|
|
||||||
geo: newGeo,
|
|
||||||
sourceRange,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error('Unknown geo type')
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} else if (geoMeta.type === 'transform') {
|
|
||||||
const referencedVar = geoMeta.sketch
|
|
||||||
const parentArtifact: ViewerArtifact = {
|
|
||||||
type: 'parent',
|
|
||||||
sourceRange: geoMeta.sourceRange,
|
|
||||||
children: processShownObjects(programMemory, referencedVar, [
|
|
||||||
{
|
|
||||||
rotation: geoMeta.rotation,
|
|
||||||
transform: geoMeta.transform,
|
|
||||||
},
|
|
||||||
...previousTransforms,
|
|
||||||
]),
|
|
||||||
}
|
|
||||||
return [parentArtifact]
|
|
||||||
} else if (geoMeta.type === 'extrudeGeo') {
|
|
||||||
const result: ViewerArtifact[] = geoMeta.surfaces.map((a) => {
|
|
||||||
const geo: BufferGeometry = a.geo.clone()
|
|
||||||
|
|
||||||
geo.translate(a.translate[0], a.translate[1], a.translate[2])
|
|
||||||
geo.applyQuaternion(a.quaternion)
|
|
||||||
return {
|
|
||||||
type: 'extrudeWall',
|
|
||||||
sourceRange: a.sourceRanges[0],
|
|
||||||
geo,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
throw new Error('Unknown geoMeta type')
|
|
||||||
}
|
|
||||||
|
@ -1,100 +1,15 @@
|
|||||||
import { ProgramMemory } from './executor'
|
import {
|
||||||
import { lineGeo, baseGeo, LineGeos, extrudeGeo } from './engine'
|
ProgramMemory,
|
||||||
import { BufferGeometry } from 'three'
|
Path,
|
||||||
|
SketchGroup,
|
||||||
|
ExtrudeGroup,
|
||||||
|
SourceRange,
|
||||||
|
ExtrudeSurface,
|
||||||
|
} from './executor'
|
||||||
|
import { lineGeo, extrudeGeo } from './engine'
|
||||||
import { Quaternion, Vector3 } from 'three'
|
import { Quaternion, Vector3 } from 'three'
|
||||||
|
|
||||||
type Coords2d = [number, number]
|
type Coords2d = [number, number]
|
||||||
type SourceRange = [number, number]
|
|
||||||
type Rotation3 = Quaternion
|
|
||||||
type Translate3 = [number, number, number]
|
|
||||||
|
|
||||||
export type Path =
|
|
||||||
| {
|
|
||||||
type: 'points'
|
|
||||||
name?: string
|
|
||||||
from: Coords2d
|
|
||||||
to: Coords2d
|
|
||||||
geo: BufferGeometry
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'horizontalLineTo'
|
|
||||||
name?: string
|
|
||||||
x: number
|
|
||||||
geo: BufferGeometry
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'verticalLineTo'
|
|
||||||
name?: string
|
|
||||||
y: number
|
|
||||||
geo: BufferGeometry
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'toPoint'
|
|
||||||
name?: string
|
|
||||||
to: Coords2d
|
|
||||||
geo: LineGeos
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'close'
|
|
||||||
name?: string
|
|
||||||
geo: LineGeos
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'base'
|
|
||||||
from: Coords2d
|
|
||||||
geo: BufferGeometry
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Transform {
|
|
||||||
type: 'transform'
|
|
||||||
rotation: Rotation3
|
|
||||||
transform: Translate3
|
|
||||||
sketch: SketchGeo | ExtrudeGeo | Transform
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SketchGeo {
|
|
||||||
type: 'sketchGeo'
|
|
||||||
sketch: Path[]
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExtrudeFace {
|
|
||||||
type: 'extrudeFace'
|
|
||||||
quaternion: Quaternion
|
|
||||||
translate: [number, number, number]
|
|
||||||
geo: BufferGeometry
|
|
||||||
sourceRanges: SourceRange[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExtrudeGeo {
|
|
||||||
type: 'extrudeGeo'
|
|
||||||
surfaces: ExtrudeFace[]
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
|
|
||||||
function addBasePath(programMemory: ProgramMemory) {
|
|
||||||
const geo = baseGeo({ from: [0, 0, 0] })
|
|
||||||
const base: Path = {
|
|
||||||
type: 'base',
|
|
||||||
from: [0, 0],
|
|
||||||
sourceRange: [0, 0],
|
|
||||||
geo,
|
|
||||||
}
|
|
||||||
if (programMemory._sketch?.length === 0) {
|
|
||||||
return {
|
|
||||||
...programMemory,
|
|
||||||
_sketch: [base],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return programMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PathReturn {
|
interface PathReturn {
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
@ -106,70 +21,74 @@ function getCoordsFromPaths(paths: Path[], index = 0): Coords2d {
|
|||||||
if (!currentPath) {
|
if (!currentPath) {
|
||||||
return [0, 0]
|
return [0, 0]
|
||||||
}
|
}
|
||||||
if (currentPath.type === 'points' || currentPath.type === 'toPoint') {
|
if (currentPath.type === 'horizontalLineTo') {
|
||||||
return currentPath.to
|
|
||||||
} else if (currentPath.type === 'base') {
|
|
||||||
return currentPath.from
|
|
||||||
} else if (currentPath.type === 'horizontalLineTo') {
|
|
||||||
const pathBefore = getCoordsFromPaths(paths, index - 1)
|
const pathBefore = getCoordsFromPaths(paths, index - 1)
|
||||||
return [currentPath.x, pathBefore[1]]
|
return [currentPath.x, pathBefore[1]]
|
||||||
} else if (currentPath.type === 'verticalLineTo') {
|
} else if (currentPath.type === 'toPoint') {
|
||||||
const pathBefore = getCoordsFromPaths(paths, index - 1)
|
return [currentPath.to[0], currentPath.to[1]]
|
||||||
return [pathBefore[0], currentPath.y]
|
|
||||||
}
|
}
|
||||||
return [0, 0]
|
return [0, 0]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sketchFns = {
|
export const sketchFns = {
|
||||||
base: (
|
// base: (
|
||||||
programMemory: ProgramMemory,
|
// programMemory: ProgramMemory,
|
||||||
name: string = '',
|
// name: string = '',
|
||||||
sourceRange: SourceRange,
|
// sourceRange: SourceRange,
|
||||||
...args: any[]
|
// ...args: any[]
|
||||||
): PathReturn => {
|
// ): PathReturn => {
|
||||||
if (programMemory._sketch?.length > 0) {
|
// if ((programMemory?._sketch?.length || 0) > 0) {
|
||||||
throw new Error('Base can only be called once')
|
// throw new Error('Base can only be called once')
|
||||||
}
|
// }
|
||||||
const [x, y] = args as [number, number]
|
// const [x, y] = args as [number, number]
|
||||||
let from: [number, number] = [x, y]
|
// let from: [number, number] = [x, y]
|
||||||
const geo = baseGeo({ from: [x, y, 0] })
|
// const geo = baseGeo({ from: [x, y, 0] })
|
||||||
const newPath: Path = {
|
// const newPath: Path = {
|
||||||
type: 'base',
|
// type: 'base',
|
||||||
from,
|
// from,
|
||||||
sourceRange,
|
// sourceRange,
|
||||||
geo,
|
// geo,
|
||||||
}
|
// }
|
||||||
return {
|
// return {
|
||||||
programMemory: {
|
// programMemory: {
|
||||||
...programMemory,
|
// ...programMemory,
|
||||||
_sketch: [...(programMemory?._sketch || []), newPath],
|
// _sketch: [...(programMemory?._sketch || []), newPath],
|
||||||
},
|
// },
|
||||||
currentPath: newPath,
|
// currentPath: newPath,
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
close: (
|
close: (
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
name: string = '',
|
name: string = '',
|
||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
): PathReturn => {
|
): PathReturn => {
|
||||||
const lastPath = programMemory?._sketch?.[
|
const firstPath = programMemory?._sketch?.[0] as Path
|
||||||
programMemory?._sketch.length - 1
|
|
||||||
] as Path
|
|
||||||
|
|
||||||
let from = getCoordsFromPaths(
|
let from = getCoordsFromPaths(
|
||||||
programMemory?._sketch,
|
programMemory?._sketch || [],
|
||||||
programMemory?._sketch.length - 1
|
(programMemory?._sketch?.length || 1) - 1
|
||||||
)
|
)
|
||||||
const firstPath = programMemory?._sketch?.[0] as Path
|
|
||||||
if (lastPath?.type === 'base') {
|
|
||||||
throw new Error('Cannot close a base path')
|
|
||||||
}
|
|
||||||
let to = getCoordsFromPaths(programMemory?._sketch, 0)
|
|
||||||
|
|
||||||
|
let to = getCoordsFromPaths(programMemory?._sketch || [], 0)
|
||||||
|
const geo = lineGeo({ from: [...from, 0], to: [...to, 0] })
|
||||||
const newPath: Path = {
|
const newPath: Path = {
|
||||||
type: 'close',
|
type: 'toPoint',
|
||||||
geo: lineGeo({ from: [...from, 0], to: [...to, 0] }),
|
from,
|
||||||
|
to,
|
||||||
|
__geoMeta: {
|
||||||
sourceRange,
|
sourceRange,
|
||||||
|
pathToNode: [], // TODO
|
||||||
|
geos: [
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
geo: geo.line,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'lineEnd',
|
||||||
|
geo: geo.tip,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if (name) {
|
if (name) {
|
||||||
newPath.name = name
|
newPath.name = name
|
||||||
@ -177,7 +96,14 @@ export const sketchFns = {
|
|||||||
return {
|
return {
|
||||||
programMemory: {
|
programMemory: {
|
||||||
...programMemory,
|
...programMemory,
|
||||||
_sketch: [...(programMemory?._sketch || []), newPath],
|
_sketch: [
|
||||||
|
{
|
||||||
|
...firstPath,
|
||||||
|
from,
|
||||||
|
},
|
||||||
|
...(programMemory?._sketch || []).slice(1),
|
||||||
|
newPath,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
currentPath: newPath,
|
currentPath: newPath,
|
||||||
}
|
}
|
||||||
@ -188,28 +114,41 @@ export const sketchFns = {
|
|||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
): PathReturn => {
|
): PathReturn => {
|
||||||
const _programMemory = addBasePath(programMemory)
|
|
||||||
const [x, y] = args
|
const [x, y] = args
|
||||||
if (!_programMemory._sketch) {
|
if (!programMemory._sketch) {
|
||||||
throw new Error('No sketch to draw on')
|
throw new Error('No sketch to draw on')
|
||||||
}
|
}
|
||||||
let from = getCoordsFromPaths(
|
let from = getCoordsFromPaths(
|
||||||
programMemory?._sketch,
|
programMemory?._sketch || [],
|
||||||
programMemory?._sketch.length - 1
|
(programMemory?._sketch?.length || 1) - 1
|
||||||
)
|
)
|
||||||
|
const geo = lineGeo({ from: [...from, 0], to: [x, y, 0] })
|
||||||
const currentPath: Path = {
|
const currentPath: Path = {
|
||||||
type: 'toPoint',
|
type: 'toPoint',
|
||||||
to: [x, y],
|
to: [x, y],
|
||||||
geo: lineGeo({ from: [...from, 0], to: [x, y, 0] }),
|
from,
|
||||||
|
__geoMeta: {
|
||||||
sourceRange,
|
sourceRange,
|
||||||
|
pathToNode: [], // TODO
|
||||||
|
geos: [
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
geo: geo.line,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'lineEnd',
|
||||||
|
geo: geo.tip,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if (name) {
|
if (name) {
|
||||||
currentPath.name = name
|
currentPath.name = name
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
programMemory: {
|
programMemory: {
|
||||||
..._programMemory,
|
...programMemory,
|
||||||
_sketch: [...(_programMemory._sketch || []), currentPath],
|
_sketch: [...(programMemory._sketch || []), currentPath],
|
||||||
},
|
},
|
||||||
currentPath,
|
currentPath,
|
||||||
}
|
}
|
||||||
@ -222,31 +161,22 @@ export const sketchFns = {
|
|||||||
name: string = '',
|
name: string = '',
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
length: number,
|
length: number,
|
||||||
sketchVal: SketchGeo | Transform
|
sketchVal: SketchGroup
|
||||||
): ExtrudeGeo | Transform => {
|
): ExtrudeGroup => {
|
||||||
const getSketchGeo = (sketchVal: SketchGeo | Transform): SketchGeo => {
|
const getSketchGeo = (sketchVal: SketchGroup): SketchGroup => {
|
||||||
if (
|
return sketchVal
|
||||||
sketchVal.type === 'transform' &&
|
|
||||||
sketchVal.sketch.type === 'extrudeGeo'
|
|
||||||
)
|
|
||||||
throw new Error('Cannot extrude a extrude')
|
|
||||||
return sketchVal.type === 'transform'
|
|
||||||
? getSketchGeo(sketchVal.sketch as any) // TODO fix types
|
|
||||||
: (sketchVal as SketchGeo) // TODO fix types
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sketch = getSketchGeo(sketchVal)
|
const sketch = getSketchGeo(sketchVal)
|
||||||
const { position, quaternion } = combineTransforms(sketchVal)
|
const { position, rotation } = sketchVal
|
||||||
|
|
||||||
const extrudeFaces: ExtrudeFace[] = []
|
const extrudeSurfaces: ExtrudeSurface[] = []
|
||||||
sketch.sketch.map((line, index) => {
|
sketch.value.map((line, index) => {
|
||||||
if (line.type === 'toPoint' && index !== 0) {
|
if (line.type === 'toPoint' && index !== 0) {
|
||||||
const lastPoint = sketch.sketch[index - 1]
|
const lastPoint = sketch.value[index - 1]
|
||||||
let from: [number, number] = [0, 0]
|
let from: [number, number] = [0, 0]
|
||||||
if (lastPoint.type === 'toPoint') {
|
if (lastPoint.type === 'toPoint') {
|
||||||
from = lastPoint.to
|
from = lastPoint.to
|
||||||
} else if (lastPoint.type === 'base') {
|
|
||||||
from = lastPoint.from
|
|
||||||
}
|
}
|
||||||
const to = line.to
|
const to = line.to
|
||||||
const geo = extrudeGeo({
|
const geo = extrudeGeo({
|
||||||
@ -254,117 +184,87 @@ export const sketchFns = {
|
|||||||
to: [to[0], to[1], 0],
|
to: [to[0], to[1], 0],
|
||||||
length,
|
length,
|
||||||
})
|
})
|
||||||
extrudeFaces.push({
|
extrudeSurfaces.push({
|
||||||
type: 'extrudeFace',
|
type: 'extrudePlane',
|
||||||
quaternion,
|
position, // todo should come from extrudeGeo
|
||||||
translate: position,
|
rotation, // todo should come from extrudeGeo
|
||||||
|
__geoMeta: {
|
||||||
geo,
|
geo,
|
||||||
sourceRanges: [line.sourceRange, sourceRange],
|
sourceRange: line.__geoMeta.sourceRange,
|
||||||
|
pathToNode: line.__geoMeta.pathToNode,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
type: 'extrudeGeo',
|
type: 'extrudeGroup',
|
||||||
|
value: extrudeSurfaces,
|
||||||
|
height: length,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
sourceRange,
|
sourceRange,
|
||||||
surfaces: extrudeFaces,
|
pathToNode: [], // TODO
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
translate,
|
translate,
|
||||||
}
|
}
|
||||||
|
|
||||||
function rotateOnAxis(axisMultiplier: [number, number, number]) {
|
function rotateOnAxis<T extends SketchGroup | ExtrudeGroup>(
|
||||||
|
axisMultiplier: [number, number, number]
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
rotationD: number,
|
rotationD: number,
|
||||||
sketch: SketchGeo | Transform
|
sketch: T
|
||||||
): Transform => {
|
): T => {
|
||||||
const rotationR = rotationD * (Math.PI / 180)
|
const rotationR = rotationD * (Math.PI / 180)
|
||||||
const rotateVec = new Vector3(...axisMultiplier)
|
const rotateVec = new Vector3(...axisMultiplier)
|
||||||
const quaternion = new Quaternion()
|
const quaternion = new Quaternion()
|
||||||
|
quaternion.setFromAxisAngle(rotateVec, rotationR)
|
||||||
|
|
||||||
|
const position = new Vector3(...sketch.position)
|
||||||
|
.applyQuaternion(quaternion)
|
||||||
|
.toArray()
|
||||||
|
|
||||||
|
const existingQuat = new Quaternion(...sketch.rotation)
|
||||||
|
const rotation = quaternion.multiply(existingQuat).toArray()
|
||||||
return {
|
return {
|
||||||
type: 'transform',
|
...sketch,
|
||||||
rotation: quaternion.setFromAxisAngle(rotateVec, rotationR),
|
rotation,
|
||||||
transform: [0, 0, 0],
|
position,
|
||||||
sketch,
|
__meta: [
|
||||||
|
...sketch.__meta,
|
||||||
|
{
|
||||||
sourceRange,
|
sourceRange,
|
||||||
|
pathToNode: [], // TODO
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function translate(
|
function translate<T extends SketchGroup | ExtrudeGroup>(
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
vec3: [number, number, number],
|
vec3: [number, number, number],
|
||||||
sketch: SketchGeo | Transform
|
sketch: T
|
||||||
): Transform {
|
): T {
|
||||||
|
const oldPosition = new Vector3(...sketch.position)
|
||||||
|
const newPosition = oldPosition.add(new Vector3(...vec3))
|
||||||
return {
|
return {
|
||||||
type: 'transform',
|
...sketch,
|
||||||
rotation: new Quaternion(),
|
position: [newPosition.x, newPosition.y, newPosition.z],
|
||||||
transform: vec3,
|
__meta: [
|
||||||
sketch,
|
...sketch.__meta,
|
||||||
sourceRange,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type PreviousTransforms = {
|
|
||||||
rotation: Quaternion
|
|
||||||
transform: [number, number, number]
|
|
||||||
}[]
|
|
||||||
|
|
||||||
function collectTransforms(
|
|
||||||
sketchVal: SketchGeo | ExtrudeGeo | Transform,
|
|
||||||
previousTransforms: PreviousTransforms = []
|
|
||||||
): PreviousTransforms {
|
|
||||||
if (sketchVal.type !== 'transform') return previousTransforms
|
|
||||||
const newTransforms = [
|
|
||||||
...previousTransforms,
|
|
||||||
{
|
{
|
||||||
rotation: sketchVal.rotation,
|
sourceRange,
|
||||||
transform: sketchVal.transform,
|
pathToNode: [], // TODO
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
return collectTransforms(sketchVal.sketch, newTransforms)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function combineTransforms(
|
|
||||||
sketchVal: SketchGeo | ExtrudeGeo | Transform
|
|
||||||
): {
|
|
||||||
quaternion: Quaternion
|
|
||||||
position: [number, number, number]
|
|
||||||
} {
|
|
||||||
const previousTransforms = collectTransforms(sketchVal)
|
|
||||||
const position = new Vector3(0, 0, 0)
|
|
||||||
const quaternion = new Quaternion()
|
|
||||||
previousTransforms.forEach(({ rotation, transform }) => {
|
|
||||||
quaternion.multiply(rotation)
|
|
||||||
position.applyQuaternion(rotation.clone().invert())
|
|
||||||
position.add(new Vector3(...transform))
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
quaternion,
|
|
||||||
position: [position.x, position.y, position.z],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function combineTransformsAlt(
|
|
||||||
sketchVal: SketchGeo | ExtrudeGeo | Transform
|
|
||||||
): {
|
|
||||||
quaternion: Quaternion
|
|
||||||
position: [number, number, number]
|
|
||||||
} {
|
|
||||||
const previousTransforms = collectTransforms(sketchVal)
|
|
||||||
let rotationQuaternion = new Quaternion()
|
|
||||||
let position = new Vector3(0, 0, 0)
|
|
||||||
previousTransforms.reverse().forEach(({ rotation, transform }) => {
|
|
||||||
const newQuant = rotation.clone()
|
|
||||||
newQuant.multiply(rotationQuaternion)
|
|
||||||
rotationQuaternion.copy(newQuant)
|
|
||||||
position.applyQuaternion(rotation)
|
|
||||||
position.add(new Vector3(...transform))
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
quaternion: rotationQuaternion,
|
|
||||||
position: [position.x, position.y, position.z],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import create from 'zustand'
|
import create from 'zustand'
|
||||||
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
||||||
import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
||||||
import { ProgramMemory } from './lang/executor'
|
import { ProgramMemory, Position, PathToNode, Rotation } from './lang/executor'
|
||||||
import { recast } from './lang/recast'
|
import { recast } from './lang/recast'
|
||||||
import { lexer } from './lang/tokeniser'
|
import { lexer } from './lang/tokeniser'
|
||||||
import { Quaternion } from 'three'
|
|
||||||
|
|
||||||
export type Range = [number, number]
|
export type Range = [number, number]
|
||||||
|
|
||||||
type PathToNode = (string | number)[]
|
|
||||||
type Position = [number, number, number]
|
|
||||||
|
|
||||||
type GuiModes =
|
type GuiModes =
|
||||||
| {
|
| {
|
||||||
mode: 'default'
|
mode: 'default'
|
||||||
@ -18,7 +14,7 @@ type GuiModes =
|
|||||||
| {
|
| {
|
||||||
mode: 'sketch'
|
mode: 'sketch'
|
||||||
sketchMode: 'points'
|
sketchMode: 'points'
|
||||||
quaternion: Quaternion
|
rotation: Rotation
|
||||||
position: Position
|
position: Position
|
||||||
id?: string
|
id?: string
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
@ -26,7 +22,7 @@ type GuiModes =
|
|||||||
| {
|
| {
|
||||||
mode: 'sketch'
|
mode: 'sketch'
|
||||||
sketchMode: 'sketchEdit'
|
sketchMode: 'sketchEdit'
|
||||||
quaternion: Quaternion
|
rotation: Rotation
|
||||||
position: Position
|
position: Position
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
@ -37,7 +33,7 @@ type GuiModes =
|
|||||||
| {
|
| {
|
||||||
mode: 'canEditSketch'
|
mode: 'canEditSketch'
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
quaternion: Quaternion
|
rotation: Rotation
|
||||||
position: Position
|
position: Position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user