@photivra/engine 2.13.0 → 2.14.0
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 +68 -1
- package/CITATION.cff +1 -1
- package/README.md +7 -7
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +2 -0
- package/dist/camera/lens-projection.js +2 -1
- package/dist/camera/lens-projection.js.map +1 -1
- package/dist/capture/dng-import.js +1 -1
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/host-yield-cadence.js +1 -1
- package/dist/capture/host-yield-cadence.js.map +1 -1
- package/dist/capture/native-environment-optical-stage.js +1 -1
- package/dist/capture/native-environment-optical-stage.js.map +1 -1
- package/dist/capture/native-raw.js +1 -1
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/packed-sensor-raw.js +3 -3
- package/dist/capture/packed-sensor-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.js +1 -1
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/color/capture-color.js +3 -3
- package/dist/color/capture-color.js.map +1 -1
- package/dist/color/chromatic-adaptation.js +2 -2
- package/dist/color/chromatic-adaptation.js.map +1 -1
- package/dist/color/color-matching-functions.js +1 -1
- package/dist/color/color-matching-functions.js.map +1 -1
- package/dist/color/colorimetry-math.js +2 -2
- package/dist/color/colorimetry-math.js.map +1 -1
- package/dist/color/illuminants.js +3 -3
- package/dist/color/illuminants.js.map +1 -1
- package/dist/color/rgb-color-spaces.js +1 -1
- package/dist/color/rgb-color-spaces.js.map +1 -1
- package/dist/color/sensor-color-development.js +1 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/core/record-validation.js +3 -3
- package/dist/core/record-validation.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/equipment/generic-prime-lenses.js +3 -3
- package/dist/equipment/generic-prime-lenses.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +23 -1
- package/dist/equipment/generic-tier-assets.js +58 -30
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.d.ts +6 -3
- package/dist/equipment/generic-tier-presets.js +26 -13
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- package/dist/exposure/auto-iso-slowest-shutter.js +2 -1
- package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
- package/dist/exposure/flash-ttl.js +1 -1
- package/dist/exposure/flash-ttl.js.map +1 -1
- package/dist/exposure/flash.js +1 -1
- package/dist/exposure/flash.js.map +1 -1
- package/dist/facade/chart-material-layers.js +2 -2
- package/dist/facade/chart-material-layers.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +1 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +45 -8
- package/dist/facade/chart-scenes.js +101 -44
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/color-patches.d.ts +129 -0
- package/dist/facade/color-patches.js +209 -0
- package/dist/facade/color-patches.js.map +1 -0
- package/dist/facade/display-view-gpu.js +1 -1
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/scene-radiance.js +2 -2
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +47 -3
- package/dist/facade/simulate-capture.js +71 -22
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +2 -2
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.js +2 -2
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.js +1 -1
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/image-kernel-execution.js +2 -2
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/temporal-sum-gpu.js +23 -23
- package/dist/gpu/webgpu-runtime.js +1 -1
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/gpu/wgsl-chart-composite-kernel.js +318 -318
- package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -1
- package/dist/gpu/wgsl-chart-kernel.js +117 -117
- package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.js +505 -505
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.js +248 -248
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-kernels.js +203 -203
- package/dist/gpu/wgsl-kernels.js.map +1 -1
- package/dist/gpu/wgsl-sensor-kernel.js +302 -302
- package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/motion/temporal-exposure-sum.js +1 -1
- package/dist/motion/temporal-exposure-sum.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.js +4 -4
- package/dist/optics/diffraction-psf-profile.js.map +1 -1
- package/dist/optics/front-of-lens-filter.js +1 -1
- package/dist/optics/front-of-lens-filter.js.map +1 -1
- package/dist/optics/irradiance-convolution.js +1 -1
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/output/native-capture-sdr.js +1 -1
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/png-encoder.js +2 -2
- package/dist/output/png-encoder.js.map +1 -1
- package/dist/output/print-jpeg.js +1 -1
- package/dist/output/print-jpeg.js.map +1 -1
- package/dist/sensor/fixed-pattern-noise.js +1 -1
- package/dist/sensor/fixed-pattern-noise.js.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +1 -1
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/docs/API_REFERENCE.md +29 -1
- package/docs/QUICK_START.md +40 -5
- package/docs/USAGE.md +35 -1
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-tier-assets.md +41 -1
- package/docs/api/equipment-generic-tier-presets.md +11 -3
- package/docs/api/exports.json +107 -2
- package/docs/api/facade-chart-scenes.md +36 -5
- package/docs/api/facade-color-patches.md +150 -0
- package/docs/api/facade-simulate-capture.md +52 -2
- package/llms.txt +1 -0
- package/package.json +2 -2
|
@@ -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 { 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"]}
|
|
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 = /* @__PURE__ */ 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"]}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { type ColorimetricSpectrum, type PreparedColorimetricSpectrum } from "../color/color-matching-functions.js";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { type ColorPatchId } from "./color-patches.js";
|
|
3
|
+
/**
|
|
4
|
+
* 2.14: one of the engine's built-in color patches (see `getColorPatchSet()`): its reflectance spectrum, Photivra's own
|
|
5
|
+
* analytic design, rendered through the same spectral path as a caller spectrum.
|
|
6
|
+
*/
|
|
7
|
+
export interface ColorPatchReflectance {
|
|
8
|
+
kind: "color-patch";
|
|
9
|
+
patchId: ColorPatchId;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A neutral (spectrally flat) reflectance in [0, 1], a caller reflectance spectrum (values in [0, 1]) or, from 2.14, a
|
|
13
|
+
* built-in color patch `{ kind: "color-patch", patchId }`.
|
|
14
|
+
*/
|
|
15
|
+
export type ChartReflectance = number | ColorimetricSpectrum | ColorPatchReflectance;
|
|
4
16
|
export interface UniformChartScene {
|
|
5
17
|
preset: "uniform";
|
|
6
18
|
reflectance?: ChartReflectance;
|
|
@@ -66,7 +78,7 @@ export interface PointGridChartScene {
|
|
|
66
78
|
}
|
|
67
79
|
export interface PatchChartScene {
|
|
68
80
|
preset: "patches";
|
|
69
|
-
/** Caller reflectance per patch, row-major; the facade bundles no
|
|
81
|
+
/** Caller reflectance per patch, row-major; the facade bundles no color-chart data. 1..1024 patches. */
|
|
70
82
|
patches: readonly {
|
|
71
83
|
patchId: string;
|
|
72
84
|
reflectance: ChartReflectance;
|
|
@@ -77,6 +89,22 @@ export interface PatchChartScene {
|
|
|
77
89
|
gapFraction?: number;
|
|
78
90
|
backgroundReflectance?: number;
|
|
79
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* 2.14: the built-in Photivra color patches (`getColorPatchSet()`) in a grid, drawn like `patches`: row-major, each
|
|
94
|
+
* patch centred in an equal cell with a neutral gap around it. The spectra are the engine's own analytic designs; no
|
|
95
|
+
* third-party chart data, names, order or layout are used.
|
|
96
|
+
*/
|
|
97
|
+
export interface ColorPatchesChartScene {
|
|
98
|
+
preset: "color-patches";
|
|
99
|
+
/** Patches to draw, row-major, distinct. Default: the whole set in `COLOR_PATCH_IDS` order (30 patches, six per row). */
|
|
100
|
+
patchIds?: readonly ColorPatchId[];
|
|
101
|
+
/** Patches per row, 1 to the patch count. Default min(6, patch count). */
|
|
102
|
+
columns?: number;
|
|
103
|
+
/** Gap between patches as a fraction of the cell size, [0, 0.5). Default 0.1. */
|
|
104
|
+
gapFraction?: number;
|
|
105
|
+
/** Neutral reflectance of the gaps and empty cells. Default 0.18. */
|
|
106
|
+
backgroundReflectance?: number;
|
|
107
|
+
}
|
|
80
108
|
/**
|
|
81
109
|
* 2.3: seeded dead-leaves texture (Matheron's random closed set model, in the scale-invariant form used for
|
|
82
110
|
* camera texture measurement by Cao, Guichard and Hornung): opaque disks with Poisson-distributed centres,
|
|
@@ -186,7 +214,7 @@ export interface FiducialChartScene {
|
|
|
186
214
|
backgroundReflectance?: number;
|
|
187
215
|
}
|
|
188
216
|
/** Any chart preset except `composite`: a composite's background or element. */
|
|
189
|
-
export type ChartElementScene = UniformChartScene | SlantedEdgeChartScene | SiemensStarChartScene | StepWedgeChartScene | GreyRampChartScene | CheckerboardChartScene | PointGridChartScene | PatchChartScene | DeadLeavesChartScene | BarGroupsChartScene | HyperbolicWedgeChartScene | ZonePlateChartScene | SlantedSquaresChartScene | LineGridChartScene | FiducialChartScene;
|
|
217
|
+
export type ChartElementScene = UniformChartScene | SlantedEdgeChartScene | SiemensStarChartScene | StepWedgeChartScene | GreyRampChartScene | CheckerboardChartScene | PointGridChartScene | PatchChartScene | ColorPatchesChartScene | DeadLeavesChartScene | BarGroupsChartScene | HyperbolicWedgeChartScene | ZonePlateChartScene | SlantedSquaresChartScene | LineGridChartScene | FiducialChartScene;
|
|
190
218
|
/**
|
|
191
219
|
* One element of a composite chart: a preset drawn inside its placement rectangle (frame units of the
|
|
192
220
|
* composite). The element sees its own frame: u = ±1 at the rectangle's left and right edges, aspect =
|
|
@@ -310,6 +338,11 @@ export declare const CHART_SCENE_DEFAULTS: Readonly<{
|
|
|
310
338
|
lightReflectance: 0.8;
|
|
311
339
|
backgroundReflectance: 0.18;
|
|
312
340
|
}>;
|
|
341
|
+
"color-patches": Readonly<{
|
|
342
|
+
patchIds: readonly ["neutral-1", "neutral-2", "neutral-3", "neutral-4", "neutral-5", "neutral-6", "red", "yellow", "green", "cyan", "blue", "magenta", "red-soft", "yellow-soft", "green-soft", "cyan-soft", "blue-soft", "magenta-soft", "skin-pale", "skin-medium", "skin-tan", "skin-deep", "sky-blue", "foliage-green", "brick-red", "sand", "teal", "lavender", "deep-orange", "deep-violet"];
|
|
343
|
+
gapFraction: 0.1;
|
|
344
|
+
backgroundReflectance: 0.18;
|
|
345
|
+
}>;
|
|
313
346
|
composite: Readonly<{
|
|
314
347
|
background: Readonly<{
|
|
315
348
|
preset: "uniform";
|
|
@@ -348,8 +381,11 @@ export declare const CHART_SPECTRAL_RANGE_NANOMETERS: Readonly<{
|
|
|
348
381
|
minimum: 380;
|
|
349
382
|
maximum: 780;
|
|
350
383
|
}>;
|
|
351
|
-
/**
|
|
352
|
-
|
|
384
|
+
/**
|
|
385
|
+
* Internal: validates a chart reflectance input (a neutral number, a reflectance spectrum covering 380–780 nm or a
|
|
386
|
+
* built-in color patch) as a material.
|
|
387
|
+
*/
|
|
388
|
+
export declare function resolveChartMaterial(value: unknown, name: string, exp?: (x: number) => number): ResolvedChartScene["materials"][number];
|
|
353
389
|
/**
|
|
354
390
|
* Internal (not exported from the package): the caller input behind each material of a resolved chart, in
|
|
355
391
|
* material order (`undefined` for an engine neutral). The facade hashes caller spectra from these inputs.
|
|
@@ -494,8 +530,9 @@ export declare function resolveChartScene(input: {
|
|
|
494
530
|
}): ResolvedChartScene;
|
|
495
531
|
/**
|
|
496
532
|
* Internal (not exported from the package): {@link resolveChartScene} with the step wedge's geometric levels computed
|
|
497
|
-
* with the portable power (`portablePow`, within 1 ulp) instead of `**`,
|
|
498
|
-
*
|
|
533
|
+
* with the portable power (`portablePow`, within 1 ulp) instead of `**`, and (2.14) the built-in color patches with the
|
|
534
|
+
* portable exponential (`portableExp`) instead of `Math.exp`, whose last bits differ between JavaScript engines. Every
|
|
535
|
+
* other value, the geometry and the material order are the same. The display view uses its materials.
|
|
499
536
|
*/
|
|
500
537
|
export declare function resolvePortableChartScene(input: {
|
|
501
538
|
scene: ChartScene;
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Procedural test charts for the capture facade, written for this engine. Every chart is a flat,
|
|
4
4
|
* Lambertian (matte) reflectance pattern, defined by formulas in normalized frame coordinates. No
|
|
5
|
-
* third-party chart data (for example ColorChecker reflectances) is bundled.
|
|
6
|
-
* spectra that the caller supplies.
|
|
5
|
+
* third-party chart data (for example ColorChecker reflectances) is bundled. Colored patches use
|
|
6
|
+
* spectra that the caller supplies or, from 2.14, Photivra's own analytic color patches
|
|
7
|
+
* (`color-patches.ts`, docs/COLOR_PATCHES.md).
|
|
7
8
|
*
|
|
8
9
|
* Frame coordinates: `u` runs from -1 (left edge of the framed chart) to +1 (right edge); `v` points up
|
|
9
10
|
* and has the same scale, so it runs from -aspect to +aspect where aspect = height/width of the frame.
|
|
@@ -23,35 +24,37 @@
|
|
|
23
24
|
* levels, like the grey ramp: the step (below 0.1 % of the range) is far below sensor noise.
|
|
24
25
|
* @see docs/QUICK_START.md for the chart catalogue and their analytic definitions.
|
|
25
26
|
*/
|
|
26
|
-
import { portablePow } from "../core/portable-math.js";
|
|
27
|
+
import { portableExp, portablePow } from "../core/portable-math.js";
|
|
27
28
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
28
29
|
import { prepareColorimetricSpectrum } from "../color/color-matching-functions.js";
|
|
29
30
|
import { requireInputObject, requireInputKeys } from "../core/record-validation.js";
|
|
30
31
|
import { createDeterministicUniformStream } from "../sensor/stochastic-seed.js";
|
|
31
|
-
|
|
32
|
+
import { COLOR_PATCH_IDS, COLOR_PATCH_SET_ID, COLOR_PATCH_SET_VERSION, findColorPatch, prepareColorPatchSpectrum } from "./color-patches.js";
|
|
33
|
+
export const CHART_SCENE_PRESETS = /* @__PURE__ */ Object.freeze(["uniform", "slanted-edge", "siemens-star",
|
|
32
34
|
"step-wedge", "grey-ramp", "checkerboard", "point-grid", "patches", "dead-leaves", "bar-groups", "hyperbolic-wedge", "zone-plate",
|
|
33
|
-
"slanted-squares", "line-grid", "fiducial", "composite"]);
|
|
35
|
+
"slanted-squares", "line-grid", "fiducial", "color-patches", "composite"]);
|
|
34
36
|
/** Neutral levels that represent a continuous pattern (grey ramp; 2.3 sine star, zone plate and dead-leaves greys). */
|
|
35
37
|
export const CHART_CONTINUOUS_LEVELS = 1024;
|
|
36
38
|
/** Engine-chosen chart parameters, each reported when the caller omitted it. */
|
|
37
|
-
export const CHART_SCENE_DEFAULTS = Object.freeze({
|
|
38
|
-
uniform: Object.freeze({ reflectance: 0.18 }),
|
|
39
|
-
"slanted-edge": Object.freeze({ angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
|
|
40
|
-
"siemens-star": Object.freeze({ cycles: 36, radiusFraction: 0.9, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
|
|
41
|
-
"step-wedge": Object.freeze({ steps: 11, minimumReflectance: 0.03, maximumReflectance: 0.9, spacing: "equal-density" }),
|
|
42
|
-
"grey-ramp": Object.freeze({ minimumReflectance: 0.03, maximumReflectance: 0.9 }),
|
|
43
|
-
checkerboard: Object.freeze({ squaresAcross: 8, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
44
|
-
"point-grid": Object.freeze({ columns: 5, rows: 3, radius: 0.01, pointReflectance: 0.9, backgroundReflectance: 0.02 }),
|
|
45
|
-
patches: Object.freeze({ gapFraction: 0.1, backgroundReflectance: 0.18 }),
|
|
46
|
-
"dead-leaves": Object.freeze({ radiusMinimum: 0.01, radiusMaximum: 0.5, minimumReflectance: 0.2, maximumReflectance: 0.6, seed: 1 }),
|
|
47
|
-
"bar-groups": Object.freeze({ lineWidthsPerPictureHeight: Object.freeze([100, 200, 400, 800]), numerals: true, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
48
|
-
"hyperbolic-wedge": Object.freeze({ orientation: "horizontal", lineCount: 5, minimumLineWidthsPerPictureHeight: 100,
|
|
39
|
+
export const CHART_SCENE_DEFAULTS = /* @__PURE__ */ Object.freeze({
|
|
40
|
+
uniform: /* @__PURE__ */ Object.freeze({ reflectance: 0.18 }),
|
|
41
|
+
"slanted-edge": /* @__PURE__ */ Object.freeze({ angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
|
|
42
|
+
"siemens-star": /* @__PURE__ */ Object.freeze({ cycles: 36, radiusFraction: 0.9, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
|
|
43
|
+
"step-wedge": /* @__PURE__ */ Object.freeze({ steps: 11, minimumReflectance: 0.03, maximumReflectance: 0.9, spacing: "equal-density" }),
|
|
44
|
+
"grey-ramp": /* @__PURE__ */ Object.freeze({ minimumReflectance: 0.03, maximumReflectance: 0.9 }),
|
|
45
|
+
checkerboard: /* @__PURE__ */ Object.freeze({ squaresAcross: 8, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
46
|
+
"point-grid": /* @__PURE__ */ Object.freeze({ columns: 5, rows: 3, radius: 0.01, pointReflectance: 0.9, backgroundReflectance: 0.02 }),
|
|
47
|
+
patches: /* @__PURE__ */ Object.freeze({ gapFraction: 0.1, backgroundReflectance: 0.18 }),
|
|
48
|
+
"dead-leaves": /* @__PURE__ */ Object.freeze({ radiusMinimum: 0.01, radiusMaximum: 0.5, minimumReflectance: 0.2, maximumReflectance: 0.6, seed: 1 }),
|
|
49
|
+
"bar-groups": /* @__PURE__ */ Object.freeze({ lineWidthsPerPictureHeight: /* @__PURE__ */ Object.freeze([100, 200, 400, 800]), numerals: true, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
50
|
+
"hyperbolic-wedge": /* @__PURE__ */ Object.freeze({ orientation: "horizontal", lineCount: 5, minimumLineWidthsPerPictureHeight: 100,
|
|
49
51
|
maximumLineWidthsPerPictureHeight: 1000, darkReflectance: 0.05, lightReflectance: 0.8 }),
|
|
50
|
-
"zone-plate": Object.freeze({ radiusFraction: 0.9, maximumCyclesPerPictureHeight: 200, darkReflectance: 0.1, lightReflectance: 0.7, backgroundReflectance: 0.18 }),
|
|
51
|
-
"slanted-squares": Object.freeze({ columns: 3, rows: 2, sizeFraction: 0.5, angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
|
|
52
|
-
"line-grid": Object.freeze({ spacing: 1 / 3, lineWidth: 0.01, lineReflectance: 0.42, backgroundReflectance: 0.6 }),
|
|
53
|
-
fiducial: Object.freeze({ radiusFraction: 0.8, ringFraction: 0.18, inverted: false, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
|
|
54
|
-
|
|
52
|
+
"zone-plate": /* @__PURE__ */ Object.freeze({ radiusFraction: 0.9, maximumCyclesPerPictureHeight: 200, darkReflectance: 0.1, lightReflectance: 0.7, backgroundReflectance: 0.18 }),
|
|
53
|
+
"slanted-squares": /* @__PURE__ */ Object.freeze({ columns: 3, rows: 2, sizeFraction: 0.5, angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
|
|
54
|
+
"line-grid": /* @__PURE__ */ Object.freeze({ spacing: 1 / 3, lineWidth: 0.01, lineReflectance: 0.42, backgroundReflectance: 0.6 }),
|
|
55
|
+
fiducial: /* @__PURE__ */ Object.freeze({ radiusFraction: 0.8, ringFraction: 0.18, inverted: false, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
|
|
56
|
+
"color-patches": /* @__PURE__ */ Object.freeze({ patchIds: COLOR_PATCH_IDS, gapFraction: 0.1, backgroundReflectance: 0.18 }),
|
|
57
|
+
composite: /* @__PURE__ */ Object.freeze({ background: /* @__PURE__ */ Object.freeze({ preset: "uniform", reflectance: 0.18 }) })
|
|
55
58
|
});
|
|
56
59
|
function reflectance(value, name) {
|
|
57
60
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1)
|
|
@@ -69,11 +72,23 @@ function finiteIn(value, name, minimum, maximum, open = false) {
|
|
|
69
72
|
return value;
|
|
70
73
|
}
|
|
71
74
|
/** Wavelength range every chart spectrum must cover (the facade's sensor quadrature range). */
|
|
72
|
-
export const CHART_SPECTRAL_RANGE_NANOMETERS = Object.freeze({ minimum: 380, maximum: 780 });
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
+
export const CHART_SPECTRAL_RANGE_NANOMETERS = /* @__PURE__ */ Object.freeze({ minimum: 380, maximum: 780 });
|
|
76
|
+
/**
|
|
77
|
+
* Internal: validates a chart reflectance input (a neutral number, a reflectance spectrum covering 380–780 nm or a
|
|
78
|
+
* built-in color patch) as a material.
|
|
79
|
+
*/
|
|
80
|
+
export function resolveChartMaterial(value, name, exp) {
|
|
75
81
|
if (typeof value === "number")
|
|
76
82
|
return { kind: "neutral", reflectance: reflectance(value, name) };
|
|
83
|
+
if (typeof value === "object" && value !== null && value.kind === "color-patch") {
|
|
84
|
+
for (const key of Object.keys(value))
|
|
85
|
+
if (key !== "kind" && key !== "patchId")
|
|
86
|
+
throw new InvalidConfigurationError(name + " has unknown field \"" + key + "\".");
|
|
87
|
+
const patch = findColorPatch(value.patchId);
|
|
88
|
+
if (!patch)
|
|
89
|
+
throw new InvalidConfigurationError(name + ".patchId must be one of the built-in color patches (COLOR_PATCH_IDS).");
|
|
90
|
+
return { kind: "spectrum", spectrum: prepareColorPatchSpectrum(patch, exp) };
|
|
91
|
+
}
|
|
77
92
|
let spectrum;
|
|
78
93
|
try {
|
|
79
94
|
spectrum = prepareColorimetricSpectrum(value, name);
|
|
@@ -88,6 +103,26 @@ export function resolveChartMaterial(value, name) {
|
|
|
88
103
|
reflectance(spectrum.evaluate(nm), name + " values");
|
|
89
104
|
return { kind: "spectrum", spectrum };
|
|
90
105
|
}
|
|
106
|
+
/** How a uniform chart reports its reflectance: the number, the color-patch record, or `"caller-spectrum"`. */
|
|
107
|
+
function reflectanceReport(raw) {
|
|
108
|
+
if (typeof raw === "number")
|
|
109
|
+
return raw;
|
|
110
|
+
const record = raw;
|
|
111
|
+
return record.kind === "color-patch" ? { kind: "color-patch", patchId: record.patchId } : "caller-spectrum";
|
|
112
|
+
}
|
|
113
|
+
/** Material at a point of a row-major patch grid (`patches`, 2.14 `color-patches`): a patch index or `backgroundIndex`. */
|
|
114
|
+
function patchGridMaterialAt(columns, rows, count, gap, aspect, backgroundIndex) {
|
|
115
|
+
const cellW = 2 / columns, cellH = 2 * aspect / rows;
|
|
116
|
+
return (u, v) => {
|
|
117
|
+
const fx = (u + 1) / cellW, fy = (aspect - v) / cellH, i = Math.floor(fx), j = Math.floor(fy);
|
|
118
|
+
if (i < 0 || j < 0 || i >= columns || j >= rows)
|
|
119
|
+
return backgroundIndex;
|
|
120
|
+
const index = j * columns + i, x = fx - i, y = fy - j;
|
|
121
|
+
if (index >= count || x < gap / 2 || x > 1 - gap / 2 || y < gap / 2 || y > 1 - gap / 2)
|
|
122
|
+
return backgroundIndex;
|
|
123
|
+
return index;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
91
126
|
/** Raw caller spectrum inputs per material (undefined for neutral materials), for the facade's scene digest. */
|
|
92
127
|
const MATERIAL_INPUTS = new WeakMap();
|
|
93
128
|
/**
|
|
@@ -136,12 +171,13 @@ export function resolveChartScene(input) {
|
|
|
136
171
|
}
|
|
137
172
|
/**
|
|
138
173
|
* Internal (not exported from the package): {@link resolveChartScene} with the step wedge's geometric levels computed
|
|
139
|
-
* with the portable power (`portablePow`, within 1 ulp) instead of `**`,
|
|
140
|
-
*
|
|
174
|
+
* with the portable power (`portablePow`, within 1 ulp) instead of `**`, and (2.14) the built-in color patches with the
|
|
175
|
+
* portable exponential (`portableExp`) instead of `Math.exp`, whose last bits differ between JavaScript engines. Every
|
|
176
|
+
* other value, the geometry and the material order are the same. The display view uses its materials.
|
|
141
177
|
*/
|
|
142
178
|
export function resolvePortableChartScene(input) {
|
|
143
179
|
requireInputObject(input, "resolveChartScene input");
|
|
144
|
-
return resolveScene(input.scene, input.aspect, { pictureHeight: 2 * input.aspect, nested: false, pow: portablePow });
|
|
180
|
+
return resolveScene(input.scene, input.aspect, { pictureHeight: 2 * input.aspect, nested: false, pow: portablePow, exp: portableExp });
|
|
145
181
|
}
|
|
146
182
|
function resolveScene(scene, aspect, context) {
|
|
147
183
|
requireInputObject(scene, "chart scene");
|
|
@@ -152,7 +188,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
152
188
|
throw new InvalidConfigurationError("A composite chart cannot contain another composite.");
|
|
153
189
|
const record = scene;
|
|
154
190
|
const defaults = CHART_SCENE_DEFAULTS[preset];
|
|
155
|
-
const extra = preset === "patches" ? ["patches", "columns"] : preset === "composite" ? ["elements"] : preset === "siemens-star" ? ["profile"] : [];
|
|
191
|
+
const extra = preset === "patches" ? ["patches", "columns"] : preset === "color-patches" ? ["columns"] : preset === "composite" ? ["elements"] : preset === "siemens-star" ? ["profile"] : [];
|
|
156
192
|
const allowed = ["preset", ...Object.keys(defaults), ...extra];
|
|
157
193
|
for (const key of Object.keys(record))
|
|
158
194
|
if (!allowed.includes(key))
|
|
@@ -183,8 +219,8 @@ function resolveScene(scene, aspect, context) {
|
|
|
183
219
|
};
|
|
184
220
|
switch (preset) {
|
|
185
221
|
case "uniform": {
|
|
186
|
-
const raw = get("reflectance"), m = resolveChartMaterial(raw, "reflectance");
|
|
187
|
-
return done({ reflectance:
|
|
222
|
+
const raw = get("reflectance"), m = resolveChartMaterial(raw, "reflectance", context.exp);
|
|
223
|
+
return done({ reflectance: reflectanceReport(raw) }, [m], () => 0, [typeof raw === "number" ? undefined : raw], { kind: "uniform" });
|
|
188
224
|
}
|
|
189
225
|
case "slanted-edge": {
|
|
190
226
|
const angle = finiteIn(get("angleDegrees"), "angleDegrees", -45, 45, true), dark = neutral("darkReflectance"), light = neutral("lightReflectance");
|
|
@@ -270,7 +306,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
270
306
|
if (typeof p.patchId !== "string" || p.patchId.trim() === "" || ids.has(p.patchId))
|
|
271
307
|
throw new InvalidConfigurationError("Each patch needs a unique non-empty patchId.");
|
|
272
308
|
ids.add(p.patchId);
|
|
273
|
-
materials.push(resolveChartMaterial(p.reflectance, "patches[" + index + "].reflectance"));
|
|
309
|
+
materials.push(resolveChartMaterial(p.reflectance, "patches[" + index + "].reflectance", context.exp));
|
|
274
310
|
inputs.push(typeof p.reflectance === "number" ? undefined : p.reflectance);
|
|
275
311
|
}
|
|
276
312
|
const columnsRaw = record.columns === undefined ? Math.ceil(Math.sqrt(list.length)) : record.columns;
|
|
@@ -284,15 +320,35 @@ function resolveScene(scene, aspect, context) {
|
|
|
284
320
|
materials.push({ kind: "neutral", reflectance: background });
|
|
285
321
|
inputs.push(undefined);
|
|
286
322
|
const cellW = 2 / columns, cellH = 2 * aspect / rows;
|
|
287
|
-
return done({ patchIds: list.map((p) => p.patchId), columns, rows, gapFraction: gap, backgroundReflectance: background }, materials, (
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
323
|
+
return done({ patchIds: list.map((p) => p.patchId), columns, rows, gapFraction: gap, backgroundReflectance: background }, materials, patchGridMaterialAt(columns, rows, list.length, gap, aspect, backgroundIndex), inputs, { kind: "patches", columns, rows, count: list.length, gap, cellW, cellH, backgroundIndex });
|
|
324
|
+
}
|
|
325
|
+
case "color-patches": {
|
|
326
|
+
const ids = get("patchIds");
|
|
327
|
+
if (!Array.isArray(ids) || ids.length < 1 || ids.length > COLOR_PATCH_IDS.length)
|
|
328
|
+
throw new InvalidConfigurationError("patchIds must list 1 to " + COLOR_PATCH_IDS.length + " built-in color patch ids.");
|
|
329
|
+
const seen = new Set(), materials = [], inputs = [];
|
|
330
|
+
for (const [index, id] of ids.entries()) {
|
|
331
|
+
if (!findColorPatch(id))
|
|
332
|
+
throw new InvalidConfigurationError("patchIds[" + index + "] must be one of the built-in color patches (COLOR_PATCH_IDS).");
|
|
333
|
+
if (seen.has(id))
|
|
334
|
+
throw new InvalidConfigurationError("patchIds[" + index + "] repeats " + JSON.stringify(id) + ".");
|
|
335
|
+
seen.add(id);
|
|
336
|
+
const input = Object.freeze({ kind: "color-patch", patchId: id });
|
|
337
|
+
materials.push(resolveChartMaterial(input, "patchIds[" + index + "]", context.exp));
|
|
338
|
+
inputs.push(input);
|
|
339
|
+
}
|
|
340
|
+
const columnsRaw = record.columns === undefined ? Math.min(6, ids.length) : record.columns;
|
|
341
|
+
if (record.columns === undefined)
|
|
342
|
+
defaulted.push("columns");
|
|
343
|
+
const columns = integerIn(columnsRaw, "columns", 1, ids.length), rows = Math.ceil(ids.length / columns);
|
|
344
|
+
const gap = finiteIn(get("gapFraction"), "gapFraction", 0, 0.5), background = neutral("backgroundReflectance");
|
|
345
|
+
if (gap >= 0.5)
|
|
346
|
+
throw new InvalidConfigurationError("gapFraction must be below 0.5.");
|
|
347
|
+
const backgroundIndex = materials.length;
|
|
348
|
+
materials.push({ kind: "neutral", reflectance: background });
|
|
349
|
+
inputs.push(undefined);
|
|
350
|
+
const cellW = 2 / columns, cellH = 2 * aspect / rows;
|
|
351
|
+
return done({ patchSet: COLOR_PATCH_SET_ID, patchSetVersion: COLOR_PATCH_SET_VERSION, patchIds: [...ids], columns, rows, gapFraction: gap, backgroundReflectance: background }, materials, patchGridMaterialAt(columns, rows, ids.length, gap, aspect, backgroundIndex), inputs, { kind: "patches", columns, rows, count: ids.length, gap, cellW, cellH, backgroundIndex });
|
|
296
352
|
}
|
|
297
353
|
case "dead-leaves": {
|
|
298
354
|
const rMin = finiteIn(get("radiusMinimum"), "radiusMinimum", DEAD_LEAVES_MINIMUM_RADIUS, 2), rMax = finiteIn(get("radiusMaximum"), "radiusMaximum", 0, 2, true);
|
|
@@ -410,7 +466,8 @@ function resolveScene(scene, aspect, context) {
|
|
|
410
466
|
const list = record.elements;
|
|
411
467
|
if (!Array.isArray(list) || list.length < 1 || list.length > 256)
|
|
412
468
|
throw new InvalidConfigurationError("elements must list 1 to 256 chart elements.");
|
|
413
|
-
const
|
|
469
|
+
const portable = { ...(context.pow ? { pow: context.pow } : {}), ...(context.exp ? { exp: context.exp } : {}) };
|
|
470
|
+
const nested = { pictureHeight: context.pictureHeight, nested: true, ...portable };
|
|
414
471
|
const background = resolveScene(get("background"), aspect, nested);
|
|
415
472
|
const materials = [...background.materials], inputs = [...chartMaterialInputs(background)];
|
|
416
473
|
for (const key of background.defaultedParameters)
|
|
@@ -430,7 +487,7 @@ function resolveScene(scene, aspect, context) {
|
|
|
430
487
|
if (e.mask === undefined)
|
|
431
488
|
defaulted.push("elements." + index + ".placement.mask");
|
|
432
489
|
// The element's own frame: u = ±1 across its width; frequencies keep the composite's picture height.
|
|
433
|
-
const chart = resolveScene(e.scene, height / width, { pictureHeight: context.pictureHeight * 2 / width, nested: true, ...
|
|
490
|
+
const chart = resolveScene(e.scene, height / width, { pictureHeight: context.pictureHeight * 2 / width, nested: true, ...portable });
|
|
434
491
|
for (const key of chart.defaultedParameters)
|
|
435
492
|
defaulted.push("elements." + index + ".parameters." + key);
|
|
436
493
|
const offset = materials.length;
|
|
@@ -574,7 +631,7 @@ function barGroupLayout(list, numerals, aspect, pictureHeight) {
|
|
|
574
631
|
/** Engine-drawn seven-segment numerals: a 0.6 × 1 cell per digit (baseline at 0, y up), stroke 0.16, advance 0.82. */
|
|
575
632
|
export const NUMERAL_WIDTH = 0.6, NUMERAL_ADVANCE = 0.82;
|
|
576
633
|
/** Internal: segment rectangles [x0, y0, x1, y1] of the seven-segment numerals and the segments each digit lights. */
|
|
577
|
-
export const SEGMENTS = Object.freeze({
|
|
634
|
+
export const SEGMENTS = /* @__PURE__ */ Object.freeze({
|
|
578
635
|
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]
|
|
579
636
|
});
|
|
580
637
|
export const DIGIT_SEGMENTS = ["abcdef", "bc", "abged", "abgcd", "fgbc", "afgcd", "afgedc", "abc", "abcdefg", "abcdfg"];
|