P3-02 P5-02: integrate Bitbybit OCCT geometry runtime
This commit is contained in:
39
src/App.tsx
39
src/App.tsx
@@ -53,7 +53,7 @@ import {
|
||||
ZoomOut,
|
||||
} from 'lucide-react'
|
||||
import { menuDefinitions, pinnedWorkbenches, workbenchDefinitions, type MenuName, type WorkbenchId } from './freecadManifest'
|
||||
import { createMockFacade, type BitBybitWebCadFacade, type DocumentSnapshot, type ModelTreeItem } from './facade'
|
||||
import { createMockFacade, type BitBybitViewportAdapter, type BitBybitWebCadFacade, type DocumentSnapshot, type ModelTreeItem, type ShapeHandle } from './facade'
|
||||
|
||||
type Page = 'start' | 'projects' | 'workspace' | 'import' | 'export' | 'settings' | 'help' | 'diagnostics' | 'sync'
|
||||
type Workbench = WorkbenchId
|
||||
@@ -135,6 +135,8 @@ function App() {
|
||||
}
|
||||
}), [facade])
|
||||
|
||||
useEffect(() => () => facade.geometry.dispose(), [facade])
|
||||
|
||||
useEffect(() => {
|
||||
const handlePopState = () => setPage(pageFromPath())
|
||||
window.addEventListener('popstate', handlePopState)
|
||||
@@ -357,20 +359,47 @@ function ViewProperties({ showNotice }: { showNotice: (message: string) => void
|
||||
|
||||
function Viewport({ selectedObject, setSelectedObject, workbench, facade, showNotice }: { selectedObject: string; setSelectedObject: (id: string) => void; workbench: Workbench; facade: BitBybitWebCadFacade; showNotice: (message: string) => void }) {
|
||||
const hostRef = useRef<HTMLDivElement>(null)
|
||||
const adapterRef = useRef<BitBybitViewportAdapter | null>(null)
|
||||
useEffect(() => {
|
||||
const host = hostRef.current
|
||||
if (!host) return
|
||||
const adapter = facade.viewport.createAdapter()
|
||||
adapterRef.current = adapter
|
||||
let cancelled = false
|
||||
let shape: ShapeHandle | null = null
|
||||
try {
|
||||
adapter.mount(host)
|
||||
adapter.setSelection(selectedObject)
|
||||
return () => adapter.dispose()
|
||||
} catch (error) {
|
||||
showNotice(`WebGL viewport unavailable: ${error instanceof Error ? error.message : 'unknown error'}`)
|
||||
adapter.dispose()
|
||||
adapterRef.current = null
|
||||
return
|
||||
}
|
||||
}, [facade, showNotice, selectedObject])
|
||||
return <div className="viewport"><div className="viewport-header"><div className="viewport-title"><span className="eyebrow">{workbench}</span><strong>Body / Fillet</strong></div><div className="viewport-actions"><IconButton icon={ZoomOut} label="Zoom out" /><IconButton icon={ZoomIn} label="Zoom in" /><IconButton icon={Rotate3D} label="Orbit view" active /></div></div><div className="viewport-grid" onClick={() => setSelectedObject('')}><div className="three-viewport-host" ref={hostRef} aria-label="Three.js viewport" /><div className="grid-lines" /><div className={`mock-part ${selectedObject === 'pad' || selectedObject.startsWith('pad') ? 'part-selected' : ''}`} onClick={(event) => { event.stopPropagation(); setSelectedObject('pad'); showNotice('Selected Pad') }}><div className="part-top" /><div className="part-front"><span className="part-hole one" /><span className="part-hole two" /><span className="part-cut" /></div><div className="part-side" /></div><div className="mock-datum-plane" /><div className="selection-callout"><span className="callout-line" /><span>Pad</span></div></div><div className="viewport-legend"><span><span className="legend-swatch selected" />Selected</span><span><span className="legend-swatch edge" />Edges</span><span><span className="legend-swatch datum" />Datum</span></div></div>
|
||||
const loadGeometry = async () => {
|
||||
const capabilities = await facade.geometry.initialize()
|
||||
if (capabilities.status !== 'ready') throw new Error(capabilities.reason || 'OCCT geometry runtime unavailable')
|
||||
shape = await facade.geometry.createBox({ width: 2.7, length: 1.4, height: 1.6, center: [0, 0, 0], originOnCenter: true, documentVersion: facade.app.document.getActive().version })
|
||||
if (cancelled) {
|
||||
await facade.geometry.release(shape)
|
||||
shape = null
|
||||
return
|
||||
}
|
||||
const mesh = await facade.geometry.mesh(shape, 0.05)
|
||||
if (!cancelled) adapter.setMesh(mesh)
|
||||
}
|
||||
void loadGeometry().catch((error: unknown) => {
|
||||
if (!cancelled) showNotice(`Geometry preview unavailable: ${error instanceof Error ? error.message : String(error)}`)
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
if (shape) void facade.geometry.release(shape)
|
||||
adapter.dispose()
|
||||
adapterRef.current = null
|
||||
}
|
||||
}, [facade, showNotice])
|
||||
|
||||
useEffect(() => adapterRef.current?.setSelection(selectedObject), [selectedObject])
|
||||
return <div className="viewport"><div className="viewport-header"><div className="viewport-title"><span className="eyebrow">{workbench}</span><strong>Body / Fillet</strong></div><div className="viewport-actions"><IconButton icon={ZoomOut} label="Zoom out" /><IconButton icon={ZoomIn} label="Zoom in" /><IconButton icon={Rotate3D} label="Orbit view" active /></div></div><div className="viewport-grid" onClick={() => setSelectedObject('')}><div className="three-viewport-host" ref={hostRef} aria-label="Three.js viewport" /></div><div className="viewport-legend"><span><span className="legend-swatch selected" />Selected</span><span><span className="legend-swatch edge" />Edges</span><span><span className="legend-swatch datum" />Datum</span></div></div>
|
||||
}
|
||||
|
||||
function PageFrame({ page, onNavigate, onOpenWorkspace, showNotice }: { page: Page; onNavigate: (page: Page) => void; onOpenWorkspace: () => void; showNotice: (message: string) => void }) {
|
||||
|
||||
Reference in New Issue
Block a user