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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246import { describe, expect, it, vi } from 'vitest';
import { INTERACTIVE_PREVIEW_CELL_SIZE, PREVIEW_CELL_SIZE, REFINE_DELAY_MS } from './rendering';import { ScenePresentationController } from './scene-presentation-controller';import { sceneContentSignature } from './scene-demand';import type { GraphDocument2d, RenderScene2d, SamplingMetadata, SceneLayer} from './types';
const document: GraphDocument2d = { viewport: { x_min: -4, x_max: 4, y_min: -3, y_max: 3, width: 800, height: 600 }, expressions: [ { id: 'curve', input_mode: 'text', source: 'y=sin(x)', enabled: true, color: '#f00' } ]};
const sampling: SamplingMetadata = { strategy: 'marching_squares', target_samples: 0, vertex_count: 0, segment_count: 0, max_depth: null, max_depth_reached: false, vertex_budget: null, vertex_budget_reached: false, warnings: []};
function planDocument( planDocument: GraphDocument2d, interactive: boolean, preserveCameraSceneOnSettle = false, retainedSceneNeedsRefinement = false) { return { document: planDocument, contentSignature: sceneContentSignature(planDocument), interactive, preserveCameraSceneOnSettle, retainedSceneNeedsRefinement };}
function scene(viewport = document.viewport): RenderScene2d { return { viewport, coverage: viewport, layers: [], diagnostics: [], variables: [], expression_roles: {}, model_revisions: {} };}
function sceneWithLayers(layers: SceneLayer[]): RenderScene2d { return { ...scene(), layers };}
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 builds: Array<{ document: GraphDocument2d; result: ReturnType<typeof deferred<RenderScene2d>> }> = []; const timers = new Map<number, { callback: () => void; delayMs: number }>(); let nextTimer = 1; const publish = vi.fn(); const setBusy = vi.fn(); const setError = vi.fn();
const controller = new ScenePresentationController({ publish, setBusy, setError, build: (requestDocument) => { const result = deferred<RenderScene2d>(); builds.push({ document: requestDocument, result });
return result.promise; }, setTimer: (callback, delayMs) => { const handle = nextTimer++; timers.set(handle, { callback, delayMs });
return handle; }, clearTimer: (handle) => { timers.delete(handle); } });
return { controller, builds, timers, publish, setBusy, setError };}
describe('ScenePresentationController', () => { it('keeps a spatially tiled preview without a redundant whole-scene refinement', async () => { const test = harness(); test.controller.setPlan(planDocument(document, false));
expect(test.builds[0].document.grid_cell_size).toBe(PREVIEW_CELL_SIZE); test.builds[0].result.resolve( sceneWithLayers([ { type: 'polyline', geometry_id: 1, expression_id: 'curve', vertices: [], breaks: [], color: '#f00', line_style: 'solid', source_kind: 'implicit', sampling: sampling } ]) ); await test.builds[0].result.promise; expect(test.publish).toHaveBeenCalledWith(expect.any(Object), document); expect(test.timers.size).toBe(0); });
it('refines a general polar classifier after its preview is available', async () => { const test = harness(); test.controller.setPlan(planDocument(document, false));
test.builds[0].result.resolve( sceneWithLayers([ { type: 'fill', geometry_id: 1, expression_id: 'curve', rectangles: [[-1, 1, -1, 1]], color: '#f00' }, { type: 'polyline', geometry_id: 2, expression_id: 'curve', vertices: [], breaks: [], color: '#f00', line_style: 'solid', source_kind: 'polar_inequality', sampling } ]) ); await test.builds[0].result.promise;
expect([...test.timers.values()][0].delayMs).toBe(REFINE_DELAY_MS); [...test.timers.values()][0].callback(); expect(test.builds[1].document).toBe(document); });
it('uses only a bounded preview while interaction remains active', () => { const test = harness(); test.controller.setPlan(planDocument(document, true));
expect(test.builds[0].document.grid_cell_size).toBe(INTERACTIVE_PREVIEW_CELL_SIZE); expect(test.timers.size).toBe(0); });
it('preserves the camera scene and schedules immediate refinement on settle', () => { const test = harness(); test.controller.setPlan(planDocument(document, false, true, true));
expect(test.builds).toHaveLength(0); expect([...test.timers.values()][0].delayMs).toBe(0); [...test.timers.values()][0].callback(); expect(test.builds[0].document).toBe(document); });
it('clears busy when a preserved settled scene needs no work', () => { const test = harness(); test.controller.setPlan(planDocument(document, false, true, false));
expect(test.builds).toHaveLength(0); expect(test.timers.size).toBe(0); expect(test.setBusy).toHaveBeenLastCalledWith(false); });
it('does not let an older completion replace a newer presented scene', async () => { const test = harness(); const firstDocument = document; const secondDocument = { ...document, viewport: { ...document.viewport, x_min: -2, x_max: 6 } }; test.controller.setPlan(planDocument(firstDocument, true)); test.controller.setPlan(planDocument(secondDocument, true));
test.builds[1].result.resolve(scene(secondDocument.viewport)); await test.builds[1].result.promise; test.builds[0].result.resolve(scene(firstDocument.viewport)); await test.builds[0].result.promise;
expect(test.publish).toHaveBeenCalledTimes(1); expect(test.publish.mock.calls[0][1]).toBe(secondDocument); });
it('rejects live-model completions after a different model settles', async () => { const test = harness();
const animatedDocument = { ...document, variable_overrides: { b: 4 } };
const settledDocument = { ...document, expressions: document.expressions.map((expression) => ({ ...expression, source: expression.id === 'curve' ? 'b := 2' : expression.source })) };
test.controller.setPlan(planDocument(animatedDocument, true)); test.controller.setPlan(planDocument(settledDocument, false));
test.builds[0].result.resolve(scene()); await test.builds[0].result.promise;
expect(test.publish).not.toHaveBeenCalled(); });
it('reports errors only for the current plan', async () => { const test = harness(); test.controller.setPlan(planDocument(document, true)); test.controller.setPlan(undefined);
test.builds[0].result.reject(new Error('obsolete')); await expect(test.builds[0].result.promise).rejects.toThrow('obsolete');
expect(test.setError).not.toHaveBeenCalled(); expect(test.setBusy).toHaveBeenLastCalledWith(false); });});