Gui scafolding toolbar, log console, axis for sketching
This commit is contained in:
54
src/App.tsx
54
src/App.tsx
@ -15,6 +15,10 @@ import {
|
||||
} from './editor/highlightextension'
|
||||
import { useStore } from './useStore'
|
||||
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 {
|
||||
path myPath = lineTo(0,1)
|
||||
@ -29,15 +33,25 @@ const OrrthographicCamera = OrthographicCamera as any
|
||||
function App() {
|
||||
const cam = useRef()
|
||||
const [code, setCode] = useState(_code)
|
||||
const { editorView, setEditorView, setSelectionRange, selectionRange } =
|
||||
useStore(
|
||||
({ editorView, setEditorView, setSelectionRange, selectionRange }) => ({
|
||||
const {
|
||||
editorView,
|
||||
setEditorView,
|
||||
setSelectionRange,
|
||||
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 = (value: string, viewUpdate: ViewUpdate) => {
|
||||
setCode(value)
|
||||
@ -60,9 +74,21 @@ function App() {
|
||||
>([])
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (!code) {
|
||||
setGeoArray([])
|
||||
removeError()
|
||||
return
|
||||
}
|
||||
const tokens = lexer(code)
|
||||
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] }[] =
|
||||
programMemory.root.mySketch
|
||||
.map(
|
||||
@ -76,14 +102,18 @@ function App() {
|
||||
)
|
||||
.filter((a: any) => !!a.geo)
|
||||
setGeoArray(geos)
|
||||
removeError()
|
||||
console.log(programMemory)
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
setGuiMode({ mode: 'codeError' })
|
||||
console.log(e)
|
||||
addLog(e)
|
||||
}
|
||||
}, [code])
|
||||
return (
|
||||
<div className="h-screen">
|
||||
<Allotment>
|
||||
<Logs />
|
||||
<div className="bg-red h-full">
|
||||
<CodeMirror
|
||||
value={_code}
|
||||
@ -96,7 +126,9 @@ function App() {
|
||||
</div>
|
||||
<div className="h-full">
|
||||
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>
|
||||
<OrbitControls
|
||||
enableDamping={false}
|
||||
@ -125,8 +157,14 @@ function App() {
|
||||
<Line key={index} geo={geo} sourceRange={sourceRange} />
|
||||
)
|
||||
)}
|
||||
<BasePlanes />
|
||||
<SketchPlane />
|
||||
</Canvas>
|
||||
</div>
|
||||
{guiMode.mode === 'codeError' && (
|
||||
<div className="absolute inset-0 bg-gray-700/20">yo</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Allotment>
|
||||
</div>
|
||||
|
27
src/Toolbar.tsx
Normal file
27
src/Toolbar.tsx
Normal 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>
|
||||
)
|
||||
}
|
74
src/components/BasePlanes.tsx
Normal file
74
src/components/BasePlanes.tsx
Normal 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
34
src/components/Logs.tsx
Normal 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>
|
||||
)
|
||||
}
|
25
src/components/SketchPlane.tsx
Normal file
25
src/components/SketchPlane.tsx
Normal 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} />
|
||||
}
|
@ -3,6 +3,23 @@ import { addLineHighlight, EditorView } from './editor/highlightextension'
|
||||
|
||||
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 {
|
||||
editorView: EditorView | null
|
||||
setEditorView: (editorView: EditorView) => void
|
||||
@ -10,6 +27,13 @@ interface StoreState {
|
||||
setHighlightRange: (range: Range) => void
|
||||
selectionRange: [number, number]
|
||||
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) => ({
|
||||
@ -29,4 +53,25 @@ export const useStore = create<StoreState>()((set, get) => ({
|
||||
setSelectionRange: (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: [] })
|
||||
},
|
||||
}))
|
||||
|
Reference in New Issue
Block a user