90 lines
6.2 KiB
HTML
90 lines
6.2 KiB
HTML
<!doctype html>
|
|
<meta charset="utf-8">
|
|
<title>Chrome Sketcher B-spline editor harness</title>
|
|
<pre id="result">running</pre>
|
|
<script type="module">
|
|
import { createSketch, editSketchBspline, sketchGeometrySignature } from '/src/facade/sketcher.ts'
|
|
import { inspectFcstdArchive, serializeFcstdMetadataArchive } from '/src/facade/fcstd.ts'
|
|
|
|
const output = document.querySelector('#result')
|
|
const report = { schemaVersion: 1, browserId: 'chrome', crossOriginIsolated: globalThis.crossOriginIsolated === true, status: 'running' }
|
|
const source = createSketch('chrome-bspline', [{ id: 'spline', type: 'bspline', degree: 2, controlPoints: [{ x: 0, y: 0 }, { x: 1, y: 2 }, { x: 3, y: 0 }], weights: [1, 1, 1], knots: [0, 0, 0, 1, 1, 1] }])
|
|
const errorFor = (patch) => {
|
|
try {
|
|
editSketchBspline(source, 'spline', patch)
|
|
return null
|
|
} catch (error) {
|
|
return error instanceof Error ? error.message : String(error)
|
|
}
|
|
}
|
|
try {
|
|
const edited = editSketchBspline(source, 'spline', {
|
|
degree: 1,
|
|
controlPoints: [{ x: 0, y: 0 }, { x: 2, y: 3 }, { x: 4, y: 0 }],
|
|
weights: [1, 0.5, 1],
|
|
knots: [0, 0, 0.5, 1, 1],
|
|
periodic: true,
|
|
})
|
|
const editedGeometry = edited.geometry[0]
|
|
const sourceGeometry = source.geometry[0]
|
|
const constrained = createSketch('Sketch', [{
|
|
id: 'weighted-spline', type: 'bspline', degree: 3,
|
|
controlPoints: [{ x: 0, y: 0 }, { x: 1, y: 2 }, { x: 3, y: 2 }, { x: 4, y: 0 }],
|
|
weights: [1, 1.5, 1.25, 1], knots: [0, 0, 0, 0, 1, 1, 1, 1], periodic: false,
|
|
}], [
|
|
{ id: 'middle-weight', type: 'weight', geometryId: 'weighted-spline', controlPointIndex: 1, value: 1.5, driving: true },
|
|
{ id: 'third-alignment', type: 'internalAlignment', geometryId: 'weighted-spline', internalGeometryIndex: 2, alignmentType: 'bspline-control-point', driving: true },
|
|
{ id: 'third-weight', type: 'weight', geometryId: 'weighted-spline', controlPointIndex: 2, value: 1.25, driving: true },
|
|
])
|
|
const document = {
|
|
id: 'chrome-bspline-fcstd', label: 'B-spline round-trip', version: 1, dirty: true, readOnly: false, units: 'mm',
|
|
tree: [{ id: 'Sketch', label: 'Sketch', type: 'sketch', state: 'valid' }],
|
|
objects: [{ id: 'Sketch', typeId: 'Sketcher::SketchObject', properties: [], sketch: constrained }],
|
|
dependencies: [], recompute: { generation: 0, status: 'idle', objectStates: { Sketch: 'up-to-date' }, dirtyObjects: [], order: [], errors: [] },
|
|
}
|
|
const archive = serializeFcstdMetadataArchive(document)
|
|
const restored = inspectFcstdArchive(archive).objects[0]?.sketch
|
|
const markerDirectory = await navigator.storage.getDirectory()
|
|
const markerName = 'sketcher-bspline-chrome-marker.json'
|
|
const marker = await markerDirectory.getFileHandle(markerName, { create: true })
|
|
const writable = await marker.createWritable()
|
|
await writable.write(JSON.stringify({ suite: 'SK-10', fields: 8 }))
|
|
await writable.close()
|
|
const markerPayload = JSON.parse(await (await marker.getFile()).text())
|
|
await markerDirectory.removeEntry(markerName)
|
|
let markerRemoved = false
|
|
try { await markerDirectory.getFileHandle(markerName) } catch (error) { markerRemoved = error?.name === 'NotFoundError' }
|
|
report.edit = {
|
|
stableId: editedGeometry?.id === 'spline',
|
|
degree: editedGeometry?.type === 'bspline' ? editedGeometry.degree : null,
|
|
poleCount: editedGeometry?.type === 'bspline' ? editedGeometry.controlPoints.length : 0,
|
|
middlePole: editedGeometry?.type === 'bspline' ? editedGeometry.controlPoints[1] : null,
|
|
weights: editedGeometry?.type === 'bspline' ? editedGeometry.weights : null,
|
|
knots: editedGeometry?.type === 'bspline' ? editedGeometry.knots : null,
|
|
periodic: editedGeometry?.type === 'bspline' ? editedGeometry.periodic === true : false,
|
|
sourceUnchanged: sketchGeometrySignature(sourceGeometry) === JSON.stringify({ id: 'spline', type: 'bspline', degree: 2, controlPoints: [{ x: 0, y: 0 }, { x: 1, y: 2 }, { x: 3, y: 0 }], weights: [1, 1, 1], knots: [0, 0, 0, 1, 1, 1] }),
|
|
}
|
|
report.validationErrors = {
|
|
invalidWeight: errorFor({ weights: [1, 0, 1] }),
|
|
invalidKnots: errorFor({ knots: [0, 1] }),
|
|
invalidDegree: errorFor({ degree: 3 }),
|
|
}
|
|
report.roundTrip = {
|
|
archiveBytes: archive.byteLength,
|
|
geometryEqual: JSON.stringify(restored?.geometry) === JSON.stringify(constrained.geometry),
|
|
constraintsEqual: JSON.stringify(restored?.constraints) === JSON.stringify(constrained.constraints),
|
|
constraintTypes: restored?.constraints.map((constraint) => constraint.type) ?? [],
|
|
weights: restored?.geometry[0]?.type === 'bspline' ? restored.geometry[0].weights : null,
|
|
knots: restored?.geometry[0]?.type === 'bspline' ? restored.geometry[0].knots : null,
|
|
}
|
|
report.opfs = { markerSuite: markerPayload.suite, markerFields: markerPayload.fields, markerRemoved }
|
|
report.status = report.crossOriginIsolated && report.edit.stableId && report.edit.degree === 1 && report.edit.poleCount === 3 && report.edit.middlePole?.x === 2 && report.edit.middlePole?.y === 3 && JSON.stringify(report.edit.weights) === JSON.stringify([1, 0.5, 1]) && JSON.stringify(report.edit.knots) === JSON.stringify([0, 0, 0.5, 1, 1]) && report.edit.periodic && report.edit.sourceUnchanged && /weights/.test(report.validationErrors.invalidWeight ?? '') && /knots/.test(report.validationErrors.invalidKnots ?? '') && /degree \+ 1/.test(report.validationErrors.invalidDegree ?? '') && report.roundTrip.archiveBytes > 0 && report.roundTrip.geometryEqual && report.roundTrip.constraintsEqual && JSON.stringify(report.roundTrip.constraintTypes) === JSON.stringify(['weight', 'internalAlignment', 'weight']) && JSON.stringify(report.roundTrip.weights) === JSON.stringify([1, 1.5, 1.25, 1]) && JSON.stringify(report.roundTrip.knots) === JSON.stringify([0, 0, 0, 0, 1, 1, 1, 1]) && report.opfs.markerSuite === 'SK-10' && report.opfs.markerFields === 8 && report.opfs.markerRemoved ? 'pass' : 'failed'
|
|
} catch (error) {
|
|
report.status = 'failed'
|
|
report.error = error instanceof Error ? error.message : String(error)
|
|
}
|
|
output.textContent = JSON.stringify(report, null, 2)
|
|
await fetch('/__chrome-sketcher-bspline-report', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(report) })
|
|
if (report.status !== 'pass') throw new Error(report.error || 'Chrome Sketcher B-spline harness failed.')
|
|
</script>
|