get nested geomtry structure working for highlighting
This commit is contained in:
92
src/App.tsx
92
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 } from './lang/executor'
|
import { executor, processShownObjects, ViewerArtifact } from './lang/executor'
|
||||||
import { recast } from './lang/recast'
|
import { recast } from './lang/recast'
|
||||||
import { BufferGeometry } from 'three'
|
import { BufferGeometry } from 'three'
|
||||||
import CodeMirror from '@uiw/react-codemirror'
|
import CodeMirror from '@uiw/react-codemirror'
|
||||||
@ -74,9 +74,7 @@ function App() {
|
|||||||
if (isNoChange) return
|
if (isNoChange) return
|
||||||
setSelectionRange([range.from, range.to])
|
setSelectionRange([range.from, range.to])
|
||||||
}
|
}
|
||||||
const [geoArray, setGeoArray] = useState<
|
const [geoArray, setGeoArray] = useState<ViewerArtifact[]>([])
|
||||||
{ geo: BufferGeometry; sourceRange: [number, number] }[]
|
|
||||||
>([])
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
if (!code) {
|
if (!code) {
|
||||||
@ -91,25 +89,23 @@ function App() {
|
|||||||
const programMemory = executor(_ast, {
|
const programMemory = executor(_ast, {
|
||||||
root: {
|
root: {
|
||||||
log: (a: any) => {
|
log: (a: any) => {
|
||||||
addLog(a)
|
let b = a
|
||||||
|
if (Array.isArray(a)) {
|
||||||
|
b = a.map(({ geo, ...rest }) => rest)
|
||||||
|
b = JSON.stringify(b, null, 2)
|
||||||
|
} else if (typeof a === 'object') {
|
||||||
|
b = JSON.stringify(a, null, 2)
|
||||||
|
}
|
||||||
|
addLog(b)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
_sketch: [],
|
_sketch: [],
|
||||||
})
|
})
|
||||||
const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] =
|
const geos: ViewerArtifact[] =
|
||||||
programMemory?.return?.flatMap(
|
programMemory?.return?.flatMap(
|
||||||
({ name }: { name: string }) =>
|
({ name }: { name: string }) =>
|
||||||
programMemory?.root?.[name]
|
processShownObjects(programMemory)(programMemory?.root?.[name]) ||
|
||||||
?.map(
|
[]
|
||||||
({
|
|
||||||
geo,
|
|
||||||
sourceRange,
|
|
||||||
}: {
|
|
||||||
geo: BufferGeometry
|
|
||||||
sourceRange: [number, number]
|
|
||||||
}) => ({ geo, sourceRange })
|
|
||||||
)
|
|
||||||
.filter((a: any) => !!a.geo) || []
|
|
||||||
) || []
|
) || []
|
||||||
setGeoArray(geos)
|
setGeoArray(geos)
|
||||||
removeError()
|
removeError()
|
||||||
@ -169,17 +165,9 @@ function App() {
|
|||||||
/>
|
/>
|
||||||
<ambientLight />
|
<ambientLight />
|
||||||
<pointLight position={[10, 10, 10]} />
|
<pointLight position={[10, 10, 10]} />
|
||||||
{geoArray.map(
|
{geoArray.map((artifact, index) => (
|
||||||
(
|
<RenderViewerArtifacts artifact={artifact} key={index} />
|
||||||
{
|
))}
|
||||||
geo,
|
|
||||||
sourceRange,
|
|
||||||
}: { geo: BufferGeometry; sourceRange: [number, number] },
|
|
||||||
index
|
|
||||||
) => (
|
|
||||||
<Line key={index} geo={geo} sourceRange={sourceRange} />
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
<BasePlanes />
|
<BasePlanes />
|
||||||
<SketchPlane />
|
<SketchPlane />
|
||||||
<AxisIndicator />
|
<AxisIndicator />
|
||||||
@ -207,9 +195,11 @@ export default App
|
|||||||
function Line({
|
function Line({
|
||||||
geo,
|
geo,
|
||||||
sourceRange,
|
sourceRange,
|
||||||
|
forceHighlight = false,
|
||||||
}: {
|
}: {
|
||||||
geo: BufferGeometry
|
geo: BufferGeometry
|
||||||
sourceRange: [number, number]
|
sourceRange: [number, number]
|
||||||
|
forceHighlight?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { setHighlightRange, selectionRange } = useStore(
|
const { setHighlightRange, selectionRange } = useStore(
|
||||||
({ setHighlightRange, selectionRange }) => ({
|
({ setHighlightRange, selectionRange }) => ({
|
||||||
@ -240,8 +230,52 @@ function Line({
|
|||||||
>
|
>
|
||||||
<primitive object={geo} />
|
<primitive object={geo} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={hovered ? 'hotpink' : editorCursor ? 'skyblue' : 'orange'}
|
color={
|
||||||
|
hovered
|
||||||
|
? 'hotpink'
|
||||||
|
: editorCursor || forceHighlight
|
||||||
|
? 'skyblue'
|
||||||
|
: 'orange'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RenderViewerArtifacts({
|
||||||
|
artifact,
|
||||||
|
forceHighlight = false,
|
||||||
|
}: {
|
||||||
|
artifact: ViewerArtifact
|
||||||
|
forceHighlight?: boolean
|
||||||
|
}) {
|
||||||
|
const { selectionRange } = useStore(({ selectionRange }) => ({
|
||||||
|
selectionRange,
|
||||||
|
}))
|
||||||
|
const [editorCursor, setEditorCursor] = useState(false)
|
||||||
|
useEffect(() => {
|
||||||
|
const shouldHighlight = isOverlapping(artifact.sourceRange, selectionRange)
|
||||||
|
setEditorCursor(shouldHighlight)
|
||||||
|
}, [selectionRange, artifact.sourceRange])
|
||||||
|
if (artifact.type === 'geo') {
|
||||||
|
const { geo, sourceRange } = artifact
|
||||||
|
return (
|
||||||
|
<Line
|
||||||
|
geo={geo}
|
||||||
|
sourceRange={sourceRange}
|
||||||
|
forceHighlight={forceHighlight}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{artifact.children.map((artifact, index) => (
|
||||||
|
<RenderViewerArtifacts
|
||||||
|
artifact={artifact}
|
||||||
|
key={index}
|
||||||
|
forceHighlight={editorCursor}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Program, BinaryPart, BinaryExpression } from './abstractSyntaxTree'
|
import { Program, BinaryPart, BinaryExpression } from './abstractSyntaxTree'
|
||||||
import { Path, sketchFns } from './sketch'
|
import { Path, Transform, sketchFns } from './sketch'
|
||||||
|
import { BufferGeometry } from 'three'
|
||||||
|
|
||||||
export interface ProgramMemory {
|
export interface ProgramMemory {
|
||||||
root: { [key: string]: any }
|
root: { [key: string]: any }
|
||||||
@ -100,6 +101,17 @@ export const executor = (
|
|||||||
)
|
)
|
||||||
_programMemory._sketch = result.programMemory._sketch
|
_programMemory._sketch = result.programMemory._sketch
|
||||||
_programMemory.root[variableName] = result.currentPath
|
_programMemory.root[variableName] = result.currentPath
|
||||||
|
} else if ('rx' === fnName) {
|
||||||
|
if (declaration.init.arguments[1].type !== 'Identifier')
|
||||||
|
throw new Error('rx must be called with an identifier')
|
||||||
|
const id = declaration.init.arguments[1].name
|
||||||
|
const result = sketchFns[fnName](
|
||||||
|
_programMemory,
|
||||||
|
[declaration.start, declaration.end],
|
||||||
|
fnArgs[0],
|
||||||
|
id
|
||||||
|
)
|
||||||
|
_programMemory.root[variableName] = result
|
||||||
} else {
|
} else {
|
||||||
_programMemory.root[variableName] = _programMemory.root[fnName](
|
_programMemory.root[variableName] = _programMemory.root[fnName](
|
||||||
...fnArgs
|
...fnArgs
|
||||||
@ -171,3 +183,60 @@ function getBinaryExpressionResult(
|
|||||||
const right = getVal(expression.right)
|
const right = getVal(expression.right)
|
||||||
return left + right
|
return left + right
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SourceRange = [number, number]
|
||||||
|
|
||||||
|
export type ViewerArtifact =
|
||||||
|
| {
|
||||||
|
type: 'geo'
|
||||||
|
sourceRange: SourceRange
|
||||||
|
geo: BufferGeometry
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'parent'
|
||||||
|
sourceRange: SourceRange
|
||||||
|
children: ViewerArtifact[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const processShownObjects =
|
||||||
|
(programMemory: ProgramMemory) =>
|
||||||
|
(geoMeta: Path[] | Transform): ViewerArtifact[] => {
|
||||||
|
if (Array.isArray(geoMeta)) {
|
||||||
|
return geoMeta.map(({ geo, sourceRange }) => ({
|
||||||
|
type: 'geo',
|
||||||
|
geo,
|
||||||
|
sourceRange,
|
||||||
|
}))
|
||||||
|
} else if (geoMeta.type === 'transform') {
|
||||||
|
console.log('transform', geoMeta, programMemory)
|
||||||
|
const geos = processShownObjects(programMemory)(
|
||||||
|
programMemory.root[geoMeta.id]
|
||||||
|
).map((arg): ViewerArtifact => {
|
||||||
|
if (arg.type !== 'geo')
|
||||||
|
throw new Error('transform must be applied to geo')
|
||||||
|
const { geo, sourceRange } = arg
|
||||||
|
const newGeo = geo.clone()
|
||||||
|
newGeo.rotateX(geoMeta.rotation[0])
|
||||||
|
newGeo.rotateY(geoMeta.rotation[1])
|
||||||
|
newGeo.rotateZ(geoMeta.rotation[2])
|
||||||
|
newGeo.translate(
|
||||||
|
geoMeta.transform[0],
|
||||||
|
geoMeta.transform[1],
|
||||||
|
geoMeta.transform[2]
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
type: 'geo',
|
||||||
|
sourceRange,
|
||||||
|
geo: newGeo,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: 'parent',
|
||||||
|
sourceRange: geoMeta.sourceRange,
|
||||||
|
children: geos,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
throw new Error('Unknown geoMeta type')
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import { BufferGeometry } from 'three'
|
|||||||
|
|
||||||
type Coords2d = [number, number]
|
type Coords2d = [number, number]
|
||||||
type SourceRange = [number, number]
|
type SourceRange = [number, number]
|
||||||
|
type Rotation3 = [number, number, number]
|
||||||
|
type Translate3 = [number, number, number]
|
||||||
|
|
||||||
export type Path =
|
export type Path =
|
||||||
| {
|
| {
|
||||||
@ -53,6 +55,14 @@ export type Path =
|
|||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Transform {
|
||||||
|
type: 'transform'
|
||||||
|
rotation: Rotation3
|
||||||
|
transform: Translate3
|
||||||
|
id: string
|
||||||
|
sourceRange: SourceRange
|
||||||
|
}
|
||||||
|
|
||||||
function addBasePath(programMemory: ProgramMemory) {
|
function addBasePath(programMemory: ProgramMemory) {
|
||||||
const geo = baseGeo({ from: [0, 0, 0] })
|
const geo = baseGeo({ from: [0, 0, 0] })
|
||||||
const base: Path = {
|
const base: Path = {
|
||||||
@ -193,4 +203,20 @@ export const sketchFns = {
|
|||||||
currentPath,
|
currentPath,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
rx: (
|
||||||
|
programMemory: ProgramMemory,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
rotationD: number,
|
||||||
|
id: string
|
||||||
|
): Transform => {
|
||||||
|
if (!programMemory.root[id]) throw new Error(`No variable with name ${id}`)
|
||||||
|
const rotationR = rotationD * (Math.PI / 180)
|
||||||
|
return {
|
||||||
|
type: 'transform',
|
||||||
|
rotation: [rotationR, 0, 0],
|
||||||
|
transform: [0, 0, 0],
|
||||||
|
id,
|
||||||
|
sourceRange,
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user