60 lines
3.2 KiB
HTML
60 lines
3.2 KiB
HTML
<!doctype html>
|
|
<meta charset="utf-8">
|
|
<title>Chrome Topology Harness</title>
|
|
<pre id="result">running</pre>
|
|
<script type="module">
|
|
import { BitbybitGeometryRuntime } from '/src/facade/geometryRuntime.ts'
|
|
|
|
const output = document.querySelector('#result')
|
|
const report = { schemaVersion: 1, browserId: 'chrome', crossOriginIsolated: globalThis.crossOriginIsolated === true, status: 'running' }
|
|
const persistTopologyInOpfs = async (topology) => {
|
|
if (!navigator.storage?.getDirectory) return false
|
|
const root = await navigator.storage.getDirectory()
|
|
const directory = await root.getDirectoryHandle('bitbybit-topology-harness', { create: true })
|
|
const name = `topology-${crypto.randomUUID()}.json`
|
|
const payload = JSON.stringify({ adjacency: topology.adjacency, ids: topology.entries.map((entry) => entry.ref.persistentId) })
|
|
try {
|
|
const handle = await directory.getFileHandle(name, { create: true })
|
|
const writable = await handle.createWritable()
|
|
await writable.write(payload)
|
|
await writable.close()
|
|
const restored = JSON.parse(await (await handle.getFile()).text())
|
|
return JSON.stringify(restored) === JSON.stringify(JSON.parse(payload))
|
|
} finally {
|
|
await directory.removeEntry(name).catch(() => {})
|
|
}
|
|
}
|
|
try {
|
|
const runtime = new BitbybitGeometryRuntime()
|
|
report.capabilities = await runtime.initialize()
|
|
const shape = await runtime.createBox({ documentId: 'chrome-topology', documentVersion: 1, width: 10, length: 20, height: 30, originOnCenter: false })
|
|
try {
|
|
const topology = await runtime.topology(shape, 0.05)
|
|
const analyticFaces = topology.entries.filter((entry) => entry.ref.kind === 'face' && entry.signature.analytic?.type === 'Plane').length
|
|
const analyticEdges = topology.entries.filter((entry) => entry.ref.kind === 'edge' && entry.signature.analytic?.type === 'Line').length
|
|
const adjacency = topology.adjacency
|
|
const opfsTopologyRoundTrip = await persistTopologyInOpfs(topology)
|
|
report.topology = {
|
|
faces: topology.faces.length,
|
|
edges: topology.edges.length,
|
|
vertices: topology.vertices.length,
|
|
analyticFaces,
|
|
analyticEdges,
|
|
adjacencyKeys: adjacency ? Object.values(adjacency).map((map) => Object.keys(map).length) : [],
|
|
transientIndexFree: topology.entries.every((entry) => !/index|faceIndex|edgeIndex|vertexIndex/i.test(entry.ref.persistentId)),
|
|
opfsTopologyRoundTrip,
|
|
}
|
|
report.status = report.crossOriginIsolated && report.capabilities.status === 'ready' && topology.faces.length === 6 && topology.edges.length === 12 && topology.vertices.length === 8 && analyticFaces === 6 && analyticEdges === 12 && adjacency && Object.values(adjacency).every((map) => Object.keys(map).length > 0) && report.topology.transientIndexFree && opfsTopologyRoundTrip ? 'pass' : 'failed'
|
|
} finally {
|
|
await runtime.release(shape)
|
|
runtime.dispose()
|
|
}
|
|
} catch (error) {
|
|
report.status = 'failed'
|
|
report.error = error instanceof Error ? error.message : String(error)
|
|
}
|
|
output.textContent = JSON.stringify(report)
|
|
await fetch('/__topology-report', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(report) })
|
|
if (report.status !== 'pass') throw new Error(report.error || 'Chrome topology harness failed.')
|
|
</script>
|