Gui scafolding toolbar, log console, axis for sketching

This commit is contained in:
Kurt Hutten IrevDev
2022-11-27 14:06:33 +11:00
parent 022788b2a3
commit 6f24e75f92
6 changed files with 284 additions and 41 deletions

View File

@ -15,6 +15,10 @@ import {
} from './editor/highlightextension' } from './editor/highlightextension'
import { useStore } from './useStore' import { useStore } from './useStore'
import { isOverlapping } from './lib/utils' import { isOverlapping } from './lib/utils'
import { Toolbar } from './Toolbar'
import { BasePlanes } from './components/BasePlanes'
import { SketchPlane } from './components/SketchPlane'
import { Logs } from './components/Logs'
const _code = `sketch mySketch { const _code = `sketch mySketch {
path myPath = lineTo(0,1) path myPath = lineTo(0,1)
@ -29,15 +33,25 @@ const OrrthographicCamera = OrthographicCamera as any
function App() { function App() {
const cam = useRef() const cam = useRef()
const [code, setCode] = useState(_code) const [code, setCode] = useState(_code)
const { editorView, setEditorView, setSelectionRange, selectionRange } = const {
useStore(
({ editorView, setEditorView, setSelectionRange, selectionRange }) => ({
editorView, editorView,
setEditorView, setEditorView,
setSelectionRange, setSelectionRange,
selectionRange, selectionRange,
}) guiMode,
) setGuiMode,
removeError,
addLog,
} = useStore((s) => ({
editorView: s.editorView,
setEditorView: s.setEditorView,
setSelectionRange: s.setSelectionRange,
selectionRange: s.selectionRange,
guiMode: s.guiMode,
setGuiMode: s.setGuiMode,
removeError: s.removeError,
addLog: s.addLog,
}))
// 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) => {
setCode(value) setCode(value)
@ -60,9 +74,21 @@ function App() {
>([]) >([])
useEffect(() => { useEffect(() => {
try { try {
if (!code) {
setGeoArray([])
removeError()
return
}
const tokens = lexer(code) const tokens = lexer(code)
const ast = abstractSyntaxTree(tokens) const ast = abstractSyntaxTree(tokens)
const programMemory = executor(ast) const programMemory = executor(ast, {
root: {
log: (a: any) => {
addLog(a)
},
},
_sketch: [],
})
const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] = const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] =
programMemory.root.mySketch programMemory.root.mySketch
.map( .map(
@ -76,14 +102,18 @@ function App() {
) )
.filter((a: any) => !!a.geo) .filter((a: any) => !!a.geo)
setGeoArray(geos) setGeoArray(geos)
removeError()
console.log(programMemory) console.log(programMemory)
} catch (e) { } catch (e: any) {
setGuiMode({ mode: 'codeError' })
console.log(e) console.log(e)
addLog(e)
} }
}, [code]) }, [code])
return ( return (
<div className="h-screen"> <div className="h-screen">
<Allotment> <Allotment>
<Logs />
<div className="bg-red h-full"> <div className="bg-red h-full">
<CodeMirror <CodeMirror
value={_code} value={_code}
@ -96,7 +126,9 @@ function App() {
</div> </div>
<div className="h-full"> <div className="h-full">
viewer viewer
<div className="border h-full border-gray-300"> <Toolbar />
<div className="border h-full border-gray-300 relative">
<div className="absolute inset-0">
<Canvas> <Canvas>
<OrbitControls <OrbitControls
enableDamping={false} enableDamping={false}
@ -125,8 +157,14 @@ function App() {
<Line key={index} geo={geo} sourceRange={sourceRange} /> <Line key={index} geo={geo} sourceRange={sourceRange} />
) )
)} )}
<BasePlanes />
<SketchPlane />
</Canvas> </Canvas>
</div> </div>
{guiMode.mode === 'codeError' && (
<div className="absolute inset-0 bg-gray-700/20">yo</div>
)}
</div>
</div> </div>
</Allotment> </Allotment>
</div> </div>

27
src/Toolbar.tsx Normal file
View File

@ -0,0 +1,27 @@
import { useStore } from './useStore'
export const Toolbar = () => {
const { setGuiMode, guiMode } = useStore(({ guiMode, setGuiMode }) => ({
guiMode,
setGuiMode,
}))
return (
<div>
{guiMode.mode === 'default' && (
<button
onClick={() => {
setGuiMode({
mode: 'sketch',
sketchMode: 'selectFace',
})
}}
>
Start sketch
</button>
)}
{guiMode.mode !== 'default' && (
<button onClick={() => setGuiMode({ mode: 'default' })}>exit</button>
)}
</div>
)
}

View File

@ -0,0 +1,74 @@
import { useState } from 'react'
import { DoubleSide } from 'three'
import { useStore } from '../useStore'
import { Intersection } from '@react-three/fiber'
const opacity = 0.1
export const BasePlanes = () => {
const [axisIndex, setAxisIndex] = useState<null | number>(null)
const { setGuiMode, guiMode } = useStore(({ guiMode, setGuiMode }) => ({
guiMode,
setGuiMode,
}))
const onPointerEvent = ({
intersections,
}: {
intersections: Intersection[]
}) => {
if (!intersections.length) {
setAxisIndex(null)
return
}
let closestIntersection = intersections[0]
intersections.forEach((intersection) => {
if (intersection.distance < closestIntersection.distance)
closestIntersection = intersection
})
const smallestIndex = Number(closestIntersection.eventObject.name)
setAxisIndex(smallestIndex)
}
const onClick = () => {
if (guiMode.mode !== 'sketch') {
return null
}
if (guiMode.sketchMode !== 'selectFace') {
return null
}
setGuiMode({
mode: 'sketch',
sketchMode: 'points',
axis: axisIndex === 0 ? 'yz' : axisIndex === 1 ? 'xy' : 'xz',
})
}
if (guiMode.mode !== 'sketch') {
return null
}
if (guiMode.sketchMode !== 'selectFace') {
return null
}
return (
<>
{Array.from({ length: 3 }).map((_, index) => (
<mesh
key={index}
rotation-x={index === 1 ? -Math.PI / 2 : 0}
rotation-y={index === 2 ? -Math.PI / 2 : 0}
onPointerMove={onPointerEvent}
onPointerOut={onPointerEvent}
onClick={onClick}
name={`${index}`}
>
<planeGeometry args={[5, 5]} />
<meshStandardMaterial
color="blue"
side={DoubleSide}
transparent
opacity={opacity + (axisIndex === index ? 0.3 : 0)}
/>
</mesh>
))}
</>
)
}

34
src/components/Logs.tsx Normal file
View File

@ -0,0 +1,34 @@
import { useEffect } from 'react'
import { useStore } from '../useStore'
export const Logs = () => {
const { logs, resetLogs } = useStore(({ logs, resetLogs }) => ({
logs,
resetLogs,
}))
useEffect(() => {
const element = document.querySelector('.console-tile')
if (element) {
element.scrollTop = element.scrollHeight - element.clientHeight
}
}, [logs])
return (
<div className="h-full relative">
<div className="absolute inset-0 flex flex-col items-start">
<button onClick={resetLogs}>reset</button>
<div className=" overflow-auto h-full console-tile w-full">
{logs.map((msg, index) => {
return (
<pre className="text-xs pl-2 text-sky-600" key={index}>
<code style={{ fontFamily: 'monospace' }} key={index}>
<span className="text-gray-400">{'- '}</span>
{String(msg)}
</code>
</pre>
)
})}
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,25 @@
import { useStore } from '../useStore'
export const SketchPlane = () => {
const { setGuiMode, guiMode } = useStore(({ guiMode, setGuiMode }) => ({
guiMode,
setGuiMode,
}))
if (guiMode.mode !== 'sketch') {
return null
}
if (guiMode.sketchMode !== 'points') {
return null
}
const ninty = Math.PI / 2
const rotation: [number, number, number] = [0, 0, 0]
if (guiMode.axis === 'yz') {
rotation[0] = ninty
} else if (guiMode.axis === 'xy') {
rotation[1] = ninty
} else if (guiMode.axis === 'xz') {
rotation[2] = ninty
}
return <gridHelper args={[30, 40, 'blue', 'hotpink']} rotation={rotation} />
}

View File

@ -3,6 +3,23 @@ import { addLineHighlight, EditorView } from './editor/highlightextension'
export type Range = [number, number] export type Range = [number, number]
type GuiModes =
| {
mode: 'default'
}
| {
mode: 'sketch'
sketchMode: 'points'
axis: 'xy' | 'xz' | 'yz'
}
| {
mode: 'sketch'
sketchMode: 'selectFace'
}
| {
mode: 'codeError'
}
interface StoreState { interface StoreState {
editorView: EditorView | null editorView: EditorView | null
setEditorView: (editorView: EditorView) => void setEditorView: (editorView: EditorView) => void
@ -10,6 +27,13 @@ interface StoreState {
setHighlightRange: (range: Range) => void setHighlightRange: (range: Range) => void
selectionRange: [number, number] selectionRange: [number, number]
setSelectionRange: (range: Range) => void setSelectionRange: (range: Range) => void
guiMode: GuiModes
lastGuiMode: GuiModes
setGuiMode: (guiMode: GuiModes) => void
removeError: () => void
logs: string[]
addLog: (log: string) => void
resetLogs: () => void
} }
export const useStore = create<StoreState>()((set, get) => ({ export const useStore = create<StoreState>()((set, get) => ({
@ -29,4 +53,25 @@ export const useStore = create<StoreState>()((set, get) => ({
setSelectionRange: (selectionRange) => { setSelectionRange: (selectionRange) => {
set({ selectionRange }) set({ selectionRange })
}, },
guiMode: { mode: 'default' },
lastGuiMode: { mode: 'default' },
setGuiMode: (guiMode) => {
const lastGuiMode = get().guiMode
set({ guiMode })
set({ lastGuiMode })
},
removeError: () => {
const lastGuiMode = get().lastGuiMode
const currentGuiMode = get().guiMode
if (currentGuiMode.mode === 'codeError') {
set({ guiMode: lastGuiMode })
}
},
logs: [],
addLog: (log) => {
set((state) => ({ logs: [...state.logs, log] }))
},
resetLogs: () => {
set({ logs: [] })
},
})) }))