get nested geomtry structure working for highlighting

This commit is contained in:
Kurt Hutten IrevDev
2022-11-29 11:24:04 +11:00
parent 1ed96ad7b6
commit 95f9c4c2b4
3 changed files with 159 additions and 30 deletions

View File

@ -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}
/>
))}
</>
)
}

View File

@ -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')
}

View File

@ -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,
}
},
}