start of code gen from direct manipulation
This commit is contained in:
@ -14,6 +14,6 @@ let listener: ((rect: any) => void) | undefined = undefined
|
|||||||
|
|
||||||
test('renders learn react link', () => {
|
test('renders learn react link', () => {
|
||||||
render(<App />)
|
render(<App />)
|
||||||
const linkElement = screen.getByText(/viewer/i)
|
const linkElement = screen.getByText(/reset/i)
|
||||||
expect(linkElement).toBeInTheDocument()
|
expect(linkElement).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
58
src/App.tsx
58
src/App.tsx
@ -20,19 +20,11 @@ import { BasePlanes } from './components/BasePlanes'
|
|||||||
import { SketchPlane } from './components/SketchPlane'
|
import { SketchPlane } from './components/SketchPlane'
|
||||||
import { Logs } from './components/Logs'
|
import { Logs } from './components/Logs'
|
||||||
|
|
||||||
const _code = `sketch mySketch {
|
|
||||||
path myPath = lineTo(0,1)
|
|
||||||
lineTo(1,5)
|
|
||||||
path rightPath = lineTo(1,0)
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
show(mySketch)`
|
|
||||||
|
|
||||||
const OrrthographicCamera = OrthographicCamera as any
|
const OrrthographicCamera = OrthographicCamera as any
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const cam = useRef()
|
const cam = useRef()
|
||||||
const [code, setCode] = useState(_code)
|
|
||||||
const {
|
const {
|
||||||
editorView,
|
editorView,
|
||||||
setEditorView,
|
setEditorView,
|
||||||
@ -40,8 +32,12 @@ function App() {
|
|||||||
selectionRange,
|
selectionRange,
|
||||||
guiMode,
|
guiMode,
|
||||||
setGuiMode,
|
setGuiMode,
|
||||||
|
lastGuiMode,
|
||||||
removeError,
|
removeError,
|
||||||
addLog,
|
addLog,
|
||||||
|
code,
|
||||||
|
setCode,
|
||||||
|
setAst,
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
editorView: s.editorView,
|
editorView: s.editorView,
|
||||||
setEditorView: s.setEditorView,
|
setEditorView: s.setEditorView,
|
||||||
@ -51,6 +47,11 @@ function App() {
|
|||||||
setGuiMode: s.setGuiMode,
|
setGuiMode: s.setGuiMode,
|
||||||
removeError: s.removeError,
|
removeError: s.removeError,
|
||||||
addLog: s.addLog,
|
addLog: s.addLog,
|
||||||
|
code: s.code,
|
||||||
|
setCode: s.setCode,
|
||||||
|
ast: s.ast,
|
||||||
|
setAst: s.setAst,
|
||||||
|
lastGuiMode: s.lastGuiMode
|
||||||
}))
|
}))
|
||||||
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
||||||
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
||||||
@ -76,12 +77,14 @@ function App() {
|
|||||||
try {
|
try {
|
||||||
if (!code) {
|
if (!code) {
|
||||||
setGeoArray([])
|
setGeoArray([])
|
||||||
|
setAst(null)
|
||||||
removeError()
|
removeError()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const tokens = lexer(code)
|
const tokens = lexer(code)
|
||||||
const ast = abstractSyntaxTree(tokens)
|
const _ast = abstractSyntaxTree(tokens)
|
||||||
const programMemory = executor(ast, {
|
setAst(_ast)
|
||||||
|
const programMemory = executor(_ast, {
|
||||||
root: {
|
root: {
|
||||||
log: (a: any) => {
|
log: (a: any) => {
|
||||||
addLog(a)
|
addLog(a)
|
||||||
@ -90,17 +93,20 @@ function App() {
|
|||||||
_sketch: [],
|
_sketch: [],
|
||||||
})
|
})
|
||||||
const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] =
|
const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] =
|
||||||
programMemory.root.mySketch
|
programMemory?.return?.flatMap(
|
||||||
.map(
|
({ name }: { name: string }) =>
|
||||||
({
|
programMemory?.root?.[name]
|
||||||
geo,
|
?.map(
|
||||||
sourceRange,
|
({
|
||||||
}: {
|
geo,
|
||||||
geo: BufferGeometry
|
sourceRange,
|
||||||
sourceRange: [number, number]
|
}: {
|
||||||
}) => ({ geo, sourceRange })
|
geo: BufferGeometry
|
||||||
)
|
sourceRange: [number, number]
|
||||||
.filter((a: any) => !!a.geo)
|
}) => ({ geo, sourceRange })
|
||||||
|
)
|
||||||
|
.filter((a: any) => !!a.geo) || []
|
||||||
|
) || []
|
||||||
setGeoArray(geos)
|
setGeoArray(geos)
|
||||||
removeError()
|
removeError()
|
||||||
console.log(programMemory)
|
console.log(programMemory)
|
||||||
@ -114,10 +120,10 @@ function App() {
|
|||||||
<div className="h-screen">
|
<div className="h-screen">
|
||||||
<Allotment>
|
<Allotment>
|
||||||
<Logs />
|
<Logs />
|
||||||
<div className="bg-red h-full">
|
<div className="bg-red h-full overflow-auto">
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
value={_code}
|
className="h-full"
|
||||||
height="200px"
|
value={code}
|
||||||
extensions={[javascript({ jsx: true }), lineHighlightField]}
|
extensions={[javascript({ jsx: true }), lineHighlightField]}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
@ -125,7 +131,6 @@ function App() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
viewer
|
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
<div className="border h-full border-gray-300 relative">
|
<div className="border h-full border-gray-300 relative">
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
@ -162,7 +167,8 @@ function App() {
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
{guiMode.mode === 'codeError' && (
|
{guiMode.mode === 'codeError' && (
|
||||||
<div className="absolute inset-0 bg-gray-700/20">yo</div>
|
<div className="absolute inset-0 bg-gray-700/20">
|
||||||
|
<pre>{'last first: \n\n' + JSON.stringify(lastGuiMode, null, 2) + '\n\n' + JSON.stringify(guiMode)}</pre></div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,6 +19,11 @@ export const Toolbar = () => {
|
|||||||
Start sketch
|
Start sketch
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{guiMode.mode === 'sketch' && guiMode.sketchMode === 'points' && (
|
||||||
|
<button>
|
||||||
|
LineTo TODO
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{guiMode.mode !== 'default' && (
|
{guiMode.mode !== 'default' && (
|
||||||
<button onClick={() => setGuiMode({ mode: 'default' })}>exit</button>
|
<button onClick={() => setGuiMode({ mode: 'default' })}>exit</button>
|
||||||
)}
|
)}
|
||||||
|
@ -2,15 +2,20 @@ import { useState } from 'react'
|
|||||||
import { DoubleSide } from 'three'
|
import { DoubleSide } from 'three'
|
||||||
import { useStore } from '../useStore'
|
import { useStore } from '../useStore'
|
||||||
import { Intersection } from '@react-three/fiber'
|
import { Intersection } from '@react-three/fiber'
|
||||||
|
import { addSketchTo, Program } from '../lang/abstractSyntaxTree'
|
||||||
|
|
||||||
const opacity = 0.1
|
const opacity = 0.1
|
||||||
|
|
||||||
export const BasePlanes = () => {
|
export const BasePlanes = () => {
|
||||||
const [axisIndex, setAxisIndex] = useState<null | number>(null)
|
const [axisIndex, setAxisIndex] = useState<null | number>(null)
|
||||||
const { setGuiMode, guiMode } = useStore(({ guiMode, setGuiMode }) => ({
|
const { setGuiMode, guiMode, ast, updateAst } = useStore(
|
||||||
guiMode,
|
({ guiMode, setGuiMode, ast, updateAst }) => ({
|
||||||
setGuiMode,
|
guiMode,
|
||||||
}))
|
setGuiMode,
|
||||||
|
ast,
|
||||||
|
updateAst,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const onPointerEvent = ({
|
const onPointerEvent = ({
|
||||||
intersections,
|
intersections,
|
||||||
@ -36,11 +41,25 @@ export const BasePlanes = () => {
|
|||||||
if (guiMode.sketchMode !== 'selectFace') {
|
if (guiMode.sketchMode !== 'selectFace') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ast: Program = ast
|
||||||
|
? ast
|
||||||
|
: {
|
||||||
|
type: 'Program',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
body: [],
|
||||||
|
}
|
||||||
|
const { modifiedAst, id } = addSketchTo(_ast)
|
||||||
|
|
||||||
setGuiMode({
|
setGuiMode({
|
||||||
mode: 'sketch',
|
mode: 'sketch',
|
||||||
sketchMode: 'points',
|
sketchMode: 'points',
|
||||||
axis: axisIndex === 0 ? 'yz' : axisIndex === 1 ? 'xy' : 'xz',
|
axis: axisIndex === 0 ? 'yz' : axisIndex === 1 ? 'xy' : 'xz',
|
||||||
|
id
|
||||||
})
|
})
|
||||||
|
|
||||||
|
updateAst(modifiedAst)
|
||||||
}
|
}
|
||||||
if (guiMode.mode !== 'sketch') {
|
if (guiMode.mode !== 'sketch') {
|
||||||
return null
|
return null
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
import { useStore } from '../useStore'
|
import { useStore } from '../useStore'
|
||||||
|
import { DoubleSide } from 'three'
|
||||||
|
import { addLine, Program } from '../lang/abstractSyntaxTree'
|
||||||
|
|
||||||
export const SketchPlane = () => {
|
export const SketchPlane = () => {
|
||||||
const { setGuiMode, guiMode } = useStore(({ guiMode, setGuiMode }) => ({
|
const { ast, setGuiMode, guiMode, updateAst } = useStore(
|
||||||
guiMode,
|
({ guiMode, setGuiMode, ast, updateAst }) => ({
|
||||||
setGuiMode,
|
guiMode,
|
||||||
}))
|
setGuiMode,
|
||||||
|
ast,
|
||||||
|
updateAst,
|
||||||
|
})
|
||||||
|
)
|
||||||
if (guiMode.mode !== 'sketch') {
|
if (guiMode.mode !== 'sketch') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -12,14 +18,66 @@ export const SketchPlane = () => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const ninty = Math.PI / 2
|
const sketchGridName = 'sketchGrid'
|
||||||
const rotation: [number, number, number] = [0, 0, 0]
|
|
||||||
|
const ninety = Math.PI / 2
|
||||||
|
const gridRotation: [number, number, number] = [0, 0, 0]
|
||||||
|
const clickDetectPlaneRotation: [number, number, number] = [0, 0, 0]
|
||||||
if (guiMode.axis === 'yz') {
|
if (guiMode.axis === 'yz') {
|
||||||
rotation[0] = ninty
|
gridRotation[0] = ninety
|
||||||
} else if (guiMode.axis === 'xy') {
|
} else if (guiMode.axis === 'xy') {
|
||||||
rotation[1] = ninty
|
clickDetectPlaneRotation[0] = ninety
|
||||||
} else if (guiMode.axis === 'xz') {
|
} else if (guiMode.axis === 'xz') {
|
||||||
rotation[2] = ninty
|
gridRotation[2] = ninety
|
||||||
|
clickDetectPlaneRotation[1] = ninety
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<mesh
|
||||||
|
rotation={clickDetectPlaneRotation}
|
||||||
|
name={sketchGridName}
|
||||||
|
onClick={(e) => {
|
||||||
|
const sketchGridIntersection = e.intersections.find(
|
||||||
|
({ object }) => object.name === sketchGridName
|
||||||
|
)
|
||||||
|
const point = roundy(sketchGridIntersection?.point)
|
||||||
|
let _ast: Program = ast
|
||||||
|
? ast
|
||||||
|
: {
|
||||||
|
type: 'Program',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
body: [],
|
||||||
|
}
|
||||||
|
const { modifiedAst, id } = addLine(_ast, guiMode.id, [
|
||||||
|
point.x,
|
||||||
|
point.y,
|
||||||
|
])
|
||||||
|
updateAst(modifiedAst)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<planeGeometry args={[30, 40]} />
|
||||||
|
<meshStandardMaterial
|
||||||
|
color="blue"
|
||||||
|
side={DoubleSide}
|
||||||
|
opacity={0}
|
||||||
|
transparent
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
<gridHelper args={[30, 40, 'blue', 'hotpink']} rotation={gridRotation} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function roundy({ x, y, z }: any) {
|
||||||
|
const roundOff = (num: number, places: number): number => {
|
||||||
|
const x = Math.pow(10, places)
|
||||||
|
return Math.round(num * x) / x
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
x: roundOff(x, 2),
|
||||||
|
y: roundOff(y, 2),
|
||||||
|
z: roundOff(z, 2),
|
||||||
}
|
}
|
||||||
return <gridHelper args={[30, 40, 'blue', 'hotpink']} rotation={rotation} />
|
|
||||||
}
|
}
|
||||||
|
@ -557,7 +557,7 @@ function makeBlockStatement(
|
|||||||
block: {
|
block: {
|
||||||
type: 'BlockStatement',
|
type: 'BlockStatement',
|
||||||
start: openingCurly.start,
|
start: openingCurly.start,
|
||||||
end: tokens[lastIndex].end,
|
end: tokens[lastIndex]?.end || 0,
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
lastIndex,
|
lastIndex,
|
||||||
@ -766,3 +766,237 @@ export function findClosingBrace(
|
|||||||
// non-brace token, increment and continue
|
// non-brace token, increment and continue
|
||||||
return findClosingBrace(tokens, index + 1, _braceCount, searchOpeningBrace)
|
return findClosingBrace(tokens, index + 1, _braceCount, searchOpeningBrace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addSketchTo(
|
||||||
|
node: Program,
|
||||||
|
name = ''
|
||||||
|
): { modifiedAst: Program; id: string } {
|
||||||
|
const _node = { ...node }
|
||||||
|
const dumbyStartend = { start: 0, end: 0 }
|
||||||
|
const _name = name || findUniqueName(node, 'mySketch')
|
||||||
|
const sketchBody: BlockStatement = {
|
||||||
|
type: 'BlockStatement',
|
||||||
|
...dumbyStartend,
|
||||||
|
body: [],
|
||||||
|
}
|
||||||
|
const sketch: SketchExpression = {
|
||||||
|
type: 'SketchExpression',
|
||||||
|
...dumbyStartend,
|
||||||
|
body: sketchBody,
|
||||||
|
}
|
||||||
|
const sketchVariableDeclaration: VariableDeclaration = {
|
||||||
|
type: 'VariableDeclaration',
|
||||||
|
...dumbyStartend,
|
||||||
|
kind: 'sketch',
|
||||||
|
declarations: [
|
||||||
|
{
|
||||||
|
type: 'VariableDeclarator',
|
||||||
|
...dumbyStartend,
|
||||||
|
id: {
|
||||||
|
type: 'Identifier',
|
||||||
|
...dumbyStartend,
|
||||||
|
name: _name,
|
||||||
|
},
|
||||||
|
init: sketch,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
const showCallIndex = getShowIndex(_node)
|
||||||
|
if (showCallIndex === -1) {
|
||||||
|
_node.body = [...node.body, sketchVariableDeclaration]
|
||||||
|
} else {
|
||||||
|
const newBody = [...node.body]
|
||||||
|
newBody.splice(showCallIndex, 0, sketchVariableDeclaration)
|
||||||
|
_node.body = newBody
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst: addToShow(_node, _name),
|
||||||
|
id: _name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findUniqueName(
|
||||||
|
ast: Program | string,
|
||||||
|
name: string,
|
||||||
|
index = 1
|
||||||
|
): string {
|
||||||
|
let searchStr = ''
|
||||||
|
if (typeof ast === 'string') {
|
||||||
|
searchStr = ast
|
||||||
|
} else {
|
||||||
|
searchStr = JSON.stringify(ast)
|
||||||
|
}
|
||||||
|
const indexStr = `${index}`.padStart(3, '0')
|
||||||
|
const newName = `${name}${indexStr}`
|
||||||
|
const isInString = searchStr.includes(newName)
|
||||||
|
if (!isInString) {
|
||||||
|
return newName
|
||||||
|
}
|
||||||
|
return findUniqueName(searchStr, name, index + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToShow(node: Program, name: string): Program {
|
||||||
|
const _node = { ...node }
|
||||||
|
const dumbyStartend = { start: 0, end: 0 }
|
||||||
|
const showCallIndex = getShowIndex(_node)
|
||||||
|
if (showCallIndex === -1) {
|
||||||
|
const showCall: CallExpression = {
|
||||||
|
type: 'CallExpression',
|
||||||
|
...dumbyStartend,
|
||||||
|
callee: {
|
||||||
|
type: 'Identifier',
|
||||||
|
...dumbyStartend,
|
||||||
|
name: 'show',
|
||||||
|
},
|
||||||
|
optional: false,
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'Identifier',
|
||||||
|
...dumbyStartend,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
const showExpressionStatement: ExpressionStatement = {
|
||||||
|
type: 'ExpressionStatement',
|
||||||
|
...dumbyStartend,
|
||||||
|
expression: showCall,
|
||||||
|
}
|
||||||
|
_node.body = [..._node.body, showExpressionStatement]
|
||||||
|
return _node
|
||||||
|
}
|
||||||
|
const showCall = { ..._node.body[showCallIndex] } as ExpressionStatement
|
||||||
|
const showCallArgs = (showCall.expression as CallExpression).arguments
|
||||||
|
const newShowCallArgs: Value[] = [
|
||||||
|
...showCallArgs,
|
||||||
|
{
|
||||||
|
type: 'Identifier',
|
||||||
|
...dumbyStartend,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const newShowExpression: CallExpression = {
|
||||||
|
type: 'CallExpression',
|
||||||
|
...dumbyStartend,
|
||||||
|
callee: {
|
||||||
|
type: 'Identifier',
|
||||||
|
...dumbyStartend,
|
||||||
|
name: 'show',
|
||||||
|
},
|
||||||
|
optional: false,
|
||||||
|
arguments: newShowCallArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
_node.body[showCallIndex] = {
|
||||||
|
...showCall,
|
||||||
|
expression: newShowExpression,
|
||||||
|
}
|
||||||
|
return _node
|
||||||
|
}
|
||||||
|
|
||||||
|
function getShowIndex(node: Program): number {
|
||||||
|
return node.body.findIndex(
|
||||||
|
(statement) =>
|
||||||
|
statement.type === 'ExpressionStatement' &&
|
||||||
|
statement.expression.type === 'CallExpression' &&
|
||||||
|
statement.expression.callee.type === 'Identifier' &&
|
||||||
|
statement.expression.callee.name === 'show'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addLine(
|
||||||
|
node: Program,
|
||||||
|
id: string,
|
||||||
|
to: [number, number]
|
||||||
|
): { modifiedAst: Program; id: string } {
|
||||||
|
const _node = { ...node }
|
||||||
|
const dumbyStartend = { start: 0, end: 0 }
|
||||||
|
const { index, sketchDeclaration, sketchExpression } = getSketchStatement(_node, id)
|
||||||
|
const line: ExpressionStatement = {
|
||||||
|
type: 'ExpressionStatement',
|
||||||
|
...dumbyStartend,
|
||||||
|
expression: {
|
||||||
|
type: 'CallExpression',
|
||||||
|
...dumbyStartend,
|
||||||
|
callee: {
|
||||||
|
type: 'Identifier',
|
||||||
|
...dumbyStartend,
|
||||||
|
name: 'lineTo',
|
||||||
|
},
|
||||||
|
optional: false,
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
...dumbyStartend,
|
||||||
|
value: to[0],
|
||||||
|
raw: `${to[0]}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
...dumbyStartend,
|
||||||
|
value: to[1],
|
||||||
|
raw: `${to[1]}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const newBody = [...sketchExpression.body.body, line]
|
||||||
|
const newSketchExpression: SketchExpression = {
|
||||||
|
...sketchExpression,
|
||||||
|
body: {
|
||||||
|
...sketchExpression.body,
|
||||||
|
body: newBody,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const newSketchDeclaration: VariableDeclaration = {
|
||||||
|
...sketchDeclaration,
|
||||||
|
declarations: [
|
||||||
|
{
|
||||||
|
...sketchDeclaration.declarations[0],
|
||||||
|
init: newSketchExpression,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
_node.body[index] = newSketchDeclaration
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSketchStatement(
|
||||||
|
node: Program,
|
||||||
|
id: string
|
||||||
|
): {
|
||||||
|
sketchDeclaration: VariableDeclaration
|
||||||
|
sketchExpression: SketchExpression
|
||||||
|
index: number
|
||||||
|
} {
|
||||||
|
const sketchStatementIndex = node.body.findIndex(
|
||||||
|
(statement) =>
|
||||||
|
statement.type === 'VariableDeclaration' &&
|
||||||
|
statement.kind === 'sketch' &&
|
||||||
|
statement.declarations[0].id.type === 'Identifier' &&
|
||||||
|
statement.declarations[0].id.name === id
|
||||||
|
)
|
||||||
|
const sketchStatement = node.body.find(
|
||||||
|
(statement) =>
|
||||||
|
statement.type === 'VariableDeclaration' &&
|
||||||
|
statement.kind === 'sketch' &&
|
||||||
|
statement.declarations[0].id.type === 'Identifier' &&
|
||||||
|
statement.declarations[0].id.name === id
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
!sketchStatement ||
|
||||||
|
sketchStatement.type !== 'VariableDeclaration' ||
|
||||||
|
sketchStatement.declarations[0].init.type !== 'SketchExpression'
|
||||||
|
)
|
||||||
|
throw new Error('No sketch found')
|
||||||
|
|
||||||
|
return {
|
||||||
|
sketchDeclaration: sketchStatement,
|
||||||
|
sketchExpression: sketchStatement.declarations[0].init,
|
||||||
|
index: sketchStatementIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { BoxGeometry, SphereGeometry, BufferGeometry } from 'three'
|
import { BoxGeometry, SphereGeometry, BufferGeometry } from 'three'
|
||||||
import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils'
|
import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils'
|
||||||
|
|
||||||
|
export function baseGeo({from}: {from: [number, number, number]}) {
|
||||||
|
const baseSphere = new SphereGeometry(0.25)
|
||||||
|
baseSphere.translate(from[0], from[1], from[2])
|
||||||
|
return baseSphere
|
||||||
|
}
|
||||||
|
|
||||||
export function lineGeo({
|
export function lineGeo({
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
|
@ -63,7 +63,9 @@ show(mySketch)
|
|||||||
`
|
`
|
||||||
const { root, return: _return } = exe(code)
|
const { root, return: _return } = exe(code)
|
||||||
expect(
|
expect(
|
||||||
root.mySketch.map(({ previousPath, geo, ...rest }: any) => rest)
|
root.mySketch.map(
|
||||||
|
({ previousPath, firstPath, geo, ...rest }: any) => rest
|
||||||
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{ type: 'base', from: [0, 0], sourceRange: [0, 0] },
|
{ type: 'base', from: [0, 0], sourceRange: [0, 0] },
|
||||||
{ type: 'toPoint', to: [0, 1], sourceRange: [25, 45], name: 'myPath' },
|
{ type: 'toPoint', to: [0, 1], sourceRange: [25, 45], name: 'myPath' },
|
||||||
@ -71,7 +73,6 @@ show(mySketch)
|
|||||||
{ type: 'toPoint', to: [1, 0], sourceRange: [67, 90], name: 'rightPath' },
|
{ type: 'toPoint', to: [1, 0], sourceRange: [67, 90], name: 'rightPath' },
|
||||||
{
|
{
|
||||||
type: 'close',
|
type: 'close',
|
||||||
firstPath: { type: 'base', from: [0, 0], sourceRange: [0, 0] },
|
|
||||||
sourceRange: [93, 100],
|
sourceRange: [93, 100],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
@ -11,7 +11,7 @@ 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' }
|
||||||
): any => {
|
): ProgramMemory => {
|
||||||
const _programMemory: ProgramMemory = {
|
const _programMemory: ProgramMemory = {
|
||||||
root: {
|
root: {
|
||||||
...programMemory.root,
|
...programMemory.root,
|
||||||
@ -39,9 +39,13 @@ export const executor = (
|
|||||||
},
|
},
|
||||||
_sketch: [],
|
_sketch: [],
|
||||||
}
|
}
|
||||||
const { _sketch } = executor(sketchInit.body, fnMemory, {
|
let { _sketch } = executor(sketchInit.body, fnMemory, {
|
||||||
bodyType: 'sketch',
|
bodyType: 'sketch',
|
||||||
})
|
})
|
||||||
|
if (_sketch.length === 0) {
|
||||||
|
const {programMemory: newProgramMemory} = sketchFns.base(fnMemory, '', [0, 0], 0, 0)
|
||||||
|
_sketch = newProgramMemory._sketch
|
||||||
|
}
|
||||||
_programMemory.root[variableName] = _sketch
|
_programMemory.root[variableName] = _sketch
|
||||||
} else if (declaration.init.type === 'FunctionExpression') {
|
} else if (declaration.init.type === 'FunctionExpression') {
|
||||||
const fnInit = declaration.init
|
const fnInit = declaration.init
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ProgramMemory } from './executor'
|
import { ProgramMemory } from './executor'
|
||||||
import { lineGeo } from './engine'
|
import { lineGeo, baseGeo } from './engine'
|
||||||
import { BufferGeometry } from 'three'
|
import { BufferGeometry } from 'three'
|
||||||
|
|
||||||
type Coords2d = [number, number]
|
type Coords2d = [number, number]
|
||||||
@ -49,14 +49,17 @@ export type Path =
|
|||||||
| {
|
| {
|
||||||
type: 'base'
|
type: 'base'
|
||||||
from: Coords2d
|
from: Coords2d
|
||||||
|
geo: BufferGeometry
|
||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
}
|
}
|
||||||
|
|
||||||
function addBasePath(programMemory: ProgramMemory) {
|
function addBasePath(programMemory: ProgramMemory) {
|
||||||
|
const geo = baseGeo({ from: [0, 0, 0] })
|
||||||
const base: Path = {
|
const base: Path = {
|
||||||
type: 'base',
|
type: 'base',
|
||||||
from: [0, 0],
|
from: [0, 0],
|
||||||
sourceRange: [0, 0],
|
sourceRange: [0, 0],
|
||||||
|
geo,
|
||||||
}
|
}
|
||||||
if (programMemory._sketch?.length === 0) {
|
if (programMemory._sketch?.length === 0) {
|
||||||
return {
|
return {
|
||||||
@ -103,10 +106,12 @@ export const sketchFns = {
|
|||||||
}
|
}
|
||||||
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 newPath: Path = {
|
const newPath: Path = {
|
||||||
type: 'base',
|
type: 'base',
|
||||||
from,
|
from,
|
||||||
sourceRange,
|
sourceRange,
|
||||||
|
geo,
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
programMemory: {
|
programMemory: {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import create from 'zustand'
|
import create from 'zustand'
|
||||||
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
||||||
|
import { Program } from './lang/abstractSyntaxTree'
|
||||||
|
import { recast } from './lang/recast'
|
||||||
|
|
||||||
export type Range = [number, number]
|
export type Range = [number, number]
|
||||||
|
|
||||||
@ -11,6 +13,7 @@ type GuiModes =
|
|||||||
mode: 'sketch'
|
mode: 'sketch'
|
||||||
sketchMode: 'points'
|
sketchMode: 'points'
|
||||||
axis: 'xy' | 'xz' | 'yz'
|
axis: 'xy' | 'xz' | 'yz'
|
||||||
|
id: string
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
mode: 'sketch'
|
mode: 'sketch'
|
||||||
@ -34,6 +37,11 @@ interface StoreState {
|
|||||||
logs: string[]
|
logs: string[]
|
||||||
addLog: (log: string) => void
|
addLog: (log: string) => void
|
||||||
resetLogs: () => void
|
resetLogs: () => void
|
||||||
|
ast: Program | null
|
||||||
|
setAst: (ast: Program | null) => void
|
||||||
|
updateAst: (ast: Program) => void
|
||||||
|
code: string
|
||||||
|
setCode: (code: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStore = create<StoreState>()((set, get) => ({
|
export const useStore = create<StoreState>()((set, get) => ({
|
||||||
@ -58,7 +66,12 @@ export const useStore = create<StoreState>()((set, get) => ({
|
|||||||
setGuiMode: (guiMode) => {
|
setGuiMode: (guiMode) => {
|
||||||
const lastGuiMode = get().guiMode
|
const lastGuiMode = get().guiMode
|
||||||
set({ guiMode })
|
set({ guiMode })
|
||||||
set({ lastGuiMode })
|
if(guiMode.mode !== 'codeError') {
|
||||||
|
// don't set lastGuiMode to and error state
|
||||||
|
// as the point fo lastGuiMode is to restore the last healthy state
|
||||||
|
// todo maybe rename to lastHealthyGuiMode and remove this comment
|
||||||
|
set({ lastGuiMode })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
removeError: () => {
|
removeError: () => {
|
||||||
const lastGuiMode = get().lastGuiMode
|
const lastGuiMode = get().lastGuiMode
|
||||||
@ -74,4 +87,16 @@ export const useStore = create<StoreState>()((set, get) => ({
|
|||||||
resetLogs: () => {
|
resetLogs: () => {
|
||||||
set({ logs: [] })
|
set({ logs: [] })
|
||||||
},
|
},
|
||||||
|
ast: null,
|
||||||
|
setAst: (ast) => {
|
||||||
|
set({ ast })
|
||||||
|
},
|
||||||
|
updateAst: (ast) => {
|
||||||
|
const newCode = recast(ast)
|
||||||
|
set({ ast, code: newCode })
|
||||||
|
},
|
||||||
|
code: '',
|
||||||
|
setCode: (code) => {
|
||||||
|
set({ code })
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
|
Reference in New Issue
Block a user