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 { lexer } from './lang/tokeniser'
|
||||
import { abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
||||
import { executor } from './lang/executor'
|
||||
import { executor, processShownObjects, ViewerArtifact } from './lang/executor'
|
||||
import { recast } from './lang/recast'
|
||||
import { BufferGeometry } from 'three'
|
||||
import CodeMirror from '@uiw/react-codemirror'
|
||||
@ -74,9 +74,7 @@ function App() {
|
||||
if (isNoChange) return
|
||||
setSelectionRange([range.from, range.to])
|
||||
}
|
||||
const [geoArray, setGeoArray] = useState<
|
||||
{ geo: BufferGeometry; sourceRange: [number, number] }[]
|
||||
>([])
|
||||
const [geoArray, setGeoArray] = useState<ViewerArtifact[]>([])
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (!code) {
|
||||
@ -91,25 +89,23 @@ function App() {
|
||||
const programMemory = executor(_ast, {
|
||||
root: {
|
||||
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: [],
|
||||
})
|
||||
const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] =
|
||||
const geos: ViewerArtifact[] =
|
||||
programMemory?.return?.flatMap(
|
||||
({ name }: { name: string }) =>
|
||||
programMemory?.root?.[name]
|
||||
?.map(
|
||||
({
|
||||
geo,
|
||||
sourceRange,
|
||||
}: {
|
||||
geo: BufferGeometry
|
||||
sourceRange: [number, number]
|
||||
}) => ({ geo, sourceRange })
|
||||
)
|
||||
.filter((a: any) => !!a.geo) || []
|
||||
processShownObjects(programMemory)(programMemory?.root?.[name]) ||
|
||||
[]
|
||||
) || []
|
||||
setGeoArray(geos)
|
||||
removeError()
|
||||
@ -169,17 +165,9 @@ function App() {
|
||||
/>
|
||||
<ambientLight />
|
||||
<pointLight position={[10, 10, 10]} />
|
||||
{geoArray.map(
|
||||
(
|
||||
{
|
||||
geo,
|
||||
sourceRange,
|
||||
}: { geo: BufferGeometry; sourceRange: [number, number] },
|
||||
index
|
||||
) => (
|
||||
<Line key={index} geo={geo} sourceRange={sourceRange} />
|
||||
)
|
||||
)}
|
||||
{geoArray.map((artifact, index) => (
|
||||
<RenderViewerArtifacts artifact={artifact} key={index} />
|
||||
))}
|
||||
<BasePlanes />
|
||||
<SketchPlane />
|
||||
<AxisIndicator />
|
||||
@ -207,9 +195,11 @@ export default App
|
||||
function Line({
|
||||
geo,
|
||||
sourceRange,
|
||||
forceHighlight = false,
|
||||
}: {
|
||||
geo: BufferGeometry
|
||||
sourceRange: [number, number]
|
||||
forceHighlight?: boolean
|
||||
}) {
|
||||
const { setHighlightRange, selectionRange } = useStore(
|
||||
({ setHighlightRange, selectionRange }) => ({
|
||||
@ -240,8 +230,52 @@ function Line({
|
||||
>
|
||||
<primitive object={geo} />
|
||||
<meshStandardMaterial
|
||||
color={hovered ? 'hotpink' : editorCursor ? 'skyblue' : 'orange'}
|
||||
color={
|
||||
hovered
|
||||
? 'hotpink'
|
||||
: editorCursor || forceHighlight
|
||||
? 'skyblue'
|
||||
: 'orange'
|
||||
}
|
||||
/>
|
||||
</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 { Path, sketchFns } from './sketch'
|
||||
import { Path, Transform, sketchFns } from './sketch'
|
||||
import { BufferGeometry } from 'three'
|
||||
|
||||
export interface ProgramMemory {
|
||||
root: { [key: string]: any }
|
||||
@ -100,6 +101,17 @@ export const executor = (
|
||||
)
|
||||
_programMemory._sketch = result.programMemory._sketch
|
||||
_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 {
|
||||
_programMemory.root[variableName] = _programMemory.root[fnName](
|
||||
...fnArgs
|
||||
@ -171,3 +183,60 @@ function getBinaryExpressionResult(
|
||||
const right = getVal(expression.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 SourceRange = [number, number]
|
||||
type Rotation3 = [number, number, number]
|
||||
type Translate3 = [number, number, number]
|
||||
|
||||
export type Path =
|
||||
| {
|
||||
@ -53,6 +55,14 @@ export type Path =
|
||||
sourceRange: SourceRange
|
||||
}
|
||||
|
||||
export interface Transform {
|
||||
type: 'transform'
|
||||
rotation: Rotation3
|
||||
transform: Translate3
|
||||
id: string
|
||||
sourceRange: SourceRange
|
||||
}
|
||||
|
||||
function addBasePath(programMemory: ProgramMemory) {
|
||||
const geo = baseGeo({ from: [0, 0, 0] })
|
||||
const base: Path = {
|
||||
@ -193,4 +203,20 @@ export const sketchFns = {
|
||||
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