@photivra/engine 2.10.0 → 2.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +139 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +21 -3
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/dist/core/portable-math.d.ts +6 -0
- package/dist/core/portable-math.js +39 -0
- package/dist/core/portable-math.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/chart-material-layers.d.ts +115 -0
- package/dist/facade/chart-material-layers.js +224 -0
- package/dist/facade/chart-material-layers.js.map +1 -0
- package/dist/facade/chart-radiance-batch.d.ts +8 -2
- package/dist/facade/chart-radiance-batch.js +11 -4
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +134 -0
- package/dist/facade/chart-scenes.js +32 -22
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +8 -2
- package/dist/facade/diffraction-provider.js +71 -2
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.d.ts +18 -0
- package/dist/facade/display-view-gpu-session.js +280 -0
- package/dist/facade/display-view-gpu-session.js.map +1 -0
- package/dist/facade/display-view-gpu.d.ts +146 -0
- package/dist/facade/display-view-gpu.js +59 -0
- package/dist/facade/display-view-gpu.js.map +1 -0
- package/dist/facade/display-view.d.ts +48 -1
- package/dist/facade/display-view.js +7 -7
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +5 -0
- package/dist/facade/expected-signal-setup.js +16 -0
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +48 -3
- package/dist/facade/expected-signal.js +8 -0
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/scene-radiance.d.ts +4 -2
- package/dist/facade/scene-radiance.js +7 -4
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +32 -5
- package/dist/facade/simulate-capture.js +148 -24
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +31 -3
- package/dist/facade/simulate-display-view.js +72 -12
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +119 -0
- package/dist/gpu/chart-composite-gpu.js +572 -0
- package/dist/gpu/chart-composite-gpu.js.map +1 -0
- package/dist/gpu/chart-radiance-gpu.d.ts +6 -6
- package/dist/gpu/chart-radiance-gpu.js +9 -11
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-chart-material-module.d.ts +10 -0
- package/dist/gpu/display-view-chart-material-module.js +58 -0
- package/dist/gpu/display-view-chart-material-module.js.map +1 -0
- package/dist/gpu/display-view-composite-material-module.d.ts +68 -0
- package/dist/gpu/display-view-composite-material-module.js +248 -0
- package/dist/gpu/display-view-composite-material-module.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +185 -0
- package/dist/gpu/display-view-quality-gpu.js +1000 -0
- package/dist/gpu/display-view-quality-gpu.js.map +1 -0
- package/dist/gpu/display-view-speed-gpu.d.ts +156 -0
- package/dist/gpu/display-view-speed-gpu.js +524 -0
- package/dist/gpu/display-view-speed-gpu.js.map +1 -0
- package/dist/gpu/image-kernel-accelerator.d.ts +23 -4
- package/dist/gpu/image-kernel-accelerator.js +58 -10
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +55 -3
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/sensor-stage-gpu.d.ts +11 -0
- package/dist/gpu/sensor-stage-gpu.js +42 -13
- package/dist/gpu/sensor-stage-gpu.js.map +1 -1
- package/dist/gpu/webgpu-runtime.d.ts +4 -2
- package/dist/gpu/webgpu-runtime.js +13 -4
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/gpu/webgpu-switch.d.ts +4 -0
- package/dist/gpu/webgpu-switch.js +29 -0
- package/dist/gpu/webgpu-switch.js.map +1 -0
- package/dist/gpu/wgsl-chart-composite-kernel.d.ts +52 -0
- package/dist/gpu/wgsl-chart-composite-kernel.js +515 -0
- package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -0
- package/dist/gpu/wgsl-chart-kernel.d.ts +11 -9
- package/dist/gpu/wgsl-chart-kernel.js +33 -27
- package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +95 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.js +769 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +75 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.js +386 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -0
- package/dist/gpu/wgsl-sensor-kernel.d.ts +28 -9
- package/dist/gpu/wgsl-sensor-kernel.js +94 -50
- package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/optics/defocus-disc-kernel.d.ts +61 -0
- package/dist/optics/defocus-disc-kernel.js +191 -0
- package/dist/optics/defocus-disc-kernel.js.map +1 -0
- package/docs/API_REFERENCE.md +45 -1
- package/docs/QUICK_START.md +102 -3
- package/docs/USAGE.md +52 -2
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +117 -2
- package/docs/api/facade-chart-material-layers.md +137 -0
- package/docs/api/facade-chart-radiance-batch.md +7 -2
- package/docs/api/facade-display-view-gpu.md +94 -0
- package/docs/api/facade-scene-radiance.md +3 -2
- package/docs/api/facade-simulate-capture.md +23 -6
- package/docs/api/facade-simulate-display-view.md +17 -2
- package/docs/api/gpu-image-kernel-accelerator.md +10 -3
- package/docs/api/gpu-image-kernel-execution.md +71 -4
- package/docs/api/gpu-webgpu-switch.md +23 -0
- package/package.json +3 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-material-layers.js","sourceRoot":"","sources":["../../src/facade/chart-material-layers.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Declared material scenes (#477): an engine chart under shape-mask layers, given as data rather than as a\n * `materialAt` function, so that both the float64 reference and the `webgpu-f32` chart stage can evaluate\n * them. An application that prints a label on a chart (a logo and a line of text as filled polygons and\n * stroked rings, in the label's own units) describes it here; the engine holds no label of its own.\n *\n * A layer maps its mask box, [0, unitsWidth] × [0, unitsHeight] with y down, onto a frame rectangle. Inside\n * that rectangle the layer decides the material: the ink material where any of its shapes contains the\n * point, its background material elsewhere. Later layers cover earlier ones, and the chart shows outside\n * every layer. Shapes are polygons under the non-zero winding rule (several rings per shape; a stroked\n * polyline is its rectangles as rings of one orientation) and annuli (stroked circles).\n *\n * Each layer carries a cell index of {@link CHART_MASK_INDEX_GRAIN} cells over its box: a cell lists the\n * shapes whose bounding box meets the cell grown by one cell on every side, so `containsUnits` tests only\n * those. A computed cell can be off by one from the true one only through the rounding of one subtraction\n * and one multiplication, far less than a cell (the grain is checked against the coordinates' magnitude\n * when the layer is resolved), and a shape left out of a cell's list lies a whole cell away from any point\n * that resolves to it, so the indexed test gives the full test's answer at every point. The GPU uses the same\n * index (docs/WEBGPU.md, \"Scene radiance on the GPU\").\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { resolveChartScene, resolveChartMaterial, chartMaterialInputs, type ChartReflectance, type ChartScene, type ResolvedChartScene } from \"./chart-scenes.js\";\nimport type { CallerMaterialScene, SceneMaterialPoint } from \"./scene-radiance.js\";\n\n/** A filled polygon: rings as flat [x0, y0, x1, y1, …] in mask units (y down), non-zero winding over all rings. */\nexport interface ChartMaskPolygon { kind: \"polygon\"; rings: readonly (readonly number[])[] }\n/** A stroked circle: the points at distance innerRadius ≤ d ≤ outerRadius from the centre, mask units. */\nexport interface ChartMaskAnnulus { kind: \"annulus\"; centerX: number; centerY: number; innerRadius: number; outerRadius: number }\nexport type ChartMaskShape = ChartMaskPolygon | ChartMaskAnnulus;\n/** A shape mask printed over the chart: its box [0, unitsWidth] × [0, unitsHeight] (y down) fills `rectangle` (frame units, v up). */\nexport interface ChartMaskLayer {\n kind: \"shape-mask\";\n rectangle: { u0: number; v0: number; u1: number; v1: number };\n unitsWidth: number;\n unitsHeight: number;\n /** 1 to 4096 shapes, in order; the ink material where any contains the point. */\n shapes: readonly ChartMaskShape[];\n backgroundReflectance: ChartReflectance;\n inkReflectance: ChartReflectance;\n}\n/** A chart under 1 to 16 shape-mask layers, as data; `resolveChartMaterialLayers` evaluates it. */\nexport interface ChartMaterialLayerScene { kind: \"material-layers\"; sceneId: string; chart: ChartScene; layers: readonly ChartMaskLayer[] }\n\n/** The cell index grain of every layer: cells across and down the mask box. */\nexport const CHART_MASK_INDEX_GRAIN = Object.freeze({ cellsX: 64, cellsY: 16 });\n/** Limits of a declared scene. */\nexport const CHART_MATERIAL_LAYER_LIMITS = Object.freeze({ maximumLayers: 16, maximumShapes: 4096, maximumRings: 256, maximumEdges: 65_536 });\n\n/** Internal: a shape with its bounding box and flattened edges (polygon) or squared radii (annulus). */\nexport type ResolvedChartMaskShape =\n | { kind: \"polygon\"; box: readonly [number, number, number, number]; edges: Float64Array }\n | { kind: \"annulus\"; box: readonly [number, number, number, number]; centerX: number; centerY: number; inner2: number; outer2: number };\nexport interface ResolvedChartMaskLayer {\n rectangle: { u0: number; v0: number; u1: number; v1: number };\n unitsWidth: number;\n unitsHeight: number;\n /** Mask units per frame unit: unitsWidth / (u1 − u0) and unitsHeight / (v1 − v0). */\n scaleX: number;\n scaleY: number;\n shapes: readonly ResolvedChartMaskShape[];\n cellsX: number;\n cellsY: number;\n cellWidth: number;\n cellHeight: number;\n /** Per cell (row by row), the shape indices whose bounding box meets the cell grown by one cell. */\n cells: readonly Uint32Array[];\n backgroundMaterial: number;\n inkMaterial: number;\n /** Whether a point in mask units is ink (through the cell index). */\n containsUnits(x: number, y: number): boolean;\n}\nexport interface ResolvedChartMaterialLayers {\n kind: \"material-layers\";\n sceneId: string;\n /** The chart under the layers; its materials come first in `materials`. */\n chart: ResolvedChartScene;\n layers: readonly ResolvedChartMaskLayer[];\n materials: ResolvedChartScene[\"materials\"];\n /** Internal: the caller input behind each material (undefined for an engine neutral), for the scene digest. */\n materialInputs: readonly unknown[];\n /** Material index at a frame point (u right, v up): the topmost layer whose rectangle holds the point, else the chart. */\n materialAt(u: number, v: number): number;\n /** The same scene as a caller material scene for `simulateCapture` and the display view (chart-local coordinates when the chart has a size). */\n callerScene: CallerMaterialScene;\n}\n\nconst SCENE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\nconst finite = (value: unknown, name: string): number => {\n if (typeof value !== \"number\" || !Number.isFinite(value)) throw new InvalidConfigurationError(name + \" must be a finite number.\");\n return value;\n};\n/** The smallest cell the one-cell margin covers: the rounding of (x − 0)·scale is a few units in the last place of the coordinates. */\nconst minimumCell = (extent: number): number => Math.max(extent, Number.MIN_VALUE) * 2 ** -20;\n\nfunction resolvePolygon(shape: ChartMaskPolygon, name: string): ResolvedChartMaskShape {\n const rings = shape.rings;\n if (!Array.isArray(rings) || rings.length < 1 || rings.length > CHART_MATERIAL_LAYER_LIMITS.maximumRings) throw new InvalidConfigurationError(name + \" needs 1 to 256 rings.\");\n let count = 0;\n for (const ring of rings) {\n if (!Array.isArray(ring) || ring.length < 6 || ring.length % 2 !== 0) throw new InvalidConfigurationError(name + \" rings need at least three (x, y) vertices.\");\n count += ring.length / 2;\n }\n if (count > CHART_MATERIAL_LAYER_LIMITS.maximumEdges) throw new InvalidConfigurationError(name + \" has more than 65536 edges.\");\n const edges = new Float64Array(4 * count);\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity, e = 0;\n for (const ring of rings) {\n const n = ring.length / 2;\n for (let k = 0; k < n; k++) {\n const x0 = finite(ring[2 * k], name + \" vertex\"), y0 = finite(ring[2 * k + 1], name + \" vertex\");\n const next = k + 1 === n ? 0 : k + 1, x1 = finite(ring[2 * next], name + \" vertex\"), y1 = finite(ring[2 * next + 1], name + \" vertex\");\n edges[e++] = x0; edges[e++] = y0; edges[e++] = x1; edges[e++] = y1;\n minX = Math.min(minX, x0); minY = Math.min(minY, y0); maxX = Math.max(maxX, x0); maxY = Math.max(maxY, y0);\n }\n }\n return { kind: \"polygon\", box: [minX, minY, maxX, maxY], edges };\n}\n\n/** Non-zero winding over the edges (x0, y0, x1, y1), as the reference evaluates it; the GPU runs the same comparisons. */\nexport function polygonWinding(edges: Float64Array, x: number, y: number): number {\n let winding = 0;\n for (let k = 0; k < edges.length; k += 4) {\n const x0 = edges[k]!, y0 = edges[k + 1]!, x1 = edges[k + 2]!, y1 = edges[k + 3]!;\n const side = (x1 - x0) * (y - y0) - (x - x0) * (y1 - y0);\n if (y0 <= y) {\n if (y1 > y && side > 0) winding++;\n } else if (y1 <= y && side < 0) winding--;\n }\n return winding;\n}\n/** Whether a resolved shape contains a point in mask units (the full test, without the cell index). */\nexport function shapeContains(shape: ResolvedChartMaskShape, x: number, y: number): boolean {\n const b = shape.box;\n if (!(x >= b[0] && x <= b[2] && y >= b[1] && y <= b[3])) return false;\n if (shape.kind === \"annulus\") {\n const d2 = (x - shape.centerX) * (x - shape.centerX) + (y - shape.centerY) * (y - shape.centerY);\n return d2 >= shape.inner2 && d2 <= shape.outer2;\n }\n return polygonWinding(shape.edges, x, y) !== 0;\n}\n\nfunction resolveLayer(value: unknown, name: string, aspect: number, materials: ResolvedChartScene[\"materials\"][number][], inputs: unknown[]): ResolvedChartMaskLayer {\n requireInputObject(value, name);\n const layer = value as Record<string, unknown>;\n for (const key of Object.keys(layer)) {\n if (![\"kind\", \"rectangle\", \"unitsWidth\", \"unitsHeight\", \"shapes\", \"backgroundReflectance\", \"inkReflectance\"].includes(key)) throw new InvalidConfigurationError(\"Unknown \" + name + \" field: \" + key + \".\");\n }\n if (layer.kind !== \"shape-mask\") throw new InvalidConfigurationError(name + \".kind must be shape-mask.\");\n requireInputObject(layer.rectangle, name + \".rectangle\");\n const r = layer.rectangle as Record<string, unknown>;\n for (const key of Object.keys(r)) if (![\"u0\", \"v0\", \"u1\", \"v1\"].includes(key)) throw new InvalidConfigurationError(\"Unknown \" + name + \".rectangle field: \" + key + \".\");\n const u0 = finite(r.u0, name + \".rectangle.u0\"), v0 = finite(r.v0, name + \".rectangle.v0\"), u1 = finite(r.u1, name + \".rectangle.u1\"), v1 = finite(r.v1, name + \".rectangle.v1\");\n if (!(u1 > u0 && v1 > v0) || Math.max(Math.abs(u0), Math.abs(u1)) > 1e6 || Math.max(Math.abs(v0), Math.abs(v1)) > 1e6 * Math.max(1, aspect)) {\n throw new InvalidConfigurationError(name + \".rectangle needs u0 < u1 and v0 < v1 within 10^6 frame units.\");\n }\n const unitsWidth = finite(layer.unitsWidth, name + \".unitsWidth\"), unitsHeight = finite(layer.unitsHeight, name + \".unitsHeight\");\n if (!(unitsWidth > 0 && unitsHeight > 0 && unitsWidth <= 1e9 && unitsHeight <= 1e9)) throw new InvalidConfigurationError(name + \" units must be positive and at most 10^9.\");\n const list = layer.shapes;\n if (!Array.isArray(list) || list.length < 1 || list.length > CHART_MATERIAL_LAYER_LIMITS.maximumShapes) throw new InvalidConfigurationError(name + \".shapes must list 1 to 4096 shapes.\");\n const shapes = list.map((shape: unknown, index: number): ResolvedChartMaskShape => {\n const label = name + \".shapes[\" + index + \"]\";\n requireInputObject(shape, label);\n const s = shape as Record<string, unknown>;\n if (s.kind === \"polygon\") {\n for (const key of Object.keys(s)) if (key !== \"kind\" && key !== \"rings\") throw new InvalidConfigurationError(\"Unknown \" + label + \" field: \" + key + \".\");\n return resolvePolygon(s as unknown as ChartMaskPolygon, label);\n }\n if (s.kind !== \"annulus\") throw new InvalidConfigurationError(label + \".kind must be polygon or annulus.\");\n for (const key of Object.keys(s)) if (![\"kind\", \"centerX\", \"centerY\", \"innerRadius\", \"outerRadius\"].includes(key)) throw new InvalidConfigurationError(\"Unknown \" + label + \" field: \" + key + \".\");\n const cx = finite(s.centerX, label + \".centerX\"), cy = finite(s.centerY, label + \".centerY\");\n const inner = finite(s.innerRadius, label + \".innerRadius\"), outer = finite(s.outerRadius, label + \".outerRadius\");\n if (!(inner >= 0 && outer > inner)) throw new InvalidConfigurationError(label + \" needs 0 ≤ innerRadius < outerRadius.\");\n return { kind: \"annulus\", box: [cx - outer, cy - outer, cx + outer, cy + outer], centerX: cx, centerY: cy, inner2: inner * inner, outer2: outer * outer };\n });\n for (const shape of shapes) {\n if (!shape.box.every((c) => Math.abs(c) <= 1e9)) throw new InvalidConfigurationError(name + \" shape coordinates must stay within 10^9 units.\");\n }\n const backgroundMaterial = materials.length;\n materials.push(resolveChartMaterial(layer.backgroundReflectance, name + \".backgroundReflectance\"));\n inputs.push(typeof layer.backgroundReflectance === \"number\" ? undefined : layer.backgroundReflectance);\n const inkMaterial = materials.length;\n materials.push(resolveChartMaterial(layer.inkReflectance, name + \".inkReflectance\"));\n inputs.push(typeof layer.inkReflectance === \"number\" ? undefined : layer.inkReflectance);\n // The cell index over the mask box; the grain must stay large against the coordinates' rounding (see the header).\n const { cellsX, cellsY } = CHART_MASK_INDEX_GRAIN, cellWidth = unitsWidth / cellsX, cellHeight = unitsHeight / cellsY;\n const extentX = Math.max(unitsWidth, ...shapes.map((s) => Math.max(Math.abs(s.box[0]), Math.abs(s.box[2]))));\n const extentY = Math.max(unitsHeight, ...shapes.map((s) => Math.max(Math.abs(s.box[1]), Math.abs(s.box[3]))));\n if (!(cellWidth > minimumCell(extentX) && cellHeight > minimumCell(extentY))) throw new InvalidConfigurationError(name + \": the shapes extend too far beyond the mask box for its cell index.\");\n const cells: Uint32Array[] = [];\n for (let j = 0; j < cellsY; j++) {\n for (let i = 0; i < cellsX; i++) {\n const x0 = (i - 1) * cellWidth, x1 = (i + 2) * cellWidth, y0 = (j - 1) * cellHeight, y1 = (j + 2) * cellHeight, listed: number[] = [];\n shapes.forEach((s, k) => { if (s.box[0] <= x1 && s.box[2] >= x0 && s.box[1] <= y1 && s.box[3] >= y0) listed.push(k); });\n cells.push(Uint32Array.from(listed));\n }\n }\n const scaleX = unitsWidth / (u1 - u0), scaleY = unitsHeight / (v1 - v0);\n return Object.freeze({ rectangle: Object.freeze({ u0, v0, u1, v1 }), unitsWidth, unitsHeight, scaleX, scaleY, shapes: Object.freeze(shapes), cellsX, cellsY, cellWidth, cellHeight,\n cells: Object.freeze(cells), backgroundMaterial, inkMaterial,\n containsUnits(x: number, y: number): boolean {\n let i = Math.floor(x / cellWidth), j = Math.floor(y / cellHeight);\n if (!(i > 0)) i = 0; else if (i >= cellsX) i = cellsX - 1;\n if (!(j > 0)) j = 0; else if (j >= cellsY) j = cellsY - 1;\n const list = cells[j * cellsX + i]!;\n for (let k = 0; k < list.length; k++) if (shapeContains(shapes[list[k]!]!, x, y)) return true;\n return false;\n } });\n}\n\n/**\n * Validates a declared material scene and resolves its chart (in a frame of `aspect` = height / width) and its\n * layers. `materialAt` is the float64 reference the GPU stage is checked against, and `callerScene` is the same\n * scene for `simulateCapture`'s `scene` option.\n */\nexport function resolveChartMaterialLayers(input: { scene: ChartMaterialLayerScene; aspect: number }): ResolvedChartMaterialLayers {\n requireInputKeys(input, \"resolveChartMaterialLayers\", [\"scene\", \"aspect\"]);\n const { scene, aspect } = input;\n requireInputObject(scene, \"scene\");\n const record = scene as unknown as Record<string, unknown>;\n for (const key of Object.keys(record)) if (![\"kind\", \"sceneId\", \"chart\", \"layers\"].includes(key)) throw new InvalidConfigurationError(\"Unknown scene field: \" + key + \".\");\n if (record.kind !== \"material-layers\") throw new InvalidConfigurationError(\"scene.kind must be material-layers.\");\n if (typeof record.sceneId !== \"string\" || !SCENE_ID.test(record.sceneId)) throw new InvalidConfigurationError(\"A material-layers scene needs a public sceneId.\");\n if (typeof aspect !== \"number\" || !Number.isFinite(aspect) || aspect <= 0) throw new InvalidConfigurationError(\"Chart aspect must be positive.\");\n const chart = resolveChartScene({ scene: record.chart as ChartScene, aspect });\n const list = record.layers;\n if (!Array.isArray(list) || list.length < 1 || list.length > CHART_MATERIAL_LAYER_LIMITS.maximumLayers) throw new InvalidConfigurationError(\"layers must list 1 to 16 shape-mask layers.\");\n const materials: ResolvedChartScene[\"materials\"][number][] = [...chart.materials], inputs: unknown[] = [...chartMaterialInputs(chart)];\n const layers = list.map((layer: unknown, index: number) => resolveLayer(layer, \"layers[\" + index + \"]\", aspect, materials, inputs));\n const materialAt = (u: number, v: number): number => {\n for (let l = layers.length - 1; l >= 0; l--) {\n const layer = layers[l]!, r = layer.rectangle;\n if (u < r.u0 || u > r.u1 || v < r.v0 || v > r.v1) continue;\n return layer.containsUnits((u - r.u0) * layer.scaleX, (r.v1 - v) * layer.scaleY) ? layer.inkMaterial : layer.backgroundMaterial;\n }\n return chart.materialAt(u, v);\n };\n const callerMaterials = materials.map((m, k) => (m.kind === \"neutral\" ? m.reflectance : inputs[k] as ChartReflectance));\n const callerScene: CallerMaterialScene = Object.freeze({ kind: \"material-function\" as const, sceneId: record.sceneId, materials: Object.freeze(callerMaterials),\n materialAt: (p: Readonly<SceneMaterialPoint>) => materialAt(p.chartU ?? p.u, p.chartV ?? p.v) });\n return Object.freeze({ kind: \"material-layers\" as const, sceneId: record.sceneId, chart, layers: Object.freeze(layers), materials: Object.freeze(materials),\n materialInputs: Object.freeze(inputs), materialAt, callerScene });\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ChartScene, type ResolvedChartScene } from "./chart-scenes.js";
|
|
2
|
+
import { type ChartMaterialLayerScene, type ResolvedChartMaterialLayers } from "./chart-material-layers.js";
|
|
2
3
|
import { type CaptureIlluminant, type ResolvedCaptureIlluminant } from "./scene-radiance.js";
|
|
3
4
|
export declare const CHART_RADIANCE_BATCH_SCHEMA_VERSION: "0.1.0";
|
|
4
5
|
/** `maximumQueries` is the default batch limit; a request may raise it with its own `maximumQueries`, up to `ceilingQueries`. */
|
|
@@ -10,8 +11,8 @@ export declare const CHART_RADIANCE_BATCH_LIMITS: Readonly<{
|
|
|
10
11
|
}>;
|
|
11
12
|
export interface ChartRadianceBatchInput {
|
|
12
13
|
schemaVersion: typeof CHART_RADIANCE_BATCH_SCHEMA_VERSION;
|
|
13
|
-
/** An engine chart preset (
|
|
14
|
-
scene: ChartScene;
|
|
14
|
+
/** An engine chart preset, or (#477) a declared `material-layers` scene: a chart under shape-mask layers. Caller scene functions are not supported here. */
|
|
15
|
+
scene: ChartScene | ChartMaterialLayerScene;
|
|
15
16
|
illuminant: CaptureIlluminant;
|
|
16
17
|
illuminanceLux: number;
|
|
17
18
|
/** Frame height / width. */
|
|
@@ -32,7 +33,12 @@ export interface ChartRadianceBatchInput {
|
|
|
32
33
|
}
|
|
33
34
|
/** Validated plan with the resolved chart, illuminant and irradiance scale. Arrays are borrowed. */
|
|
34
35
|
export interface ChartRadianceBatchPlan {
|
|
36
|
+
/** The resolved chart; with a `material-layers` scene, the chart under the layers. */
|
|
35
37
|
chart: ResolvedChartScene;
|
|
38
|
+
/** The resolved layers of a `material-layers` scene, or null for a chart preset. */
|
|
39
|
+
layers: ResolvedChartMaterialLayers | null;
|
|
40
|
+
/** Every material a query can resolve to: the chart's, then each layer's background and ink. */
|
|
41
|
+
materials: ResolvedChartScene["materials"];
|
|
36
42
|
illuminant: ResolvedCaptureIlluminant;
|
|
37
43
|
irradianceScale: number;
|
|
38
44
|
queryCount: number;
|
|
@@ -19,6 +19,7 @@ import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
|
19
19
|
import { requireAllowlistedRecord } from "../core/record-validation.js";
|
|
20
20
|
import { FULL_RESOLUTION_CEILING_PIXELS, resolveRaisedLimit } from "../core/pixel-limits.js";
|
|
21
21
|
import { resolveChartScene } from "./chart-scenes.js";
|
|
22
|
+
import { resolveChartMaterialLayers } from "./chart-material-layers.js";
|
|
22
23
|
import { buildPlanarSceneRadianceProvider, calculateIlluminantIrradianceScale, resolveCaptureIlluminant, tabulatedSpectrum } from "./scene-radiance.js";
|
|
23
24
|
export const CHART_RADIANCE_BATCH_SCHEMA_VERSION = "0.1.0";
|
|
24
25
|
/** `maximumQueries` is the default batch limit; a request may raise it with its own `maximumQueries`, up to `ceilingQueries`. */
|
|
@@ -31,7 +32,11 @@ export function calculateChartRadianceBatchPlan(input) {
|
|
|
31
32
|
throw new InvalidConfigurationError("Unsupported chart radiance batch schema.");
|
|
32
33
|
if (typeof r.aspect !== "number" || !(r.aspect > 0 && r.aspect <= Number.MAX_VALUE))
|
|
33
34
|
throw new InvalidConfigurationError("aspect must be positive.");
|
|
34
|
-
const
|
|
35
|
+
const sceneRecord = r.scene;
|
|
36
|
+
const layers = sceneRecord !== null && typeof sceneRecord === "object" && sceneRecord.kind === "material-layers"
|
|
37
|
+
? resolveChartMaterialLayers({ scene: r.scene, aspect: r.aspect }) : null;
|
|
38
|
+
const chart = layers ? layers.chart : resolveChartScene({ scene: r.scene, aspect: r.aspect });
|
|
39
|
+
const materials = layers ? layers.materials : chart.materials;
|
|
35
40
|
const illuminant = resolveCaptureIlluminant(r.illuminant);
|
|
36
41
|
const irradianceScale = calculateIlluminantIrradianceScale(illuminant.shape, r.illuminanceLux);
|
|
37
42
|
const L = CHART_RADIANCE_BATCH_LIMITS, points = r.points;
|
|
@@ -63,19 +68,21 @@ export function calculateChartRadianceBatchPlan(input) {
|
|
|
63
68
|
throw new InvalidConfigurationError("channelWeights must hold 1 to 4 rows of one finite weight per wavelength.");
|
|
64
69
|
}
|
|
65
70
|
// The provider's own radiance function: chart reflectance × scaled illuminant / π, in its float64 order.
|
|
66
|
-
const
|
|
71
|
+
const providerScene = layers ? { kind: "material-function", sceneId: layers.sceneId, materials: layers.materials, materialInputs: layers.materialInputs,
|
|
72
|
+
materialAt: (point) => layers.materialAt(point.u, point.v) } : chart;
|
|
73
|
+
const provider = buildPlanarSceneRadianceProvider({ scene: providerScene, sceneId: "chart-radiance-batch", providerProfileId: "chart-radiance-batch", illuminant,
|
|
67
74
|
irradianceScale, geometry: { distanceM: 1, metresPerFrameUnit: 1 }, evidence: [], limitations: [] });
|
|
68
75
|
const channelWeights = weights === undefined ? null : weights.map(row => Object.freeze([...row]));
|
|
69
76
|
const nm = Object.freeze([...wavelengths]);
|
|
70
77
|
const shapeAt = tabulatedSpectrum(illuminant.shape);
|
|
71
78
|
const materialRadiance = (material, wavelength) => {
|
|
72
|
-
const m =
|
|
79
|
+
const m = materials[material];
|
|
73
80
|
const reflectance = m.kind === "neutral" ? m.reflectance : m.spectrum.evaluate(wavelength);
|
|
74
81
|
if (m.kind === "spectrum" && !(reflectance >= 0 && reflectance <= 1))
|
|
75
82
|
throw new InvalidConfigurationError("Chart reflectance spectrum left [0, 1].");
|
|
76
83
|
return reflectance * irradianceScale * shapeAt(wavelength) / Math.PI;
|
|
77
84
|
};
|
|
78
|
-
return Object.freeze({ chart, illuminant, irradianceScale, queryCount, columns: channelWeights === null ? 1 : channelWeights.length,
|
|
85
|
+
return Object.freeze({ chart, layers, materials, illuminant, irradianceScale, queryCount, columns: channelWeights === null ? 1 : channelWeights.length,
|
|
79
86
|
mode: channelWeights === null ? "radiance" : "spectral-integral", wavelengthsNanometers: nm, channelWeights, materialRadiance,
|
|
80
87
|
queryValue(p, w, query, column) {
|
|
81
88
|
const u = p[2 * query], v = p[2 * query + 1];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-radiance-batch.js","sourceRoot":"","sources":["../../src/facade/chart-radiance-batch.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Batched radiance queries for the engine's procedural chart scenes (#277): many frame points in one\n * call, each answered with spectral radiance at a declared wavelength, or with the spectral integral of\n * that radiance against declared channel weights. This is the float64 CPU reference for the\n * `webgpu-f32` chart-radiance stage (`ImageKernelAccelerator.evaluateChartRadiance`).\n *\n * Every value comes from the chart provider's own `radianceAt` (createPlanarSceneRadianceProvider): a\n * Lambertian reflectance ρ(λ) under the scaled illuminant, L = ρ·k·S(λ)/π. The spectral integral of a\n * query is Σ_j w_cj·L(u, v, λ_j), summed in wavelength order. Charts are uniform-illuminant Lambertian\n * patterns, so a query's integral depends only on its material: the GPU stage uses exactly that.\n *\n * The query shape mirrors a batched provider (one call, queries in, results in the same order). Points\n * are frame units (u right, v up; see chart-scenes.ts); `mapEnvironmentDirectionsToChartPoints` converts\n * environment-direction queries exactly as `evaluateRadiance` does.\n * @see docs/WEBGPU.md, \"Scene radiance on the GPU\".\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord } from \"../core/record-validation.js\";\nimport { FULL_RESOLUTION_CEILING_PIXELS, resolveRaisedLimit } from \"../core/pixel-limits.js\";\nimport { resolveChartScene, type ChartScene, type ResolvedChartScene } from \"./chart-scenes.js\";\nimport { buildPlanarSceneRadianceProvider, calculateIlluminantIrradianceScale, resolveCaptureIlluminant, tabulatedSpectrum, type CaptureIlluminant,\n type ResolvedCaptureIlluminant } from \"./scene-radiance.js\";\n\nexport const CHART_RADIANCE_BATCH_SCHEMA_VERSION = \"0.1.0\" as const;\n/** `maximumQueries` is the default batch limit; a request may raise it with its own `maximumQueries`, up to `ceilingQueries`. */\nexport const CHART_RADIANCE_BATCH_LIMITS = Object.freeze({ maximumQueries: 24_000_000, ceilingQueries: FULL_RESOLUTION_CEILING_PIXELS, maximumWavelengths: 256,\n maximumChannels: 4 });\n\nexport interface ChartRadianceBatchInput {\n schemaVersion: typeof CHART_RADIANCE_BATCH_SCHEMA_VERSION;\n /** An engine chart preset (caller scene functions are not supported here). */\n scene: ChartScene;\n illuminant: CaptureIlluminant;\n illuminanceLux: number;\n /** Frame height / width. */\n aspect: number;\n /** Frame points, interleaved [u0, v0, u1, v1, …]. Finite. */\n points: Float32Array | Float64Array;\n /** The wavelengths the queries use, in nanometres (1–256). */\n wavelengthsNanometers: readonly number[];\n /** Radiance queries: one wavelength index per point. Exactly one of this and `channelWeights`. */\n wavelengthIndices?: Uint8Array | Uint16Array;\n /** Spectral integration: per channel (1–4), one finite weight per wavelength. */\n channelWeights?: readonly (readonly number[])[];\n /**\n * Optional opt-in batch limit: an integer from 1 to `CHART_RADIANCE_BATCH_LIMITS.ceilingQueries`. Default\n * `CHART_RADIANCE_BATCH_LIMITS.maximumQueries` (24 000 000). It does not change the values.\n */\n maximumQueries?: number;\n}\n\n/** Validated plan with the resolved chart, illuminant and irradiance scale. Arrays are borrowed. */\nexport interface ChartRadianceBatchPlan {\n chart: ResolvedChartScene;\n illuminant: ResolvedCaptureIlluminant;\n irradianceScale: number;\n queryCount: number;\n /** Output values per query: 1 for radiance queries, the channel count for spectral integration. */\n columns: number;\n mode: \"radiance\" | \"spectral-integral\";\n wavelengthsNanometers: readonly number[];\n channelWeights: readonly (readonly number[])[] | null;\n /** Spectral radiance of one material at a wavelength: the provider's own radianceAt for that material. */\n materialRadiance(material: number, wavelengthNanometers: number): number;\n /** The reference value of one query and output column (radianceAt, and its wavelength-ordered sum). */\n queryValue(points: Float32Array | Float64Array, wavelengthIndices: Uint8Array | Uint16Array | undefined, query: number, column: number): number;\n}\n\nexport function calculateChartRadianceBatchPlan(input: ChartRadianceBatchInput): ChartRadianceBatchPlan {\n const r = requireAllowlistedRecord(input, [\"schemaVersion\", \"scene\", \"illuminant\", \"illuminanceLux\", \"aspect\", \"points\", \"wavelengthsNanometers\",\n \"wavelengthIndices\", \"channelWeights\", \"maximumQueries\"], \"Invalid chart radiance batch input.\");\n if (r.schemaVersion !== CHART_RADIANCE_BATCH_SCHEMA_VERSION) throw new InvalidConfigurationError(\"Unsupported chart radiance batch schema.\");\n if (typeof r.aspect !== \"number\" || !(r.aspect > 0 && r.aspect <= Number.MAX_VALUE)) throw new InvalidConfigurationError(\"aspect must be positive.\");\n const chart = resolveChartScene({ scene: r.scene as ChartScene, aspect: r.aspect });\n const illuminant = resolveCaptureIlluminant(r.illuminant as CaptureIlluminant);\n const irradianceScale = calculateIlluminantIrradianceScale(illuminant.shape, r.illuminanceLux as number);\n const L = CHART_RADIANCE_BATCH_LIMITS, points = r.points;\n const maximumQueries = resolveRaisedLimit(r.maximumQueries, L.maximumQueries, L.ceilingQueries,\n \"Chart radiance batch maximumQueries must be an integer from 1 to \" + L.ceilingQueries + \".\");\n if (!(points instanceof Float32Array || points instanceof Float64Array) || points.length % 2 !== 0 || points.length / 2 > maximumQueries) {\n throw new InvalidConfigurationError(\"points must be an interleaved Float32Array or Float64Array of (u, v) pairs.\");\n }\n for (let i = 0; i < points.length; i++) if (!Number.isFinite(points[i]!)) throw new InvalidConfigurationError(\"Chart points must be finite.\");\n const queryCount = points.length / 2, wavelengths = r.wavelengthsNanometers;\n if (!Array.isArray(wavelengths) || wavelengths.length < 1 || wavelengths.length > L.maximumWavelengths ||\n wavelengths.some(nm => typeof nm !== \"number\" || !Number.isFinite(nm) || nm <= 0)) {\n throw new InvalidConfigurationError(\"wavelengthsNanometers must list 1 to 256 positive wavelengths.\");\n }\n const indices = r.wavelengthIndices, weights = r.channelWeights;\n if ((indices === undefined) === (weights === undefined)) throw new InvalidConfigurationError(\"Give exactly one of wavelengthIndices and channelWeights.\");\n if (indices !== undefined) {\n if (!(indices instanceof Uint8Array || indices instanceof Uint16Array) || indices.length !== queryCount) {\n throw new InvalidConfigurationError(\"wavelengthIndices must hold one index per point.\");\n }\n for (let i = 0; i < queryCount; i++) if (indices[i]! >= wavelengths.length) throw new InvalidConfigurationError(\"A wavelength index is out of range.\");\n } else if (!Array.isArray(weights) || weights.length < 1 || weights.length > L.maximumChannels ||\n weights.some(row => !Array.isArray(row) || row.length !== wavelengths.length || row.some(w => typeof w !== \"number\" || !Number.isFinite(w)))) {\n throw new InvalidConfigurationError(\"channelWeights must hold 1 to 4 rows of one finite weight per wavelength.\");\n }\n // The provider's own radiance function: chart reflectance × scaled illuminant / π, in its float64 order.\n const provider = buildPlanarSceneRadianceProvider({ scene: chart, sceneId: \"chart-radiance-batch\", providerProfileId: \"chart-radiance-batch\", illuminant,\n irradianceScale, geometry: { distanceM: 1, metresPerFrameUnit: 1 }, evidence: [], limitations: [] });\n const channelWeights = weights === undefined ? null : (weights as number[][]).map(row => Object.freeze([...row]));\n const nm = Object.freeze([...(wavelengths as number[])]);\n const shapeAt = tabulatedSpectrum(illuminant.shape);\n const materialRadiance = (material: number, wavelength: number): number => {\n const m = chart.materials[material]!;\n const reflectance = m.kind === \"neutral\" ? m.reflectance : m.spectrum.evaluate(wavelength);\n if (m.kind === \"spectrum\" && !(reflectance >= 0 && reflectance <= 1)) throw new InvalidConfigurationError(\"Chart reflectance spectrum left [0, 1].\");\n return reflectance * irradianceScale * shapeAt(wavelength) / Math.PI;\n };\n return Object.freeze({ chart, illuminant, irradianceScale, queryCount, columns: channelWeights === null ? 1 : channelWeights.length,\n mode: channelWeights === null ? \"radiance\" as const : \"spectral-integral\" as const, wavelengthsNanometers: nm, channelWeights, materialRadiance,\n queryValue(p: Float32Array | Float64Array, w: Uint8Array | Uint16Array | undefined, query: number, column: number): number {\n const u = p[2 * query]!, v = p[2 * query + 1]!;\n if (channelWeights === null) return provider.radianceAt(u, v, nm[w![query]!]!);\n let sum = 0;\n const row = channelWeights[column]!;\n for (let j = 0; j < nm.length; j++) sum += row[j]! * provider.radianceAt(u, v, nm[j]!);\n return sum;\n } });\n}\n\n/** Float64 CPU reference: every query's value(s), row-major (query, column). */\nexport function calculateChartRadianceBatch(input: ChartRadianceBatchInput): { plan: ChartRadianceBatchPlan; values: Float64Array } {\n const plan = calculateChartRadianceBatchPlan(input), values = new Float64Array(plan.queryCount * plan.columns);\n for (let q = 0; q < plan.queryCount; q++) for (let c = 0; c < plan.columns; c++) values[q * plan.columns + c] = plan.queryValue(input.points, input.wavelengthIndices, q, c);\n return { plan, values };\n}\n\n/**\n * Frame points of environment-direction queries on the chart plane, exactly as the planar provider's\n * `evaluateRadiance` computes them: look direction l = −o, plane point l·distance/l.z, in frame units.\n * @param directions - Outgoing unit vectors (scene toward camera), interleaved [x, y, z, …].\n */\nexport function mapEnvironmentDirectionsToChartPoints(input: { directions: Float32Array | Float64Array; distanceM: number; metresPerFrameUnit: number }): Float64Array {\n const r = requireAllowlistedRecord(input, [\"directions\", \"distanceM\", \"metresPerFrameUnit\"], \"Invalid planar direction input.\");\n const d = r.directions, distance = r.distanceM, scale = r.metresPerFrameUnit;\n if (!(d instanceof Float32Array || d instanceof Float64Array) || d.length % 3 !== 0 || typeof distance !== \"number\" || !(distance > 0 && distance <= Number.MAX_VALUE) ||\n typeof scale !== \"number\" || !(scale > 0 && scale <= Number.MAX_VALUE)) {\n throw new InvalidConfigurationError(\"Planar directions need interleaved unit vectors, a positive distance and a positive frame scale.\");\n }\n const points = new Float64Array(d.length / 3 * 2);\n for (let i = 0; i < d.length / 3; i++) {\n const lz = -d[3 * i + 2]!;\n if (!(lz > 0)) throw new InvalidConfigurationError(\"Planar scene query does not point toward the scene plane.\");\n const t = distance / lz;\n const x = -d[3 * i]! * t / scale, y = -d[3 * i + 1]! * t / scale;\n // A grazing direction (lz near 0, e.g. 1e-320) or a non-finite component would give ±Infinity or NaN.\n if (!Number.isFinite(x) || !Number.isFinite(y)) throw new InvalidConfigurationError(\"Planar scene query maps to a non-finite chart point (grazing or non-finite direction).\");\n points[2 * i] = x;\n points[2 * i + 1] = y;\n }\n return points;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"chart-radiance-batch.js","sourceRoot":"","sources":["../../src/facade/chart-radiance-batch.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Batched radiance queries for the engine's procedural chart scenes (#277): many frame points in one\n * call, each answered with spectral radiance at a declared wavelength, or with the spectral integral of\n * that radiance against declared channel weights. This is the float64 CPU reference for the\n * `webgpu-f32` chart-radiance stage (`ImageKernelAccelerator.evaluateChartRadiance`).\n *\n * Every value comes from the chart provider's own `radianceAt` (createPlanarSceneRadianceProvider): a\n * Lambertian reflectance ρ(λ) under the scaled illuminant, L = ρ·k·S(λ)/π. The spectral integral of a\n * query is Σ_j w_cj·L(u, v, λ_j), summed in wavelength order. Charts are uniform-illuminant Lambertian\n * patterns, so a query's integral depends only on its material: the GPU stage uses exactly that.\n *\n * The query shape mirrors a batched provider (one call, queries in, results in the same order). Points\n * are frame units (u right, v up; see chart-scenes.ts); `mapEnvironmentDirectionsToChartPoints` converts\n * environment-direction queries exactly as `evaluateRadiance` does.\n * @see docs/WEBGPU.md, \"Scene radiance on the GPU\".\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord } from \"../core/record-validation.js\";\nimport { FULL_RESOLUTION_CEILING_PIXELS, resolveRaisedLimit } from \"../core/pixel-limits.js\";\nimport { resolveChartScene, type ChartScene, type ResolvedChartScene } from \"./chart-scenes.js\";\nimport { resolveChartMaterialLayers, type ChartMaterialLayerScene, type ResolvedChartMaterialLayers } from \"./chart-material-layers.js\";\nimport { buildPlanarSceneRadianceProvider, calculateIlluminantIrradianceScale, resolveCaptureIlluminant, tabulatedSpectrum, type CaptureIlluminant,\n type ResolvedCaptureIlluminant } from \"./scene-radiance.js\";\n\nexport const CHART_RADIANCE_BATCH_SCHEMA_VERSION = \"0.1.0\" as const;\n/** `maximumQueries` is the default batch limit; a request may raise it with its own `maximumQueries`, up to `ceilingQueries`. */\nexport const CHART_RADIANCE_BATCH_LIMITS = Object.freeze({ maximumQueries: 24_000_000, ceilingQueries: FULL_RESOLUTION_CEILING_PIXELS, maximumWavelengths: 256,\n maximumChannels: 4 });\n\nexport interface ChartRadianceBatchInput {\n schemaVersion: typeof CHART_RADIANCE_BATCH_SCHEMA_VERSION;\n /** An engine chart preset, or (#477) a declared `material-layers` scene: a chart under shape-mask layers. Caller scene functions are not supported here. */\n scene: ChartScene | ChartMaterialLayerScene;\n illuminant: CaptureIlluminant;\n illuminanceLux: number;\n /** Frame height / width. */\n aspect: number;\n /** Frame points, interleaved [u0, v0, u1, v1, …]. Finite. */\n points: Float32Array | Float64Array;\n /** The wavelengths the queries use, in nanometres (1–256). */\n wavelengthsNanometers: readonly number[];\n /** Radiance queries: one wavelength index per point. Exactly one of this and `channelWeights`. */\n wavelengthIndices?: Uint8Array | Uint16Array;\n /** Spectral integration: per channel (1–4), one finite weight per wavelength. */\n channelWeights?: readonly (readonly number[])[];\n /**\n * Optional opt-in batch limit: an integer from 1 to `CHART_RADIANCE_BATCH_LIMITS.ceilingQueries`. Default\n * `CHART_RADIANCE_BATCH_LIMITS.maximumQueries` (24 000 000). It does not change the values.\n */\n maximumQueries?: number;\n}\n\n/** Validated plan with the resolved chart, illuminant and irradiance scale. Arrays are borrowed. */\nexport interface ChartRadianceBatchPlan {\n /** The resolved chart; with a `material-layers` scene, the chart under the layers. */\n chart: ResolvedChartScene;\n /** The resolved layers of a `material-layers` scene, or null for a chart preset. */\n layers: ResolvedChartMaterialLayers | null;\n /** Every material a query can resolve to: the chart's, then each layer's background and ink. */\n materials: ResolvedChartScene[\"materials\"];\n illuminant: ResolvedCaptureIlluminant;\n irradianceScale: number;\n queryCount: number;\n /** Output values per query: 1 for radiance queries, the channel count for spectral integration. */\n columns: number;\n mode: \"radiance\" | \"spectral-integral\";\n wavelengthsNanometers: readonly number[];\n channelWeights: readonly (readonly number[])[] | null;\n /** Spectral radiance of one material at a wavelength: the provider's own radianceAt for that material. */\n materialRadiance(material: number, wavelengthNanometers: number): number;\n /** The reference value of one query and output column (radianceAt, and its wavelength-ordered sum). */\n queryValue(points: Float32Array | Float64Array, wavelengthIndices: Uint8Array | Uint16Array | undefined, query: number, column: number): number;\n}\n\nexport function calculateChartRadianceBatchPlan(input: ChartRadianceBatchInput): ChartRadianceBatchPlan {\n const r = requireAllowlistedRecord(input, [\"schemaVersion\", \"scene\", \"illuminant\", \"illuminanceLux\", \"aspect\", \"points\", \"wavelengthsNanometers\",\n \"wavelengthIndices\", \"channelWeights\", \"maximumQueries\"], \"Invalid chart radiance batch input.\");\n if (r.schemaVersion !== CHART_RADIANCE_BATCH_SCHEMA_VERSION) throw new InvalidConfigurationError(\"Unsupported chart radiance batch schema.\");\n if (typeof r.aspect !== \"number\" || !(r.aspect > 0 && r.aspect <= Number.MAX_VALUE)) throw new InvalidConfigurationError(\"aspect must be positive.\");\n const sceneRecord = r.scene as Record<string, unknown> | null;\n const layers = sceneRecord !== null && typeof sceneRecord === \"object\" && sceneRecord.kind === \"material-layers\"\n ? resolveChartMaterialLayers({ scene: r.scene as ChartMaterialLayerScene, aspect: r.aspect }) : null;\n const chart = layers ? layers.chart : resolveChartScene({ scene: r.scene as ChartScene, aspect: r.aspect });\n const materials = layers ? layers.materials : chart.materials;\n const illuminant = resolveCaptureIlluminant(r.illuminant as CaptureIlluminant);\n const irradianceScale = calculateIlluminantIrradianceScale(illuminant.shape, r.illuminanceLux as number);\n const L = CHART_RADIANCE_BATCH_LIMITS, points = r.points;\n const maximumQueries = resolveRaisedLimit(r.maximumQueries, L.maximumQueries, L.ceilingQueries,\n \"Chart radiance batch maximumQueries must be an integer from 1 to \" + L.ceilingQueries + \".\");\n if (!(points instanceof Float32Array || points instanceof Float64Array) || points.length % 2 !== 0 || points.length / 2 > maximumQueries) {\n throw new InvalidConfigurationError(\"points must be an interleaved Float32Array or Float64Array of (u, v) pairs.\");\n }\n for (let i = 0; i < points.length; i++) if (!Number.isFinite(points[i]!)) throw new InvalidConfigurationError(\"Chart points must be finite.\");\n const queryCount = points.length / 2, wavelengths = r.wavelengthsNanometers;\n if (!Array.isArray(wavelengths) || wavelengths.length < 1 || wavelengths.length > L.maximumWavelengths ||\n wavelengths.some(nm => typeof nm !== \"number\" || !Number.isFinite(nm) || nm <= 0)) {\n throw new InvalidConfigurationError(\"wavelengthsNanometers must list 1 to 256 positive wavelengths.\");\n }\n const indices = r.wavelengthIndices, weights = r.channelWeights;\n if ((indices === undefined) === (weights === undefined)) throw new InvalidConfigurationError(\"Give exactly one of wavelengthIndices and channelWeights.\");\n if (indices !== undefined) {\n if (!(indices instanceof Uint8Array || indices instanceof Uint16Array) || indices.length !== queryCount) {\n throw new InvalidConfigurationError(\"wavelengthIndices must hold one index per point.\");\n }\n for (let i = 0; i < queryCount; i++) if (indices[i]! >= wavelengths.length) throw new InvalidConfigurationError(\"A wavelength index is out of range.\");\n } else if (!Array.isArray(weights) || weights.length < 1 || weights.length > L.maximumChannels ||\n weights.some(row => !Array.isArray(row) || row.length !== wavelengths.length || row.some(w => typeof w !== \"number\" || !Number.isFinite(w)))) {\n throw new InvalidConfigurationError(\"channelWeights must hold 1 to 4 rows of one finite weight per wavelength.\");\n }\n // The provider's own radiance function: chart reflectance × scaled illuminant / π, in its float64 order.\n const providerScene = layers ? { kind: \"material-function\" as const, sceneId: layers.sceneId, materials: layers.materials, materialInputs: layers.materialInputs,\n materialAt: (point: { u: number; v: number }): number => layers.materialAt(point.u, point.v) } : chart;\n const provider = buildPlanarSceneRadianceProvider({ scene: providerScene, sceneId: \"chart-radiance-batch\", providerProfileId: \"chart-radiance-batch\", illuminant,\n irradianceScale, geometry: { distanceM: 1, metresPerFrameUnit: 1 }, evidence: [], limitations: [] });\n const channelWeights = weights === undefined ? null : (weights as number[][]).map(row => Object.freeze([...row]));\n const nm = Object.freeze([...(wavelengths as number[])]);\n const shapeAt = tabulatedSpectrum(illuminant.shape);\n const materialRadiance = (material: number, wavelength: number): number => {\n const m = materials[material]!;\n const reflectance = m.kind === \"neutral\" ? m.reflectance : m.spectrum.evaluate(wavelength);\n if (m.kind === \"spectrum\" && !(reflectance >= 0 && reflectance <= 1)) throw new InvalidConfigurationError(\"Chart reflectance spectrum left [0, 1].\");\n return reflectance * irradianceScale * shapeAt(wavelength) / Math.PI;\n };\n return Object.freeze({ chart, layers, materials, illuminant, irradianceScale, queryCount, columns: channelWeights === null ? 1 : channelWeights.length,\n mode: channelWeights === null ? \"radiance\" as const : \"spectral-integral\" as const, wavelengthsNanometers: nm, channelWeights, materialRadiance,\n queryValue(p: Float32Array | Float64Array, w: Uint8Array | Uint16Array | undefined, query: number, column: number): number {\n const u = p[2 * query]!, v = p[2 * query + 1]!;\n if (channelWeights === null) return provider.radianceAt(u, v, nm[w![query]!]!);\n let sum = 0;\n const row = channelWeights[column]!;\n for (let j = 0; j < nm.length; j++) sum += row[j]! * provider.radianceAt(u, v, nm[j]!);\n return sum;\n } });\n}\n\n/** Float64 CPU reference: every query's value(s), row-major (query, column). */\nexport function calculateChartRadianceBatch(input: ChartRadianceBatchInput): { plan: ChartRadianceBatchPlan; values: Float64Array } {\n const plan = calculateChartRadianceBatchPlan(input), values = new Float64Array(plan.queryCount * plan.columns);\n for (let q = 0; q < plan.queryCount; q++) for (let c = 0; c < plan.columns; c++) values[q * plan.columns + c] = plan.queryValue(input.points, input.wavelengthIndices, q, c);\n return { plan, values };\n}\n\n/**\n * Frame points of environment-direction queries on the chart plane, exactly as the planar provider's\n * `evaluateRadiance` computes them: look direction l = −o, plane point l·distance/l.z, in frame units.\n * @param directions - Outgoing unit vectors (scene toward camera), interleaved [x, y, z, …].\n */\nexport function mapEnvironmentDirectionsToChartPoints(input: { directions: Float32Array | Float64Array; distanceM: number; metresPerFrameUnit: number }): Float64Array {\n const r = requireAllowlistedRecord(input, [\"directions\", \"distanceM\", \"metresPerFrameUnit\"], \"Invalid planar direction input.\");\n const d = r.directions, distance = r.distanceM, scale = r.metresPerFrameUnit;\n if (!(d instanceof Float32Array || d instanceof Float64Array) || d.length % 3 !== 0 || typeof distance !== \"number\" || !(distance > 0 && distance <= Number.MAX_VALUE) ||\n typeof scale !== \"number\" || !(scale > 0 && scale <= Number.MAX_VALUE)) {\n throw new InvalidConfigurationError(\"Planar directions need interleaved unit vectors, a positive distance and a positive frame scale.\");\n }\n const points = new Float64Array(d.length / 3 * 2);\n for (let i = 0; i < d.length / 3; i++) {\n const lz = -d[3 * i + 2]!;\n if (!(lz > 0)) throw new InvalidConfigurationError(\"Planar scene query does not point toward the scene plane.\");\n const t = distance / lz;\n const x = -d[3 * i]! * t / scale, y = -d[3 * i + 1]! * t / scale;\n // A grazing direction (lz near 0, e.g. 1e-320) or a non-finite component would give ±Infinity or NaN.\n if (!Number.isFinite(x) || !Number.isFinite(y)) throw new InvalidConfigurationError(\"Planar scene query maps to a non-finite chart point (grazing or non-finite direction).\");\n points[2 * i] = x;\n points[2 * i + 1] = y;\n }\n return points;\n}\n"]}
|
|
@@ -355,6 +355,135 @@ export declare function resolveChartMaterial(value: unknown, name: string): Reso
|
|
|
355
355
|
* material order (`undefined` for an engine neutral). The facade hashes caller spectra from these inputs.
|
|
356
356
|
*/
|
|
357
357
|
export declare function chartMaterialInputs(chart: ResolvedChartScene): readonly unknown[];
|
|
358
|
+
/** One group of a `bar-groups` chart in its own frame units (internal). */
|
|
359
|
+
export interface ChartBarGroupGeometry {
|
|
360
|
+
lineWidthsPerPictureHeight: number;
|
|
361
|
+
lineWidth: number;
|
|
362
|
+
cell: {
|
|
363
|
+
u0: number;
|
|
364
|
+
u1: number;
|
|
365
|
+
};
|
|
366
|
+
verticalBars: ChartRectangle;
|
|
367
|
+
horizontalBars: ChartRectangle;
|
|
368
|
+
label?: {
|
|
369
|
+
text: string;
|
|
370
|
+
u0: number;
|
|
371
|
+
v0: number;
|
|
372
|
+
height: number;
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Internal (not exported from the package): the float64 constants and tables a resolved chart's `materialAt` decides
|
|
377
|
+
* with, so another evaluator (the `webgpu-f32` chart stage, #477) can classify points with the same geometry. Every
|
|
378
|
+
* value is the one the closure holds; nothing is recomputed. `composite` lists its resolved background and elements.
|
|
379
|
+
*/
|
|
380
|
+
export type ChartGeometry = {
|
|
381
|
+
kind: "uniform";
|
|
382
|
+
} | {
|
|
383
|
+
kind: "slanted-edge";
|
|
384
|
+
c: number;
|
|
385
|
+
s: number;
|
|
386
|
+
} | {
|
|
387
|
+
kind: "siemens-star";
|
|
388
|
+
profile: "bar" | "sine";
|
|
389
|
+
cycles: number;
|
|
390
|
+
r: number;
|
|
391
|
+
dark: number;
|
|
392
|
+
light: number;
|
|
393
|
+
background: number;
|
|
394
|
+
} | {
|
|
395
|
+
kind: "step-wedge";
|
|
396
|
+
steps: number;
|
|
397
|
+
} | {
|
|
398
|
+
kind: "checkerboard";
|
|
399
|
+
size: number;
|
|
400
|
+
} | {
|
|
401
|
+
kind: "point-grid";
|
|
402
|
+
columns: number;
|
|
403
|
+
rows: number;
|
|
404
|
+
cellW: number;
|
|
405
|
+
cellH: number;
|
|
406
|
+
radius: number;
|
|
407
|
+
centers: readonly {
|
|
408
|
+
u: number;
|
|
409
|
+
v: number;
|
|
410
|
+
}[];
|
|
411
|
+
} | {
|
|
412
|
+
kind: "patches";
|
|
413
|
+
columns: number;
|
|
414
|
+
rows: number;
|
|
415
|
+
count: number;
|
|
416
|
+
gap: number;
|
|
417
|
+
cellW: number;
|
|
418
|
+
cellH: number;
|
|
419
|
+
backgroundIndex: number;
|
|
420
|
+
} | {
|
|
421
|
+
kind: "dead-leaves";
|
|
422
|
+
u0: number;
|
|
423
|
+
v0: number;
|
|
424
|
+
cell: number;
|
|
425
|
+
nx: number;
|
|
426
|
+
ny: number;
|
|
427
|
+
head: Int32Array;
|
|
428
|
+
next: Int32Array;
|
|
429
|
+
entryDisk: Int32Array;
|
|
430
|
+
entries: number;
|
|
431
|
+
X: Float64Array;
|
|
432
|
+
Y: Float64Array;
|
|
433
|
+
R2: Float64Array;
|
|
434
|
+
level: Uint16Array;
|
|
435
|
+
diskCount: number;
|
|
436
|
+
mean: number;
|
|
437
|
+
} | {
|
|
438
|
+
kind: "bar-groups";
|
|
439
|
+
groups: readonly ChartBarGroupGeometry[];
|
|
440
|
+
} | {
|
|
441
|
+
kind: "hyperbolic-wedge";
|
|
442
|
+
orientation: "horizontal" | "vertical";
|
|
443
|
+
lines: number;
|
|
444
|
+
ph: number;
|
|
445
|
+
lw0: number;
|
|
446
|
+
lw1: number;
|
|
447
|
+
} | {
|
|
448
|
+
kind: "zone-plate";
|
|
449
|
+
r: number;
|
|
450
|
+
k: number;
|
|
451
|
+
dark: number;
|
|
452
|
+
light: number;
|
|
453
|
+
} | {
|
|
454
|
+
kind: "slanted-squares";
|
|
455
|
+
columns: number;
|
|
456
|
+
rows: number;
|
|
457
|
+
cellW: number;
|
|
458
|
+
cellH: number;
|
|
459
|
+
c: number;
|
|
460
|
+
s: number;
|
|
461
|
+
half: number;
|
|
462
|
+
} | {
|
|
463
|
+
kind: "line-grid";
|
|
464
|
+
spacing: number;
|
|
465
|
+
half: number;
|
|
466
|
+
} | {
|
|
467
|
+
kind: "fiducial";
|
|
468
|
+
r: number;
|
|
469
|
+
inner: number;
|
|
470
|
+
inverted: boolean;
|
|
471
|
+
} | {
|
|
472
|
+
kind: "composite";
|
|
473
|
+
background: ResolvedChartScene;
|
|
474
|
+
elements: readonly {
|
|
475
|
+
chart: ResolvedChartScene;
|
|
476
|
+
offset: number;
|
|
477
|
+
cu: number;
|
|
478
|
+
cv: number;
|
|
479
|
+
hw: number;
|
|
480
|
+
hh: number;
|
|
481
|
+
scale: number;
|
|
482
|
+
ellipse: boolean;
|
|
483
|
+
}[];
|
|
484
|
+
};
|
|
485
|
+
/** Internal: the geometry of a chart resolved by {@link resolveChartScene} (see {@link ChartGeometry}). */
|
|
486
|
+
export declare function chartGeometry(chart: ResolvedChartScene): ChartGeometry;
|
|
358
487
|
/**
|
|
359
488
|
* Validates a chart description and resolves every default. `aspect` is the frame height/width. Unknown
|
|
360
489
|
* fields are rejected so that a mistyped parameter never silently falls back to a default.
|
|
@@ -379,6 +508,11 @@ export interface ChartRectangle {
|
|
|
379
508
|
u1: number;
|
|
380
509
|
v1: number;
|
|
381
510
|
}
|
|
511
|
+
/** Engine-drawn seven-segment numerals: a 0.6 × 1 cell per digit (baseline at 0, y up), stroke 0.16, advance 0.82. */
|
|
512
|
+
export declare const NUMERAL_WIDTH = 0.6, NUMERAL_ADVANCE = 0.82;
|
|
513
|
+
/** Internal: segment rectangles [x0, y0, x1, y1] of the seven-segment numerals and the segments each digit lights. */
|
|
514
|
+
export declare const SEGMENTS: Readonly<Record<string, readonly [number, number, number, number]>>;
|
|
515
|
+
export declare const DIGIT_SEGMENTS: string[];
|
|
382
516
|
/** One bar group of a `bar-groups` chart or composite element, in the chart's frame units. */
|
|
383
517
|
export interface ChartBarGroupTarget {
|
|
384
518
|
/** Composite element index, or null for a plain `bar-groups` chart. */
|
|
@@ -97,6 +97,14 @@ const MATERIAL_INPUTS = new WeakMap();
|
|
|
97
97
|
export function chartMaterialInputs(chart) {
|
|
98
98
|
return MATERIAL_INPUTS.get(chart) ?? chart.materials.map(() => undefined);
|
|
99
99
|
}
|
|
100
|
+
const GEOMETRY = new WeakMap();
|
|
101
|
+
/** Internal: the geometry of a chart resolved by {@link resolveChartScene} (see {@link ChartGeometry}). */
|
|
102
|
+
export function chartGeometry(chart) {
|
|
103
|
+
const geometry = GEOMETRY.get(chart);
|
|
104
|
+
if (!geometry)
|
|
105
|
+
throw new InvalidConfigurationError("Not a chart resolved by resolveChartScene.");
|
|
106
|
+
return geometry;
|
|
107
|
+
}
|
|
100
108
|
const neutralMaterials = (values) => values.map((reflectanceValue) => ({ kind: "neutral", reflectance: reflectanceValue }));
|
|
101
109
|
/** CHART_CONTINUOUS_LEVELS equal neutral levels between `from` and `to` (level i at from + (to − from)(i + ½)/levels). */
|
|
102
110
|
function continuousMaterials(from, to) {
|
|
@@ -156,7 +164,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
156
164
|
return defaults[key];
|
|
157
165
|
} return record[key]; };
|
|
158
166
|
const neutral = (key) => reflectance(get(key), key);
|
|
159
|
-
const done = (parameters, materials, materialAt, inputs) => {
|
|
167
|
+
const done = (parameters, materials, materialAt, inputs, geometry) => {
|
|
160
168
|
const chart = Object.freeze({ preset: preset, parameters: Object.freeze(parameters), defaultedParameters: Object.freeze(defaulted), aspect,
|
|
161
169
|
materials: Object.freeze(materials), materialAt,
|
|
162
170
|
reflectanceAt(u, v, nm) {
|
|
@@ -170,18 +178,19 @@ function resolveScene(scene, aspect, context) {
|
|
|
170
178
|
} });
|
|
171
179
|
if (inputs)
|
|
172
180
|
MATERIAL_INPUTS.set(chart, Object.freeze([...inputs]));
|
|
181
|
+
GEOMETRY.set(chart, geometry);
|
|
173
182
|
return chart;
|
|
174
183
|
};
|
|
175
184
|
switch (preset) {
|
|
176
185
|
case "uniform": {
|
|
177
186
|
const raw = get("reflectance"), m = resolveChartMaterial(raw, "reflectance");
|
|
178
|
-
return done({ reflectance: typeof raw === "number" ? raw : "caller-spectrum" }, [m], () => 0, [typeof raw === "number" ? undefined : raw]);
|
|
187
|
+
return done({ reflectance: typeof raw === "number" ? raw : "caller-spectrum" }, [m], () => 0, [typeof raw === "number" ? undefined : raw], { kind: "uniform" });
|
|
179
188
|
}
|
|
180
189
|
case "slanted-edge": {
|
|
181
190
|
const angle = finiteIn(get("angleDegrees"), "angleDegrees", -45, 45, true), dark = neutral("darkReflectance"), light = neutral("lightReflectance");
|
|
182
191
|
const t = angle * Math.PI / 180, c = Math.cos(t), s = Math.sin(t);
|
|
183
192
|
// Edge through the frame centre along (sin t, cos t); the light side is to the right.
|
|
184
|
-
return done({ angleDegrees: angle, darkReflectance: dark, lightReflectance: light }, [{ kind: "neutral", reflectance: dark }, { kind: "neutral", reflectance: light }], (u, v) => (u * c - v * s >= 0 ? 1 : 0));
|
|
193
|
+
return done({ angleDegrees: angle, darkReflectance: dark, lightReflectance: light }, [{ kind: "neutral", reflectance: dark }, { kind: "neutral", reflectance: light }], (u, v) => (u * c - v * s >= 0 ? 1 : 0), undefined, { kind: "slanted-edge", c, s });
|
|
185
194
|
}
|
|
186
195
|
case "siemens-star": {
|
|
187
196
|
const cycles = integerIn(get("cycles"), "cycles", 2, 360), radius = finiteIn(get("radiusFraction"), "radiusFraction", 0, 1, false);
|
|
@@ -203,7 +212,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
203
212
|
return CHART_CONTINUOUS_LEVELS;
|
|
204
213
|
const phase = Math.atan2(v, u) / (2 * Math.PI) + 0.5;
|
|
205
214
|
return continuousLevel(mid + amp * Math.sin(2 * Math.PI * cycles * phase), dark, light);
|
|
206
|
-
});
|
|
215
|
+
}, undefined, { kind: "siemens-star", profile: "sine", cycles, r, dark, light, background });
|
|
207
216
|
}
|
|
208
217
|
if (profile === "bar")
|
|
209
218
|
parameters.profile = profile;
|
|
@@ -212,7 +221,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
212
221
|
return 2;
|
|
213
222
|
const phase = Math.atan2(v, u) / (2 * Math.PI) + 0.5; // [0, 1]
|
|
214
223
|
return Math.floor(phase * 2 * cycles) % 2 === 0 ? 1 : 0;
|
|
215
|
-
});
|
|
224
|
+
}, undefined, { kind: "siemens-star", profile: "bar", cycles, r, dark, light, background });
|
|
216
225
|
}
|
|
217
226
|
case "step-wedge": {
|
|
218
227
|
const steps = integerIn(get("steps"), "steps", 2, 64), lo = neutral("minimumReflectance"), hi = neutral("maximumReflectance"), spacing = get("spacing");
|
|
@@ -221,7 +230,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
221
230
|
if (!(hi > lo) || (spacing === "equal-density" && lo <= 0))
|
|
222
231
|
throw new InvalidConfigurationError("Step wedge needs 0 < minimum < maximum reflectance.");
|
|
223
232
|
const values = stepWedgeReflectances(steps, lo, hi, spacing, context.pow);
|
|
224
|
-
return done({ steps, minimumReflectance: lo, maximumReflectance: hi, spacing, stepReflectances: values }, values.map((reflectanceValue) => ({ kind: "neutral", reflectance: reflectanceValue })), (u) => Math.min(steps - 1, Math.max(0, Math.floor((u + 1) / 2 * steps))));
|
|
233
|
+
return done({ steps, minimumReflectance: lo, maximumReflectance: hi, spacing, stepReflectances: values }, values.map((reflectanceValue) => ({ kind: "neutral", reflectance: reflectanceValue })), (u) => Math.min(steps - 1, Math.max(0, Math.floor((u + 1) / 2 * steps))), undefined, { kind: "step-wedge", steps });
|
|
225
234
|
}
|
|
226
235
|
case "grey-ramp": {
|
|
227
236
|
const lo = neutral("minimumReflectance"), hi = neutral("maximumReflectance");
|
|
@@ -229,11 +238,11 @@ function resolveScene(scene, aspect, context) {
|
|
|
229
238
|
throw new InvalidConfigurationError("Grey ramp needs minimum < maximum reflectance.");
|
|
230
239
|
// A continuous ramp is stored as 1024 neutral levels. The step size (< 0.1 % of the range) is far below sensor noise.
|
|
231
240
|
const levels = 1024, materials = Array.from({ length: levels }, (_, i) => ({ kind: "neutral", reflectance: lo + (hi - lo) * (i + 0.5) / levels }));
|
|
232
|
-
return done({ minimumReflectance: lo, maximumReflectance: hi, representation: "1024 equal neutral levels" }, materials, (u) => Math.min(levels - 1, Math.max(0, Math.floor((u + 1) / 2 * levels))));
|
|
241
|
+
return done({ minimumReflectance: lo, maximumReflectance: hi, representation: "1024 equal neutral levels" }, materials, (u) => Math.min(levels - 1, Math.max(0, Math.floor((u + 1) / 2 * levels))), undefined, { kind: "step-wedge", steps: levels });
|
|
233
242
|
}
|
|
234
243
|
case "checkerboard": {
|
|
235
244
|
const across = integerIn(get("squaresAcross"), "squaresAcross", 2, 256), dark = neutral("darkReflectance"), light = neutral("lightReflectance"), size = 2 / across;
|
|
236
|
-
return done({ squaresAcross: across, darkReflectance: dark, lightReflectance: light }, [{ kind: "neutral", reflectance: dark }, { kind: "neutral", reflectance: light }], (u, v) => ((Math.floor((u + 1) / size) + Math.floor((aspect - v) / size)) % 2 === 0 ? 1 : 0));
|
|
245
|
+
return done({ squaresAcross: across, darkReflectance: dark, lightReflectance: light }, [{ kind: "neutral", reflectance: dark }, { kind: "neutral", reflectance: light }], (u, v) => ((Math.floor((u + 1) / size) + Math.floor((aspect - v) / size)) % 2 === 0 ? 1 : 0), undefined, { kind: "checkerboard", size });
|
|
237
246
|
}
|
|
238
247
|
case "point-grid": {
|
|
239
248
|
const columns = integerIn(get("columns"), "columns", 1, 256), rows = integerIn(get("rows"), "rows", 1, 256);
|
|
@@ -244,7 +253,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
244
253
|
const i = Math.min(columns - 1, Math.max(0, Math.floor((u + 1) / cellW))), j = Math.min(rows - 1, Math.max(0, Math.floor((aspect - v) / cellH)));
|
|
245
254
|
const c = centers[j * columns + i];
|
|
246
255
|
return (u - c.u) ** 2 + (v - c.v) ** 2 <= radius * radius ? 1 : 0;
|
|
247
|
-
});
|
|
256
|
+
}, undefined, { kind: "point-grid", columns, rows, cellW, cellH, radius, centers });
|
|
248
257
|
}
|
|
249
258
|
case "patches": {
|
|
250
259
|
const list = record.patches;
|
|
@@ -283,7 +292,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
283
292
|
if (index >= list.length || x < gap / 2 || x > 1 - gap / 2 || y < gap / 2 || y > 1 - gap / 2)
|
|
284
293
|
return backgroundIndex;
|
|
285
294
|
return index;
|
|
286
|
-
}, inputs);
|
|
295
|
+
}, inputs, { kind: "patches", columns, rows, count: list.length, gap, cellW, cellH, backgroundIndex });
|
|
287
296
|
}
|
|
288
297
|
case "dead-leaves": {
|
|
289
298
|
const rMin = finiteIn(get("radiusMinimum"), "radiusMinimum", DEAD_LEAVES_MINIMUM_RADIUS, 2), rMax = finiteIn(get("radiusMaximum"), "radiusMaximum", 0, 2, true);
|
|
@@ -294,7 +303,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
294
303
|
throw new InvalidConfigurationError("Dead leaves need minimum ≤ maximum reflectance.");
|
|
295
304
|
const leaves = generateDeadLeaves(rMin, rMax, seed, aspect);
|
|
296
305
|
return done({ radiusMinimum: rMin, radiusMaximum: rMax, minimumReflectance: lo, maximumReflectance: hi, seed, diskCount: leaves.diskCount,
|
|
297
|
-
radiusDensity: "proportional-to-r^-3", representation: CHART_CONTINUOUS_LEVELS + " equal neutral levels" }, continuousMaterials(lo, hi), leaves.levelAt);
|
|
306
|
+
radiusDensity: "proportional-to-r^-3", representation: CHART_CONTINUOUS_LEVELS + " equal neutral levels" }, continuousMaterials(lo, hi), leaves.levelAt, undefined, { kind: "dead-leaves", ...leaves.tables });
|
|
298
307
|
}
|
|
299
308
|
case "bar-groups": {
|
|
300
309
|
const lws = get("lineWidthsPerPictureHeight");
|
|
@@ -317,7 +326,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
317
326
|
return 1;
|
|
318
327
|
}
|
|
319
328
|
return 1;
|
|
320
|
-
});
|
|
329
|
+
}, undefined, { kind: "bar-groups", groups: layout });
|
|
321
330
|
}
|
|
322
331
|
case "hyperbolic-wedge": {
|
|
323
332
|
const orientation = get("orientation");
|
|
@@ -340,7 +349,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
340
349
|
if (y < -half || y >= half)
|
|
341
350
|
return 1;
|
|
342
351
|
return Math.floor((y + half) / w) % 2 === 0 ? 0 : 1;
|
|
343
|
-
});
|
|
352
|
+
}, undefined, { kind: "hyperbolic-wedge", orientation, lines, ph, lw0, lw1 });
|
|
344
353
|
}
|
|
345
354
|
case "zone-plate": {
|
|
346
355
|
const radius = finiteIn(get("radiusFraction"), "radiusFraction", 0, 1, false);
|
|
@@ -360,7 +369,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
360
369
|
if (r2 > r * r)
|
|
361
370
|
return CHART_CONTINUOUS_LEVELS;
|
|
362
371
|
return continuousLevel(mid + amp * Math.cos(Math.PI * k * r2), dark, light);
|
|
363
|
-
});
|
|
372
|
+
}, undefined, { kind: "zone-plate", r, k, dark, light });
|
|
364
373
|
}
|
|
365
374
|
case "slanted-squares": {
|
|
366
375
|
const columns = integerIn(get("columns"), "columns", 1, 64), rows = integerIn(get("rows"), "rows", 1, 64);
|
|
@@ -375,12 +384,12 @@ function resolveScene(scene, aspect, context) {
|
|
|
375
384
|
const du = u - (-1 + (i + 0.5) * cellW), dv = v - (aspect - (j + 0.5) * cellH);
|
|
376
385
|
const x = du * c - dv * s, y = du * s + dv * c;
|
|
377
386
|
return Math.abs(x) <= half && Math.abs(y) <= half ? 0 : 1;
|
|
378
|
-
});
|
|
387
|
+
}, undefined, { kind: "slanted-squares", columns, rows, cellW, cellH, c, s, half });
|
|
379
388
|
}
|
|
380
389
|
case "line-grid": {
|
|
381
390
|
const spacing = finiteIn(get("spacing"), "spacing", 0, 2, true), width = finiteIn(get("lineWidth"), "lineWidth", 0, spacing, true);
|
|
382
391
|
const line = neutral("lineReflectance"), background = neutral("backgroundReflectance"), half = width / 2;
|
|
383
|
-
return done({ spacing, lineWidth: width, lineReflectance: line, backgroundReflectance: background }, neutralMaterials([background, line]), (u, v) => (Math.abs(u - spacing * Math.round(u / spacing)) <= half || Math.abs(v - spacing * Math.round(v / spacing)) <= half ? 1 : 0));
|
|
392
|
+
return done({ spacing, lineWidth: width, lineReflectance: line, backgroundReflectance: background }, neutralMaterials([background, line]), (u, v) => (Math.abs(u - spacing * Math.round(u / spacing)) <= half || Math.abs(v - spacing * Math.round(v / spacing)) <= half ? 1 : 0), undefined, { kind: "line-grid", spacing, half });
|
|
384
393
|
}
|
|
385
394
|
case "fiducial": {
|
|
386
395
|
const radius = finiteIn(get("radiusFraction"), "radiusFraction", 0, 1, false), ring = finiteIn(get("ringFraction"), "ringFraction", 0, 1, true);
|
|
@@ -395,7 +404,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
395
404
|
if (r2 > inner * inner)
|
|
396
405
|
return 0;
|
|
397
406
|
return ((u >= 0) === (v >= 0)) !== inverted ? 0 : 1;
|
|
398
|
-
});
|
|
407
|
+
}, undefined, { kind: "fiducial", r, inner, inverted });
|
|
399
408
|
}
|
|
400
409
|
case "composite": {
|
|
401
410
|
const list = record.elements;
|
|
@@ -440,7 +449,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
440
449
|
return e.offset + e.chart.materialAt(du * e.scale, dv * e.scale);
|
|
441
450
|
}
|
|
442
451
|
return background.materialAt(u, v);
|
|
443
|
-
}, inputs);
|
|
452
|
+
}, inputs, { kind: "composite", background, elements: elements.map((e) => ({ chart: e.chart, offset: e.offset, cu: e.cu, cv: e.cv, hw: e.hw, hh: e.hh, scale: e.scale, ellipse: e.ellipse })) });
|
|
444
453
|
}
|
|
445
454
|
}
|
|
446
455
|
}
|
|
@@ -521,7 +530,7 @@ function generateDeadLeaves(rMin, rMax, seed, aspect) {
|
|
|
521
530
|
n++;
|
|
522
531
|
}
|
|
523
532
|
const mean = CHART_CONTINUOUS_LEVELS / 2;
|
|
524
|
-
return { diskCount: n, levelAt(u, v) {
|
|
533
|
+
return { diskCount: n, tables: { u0, v0, cell, nx, ny, head, next, entryDisk, entries, X, Y, R2, level, diskCount: n, mean }, levelAt(u, v) {
|
|
525
534
|
const i = Math.floor((u - u0) / cell), j = Math.floor((v - v0) / cell);
|
|
526
535
|
if (i < 0 || j < 0 || i >= nx || j >= ny)
|
|
527
536
|
return mean;
|
|
@@ -563,11 +572,12 @@ function barGroupLayout(list, numerals, aspect, pictureHeight) {
|
|
|
563
572
|
});
|
|
564
573
|
}
|
|
565
574
|
/** Engine-drawn seven-segment numerals: a 0.6 × 1 cell per digit (baseline at 0, y up), stroke 0.16, advance 0.82. */
|
|
566
|
-
const NUMERAL_WIDTH = 0.6, NUMERAL_ADVANCE = 0.82;
|
|
567
|
-
|
|
575
|
+
export const NUMERAL_WIDTH = 0.6, NUMERAL_ADVANCE = 0.82;
|
|
576
|
+
/** Internal: segment rectangles [x0, y0, x1, y1] of the seven-segment numerals and the segments each digit lights. */
|
|
577
|
+
export const SEGMENTS = Object.freeze({
|
|
568
578
|
a: [0.08, 0.84, 0.52, 1], b: [0.44, 0.5, 0.6, 1], c: [0.44, 0, 0.6, 0.5], d: [0.08, 0, 0.52, 0.16], e: [0, 0, 0.16, 0.5], f: [0, 0.5, 0.16, 1], g: [0.08, 0.42, 0.52, 0.58]
|
|
569
579
|
});
|
|
570
|
-
const DIGIT_SEGMENTS = ["abcdef", "bc", "abged", "abgcd", "fgbc", "afgcd", "afgedc", "abc", "abcdefg", "abcdfg"];
|
|
580
|
+
export const DIGIT_SEGMENTS = ["abcdef", "bc", "abged", "abgcd", "fgbc", "afgcd", "afgedc", "abc", "abcdefg", "abcdfg"];
|
|
571
581
|
function numeralInk(label, u, v) {
|
|
572
582
|
const x = (u - label.u0) / label.height, y = (v - label.v0) / label.height;
|
|
573
583
|
if (y < 0 || y > 1 || x < 0)
|