Something went wrong. Try again.
[PRE-ALPHA] an open source web-based 2D/3D graphing calculator cartesium.org
Something went wrong. Try again.
TypeScript
at mathquill
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196import { describe, expect, it, vi } from 'vitest';
import { Scene3dPresentationController } from './scene3d-presentation-controller';import { Scene3dSupersededError } from './scene3d-worker-client';import type { Scene3dPartResult } from './scene3d-merge';import type { Scene3dViewSnapshot } from './scene3d-view';import type { GraphDocument3d, MeshResource3d, RenderScene3d, Scene3dRefinementTarget } from './types';
const bounds = { x_min: -1, x_max: 1, y_min: -1, y_max: 1, z_min: -1, z_max: 1 };
const document: GraphDocument3d = { bounds, expressions: [{ id: 'implicit', input_mode: 'text', source: 'x^2+y^2+z^2=1', enabled: true, color: '#2563eb' }]};
function scene(resources: RenderScene3d['resources'] = []): RenderScene3d { return { bounds, resources, diagnostics: [], variables: [], expression_roles: { implicit: 'plot' }, model_revisions: { implicit: 3 }, implicit_expression_ids: ['implicit'], implicit_cell_targets: [ { expression_id: 'implicit', cell: { level: 0, index: [0, 0, 0] }, model_revision: 3 } ] };}
function meshResource( qualityLevel: 0 | 1, resourceBounds = bounds, geometryId = qualityLevel + 1): MeshResource3d { return { type: 'mesh', key: { expression_id: 'implicit', representation: 'surface_mesh', region: 'cell-l0-x0-y0-z0', quality_level: qualityLevel }, geometry_id: geometryId, model_revision: 3, bounds: resourceBounds, positions: [[0, 0, 0], [0.1, 0, 0], [0, 0.1, 0]], normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]], indices: [0, 1, 2], color: '#2563eb' };}
function sceneWithMesh(resourceBounds = bounds, qualityLevel: 0 | 1 = 0) { return { ...scene([meshResource(qualityLevel, resourceBounds)]), bounds: resourceBounds };}
function sceneWithQ3Candidate() { return { ...scene(), implicit_adaptive_targets: [{ expression_id: 'implicit', parent: { level: 0, index: [0, 0, 0] as [number, number, number] }, estimated_error: 1, quality_level: 3 as const, transition_mask: 0, model_revision: 3, coverage_revision: 0 }] };}
const settledDocument: GraphDocument3d = { bounds, expressions: [ { id: 'variable', input_mode: 'text', source: 'a := 1', enabled: true, color: null }, { id: 'crease', input_mode: 'text', source: 'z = a*abs(x)', enabled: true, color: '#16a34a' } ]};
function settledPreviewScene(): RenderScene3d { const preview = scene([{ type: 'gpu_surface', key: { expression_id: 'crease', representation: 'surface_gpu', region: 'whole', quality_level: 0 }, geometry_id: 90, model_revision: 11, coverage_revision: 4, bounds, grid_size: 257, depth_mode: 'interpolated', value_glsl: 'abs(x)', dx_glsl: '1.0', dy_glsl: '0.0', uniforms: [{ name: 'u_cartesium_a', variable: 'a', value: 1 }], color: '#16a34a' }]);
preview.expression_roles = { variable: 'variable', crease: 'plot' }; preview.model_revisions = { crease: 11 }; preview.coverage_revisions = { crease: 4 }; preview.variables = [{ expression_id: 'variable', name: 'a', value: 1, slider: null }]; preview.implicit_expression_ids = []; preview.implicit_cell_targets = []; preview.settled_continuous_crease_patch_targets = [{ expression_id: 'crease', patch: [0, 0], model_revision: 11, coverage_revision: 4, estimated_error: 1, bounds, feature_kind: 'crease', settled: true }];
return preview;}
function settledPartFor( target: Extract<Scene3dRefinementTarget, { kind: 'settled-continuous-crease-patch' }>, geometryId: number): Scene3dPartResult { const resource = { type: 'mesh' as const, key: { expression_id: target.expressionId, representation: 'surface_mesh' as const, region: target.region, quality_level: 1 }, geometry_id: geometryId, model_revision: target.modelRevision, coverage_revision: target.coverageRevision, bounds, positions: [[0, 0, 0] as [number, number, number]], normals: [[0, 0, 1] as [number, number, number]], indices: [0], color: '#16a34a' };
const fragment = scene([resource]); fragment.expression_roles = { crease: 'plot' }; fragment.model_revisions = { crease: target.modelRevision }; fragment.coverage_revisions = { crease: target.coverageRevision ?? 0 };
return { part: target, scene: fragment, geometry: { meshes: [{ geometryId, positions: new Float32Array([0, 0, 0]), normals: new Float32Array([0, 0, 1]), indices: new Uint32Array([0]) }], points: [], curves: [], vectors: [] } };}
function settledAdaptivePartFor( target: Extract<Scene3dRefinementTarget, { kind: 'settled-continuous-crease-adaptive-child' }>, geometryId: number): Scene3dPartResult { const resource = { type: 'mesh' as const, key: { expression_id: target.expressionId, representation: 'surface_mesh' as const, region: target.region, quality_level: 2 }, geometry_id: geometryId, model_revision: target.modelRevision, coverage_revision: target.coverageRevision, bounds, positions: [[0, 0, 0] as [number, number, number]], normals: [[0, 0, 1] as [number, number, number]], indices: [0], color: '#16a34a' };
const fragment = scene([resource]); fragment.expression_roles = { crease: 'plot' }; fragment.model_revisions = { crease: target.modelRevision }; fragment.coverage_revisions = { crease: target.coverageRevision ?? 0 };
return { part: target, scene: fragment, geometry: { meshes: [{ geometryId, positions: new Float32Array([0, 0, 0]), normals: new Float32Array([0, 0, 1]), indices: new Uint32Array([0]) }], points: [], curves: [], vectors: [] } };}
function deferred<T>() { let resolve!: (value: T) => void; let reject!: (error: unknown) => void;
const promise = new Promise<T>((resolvePromise, rejectPromise) => { resolve = resolvePromise; reject = rejectPromise; });
return { promise, resolve, reject };}
function harness() { const previews: Array<ReturnType<typeof deferred<RenderScene3d>>> = []; const previewViews: Array<Scene3dViewSnapshot | undefined> = [];
const refinements: Array<{ resolve: () => void; reject: (error: unknown) => void; targets: Scene3dRefinementTarget[]; onpart: (part: Scene3dPartResult) => void; onstage?: (stage: 'q2') => void; view?: Scene3dViewSnapshot; }> = [];
const q3Refinements: Array<{ resolve: () => void; reject: (error: unknown) => void; candidates: RenderScene3d['implicit_adaptive_targets']; onpart: (part: Scene3dPartResult) => void; view?: Scene3dViewSnapshot; }> = [];
const adaptiveRefinements: Array<{ resolve: () => void; reject: (error: unknown) => void; targets: Scene3dRefinementTarget[]; onpart: (part: Scene3dPartResult) => void; view?: Scene3dViewSnapshot; }> = [];
const timers = new Map<number, () => void>(); const frames = new Map<number, (timestamp: number) => void>(); const delays: number[] = []; let nextTimer = 1; let now = 1000; const publish = vi.fn(); const setBusy = vi.fn(); const setError = vi.fn(); const cancel = vi.fn(); const setPhase = vi.fn();
const controller = new Scene3dPresentationController({ publish, setBusy, setError, cancel, buildPreview: (_document, _revision, view) => { previewViews.push(view); const result = deferred<RenderScene3d>(); previews.push(result);
return result.promise; }, refine: (_document, _revision, _targets, onpart, onstage, view) => { const result = deferred<void>(); refinements.push({ resolve: result.resolve, reject: result.reject, targets: _targets, onpart, onstage, view });
return result.promise; }, refineQ3: (_document, _revision, candidates, onpart, view) => { const result = deferred<void>(); q3Refinements.push({ resolve: result.resolve, reject: result.reject, candidates, onpart, view });
return result.promise; }, refineAdaptive: (_document, _revision, targets, onpart, view) => { const result = deferred<void>(); adaptiveRefinements.push({ resolve: result.resolve, reject: result.reject, targets, onpart, view });
return result.promise; }, setTimer: (callback, delay) => { const handle = nextTimer++; timers.set(handle, callback); delays.push(delay);
return handle; }, clearTimer: (handle) => timers.delete(handle), requestAnimationFrame: (callback) => { const handle = nextTimer++; frames.set(handle, callback);
return handle; }, cancelAnimationFrame: (handle) => frames.delete(handle), now: () => now, setPhase });
return { controller, previews, previewViews, refinements, q3Refinements, adaptiveRefinements, timers, frames, delays, publish, setBusy, setError, cancel, setPhase, setNow: (value: number) => (now = value) };}
describe('Scene3dPresentationController', () => { it('captures the latest view for a scheduled refinement without creating camera demand', async () => { const test = harness();
const firstView: Scene3dViewSnapshot = { viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], projectionMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], viewportWidth: 640, viewportHeight: 480 };
const latestView = { ...firstView, viewportWidth: 800 }; test.controller.setViewSnapshot(firstView); test.controller.setDocument(document); test.controller.setViewSnapshot(latestView); expect(test.cancel).not.toHaveBeenCalled(); expect(test.previewViews[0]).toEqual(firstView); test.previews[0].resolve(scene()); await test.previews[0].promise; [...test.timers.values()][0](); expect(test.refinements[0].view).toEqual(latestView); expect(test.cancel).not.toHaveBeenCalled(); });
it('debounces settled view changes into silent q3-only refinement', async () => { const test = harness();
const view: Scene3dViewSnapshot = { viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], projectionMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], viewportWidth: 640, viewportHeight: 480 };
test.controller.setViewSnapshot(view); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise; [...test.timers.values()][0](); const q2Scene = sceneWithQ3Candidate(); test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', region: 'cell-l0-x0-y0-z0', qualityLevel: 2, modelRevision: 3, priority: 0, cell: { level: 0, index: [0, 0, 0] } }, scene: q2Scene, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); test.refinements[0].onstage?.('q2'); expect(test.q3Refinements).toHaveLength(0); const busyCalls = test.setBusy.mock.calls.length; const phaseCalls = test.setPhase.mock.calls.length;
test.setNow(1100); test.controller.setViewSnapshot({ ...view, viewportWidth: 800 }); test.setNow(1150); test.controller.setViewSnapshot({ ...view, viewportWidth: 900 }); expect(test.q3Refinements).toHaveLength(0); const cameraTimer = [...test.timers.values()].at(-1)!; cameraTimer(); expect(test.q3Refinements).toHaveLength(1); expect(test.q3Refinements[0].view?.viewportWidth).toBe(900); expect(test.q3Refinements[0].candidates).toHaveLength(1); expect(test.setBusy.mock.calls.length).toBe(busyCalls); expect(test.setPhase.mock.calls.length).toBe(phaseCalls);
test.setNow(1400); test.controller.setViewSnapshot({ ...view, viewportWidth: 1000 }); [...test.timers.values()].at(-1)!(); expect(test.q3Refinements).toHaveLength(2); expect(test.cancel).toHaveBeenCalledTimes(2); expect(test.q3Refinements[1].view?.viewportWidth).toBe(1000); test.q3Refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2);
const q3Scene = scene(); q3Scene.implicit_adaptive_targets = []; test.q3Refinements[1].onpart({ part: { kind: 'implicit-adaptive-child', expressionId: 'implicit', representation: 'surface_mesh', region: 'cell-l-1-x0-y0-z0', qualityLevel: 3, modelRevision: 3, coverageRevision: 0, priority: -1, parent: { level: 0, index: [0, 0, 0] }, cell: { level: -1, index: [0, 0, 0] }, childBits: [0, 0, 0], atomicGroup: 'implicit|cell-l0-x0-y0-z0', replacementRegion: 'cell-l0-x0-y0-z0', transitionMask: 0, estimatedError: 1 }, scene: q3Scene, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); test.q3Refinements[1].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(3); });
it('admits settled-view explicit groups and never publishes an incomplete group', async () => { const test = harness();
const view: Scene3dViewSnapshot = { viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], projectionMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], viewportWidth: 640, viewportHeight: 480 };
test.controller.setViewSnapshot(view); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise; [...test.timers.values()][0](); const q2Scene = scene(); q2Scene.explicit_surface_adaptive_targets = [{ expression_id: 'implicit', patch: [0, 0], depth: 0, index: [0, 0], model_revision: 3, coverage_revision: 0, estimated_error: 1, bounds, feature_kind: 'crease' }]; test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', region: 'cell-l0-x0-y0-z0', qualityLevel: 2, modelRevision: 3, priority: 0, cell: { level: 0, index: [0, 0, 0] } }, scene: q2Scene, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); test.refinements[0].onstage?.('q2'); const settledPublishCount = test.publish.mock.calls.length;
test.setNow(1100); test.controller.setViewSnapshot({ ...view, viewportWidth: 800 }); [...test.timers.values()].at(-1)!(); expect(test.adaptiveRefinements).toHaveLength(1); expect(test.adaptiveRefinements[0].targets).toHaveLength(4); expect(new Set(test.adaptiveRefinements[0].targets.map((target) => target.kind === 'explicit-surface-adaptive-child' ? target.atomicGroup : '' )).size).toBe(1);
for (const target of test.adaptiveRefinements[0].targets.slice(0, 3)) { test.adaptiveRefinements[0].onpart({ part: target, scene: { ...scene(), explicit_surface_adaptive_targets: [] }, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); }
test.adaptiveRefinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(settledPublishCount);
test.setNow(1400); test.controller.setViewSnapshot({ ...view, viewportWidth: 900 }); [...test.timers.values()].at(-1)!(); expect(test.adaptiveRefinements).toHaveLength(2);
for (const target of test.adaptiveRefinements[1].targets) { test.adaptiveRefinements[1].onpart({ part: target, scene: { ...scene(), explicit_surface_adaptive_targets: [] }, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); }
test.adaptiveRefinements[1].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(settledPublishCount + 1); });
it('publishes coarse preview before settled cell refinement and merges parts', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise; expect(test.publish).toHaveBeenCalledTimes(1); expect(test.timers.size).toBe(1); expect(test.delays).toEqual([200]); const timer = [...test.timers.values()][0]; timer(); expect(test.refinements).toHaveLength(1); expect(test.setPhase).toHaveBeenLastCalledWith('refining'); const refinedPart = scene(); refinedPart.implicit_cell_targets = []; test.refinements[0].onpart({ part: { expressionId: 'implicit', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 1, modelRevision: 3 }, scene: refinedPart, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2); expect(test.publish.mock.calls[1][0].implicit_cell_targets).toEqual([]); expect(test.setBusy).toHaveBeenLastCalledWith(false); });
it('fences an obsolete preview and cancels its worker request', async () => { const test = harness(); test.controller.setDocument(document);
const changed = { ...document, expressions: [{ ...document.expressions[0], source: 'x^2+y^2+z^2=2' }] };
test.controller.setDocument(changed); expect(test.cancel).toHaveBeenCalled(); test.previews[1].resolve(scene()); test.previews[0].resolve(scene()); await Promise.all([test.previews[0].promise, test.previews[1].promise]); expect(test.publish).toHaveBeenCalledTimes(1); });
it('treats a bounds-only edit as a new preview instead of an override update', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise;
test.controller.setDocument({ ...document, bounds: { ...bounds, z_min: -2, z_max: 2 } });
expect(test.cancel).toHaveBeenCalledTimes(1); expect(test.previews).toHaveLength(2); expect(test.publish).toHaveBeenCalledTimes(1); });
it('publishes only the latest settled scene after rapid bounds revisions', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(sceneWithMesh()); await test.previews[0].promise; const firstExpanded = { ...bounds, z_min: -2, z_max: 2 }; const secondExpanded = { ...bounds, x_min: -3, x_max: 3 }; test.controller.setDocument({ ...document, bounds: firstExpanded }); test.controller.setDocument({ ...document, bounds: secondExpanded }); test.previews[2].resolve(sceneWithMesh(secondExpanded)); await test.previews[2].promise; expect(test.publish).toHaveBeenCalledTimes(1); const timer = [...test.timers.values()][0]; timer(); const refined = sceneWithMesh(secondExpanded, 1); refined.implicit_cell_targets = []; test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 1, modelRevision: 3, priority: 0 }, scene: refined, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2); expect(test.publish.mock.calls[1][0].bounds).toEqual(secondExpanded); });
it('publishes a compatible bounds preview at q2 before optional q3 closes', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(sceneWithMesh()); await test.previews[0].promise; const expanded = { ...bounds, z_min: -2, z_max: 2 }; test.controller.setDocument({ ...document, bounds: expanded }); const staged = sceneWithMesh(expanded); staged.implicit_cell_targets = [ { expression_id: 'implicit', cell: { level: 0, index: [0, 0, 0] }, model_revision: 3 }, { expression_id: 'implicit', cell: { level: 0, index: [1, 0, 0] }, model_revision: 3 } ]; test.previews[1].resolve(staged); await test.previews[1].promise; expect(test.publish).toHaveBeenCalledTimes(1); [...test.timers.values()][0](); const q1 = scene(); q1.bounds = expanded; q1.implicit_cell_targets = []; test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 1, modelRevision: 3, priority: 0 }, scene: q1, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); expect(test.publish).toHaveBeenCalledTimes(1); test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [1, 0, 0] }, region: 'cell-l0-x1-y0-z0', qualityLevel: 1, modelRevision: 3, priority: 0 }, scene: q1, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); expect(test.publish).toHaveBeenCalledTimes(1); test.refinements[0].onstage?.('q2'); expect(test.publish).toHaveBeenCalledTimes(2); expect(test.setBusy).toHaveBeenLastCalledWith(false); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2); expect(test.publish.mock.calls[1][0].bounds).toEqual(expanded); expect(test.publish.mock.calls[1][0].resources).toEqual([]); });
it('uses displayed geometry for bounds compatibility but never hides a changed-math preview', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(sceneWithMesh()); await test.previews[0].promise;
const changed = { ...document, expressions: [{ ...document.expressions[0], source: 'x^2+y^2+z^2=2' }] };
test.controller.setDocument(changed); const expanded = { ...bounds, z_min: -2, z_max: 2 }; test.controller.setDocument({ ...changed, bounds: expanded }); test.previews[2].resolve(sceneWithMesh(expanded)); await test.previews[2].promise; expect(test.publish).toHaveBeenCalledTimes(2); expect(test.publish.mock.calls[1][0].bounds).toEqual(expanded); });
it('publishes a staged preview immediately when no q1 targets remain', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(sceneWithMesh()); await test.previews[0].promise; const expanded = { ...bounds, z_min: -2, z_max: 2 }; test.controller.setDocument({ ...document, bounds: expanded }); const emptyPreview = scene(); emptyPreview.bounds = expanded; emptyPreview.resources = []; emptyPreview.implicit_cell_targets = []; test.previews[1].resolve(emptyPreview); await test.previews[1].promise; expect(test.publish).toHaveBeenCalledTimes(2); expect(test.publish.mock.calls[1][0].bounds).toEqual(expanded); });
it('keeps the old scene when a retained bounds refinement is cancelled before q1', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(sceneWithMesh()); await test.previews[0].promise; const expanded = { ...bounds, z_min: -2, z_max: 2 }; test.controller.setDocument({ ...document, bounds: expanded }); test.previews[1].resolve(sceneWithMesh(expanded)); await test.previews[1].promise; [...test.timers.values()][0](); test.refinements[0].reject(new Scene3dSupersededError()); await Promise.resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(1); expect(test.setBusy).toHaveBeenLastCalledWith(false); });
it('keeps the old scene when a retained bounds refinement errors', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(sceneWithMesh()); await test.previews[0].promise; const expanded = { ...bounds, z_min: -2, z_max: 2 }; test.controller.setDocument({ ...document, bounds: expanded }); test.previews[1].resolve(sceneWithMesh(expanded)); await test.previews[1].promise; [...test.timers.values()][0](); const q1 = sceneWithMesh(expanded, 1); q1.implicit_cell_targets = []; test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 1, modelRevision: 3, priority: 0 }, scene: q1, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); expect(test.publish).toHaveBeenCalledTimes(1); test.refinements[0].reject(new Error('q2 failed')); await Promise.resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(1); expect(test.setError).toHaveBeenLastCalledWith('q2 failed'); });
it('keeps bounds-transition q2 parts hidden until refinement completes', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(sceneWithMesh()); await test.previews[0].promise; const expanded = { ...bounds, z_min: -2, z_max: 2 }; test.controller.setDocument({ ...document, bounds: expanded }); test.previews[1].resolve(sceneWithMesh(expanded)); await test.previews[1].promise; [...test.timers.values()][0]();
const q1 = sceneWithMesh(expanded, 1); q1.implicit_cell_targets = []; test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 1, modelRevision: 3, priority: 0 }, scene: q1, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); expect(test.publish).toHaveBeenCalledTimes(1);
test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 2, modelRevision: 3, priority: 0 }, scene: { ...scene(), bounds: expanded }, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); expect(test.publish).toHaveBeenCalledTimes(1); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2); });
it('does not publish a staged preview when only q2 work remains', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(sceneWithMesh()); await test.previews[0].promise; const expanded = { ...bounds, z_min: -2, z_max: 2 }; test.controller.setDocument({ ...document, bounds: expanded }); const staged = sceneWithMesh(expanded, 1); staged.implicit_cell_targets = []; staged.packed_geometry = { meshes: [{ geometryId: 2, positions: new Float32Array([0, 0, 0, 0.1, 0, 0, 0, 0.1, 0]), normals: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1]), indices: new Uint32Array([0, 1, 2]) }], points: [], curves: [], vectors: [] }; test.previews[1].resolve(staged); await test.previews[1].promise; expect(test.publish).toHaveBeenCalledTimes(1); [...test.timers.values()][0](); test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 2, modelRevision: 3, priority: 0 }, scene: { ...scene(), bounds: expanded }, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); expect(test.publish).toHaveBeenCalledTimes(1); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2); });
it('publishes several refined parts once per animation frame', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise; [...test.timers.values()][0](); const refinedPart = scene(); refinedPart.implicit_cell_targets = [];
const part = { part: { kind: 'implicit-cell' as const, expressionId: 'implicit', representation: 'surface_mesh' as const, cell: { level: 0, index: [0, 0, 0] as [number, number, number] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 1 as const, modelRevision: 3, priority: 0 }, scene: refinedPart, geometry: { meshes: [], points: [], curves: [], vectors: [] } };
test.refinements[0].onpart(part); test.refinements[0].onpart(part); expect(test.publish).toHaveBeenCalledTimes(1); const frame = [...test.frames.values()][0]; frame(1010); expect(test.publish).toHaveBeenCalledTimes(2); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2); });
it('buffers quality-2 replacements until the refinement transaction completes', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise; [...test.timers.values()][0](); const refinedPart = scene(); test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 2, modelRevision: 3, priority: 0 }, scene: refinedPart, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); expect(test.publish).toHaveBeenCalledTimes(1); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2); expect(test.publish.mock.calls[1][0].resources).toEqual([]); });
it('settles at q2 while retaining an atomic q3 upgrade for later', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise; [...test.timers.values()][0](); const q2Scene = scene(); test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 2, modelRevision: 3, priority: 0 }, scene: q2Scene, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); test.refinements[0].onstage?.('q2'); expect(test.publish).toHaveBeenCalledTimes(2); expect(test.setBusy).toHaveBeenLastCalledWith(false); expect(test.setPhase).toHaveBeenLastCalledWith('idle');
test.refinements[0].onpart({ part: { kind: 'implicit-adaptive-child', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: -1, index: [0, 0, 0] }, region: 'cell-l-1-x0-y0-z0', qualityLevel: 3, modelRevision: 3, priority: 0, parent: { level: 0, index: [0, 0, 0] }, childBits: [0, 0, 0], atomicGroup: 'group', replacementRegion: 'cell-l0-x0-y0-z0', transitionMask: 0, estimatedError: 1 }, scene: scene(), geometry: { meshes: [], points: [], curves: [], vectors: [] } }); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(3); });
it('keeps q2 visible when optional q3 refinement fails', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise; [...test.timers.values()][0](); test.refinements[0].onstage?.('q2'); test.refinements[0].reject(new Error('q3 failed')); await Promise.resolve(); await Promise.resolve(); expect(test.setError).not.toHaveBeenCalledWith('q3 failed'); expect(test.setBusy).toHaveBeenLastCalledWith(false); expect(test.setPhase).toHaveBeenLastCalledWith('idle'); });
it('discards an unflushed refinement batch when the document changes', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].resolve(scene()); await test.previews[0].promise; [...test.timers.values()][0](); const refinedPart = scene(); test.refinements[0].onpart({ part: { kind: 'implicit-cell', expressionId: 'implicit', representation: 'surface_mesh', cell: { level: 0, index: [0, 0, 0] }, region: 'cell-l0-x0-y0-z0', qualityLevel: 1, modelRevision: 3, priority: 0 }, scene: refinedPart, geometry: { meshes: [], points: [], curves: [], vectors: [] } }); expect(test.frames.size).toBe(1);
test.controller.setDocument({ ...document, expressions: [{ ...document.expressions[0], source: 'x^2+y^2+z^2=2' }] }); expect(test.frames.size).toBe(0); test.previews[1].resolve(scene()); await test.previews[1].promise; expect(test.publish).toHaveBeenCalledTimes(2); });
it('fences preview errors and returns to idle after the current revision fails', async () => { const test = harness(); test.controller.setDocument(document); test.previews[0].reject(new Error('preview failed')); await expect(test.previews[0].promise).rejects.toThrow('preview failed'); await Promise.resolve(); expect(test.setBusy).toHaveBeenLastCalledWith(false); expect(test.setPhase).toHaveBeenLastCalledWith('idle'); });
it('keeps the GPU preview until a complete settled crease group is available', async () => { const test = harness(); test.controller.setDocument(settledDocument); test.previews[0].resolve(settledPreviewScene()); await test.previews[0].promise; [...test.timers.values()][0]();
const targets = test.refinements[0].targets.filter( (target): target is Extract<Scene3dRefinementTarget, { kind: 'settled-continuous-crease-patch' }> => target.kind === 'settled-continuous-crease-patch' );
expect(targets).toHaveLength(16);
for (const target of targets.slice(0, 15)) { test.refinements[0].onpart(settledPartFor(target, target.patch[1] * 4 + target.patch[0] + 1)); }
test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(1); expect(test.publish.mock.calls[0][0].resources.some((resource: RenderScene3d['resources'][number]) => resource.type === 'gpu_surface')).toBe(true);
const second = harness(); second.controller.setDocument(settledDocument); second.previews[0].resolve(settledPreviewScene()); await second.previews[0].promise; [...second.timers.values()][0]();
const secondTargets = second.refinements[0].targets.filter( (target): target is Extract<Scene3dRefinementTarget, { kind: 'settled-continuous-crease-patch' }> => target.kind === 'settled-continuous-crease-patch' );
for (const target of secondTargets) { second.refinements[0].onpart(settledPartFor(target, target.patch[1] * 4 + target.patch[0] + 1)); }
second.refinements[0].resolve(); await Promise.resolve(); expect(second.publish).toHaveBeenCalledTimes(2); const settled = second.publish.mock.calls[1][0] as RenderScene3d; expect(settled.resources.some((resource) => resource.type === 'gpu_surface')).toBe(false); expect(settled.resources.filter((resource) => resource.type === 'mesh' && resource.key.expression_id === 'crease' )).toHaveLength(16); });
it('treats a failed optional settled crease group as a silent GPU-preserving fallback', async () => { const test = harness(); test.controller.setDocument(settledDocument); test.previews[0].resolve(settledPreviewScene()); await test.previews[0].promise; [...test.timers.values()][0]();
const target = test.refinements[0].targets.find( (candidate): candidate is Extract<Scene3dRefinementTarget, { kind: 'settled-continuous-crease-patch' }> => candidate.kind === 'settled-continuous-crease-patch' )!;
test.refinements[0].onpart(settledPartFor(target, 1)); test.refinements[0].reject(new Error('optional settled mesh failed')); await Promise.resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(1); expect(test.setError.mock.calls.filter(([value]) => value !== null)).toHaveLength(0); expect(test.publish.mock.calls[0][0].resources.some((resource: RenderScene3d['resources'][number]) => resource.type === 'gpu_surface')).toBe(true); });
it('routes retained settled roots to atomic q2 children after the q1 publication', async () => { const test = harness();
const view: Scene3dViewSnapshot = { viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], projectionMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], viewportWidth: 640, viewportHeight: 480 };
test.controller.setViewSnapshot(view); test.controller.setDocument(settledDocument); test.previews[0].resolve(settledPreviewScene()); await test.previews[0].promise; [...test.timers.values()][0]();
const roots = test.refinements[0].targets.filter( (target): target is Extract<Scene3dRefinementTarget, { kind: 'settled-continuous-crease-patch' }> => target.kind === 'settled-continuous-crease-patch' );
expect(roots).toHaveLength(16);
for (const target of roots) { test.refinements[0].onpart(settledPartFor(target, target.patch[1] * 4 + target.patch[0] + 1)); }
test.refinements[0].onstage?.('q2'); test.refinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(2);
test.setNow(1100); test.controller.setViewSnapshot({ ...view, viewportWidth: 800 }); [...test.timers.values()].at(-1)!(); expect(test.adaptiveRefinements).toHaveLength(1); const children = test.adaptiveRefinements[0].targets; expect(children).toHaveLength(64); expect(children.every((target) => target.kind === 'settled-continuous-crease-adaptive-child')).toBe(true);
for (const [index, target] of children.entries()) { if (target.kind === 'settled-continuous-crease-adaptive-child') { test.adaptiveRefinements[0].onpart(settledAdaptivePartFor(target, 100 + index)); } }
test.adaptiveRefinements[0].resolve(); await Promise.resolve(); expect(test.publish).toHaveBeenCalledTimes(3); const merged = test.publish.mock.calls[2][0] as RenderScene3d; expect(merged.resources.some((resource) => resource.type === 'gpu_surface')).toBe(false); expect(merged.resources.filter((resource) => resource.type === 'mesh' && resource.key.expression_id === 'crease' )).toHaveLength(64); expect(merged.resources.some((resource) => /^patch-\d-\d$/.test(resource.key.region))).toBe(false); expect(merged.settled_continuous_crease_adaptive_targets).toEqual([]); });
it('restores the cached GPU immediately for an override-only edit after settlement', async () => { const test = harness(); test.controller.setDocument(settledDocument); test.previews[0].resolve(settledPreviewScene()); await test.previews[0].promise; [...test.timers.values()][0]();
const targets = test.refinements[0].targets.filter( (target): target is Extract<Scene3dRefinementTarget, { kind: 'settled-continuous-crease-patch' }> => target.kind === 'settled-continuous-crease-patch' );
for (const target of targets) { test.refinements[0].onpart(settledPartFor(target, target.patch[1] * 4 + target.patch[0] + 1)); }
test.refinements[0].resolve(); await Promise.resolve(); test.controller.setDocument({ ...settledDocument, variable_overrides: { a: 2 } }); expect(test.publish).toHaveBeenCalledTimes(3); const restored = test.publish.mock.calls[2][0] as RenderScene3d; expect(restored.resources.filter((resource) => resource.type === 'mesh')).toHaveLength(0); expect(restored.resources[0]).toMatchObject({ type: 'gpu_surface', depth_mode: 'interpolated', uniforms: [{ variable: 'a', value: 2 }] }); expect(test.previews).toHaveLength(2); });
it.each([ ['source', { expressions: settledDocument.expressions.map((expression) => expression.id === 'crease' ? { ...expression, source: 'z = a*x' } : expression ) }], ['complex mode', { complex_mode: true }], ['bounds', { bounds: { ...bounds, x_max: 2 } }] ])('does not resurrect cached GPU after an incompatible %s edit', async (_kind, change) => { const test = harness(); test.controller.setDocument(settledDocument); test.previews[0].resolve(settledPreviewScene()); await test.previews[0].promise; [...test.timers.values()][0]();
const targets = test.refinements[0].targets.filter( (target): target is Extract<Scene3dRefinementTarget, { kind: 'settled-continuous-crease-patch' }> => target.kind === 'settled-continuous-crease-patch' );
for (const target of targets) { test.refinements[0].onpart(settledPartFor(target, target.patch[1] * 4 + target.patch[0] + 1)); }
test.refinements[0].resolve(); await Promise.resolve(); const changed = { ...settledDocument, ...change }; test.controller.setDocument(changed); test.previews[1].resolve(scene()); await test.previews[1].promise;
// A later override-only update is the synchronous restore entry point. If // the old cache survived the incompatible edit, it would reintroduce the // settled document's whole GPU surface here. test.controller.setDocument({ ...changed, variable_overrides: { a: 2 } }); const restored = test.publish.mock.calls.at(-1)?.[0] as RenderScene3d; expect(restored.resources.some((resource) => resource.type === 'gpu_surface')).toBe(false); });
it('updates colors without rebuilding an already displayed settled scene', async () => { const test = harness(); test.controller.setDocument(settledDocument); test.previews[0].resolve(settledPreviewScene()); await test.previews[0].promise;
const recolored = { ...settledDocument, expressions: settledDocument.expressions.map((expression) => expression.id === 'crease' ? { ...expression, color: '#dc2626' } : expression ) };
test.controller.setDocument(recolored); expect(test.previews).toHaveLength(1); expect(test.publish).toHaveBeenCalledTimes(2); expect(test.publish.mock.calls[1][0].resources[0]).toMatchObject({ color: '#dc2626' }); });
it('updates retained GPU uniforms immediately for override-only edits', async () => { const test = harness();
const dynamicDocument: GraphDocument3d = { ...document, expressions: [{ id: 'surface', input_mode: 'text', source: 'a := 1', enabled: true, color: '#2563eb' }], variable_overrides: { a: 1 } };
const gpuScene = scene([{ type: 'gpu_surface', key: { expression_id: 'surface', representation: 'surface_gpu', region: 'whole', quality_level: 0 }, geometry_id: 7, model_revision: 1, bounds, grid_size: 257, depth_mode: 'exact', value_glsl: 'u_cartesium_a * x', dx_glsl: 'u_cartesium_a', dy_glsl: '0.0', uniforms: [{ name: 'u_cartesium_a', variable: 'a', value: 1 }], color: '#2563eb' }]);
gpuScene.variables = [{ expression_id: 'surface', name: 'a', value: 1, slider: null }]; test.controller.setDocument(dynamicDocument); test.previews[0].resolve(gpuScene); await test.previews[0].promise; test.controller.setDocument({ ...dynamicDocument, variable_overrides: { a: 2 } }); expect(test.publish).toHaveBeenCalledTimes(2); expect(test.publish.mock.calls[1][0].resources[0]).toMatchObject({ type: 'gpu_surface', depth_mode: 'interpolated', uniforms: [{ variable: 'a', value: 2 }] }); expect(test.publish.mock.calls[1][0].variables[0].value).toBe(2);
const authoritativeScene = { ...gpuScene, implicit_cell_targets: [], resources: [{ ...gpuScene.resources[0], depth_mode: 'exact' as const, uniforms: [{ name: 'u_cartesium_a', variable: 'a', value: 2 }] }], variables: [{ expression_id: 'surface', name: 'a', value: 2, slider: null }] };
test.previews[1].resolve(authoritativeScene); await test.previews[1].promise; expect(test.publish).toHaveBeenCalledTimes(3); expect(test.publish.mock.calls[2][0].resources[0]).toMatchObject({ type: 'gpu_surface', depth_mode: 'exact', uniforms: [{ variable: 'a', value: 2 }] }); });});