@photivra/engine 2.13.0 → 2.14.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 +75 -1
- package/CITATION.cff +1 -1
- package/NOTICE +5 -4
- package/README.md +10 -8
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +16 -10
- 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-contrast-reference.js +2 -2
- package/dist/output/print-contrast-reference.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
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,80 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 2.
|
|
3
|
+
## 2.14.1 — unreleased (prepared November 3, 2026)
|
|
4
|
+
|
|
5
|
+
A patch release of docs and legal files only. **No API, behavior or value change from 2.14.0:** `src/` differs only in `ENGINE_API_VERSION` and one source comment, root exports are unchanged (1,801), and captures, RAW codes, files and assumptions are the same.
|
|
6
|
+
- **Copyright holder and trademark owner (#543, #545):** `NOTICE` names Shackelford Creative, LLC as the copyright holder (`Copyright 2026 Shackelford Creative, LLC`) and as the owner of the Photivra trade names, trademarks, logos and branding; `README.md` ("Licensing") says the same and gains a **Trademarks** paragraph for the third-party names it mentions (ColorChecker, RAL and others). `CONTRIBUTING.md` and the licence scan's allowed holder follow.
|
|
7
|
+
- **Licence audit fixes (#543):** `THIRD_PARTY.md` says that `NOTICE` carries castleCSF's full MIT text (the repository-only `docs/licenses/castleCSF-MIT.txt` is not packed), lists the reference colors reproduced in `docs/COLOR_PATCHES.md` with their sources, names CSS Color 4's licence as the W3C Software and Document License, and gives the pending licence reviews a dated status (#544). `docs/COLOR_PATCHES.md` attributes its reference-color column per source. The castleCSF adapter's header comment (`src/output/print-contrast-reference.ts`) now points to `NOTICE`; the emitted code is unchanged.
|
|
8
|
+
- **Packaged files that change:** `NOTICE`, `README.md`, `THIRD_PARTY.md`, `CHANGELOG.md`, `SECURITY.md`, `CITATION.cff`, the version fields and the generated reference's version, and that one comment in `dist/output/print-contrast-reference.*`.
|
|
9
|
+
|
|
10
|
+
## 2.14.0 — published November 1, 2026
|
|
11
|
+
|
|
12
|
+
A minor release for the app's Beta: color test charts from the engine's own spectra (#537, with Photivra names, codes and reasons, #542), grey-card (incident) exposure (#538), third-stop ISO and shutter grids (#541) and a smaller package (#540). Captures, RAW codes, files and assumptions are the same as 2.13.0 for every request that uses none of the new patches, `exposure.metering: "grey-card"`, third-stop ISOs or `exposure.autoIso.isoSteps`.
|
|
13
|
+
- **New:** built-in Photivra color patches: the `color-patches` chart preset, the chart reflectance `{ kind: "color-patch", patchId }`, `getColorPatchSet()`, `evaluateColorPatchReflectance()`, each patch with a short code, a name and a reason (#537, #542). `exposure.greyCardDeviationStops` and `exposure.metering: "scene-average" | "grey-card"` (#538). Third-stop ISO settings on the generic tier bodies (presets 2.1.0), `exposure.autoIso.isoSteps`, and `GENERIC_THIRD_STOP_SHUTTER_SECONDS` (#541).
|
|
14
|
+
- **Changed:** the published shader text is minified and module-level frozen tables are `/* @__PURE__ */`, so the app's bundle is 6.6 KiB gzip smaller with the same GPU results (#540).
|
|
15
|
+
- **Compatibility:** 21 new root exports (1,801; runtime exports 600 → 611), none removed or changed:
|
|
16
|
+
- color patches, 14: 6 runtime exports (`COLOR_PATCH_SET_ID`, `COLOR_PATCH_SET_VERSION`, `COLOR_PATCH_IDS`, `COLOR_PATCH_WAVELENGTH_RANGE_NANOMETERS`, `getColorPatchSet`, `evaluateColorPatchReflectance`) and 8 types (`ColorPatch`, `ColorPatchId`, `ColorPatchGroup`, `ColorPatchSet`, `ColorPatchSpectralTerm`, `EvaluateColorPatchReflectanceInput`, `ColorPatchReflectance`, `ColorPatchesChartScene`);
|
|
17
|
+
- grey card, 2: `SIMULATE_CAPTURE_METERING_MODES` and the type `SimulateCaptureMetering`;
|
|
18
|
+
- third-stop grids, 5: `GENERIC_THIRD_STOP_ISO_SETTINGS`, `GENERIC_THIRD_STOP_SHUTTER_SECONDS`, `GENERIC_WHOLE_STOP_ISO_SETTINGS`, `GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS` and the type `GenericTierPresetVersion`.
|
|
19
|
+
- `ChartReflectance` and `ChartElementScene` gain a member; `CHART_SCENE_PRESETS` and `CHART_SCENE_DEFAULTS` gain `color-patches`; the capture's and the display view's `exposure` gain `greyCardDeviationStops` and `metering`; the Auto ISO report gains `isoSteps` when it is given.
|
|
20
|
+
- `GENERIC_EQUIPMENT_TIER_PRESET_VERSION` is now `"2.1.0"`; `"2.0.0"` still resolves, unchanged. Captures by tier id report preset and profile version 2.1.0, and their request records list a readout regime per third-stop ISO.
|
|
21
|
+
- No default changed: the meter stays `"scene-average"`, Auto ISO stays in whole stops, and a scalar reflectance is a flat grey as before.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Color patches designed by Photivra (docs/COLOR_PATCHES.md).** 30 reflectance spectra in six groups: a six-step flat neutral ramp from white (90.1 %) to black (3.5 %); red, yellow, green, cyan, blue and magenta, each strong and soft; four skin tones; sky blue, leaf green, brick red, sand, teal and lilac; a deep orange and a deep violet near the sRGB gamut edge. Each has a stable id, a stable short code (`PV-N1`…`PV-N6`, `PV-S1`…, `PV-T1`…, `PV-P1`…`PV-P4`, `PV-M1`…, `PV-G1`, `PV-G2`), an original Photivra name in US English ("Studio Gray 4", "Darkroom Red", "Midday Sky") and one sentence on why it is on the chart, all published in a table in docs/COLOR_PATCHES.md.
|
|
26
|
+
- **Distinct from third-party colors (owner request, 2026-11-01):** no published hex equals, and no colored patch is within ΔE00 2.0 (CIEDE2000, D65) of, a CSS named color, a published ColorChecker Classic sRGB or CIELAB value, a RAL Classic approximate sRGB value or one of 10,386 brand colors from Simple Icons; neutrals, flat by definition, only avoid equal hexes (they stay within ΔE00 0.35 of the ColorChecker's neutral ramp). `scripts/check-color-patch-distinctness.mjs` downloads the pinned sources, checks their SHA-256 and discards them; no third-party color data is committed. Thirteen colors of the first design were nudged and their spectra refit: five neutrals by 0.001–0.004 in reflectance and eight colors by ΔE00 0.56–4.04 (the strong red 3.53 and the deep orange 4.04 most); Pantone was not checked (proprietary). The names do not reuse ColorChecker, CSS or RAL names.
|
|
27
|
+
- **Spectra:** R(λ) = min(1, max(0, b + Σ aₖφₖ(λ))) on 360–830 nm, with Gaussian bumps exp(−½((λ − μ)/σ)²) and logistic steps 1/(1 + exp(−(λ − λ₀)/s)). Photivra fitted the parameters once to its own target colors and rounded them; the rounded values are the definition and are published in full. Every spectrum lies in [0.03, 0.901], so the clamp never acts; no fluorescence. No ColorChecker, IT8 or other commercial target's patch data, names, order, layout or measured spectra are used.
|
|
28
|
+
- **API:** `{ kind: "color-patch", patchId }` is accepted wherever a chart takes a reflectance spectrum (`uniform`, `patches`, material scenes and material layers). The `color-patches` preset draws a list of patches (default: all 30, six per row) as `patches` does, with `columns`, `gapFraction` and `backgroundReflectance`; its parameters name the set and its version, and its spectra enter the scene digest. `getColorPatchSet()` returns the frozen set (ids, codes, names, groups, reasons, parameters, reference values); `evaluateColorPatchReflectance()` evaluates one spectrum (approximation, model `photivra-color-patch-spectrum` 1.0.0). Invalid ids, fields and values fail with `InvalidConfigurationError`, wavelengths outside 360–830 nm with `InvalidScientificInputError`.
|
|
29
|
+
- **Reference colorimetry:** each patch's CIELAB under CIE D65 with the CIE 1931 2° observer (CIE tables, 1 nm, 360–830 nm) and its 8-bit sRGB. All 30 are inside sRGB; the gamut-edge patches' smallest linear components are 0.0105 and 0.0133. `scripts/compute-color-patch-reference.mjs` recomputes them from the CIE tables, which it downloads, checks by SHA-256 and deletes (nothing from them is stored), and with `--render` renders the chart under D65: maximum ΔE00 0.19 at 1 nm and 0.24 at 5 nm against the published values.
|
|
30
|
+
- **Display view:** it evaluates the patches of chart presets with the portable exponential, so those values have the same bits in every JavaScript engine; captures use `Math.exp`.
|
|
31
|
+
- **Cost:** a 600 × 500 capture of the chart costs the same as a neutral 30-patch chart (95–101 ms against 93–95 ms, one machine). The patch table adds about 9,800 B minified (about 4,100 B gzip, 3,285 B and 1,420 B of it the codes, names and reasons) to a capture-only bundle; the display view's addition is unchanged.
|
|
32
|
+
- **Tests:** `test/color-patches.test.ts`:
|
|
33
|
+
- the set contract, frozen records and the documented groups; codes numbered per group, names in one style and in US English, one-sentence reasons, no ColorChecker patch names;
|
|
34
|
+
- the formula against the published terms on a 0.25 nm grid, and the unused clamp;
|
|
35
|
+
- the neutral L* against 116·R^(1/3) − 16, the published hex against the published CIELAB, and the published hue order and skin lightness order;
|
|
36
|
+
- 35 invalid reflectances, charts and inputs;
|
|
37
|
+
- `color-patches` renders exactly what `patches` with the same reflectances renders, and a material scene what a uniform chart renders;
|
|
38
|
+
- neutral after white balance under the 6500 K Planckian and illuminant A (ideal reference and a noisy capture);
|
|
39
|
+
- hue order red, yellow, green, cyan, blue, magenta for the strong and the soft patches, lower chroma for the soft ones, skin tones by lightness;
|
|
40
|
+
- CIELAB rendered under the 6500 K Planckian within ΔE00 1.35 of the published D65 values (measured 1.12, mean 0.39);
|
|
41
|
+
- the display view;
|
|
42
|
+
- bit-identical renders of existing charts against digests recorded with 2.13.0.
|
|
43
|
+
- **Incident (grey-card) exposure deviation.** `exposure.greyCardDeviationStops` = log₂(L_ref(N, t, S)/(0.18·E/π)), with L_ref = 10·N²/(0.65·t·S) the luminance that the settings expose at the CIPA DC-004-2020 standard output sensitivity reference exposure H = 10/S (§3.2; camera equation §5.5 (4), the same constants as `standardExposureDeviationStops` and `SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX`), and 0.18·E/π the luminance of an 18 % Lambertian grey card under the request's illuminance E. Positive is darker (under), as for the metered deviation. It reads the light falling on the scene, as a grey card or a hand-held incident meter does, so it does not depend on the scene's reflectances: a white chart, which makes the scene meter read bright, leaves it unchanged. `null` for a `radiance-function` scene (its radiance ignores the illuminance).
|
|
44
|
+
- **Exposure modes unchanged:** aperture priority, shutter priority and program still meter the scene with the body's zone meter; the new field only reports where their exposure sits against the grey card (docs/QUICK_START.md, "Exposure"). On the app's round 4 All-in-one chart (90 % white paper) at the default illuminance they expose 1.78 stops darker than the grey card.
|
|
45
|
+
- **Tests:** `test/exposure-grey-card.test.ts`:
|
|
46
|
+
- exactly 0 at the defaults, in the capture and the display view;
|
|
47
|
+
- one stop per stop of shutter, aperture and ISO and per doubling of the illuminance (to 1e-12 stops), and the closed form at marked settings;
|
|
48
|
+
- the same value for four scenes of different reflectances and for a fixed-size chart with a dark surround, while the metered deviation differs;
|
|
49
|
+
- in aperture priority on a 90 % white field it exceeds the metered deviation by log₂(0.9/0.18);
|
|
50
|
+
- shutter priority and program with Auto ISO follow the closed form of the settings used;
|
|
51
|
+
- null for a radiance scene, 0 for a black scene at the defaults.
|
|
52
|
+
|
|
53
|
+
- **Grey-card metering (`exposure.metering`).** `"scene-average"` (default) keeps the body's 16 × 16-zone scene meter. `"grey-card"` gives every meter zone the luminance 0.18·E/π of an 18 % Lambertian grey card under the request's illuminance, so aperture priority, shutter priority and program, with a fixed ISO or Auto ISO, choose settings with `greyCardDeviationStops` = 0 whatever the scene's reflectances; only the mode's own steps and limits remain (whole-stop Auto ISO: at most half a stop, equal to the Auto ISO report's `residualStops`; the lens's f/4 or the shutter range when the light is beyond them). The result reports `exposure.metering`; with `"grey-card"` an assumption `exposure.meteringSource` gives the card's luminance. A `radiance-function` scene is refused with grey-card metering. Unknown values fail with `InvalidConfigurationError`. The scene-luminance fields keep describing the scene.
|
|
54
|
+
- **Tests** (`test/exposure-grey-card.test.ts`): the default is scene-average and an explicit `"scene-average"` gives the same bytes, settings and assumptions; unknown values and a radiance scene are refused; each automatic mode lands within 1e-6 stops of 0 at two illuminances, and compensation moves it by exactly the compensation; settings do not depend on the scene's reflectances; with Auto ISO in all four modes the deviation equals the report's `residualStops` (to 1e-9), within half a stop for `iso-grid-quantization`, and an `iso-maximum` limit is reported as such; the display view and the ideal reference resolve the same settings.
|
|
55
|
+
|
|
56
|
+
Images, RAW codes, every other exposure field, the assumptions and the scene digests are identical to 2.13.0 for manual, aperture-priority, program Auto ISO and shutter-priority captures and a display view (compared by SHA-256 between the two builds; every hash lock holds).
|
|
57
|
+
|
|
58
|
+
- **Third-stop exposure grids (owner's round 5, item 8).** The app's dials step in thirds.
|
|
59
|
+
- **ISO:** the generic tier presets 2.1.0 (`GENERIC_EQUIPMENT_TIER_PRESET_VERSION`) take third-stop ISO settings, `GENERIC_THIRD_STOP_ISO_SETTINGS` = 100, 125, 160, 200 … 25 600 (the rounded values of 100·2^(k/3), each within 2 %), up to each body's maximum (Consumer 6400, Prosumer 12 800, Professional 25 600), each with its own readout regime from the same gain model. Preset 2.0.0 is kept exactly (manifest pin unchanged) and still resolves; `GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS` = `["2.0.0", "2.1.0"]`.
|
|
60
|
+
- **Auto ISO keeps whole stops** by default, so its choices are those of 2.13; `exposure.autoIso.isoSteps: "third-stop"` lets it choose every setting (reported as `autoIso.isoSteps` when given). Auto ISO limits must lie on the grid it uses.
|
|
61
|
+
- **Shutter:** `GENERIC_THIRD_STOP_SHUTTER_SECONDS`, the standard third-stop times from 1/8000 s to 30 s (55 values, the rounded values of 2^(k/3) s, each within 7 %). The bodies' shutters stay continuous and take every value as the exact time.
|
|
62
|
+
- **Same output:** whole-stop requests on the tier ids give the same samples, RAW codes, files, settings and assumptions as the 2.0.0 records (`test/third-stop-settings.test.ts`, five requests on each tier), and the 22-result fingerprint is identical in every typed array. The result fields that change are the version identifiers (`provenance.cameraPresetVersion` and `exposure.resolution.capabilityProfiles.{body,lens}ProfileVersion` read 2.1.0) and the request records (`request.environmentRaw`, `request.siteInput(i)`), whose readout profile now lists a regime per third-stop ISO; `test/simulate-capture-integration.test.ts` re-pins those six request digests and none of its sample, code, flag, development or scene digests.
|
|
63
|
+
- **Tests:** `test/third-stop-settings.test.ts`: the series against 100·2^(k/3) and 2^(k/3) s; both catalogs pinned; selections of both versions; ISO 125–320 expose in proportion and are refused on a 2.0.0 record; whole-stop byte identity; Auto ISO whole-stop by default and thirds on request (300 lx: 6400 → 8000); limits on the grid; every shutter time accepted as given.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- **Smaller published package (#536).** The app was 2 KiB under its 500 KiB JavaScript budget.
|
|
68
|
+
- **Shader text:** `npm run build` strips comments and indentation from the `/* wgsl */` shader literals (`scripts/minify-wgsl-dist.mjs`, `scripts/lib/wgsl-minify.mjs`), keeping every newline and every line that holds an interpolation, so the WGSL token stream, line numbers and the engine's own shader-text anchors are unchanged; the build fails if any of that changes. Tests and the WebGPU suites run the minified text (`PHOTIVRA_WGSL_MINIFY=0` for the source). docs/WEBGPU.md, "Shipped shader text".
|
|
69
|
+
- **Pure annotations:** 110 module-level `Object.freeze(<literal>)` table constructors are `/* @__PURE__ */`, so bundlers drop unused tables; in-function freezes are untouched.
|
|
70
|
+
- **Measured:** on the app's production build (0.50.0, Vite 8), the GPU session chunk 52,917 → 46,430 B gzip and the app's total JavaScript 500.7 → 494.1 KiB gzip; importing only `SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX` 1,175 → 457 B gzip.
|
|
71
|
+
- **Same output:** on an Apple M5 Pro, `npm run test:webgpu` passed all 167 tests with the minified and with the source shader text, with identical recorded values (2,911); the CPU suite and every hash lock pass with the minified text. `test/wgsl-minify.test.ts` checks every literal's tokens with an independent tokenizer, the 14 shader-text anchors, and the edge cases.
|
|
72
|
+
|
|
73
|
+
### Known
|
|
74
|
+
|
|
75
|
+
- At the default `sensor.spectralStepNanometers` (20 nm) the facade's scene-illuminant white balance is measured on 1 nm nodes while the sensor integrates on 20 nm nodes, which leaves a small neutral cast that depends on the illuminant's spectral detail: white a* 0.17, b* 0.36 under the default 6500 K Planckian, b* −0.17 under illuminant A, and a* −0.74, b* +0.95 under the CIE D65 table as a caller spectrum. With 5 nm nodes it is below 0.03. Unchanged in this release (changing it would change existing captures); docs/COLOR_PATCHES.md recommends 5 nm for color work.
|
|
76
|
+
|
|
77
|
+
## 2.13.0 — published October 31, 2026
|
|
4
78
|
|
|
5
79
|
A minor release for the app's Beta camera: Auto ISO with photographer-set limits and an automatic slowest shutter, the illuminance that gives the standard exposure, and root exports for adding text metadata to a file the caller encoded. Captures, RAW codes and files are the same as 2.12.0 for the same request; Quality (AHD) display views change where the scene decides AHD's choice, closer to the capture (#457).
|
|
6
80
|
- **New:** `estimateAutoIsoSlowestShutter()` and `exposure.autoIso.slowestPreferredShutterSeconds: "auto"` (#504); `autoIsoLimits` on every Auto ISO resolver and `exposure.autoIso.limits` (#505); `exposure.standardExposureIlluminanceLux`; `addJpegTextMetadata` and `addPngTextMetadata` (#388).
|
package/CITATION.cff
CHANGED
|
@@ -9,7 +9,7 @@ abstract: "An open TypeScript engine for the science and mathematics of photogra
|
|
|
9
9
|
repository-code: "https://github.com/photivra/photivra"
|
|
10
10
|
url: "https://photivra.com"
|
|
11
11
|
license: "Apache-2.0"
|
|
12
|
-
version: "2.
|
|
12
|
+
version: "2.14.1"
|
|
13
13
|
keywords:
|
|
14
14
|
- photography
|
|
15
15
|
- camera optics
|
package/NOTICE
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Photivra
|
|
2
|
-
Copyright 2026
|
|
2
|
+
Copyright 2026 Shackelford Creative, LLC
|
|
3
3
|
|
|
4
4
|
This repository contains the Photivra scientific/business-logic engine.
|
|
5
5
|
|
|
6
6
|
Source code in this repository is made available under the Apache License,
|
|
7
|
-
Version 2.0.
|
|
8
|
-
|
|
9
|
-
except as allowed by applicable law and the
|
|
7
|
+
Version 2.0. Photivra trade names, trademarks, service marks, product names, logos, and
|
|
8
|
+
branding are owned by Shackelford Creative, LLC. The Apache License does not
|
|
9
|
+
grant permission to use them except as allowed by applicable law and the
|
|
10
|
+
license itself.
|
|
10
11
|
|
|
11
12
|
This product includes DNG technology under license by Adobe.
|
|
12
13
|
|
package/README.md
CHANGED
|
@@ -32,10 +32,10 @@ The source is licensed under Apache-2.0.
|
|
|
32
32
|
|
|
33
33
|
## Quick start
|
|
34
34
|
|
|
35
|
-
Install the published ESM package below. The [2.
|
|
35
|
+
Install the published ESM package below. The [2.14.1 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_14_1.md) and [changelog](https://github.com/Photivra/photivra/blob/main/CHANGELOG.md) describe the current release:
|
|
36
36
|
|
|
37
37
|
```sh
|
|
38
|
-
npm install @photivra/engine@2.
|
|
38
|
+
npm install @photivra/engine@2.14.1
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
Simulate a photograph of a test chart with a generic camera, then read the developed sRGB pixels:
|
|
@@ -57,7 +57,7 @@ console.log(capture.assumptions.map((a) => `${a.id}: ${a.explanation}`));
|
|
|
57
57
|
|
|
58
58
|
`simulateCapture()` runs the full physical pipeline through the engine's existing contracts: scene radiance, lens, spectral sensor, noise, RAW readout, RAW development and optional JPEG. It lists every default and modelling choice it made in `assumptions`.
|
|
59
59
|
|
|
60
|
-
The [Quick Start](https://github.com/Photivra/photivra/blob/main/docs/QUICK_START.md) covers the inputs, the chart presets (slanted edge, Siemens star, step wedge, grey ramp, checkerboard, point grid, caller patches), illuminants, white balance and limits.
|
|
60
|
+
The [Quick Start](https://github.com/Photivra/photivra/blob/main/docs/QUICK_START.md) covers the inputs, the chart presets (slanted edge, Siemens star, step wedge, grey ramp, checkerboard, point grid, caller patches and, from 2.14, Photivra's own [color patches](https://github.com/Photivra/photivra/blob/main/docs/COLOR_PATCHES.md)), illuminants, white balance and limits.
|
|
61
61
|
|
|
62
62
|
The individual calculations are still available as small functions. For example, the horizontal field of view for a 36 mm sensor dimension and a 50 mm focal length:
|
|
63
63
|
|
|
@@ -75,7 +75,7 @@ console.log(horizontal.provenance);
|
|
|
75
75
|
|
|
76
76
|
Start with the [Developer Guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Then use the [Usage Guide](https://github.com/Photivra/photivra/blob/main/docs/USAGE.md) and the [API reference](https://github.com/Photivra/photivra/blob/main/docs/API_REFERENCE.md), which is generated from the package exports.
|
|
77
77
|
|
|
78
|
-
Repository `main` may describe newer, unreleased work. For the stable contract, use the docs bundled in the published package, or the exact `v2.
|
|
78
|
+
Repository `main` may describe newer, unreleased work. For the stable contract, use the docs bundled in the published package, or the exact `v2.14.1` tag.
|
|
79
79
|
|
|
80
80
|
## Documentation
|
|
81
81
|
|
|
@@ -363,13 +363,13 @@ The application can then use those results, while the photography calculations t
|
|
|
363
363
|
|
|
364
364
|
## Status
|
|
365
365
|
|
|
366
|
-
- Repository package version: `2.
|
|
367
|
-
- Engine API contract: `2.
|
|
366
|
+
- Repository package version: `2.14.1` (release candidate prepared November 3, 2026; publication is separate)
|
|
367
|
+
- Engine API contract: `2.14.1`
|
|
368
368
|
- Versioned generic tier reference assets: [GENERIC_TIER_PRESETS.md](https://github.com/Photivra/photivra/blob/main/docs/GENERIC_TIER_PRESETS.md).
|
|
369
369
|
- Composed POC simulation API contract: `1.0.0`
|
|
370
370
|
- Stability: 1.0 root-package compatibility policy; bounded scientific models and independent POC contract
|
|
371
371
|
|
|
372
|
-
From 1.0.1, the package version and `ENGINE_API_VERSION` are equal. New captures/plans record 2.
|
|
372
|
+
From 1.0.1, the package version and `ENGINE_API_VERSION` are equal. New captures/plans record 2.14.1; existing archives keep their original creator identities. Schema, model and POC contract versions stay independent.
|
|
373
373
|
|
|
374
374
|
A breaking change to the root contracts now needs a new package major version. Additive and corrective changes follow [compatibility and migration](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_1_3_0.md). A 1.0 package is not a claim of calibrated physical accuracy.
|
|
375
375
|
|
|
@@ -504,6 +504,8 @@ Source code in this repository is licensed under Apache-2.0 unless a file clearl
|
|
|
504
504
|
|
|
505
505
|
This product includes DNG technology under license by Adobe.
|
|
506
506
|
|
|
507
|
-
The Apache license does not grant trademark rights to
|
|
507
|
+
The Photivra name, logos and branding are owned by Shackelford Creative, LLC. The Apache license does not grant trademark rights to them, except as allowed by applicable law and the license itself.
|
|
508
|
+
|
|
509
|
+
**Trademarks.** ColorChecker, RAL and the other product, company and brand names mentioned are trademarks of their respective owners. They are used only to identify the color sets and products compared against or cited; Photivra is not affiliated with or endorsed by them.
|
|
508
510
|
|
|
509
511
|
Outside contributions are merged only under the project's contributor licence agreement; see [Licensing of contributions](https://github.com/Photivra/photivra/blob/main/CONTRIBUTING.md#licensing-of-contributions).
|
package/SECURITY.md
CHANGED
|
@@ -4,7 +4,7 @@ Please do not open a public issue that contains vulnerability details.
|
|
|
4
4
|
|
|
5
5
|
## Supported versions
|
|
6
6
|
|
|
7
|
-
The published 1.0.0 release started the 1.x root-package compatibility line, and 2.0.0 started the 2.x line. The current release is **2.
|
|
7
|
+
The published 1.0.0 release started the 1.x root-package compatibility line, and 2.0.0 started the 2.x line. The current release is **2.14.0**, published on November 1, 2026 (see [CHANGELOG.md](CHANGELOG.md)).
|
|
8
8
|
|
|
9
9
|
Security fixes target the current maintained development and release line. Do not assume that older 1.x releases, snapshots or pre-1.0 releases will receive fixes.
|
|
10
10
|
|
package/THIRD_PARTY.md
CHANGED
|
@@ -31,24 +31,28 @@ This inventory does not replace a transitive dependency/SBOM review before a rel
|
|
|
31
31
|
|
|
32
32
|
## Pending licence and source reviews
|
|
33
33
|
|
|
34
|
-
These human reviews are **pending**. This inventory records them; it does not complete them. Each is described in full in its section below.
|
|
34
|
+
These human reviews are **pending**. This inventory records them and the evidence for each; it does not complete them. A review is complete only when the owner signs it off: a check by an AI system is not legal evidence (`CONTRIBUTING.md`). Each is described in full in its section below.
|
|
35
35
|
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
36
|
+
**Status on 2026-11-02.** 2.14.0 was tagged and published to npm with the owner's approval ([release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_14_0.md)) while these five reviews were open. **The owner accepted 2.14.0 shipping with these reviews open, 2026-11-02.** None of them is signed off. What each still needs is tracked in [#544](https://github.com/Photivra/photivra/issues/544). The evidence below was checked on 2026-11-02 against `main` at `v2.14.0` (`f4b09a7`), using the repository's own records.
|
|
37
|
+
|
|
38
|
+
| Review | Evidence checked on 2026-11-02 | Still needed from the owner |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| castleCSF-derived contrast reference (`src/output/print-contrast-reference.ts`) | **Licence evidence complete.** The MIT text in `NOTICE` is identical to `docs/licenses/castleCSF-MIT.txt`. A licence-compliance pass on 2026-11-02 compared that file with upstream `LICENSE` at revision `f4b0b722af83001d7af979281e06ca642d36e4e8` through the GitHub API: identical. The adapter's header names the holder and is kept in the published `dist/output/print-contrast-reference.js`. | Sign-off of the licence summary, and the separate scientific/source review ([the model/protocol](https://github.com/Photivra/photivra/blob/main/docs/PRINT_CONTRAST_REFERENCE.md)), including whether upstream holds the model material under other terms. |
|
|
41
|
+
| ReportLab/pypdf offline PDF test-artifact tools | The committed `output/pdf/print-proof-reference.pdf` has no `FontFile` entry and declares only `/BaseFont /Helvetica`, so no font program is embedded. The versions used are recorded as ReportLab 4.4.9 and pypdf 6.10.0 (`test/fixtures/print-detail/proof-layout-reference.json`, `proof-inspection-reference.json`, `docs/validation/print-196-candidate-evidence.json`). Neither tool is vendored or in the package. **Not verified:** the licence files of those exact versions (not installed locally). | The substantive human source review. |
|
|
42
|
+
| DNG/TIFF and JPEG sample provenance | All 26 tracked `.dng`/`.jpg` files name only "Photivra", "Photivra Virtual Camera" or "Photivra Print JPEG" (`strings` scan); no camera maker's name appears. The Adobe DNG notice is in `NOTICE`, `README.md` and the three DNG sources. **Not done:** byte-for-byte regeneration of the files. | The provenance/licence review. |
|
|
43
|
+
| Transitive dependency/SBOM review | **Evidence complete for the 2.14.0 lockfile.** Recount of `package-lock.json`: 168 entries, with the licence counts given in [its summary](#per-release-transitive-dependency--sbom-review); no entry is a runtime dependency. `npm run check:licenses` passes. GitHub reports MIT for all four GitHub Actions in the table above (their repositories' detected licence). | Sign-off for 2.14.0. The review is repeated for each release's exact lockfile. |
|
|
44
|
+
| Colorimetry published constants (`src/color/`). See [Colorimetry published constants and excluded datasets](#colorimetry-published-constants-and-excluded-datasets). | No CIE table is committed: the largest set of decimal constants in `src/color/` is 37, in `color-matching-functions.ts` (the engine's own fit). Each constant's source is cited in that section. The primary sources were not re-read in this check. | The human source/licence review of each cited source. |
|
|
41
45
|
|
|
42
46
|
## Licence summaries (prepared for owner sign-off)
|
|
43
47
|
|
|
44
|
-
Prepared on 2026-10-11 from local files only (repository files, `package-lock.json` and locally installed package metadata). These are factual summaries for the owner to sign off; they are **not** completed reviews, and every item above stays pending until the owner signs off
|
|
48
|
+
Prepared on 2026-10-11 from local files only (repository files, `package-lock.json` and locally installed package metadata). These are factual summaries for the owner to sign off; they are **not** completed reviews, and every item above stays pending until the owner signs it off (see the status above: 2.14.0 was released with them open). "Not verified" means the fact could not be checked locally.
|
|
45
49
|
|
|
46
50
|
### castleCSF-derived contrast reference
|
|
47
51
|
|
|
48
52
|
- **Declared licence:** MIT, from the retained upstream text `docs/licenses/castleCSF-MIT.txt` (revision `f4b0b722af83001d7af979281e06ca642d36e4e8`).
|
|
49
53
|
- **Copyright holder:** "Copyright (c) 2023 Graphics and Displays group - University of Cambridge" (from that file).
|
|
50
54
|
- **Permissions:** MIT permits commercial use, modification, redistribution and sublicensing.
|
|
51
|
-
- **Obligations:** the copyright and permission notice must be included in all copies or substantial portions.
|
|
55
|
+
- **Obligations:** the copyright and permission notice must be included in all copies or substantial portions. `NOTICE`, which is in the published package, carries the full MIT text, and the header of `src/output/print-contrast-reference.ts` (kept in the published `dist` JavaScript) names the holder. The copy at `docs/licenses/castleCSF-MIT.txt` stays in the repository only: the package's `files` list publishes only selected docs, not `docs/licenses/`.
|
|
52
56
|
- **Concerns:** whether the upstream repository holds model or parameter material under other terms (paper, datasets) is not verified locally; this summary covers only the retained MIT text. The licence summary does not cover the separate scientific/source review.
|
|
53
57
|
|
|
54
58
|
### ReportLab / pypdf offline PDF test-artifact tools
|
|
@@ -64,7 +68,7 @@ Prepared on 2026-10-11 from local files only (repository files, `package-lock.js
|
|
|
64
68
|
### DNG/TIFF and JPEG sample provenance
|
|
65
69
|
|
|
66
70
|
- **Declared licence:** project Apache-2.0 (`LICENSE`, `package.json`). There are 26 tracked `.dng`/`.jpg` files under `test/fixtures/paired-export-acceptance`, `test/fixtures/producer-export-acceptance`, `test/fixtures/print-detail/jpeg` and `docs/validation/native-raw-print-2026-10-04`. Their READMEs and manifests describe them as Photivra-owned synthetic outputs of this repository's own encoders. The generators are `scripts/generate-photographic-export-fixtures.mjs`, `scripts/generate-producer-export-acceptance.mjs` and `scripts/generate-native-print-jpeg.mjs`. The adjacent `srgb.icc` is written by the engine's own `createSrgbIccProfile()`.
|
|
67
|
-
- **Copyright holder:** "Copyright 2026
|
|
71
|
+
- **Copyright holder:** "Copyright 2026 Shackelford Creative, LLC" (`NOTICE`).
|
|
68
72
|
- **Permissions:** Apache-2.0 permits commercial use, modification and redistribution.
|
|
69
73
|
- **Obligations:**
|
|
70
74
|
- Apache-2.0 requires keeping `LICENSE` and `NOTICE`, and marking modified files.
|
|
@@ -83,7 +87,7 @@ Prepared on 2026-10-11 from local files only (repository files, `package-lock.js
|
|
|
83
87
|
- **Concerns:**
|
|
84
88
|
- The figures come from declared lockfile metadata, not licence texts.
|
|
85
89
|
- The review must be repeated against each release's exact lockfile.
|
|
86
|
-
- The GitHub Action licences in the table above are not verified locally.
|
|
90
|
+
- The GitHub Action licences in the table above are not verified locally; GitHub's detected licence for each of the four repositories is MIT (checked 2026-11-02).
|
|
87
91
|
|
|
88
92
|
## Colorimetry published constants and excluded datasets
|
|
89
93
|
|
|
@@ -92,6 +96,8 @@ The colorimetry modules in `src/color/` (see [docs/COLORIMETRY.md](https://githu
|
|
|
92
96
|
| Constants | Source | Licence / basis for use |
|
|
93
97
|
| --- | --- | --- |
|
|
94
98
|
| 28 coefficients of the engine's own multi-lobe CIE 1931 CMF fit (`color-matching-functions.ts`) | Fitted by Photivra with `scripts/fit-cie-1931-cmf.mjs` to the CIE 1931 2° table, DOI 10.25039/CIE.DS.xvudnb9b (CIE, CC BY-SA 4.0). The model form, a sum of piecewise Gaussians, follows the idea in Wyman, Sloan & Shirley (2013), JCGT 2(2); none of that paper's coefficients or text are used. | Fit-time use only: the script downloads the table into a temporary directory outside the repository, checks its SHA-256 and deletes it. No CIE value is stored in the repository, the package, the tests or the script's output. The committed coefficients are our own least-squares result; 28 numbers cannot reproduce the 1,413-value table (maximum error 0.022). Attribution to the CIE dataset is given here and in `docs/COLORIMETRY.md`. Replaced the Wyman et al. coefficients by owner decision (2026-10-17). |
|
|
99
|
+
| Reference CIELAB and sRGB values of the engine's own color patches (`color-patches.ts`, `docs/COLOR_PATCHES.md`) | Computed by Photivra with `scripts/compute-color-patch-reference.mjs` from Photivra's own analytic spectra, using the CIE standard illuminant D65 table (DOI 10.25039/CIE.DS.hjfjmt59) and the CIE 1931 2° table (DOI 10.25039/CIE.DS.xvudnb9b), both CIE, CC BY-SA 4.0. The spectra themselves are Photivra's design; no ColorChecker, IT8 or other commercial target data, names, order or layout are used. | Computation-time use only, as for the CMF fit: the script downloads both tables into a temporary directory, checks their SHA-256 and deletes them. No CIE value is stored in the repository, the package, the tests or the script's output; the committed values (three CIELAB coordinates and one 8-bit sRGB colour per patch) are results computed from our spectra. Attribution to the CIE datasets is given here and in `docs/COLOR_PATCHES.md`. |
|
|
100
|
+
| Distinctness check of the color patches' published colors and names (`scripts/check-color-patch-distinctness.mjs`, `docs/COLOR_PATCHES.md`) | Reference colors compared against, each downloaded from a pinned URL and checked by SHA-256: the CSS named colors (W3C, CSS Color Module Level 4, CRD 2025-04-24; W3C Software and Document License, as linked from that document); the ColorChecker Classic's manufacturer sRGB values as tabulated in Wikipedia, "ColorChecker", revision 1367818471 (CC BY-SA 4.0); the ColorChecker's published X-Rite (before and after November 2014), GretagMacbeth 2005 and BabelColor average values as collected in colour-science 0.4.6 (BSD-3-Clause); RAL Classic approximate sRGB values as tabulated in Wikipedia, "List of RAL colours", revision 1369650571 (CC BY-SA 4.0); brand colors from Simple Icons 1.20.0, 2.19.0, 4.25.0, 9.21.0 and 16.36.0 (its data is dedicated to the public domain). Pantone data is proprietary and is not used. | Check-time use only: the script parses each source in memory, compares it with Photivra's own values and discards it. No third-party color value, name or table is stored in the repository, the package, the tests or the script's output, apart from what the script prints and `docs/COLOR_PATCHES.md` reports per patch as a factual result: the closest reference color's name, its set, its 8-bit sRGB hex (the "Its sRGB" column, one value per patch, 30 in all) and the ΔE00. The "Nudged patches" table there also names the reference colors the first design was too close to. `docs/COLOR_PATCHES.md` is repository documentation and is not in the npm package. The 30 hex values are attributed under its Result table: 7 ColorChecker and 6 RAL Classic values copied unchanged from the Wikipedia tables (CC BY-SA 4.0, credited with the title, authors, revision link and licence link that CC BY-SA 4.0 §3(a) asks for), 1 ColorChecker value converted to sRGB by the script from colour-science 0.4.6's CIELAB data (BSD-3-Clause, by the Colour Developers), 2 CSS named colors (W3C) and 14 Simple Icons brand colors (CC0 1.0). The patch names and colors are Photivra's own; the check only confirms that they differ. |
|
|
95
101
|
| Daylight-locus polynomial and M₁/M₂ coefficients; illuminant A formula constants; CIELAB, CIE94 and CIEDE2000 formula constants (`illuminants.ts`, `color-difference.ts`) | CIE 15:2018 (daylight and illuminant A constants; values checked against the free CIE datasets); CIELAB as in W3C CSS Color 4; CIE94 as stated in Wikipedia, "Color difference" (secondary source; the primary CIE 116-1995 is not free); Sharma, Wu & Dalal (2005), also CIE 142-2001 | Coefficients of standardized equations, used as factual constants. |
|
|
96
102
|
| McCamy CCT cubic | McCamy (1992), Color Res. Appl. 17(2) | Published equation coefficients. |
|
|
97
103
|
| Bradford and CAT16 matrices (`chromatic-adaptation.ts`) | Lam (1985) / ICC.1:2010 Annex E; CAT16: Li et al. (2016), IS&T CIC24, Eq. (15), doi:10.2352/ISSN.2169-2629.2017.32.208 (free; checked 2026-10-17 to match the engine; journal version Li et al. 2017) | Published 3×3 model constants. CAT02, the Hunt–Pointer–Estévez matrix and the CIECAM02 degree-of-adaptation formula were removed from 2.0 (owner decisions, 2026-10-17) because no confirmed free source prints them. |
|
|
@@ -33,7 +33,8 @@ import { calculatedResult } from "../core/calculation-result.js";
|
|
|
33
33
|
import { InvalidScientificInputError, requirePositiveFinite } from "../core/validation.js";
|
|
34
34
|
import { rejectUnknownFields, requireInputKeys } from "../core/record-validation.js";
|
|
35
35
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
36
|
-
export const LENS_PROJECTION_MODELS =
|
|
36
|
+
export const LENS_PROJECTION_MODELS =
|
|
37
|
+
/* @__PURE__ */ Object.freeze([
|
|
37
38
|
"rectilinear",
|
|
38
39
|
"fisheye-equidistant",
|
|
39
40
|
"fisheye-equisolid-angle",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lens-projection.js","sourceRoot":"","sources":["../../src/camera/lens-projection.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Ideal lens projection (mapping) functions. They convert between a scene direction and a point on the\n * image plane.\n *\n * A radial projection maps the field angle theta (the angle from the optical axis) to the image\n * radius r:\n * rectilinear r = f tan(theta) (gnomonic / pinhole)\n * equidistant r = f theta (\"f-theta\" fisheye)\n * equisolid-angle r = 2 f sin(theta / 2) (equal-area fisheye)\n * stereographic r = 2 f tan(theta / 2) (conformal fisheye)\n * orthographic r = f sin(theta)\n * The image point keeps the direction's azimuth about the optical axis, so every radial projection is\n * rotationally symmetric. Equirectangular (spherical, or cylindrical equidistant) is not radial:\n * x = f * longitude, y = f * latitude, where f is the image distance per radian.\n *\n * Sources: Kannala & Brandt 2006 (IEEE TPAMI 28:1335, generic camera model listing these\n * projections, eqs. 1-5; free author copy); Miyamoto 1964 (JOSA 54:1060, fish-eye projection\n * functions); Snyder 1987 (Map Projections - A Working Manual, USGS PP 1395, free) for the\n * equirectangular and stereographic projections. Also see: Kingslake 1989 (A History of the\n * Photographic Lens, ch. on fish-eye lenses); Ray 2002 (Applied Photographic Optics, 3rd ed., ch. 6\n * and 47).\n *\n * These are ideal mapping functions. Real fisheye lenses deviate from them. Modelling a real lens\n * would need a calibrated polynomial (for example Kannala-Brandt) with defensible provenance.\n *\n * Camera frame: +X right, +Y up, +Z along the optical axis toward the scene. Image-plane coordinates\n * use the same non-inverted +X right / +Y up convention as the rest of the engine.\n * @see docs/PHYSICS_FOUNDATION.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport {\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveFinite\n} from \"../core/validation.js\";\nimport { rejectUnknownFields, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\n/** Ideal projection model discriminator. `rectilinear` is the engine default. */\nexport type LensProjectionModel =\n | \"rectilinear\"\n | \"fisheye-equidistant\"\n | \"fisheye-equisolid-angle\"\n | \"fisheye-stereographic\"\n | \"fisheye-orthographic\"\n | \"equirectangular\";\n\n/** Radial (rotationally symmetric) projection models. */\nexport type RadialLensProjectionModel = Exclude<\n LensProjectionModel,\n \"equirectangular\"\n>;\n\nexport const LENS_PROJECTION_MODELS: readonly LensProjectionModel[] =\n Object.freeze([\n \"rectilinear\",\n \"fisheye-equidistant\",\n \"fisheye-equisolid-angle\",\n \"fisheye-stereographic\",\n \"fisheye-orthographic\",\n \"equirectangular\"\n ]);\n\nexport interface LensDirection {\n /** Camera-frame direction component, +X right. Any positive length. */\n x: number;\n /** Camera-frame direction component, +Y up. */\n y: number;\n /** Camera-frame direction component along the optical axis toward the scene. */\n z: number;\n}\n\nexport interface CalculateLensProjectionImagePointInput {\n projectionModel: LensProjectionModel;\n /**\n * Projection scale in millimetres: the focal length f of the mapping function, or for\n * equirectangular the image distance per radian.\n */\n focalLengthMm: number;\n /** Scene direction in the camera frame. Need not be normalized; must be non-zero. */\n direction: LensDirection;\n}\n\nexport interface LensProjectionImagePoint {\n projectionModel: LensProjectionModel;\n imagePointMm: { x: number; y: number };\n /** Angle between the direction and the optical axis, in radians. */\n fieldAngleRadians: number;\n /** Distance of the image point from the optical axis, in millimetres. */\n imageRadiusMm: number;\n}\n\nexport interface CalculateLensProjectionDirectionInput {\n projectionModel: LensProjectionModel;\n focalLengthMm: number;\n /** Image-plane point relative to the optical axis, in millimetres. */\n imagePointMm: { x: number; y: number };\n}\n\nexport interface LensProjectionDirection {\n projectionModel: LensProjectionModel;\n /** Unit direction in the camera frame. */\n direction: LensDirection;\n fieldAngleRadians: number;\n imageRadiusMm: number;\n}\n\nexport interface CalculateLensProjectionFieldOfViewInput {\n projectionModel: LensProjectionModel;\n focalLengthMm: number;\n /** Centred sensor/capture dimension along one axis through the optical axis, in mm. */\n sensorDimensionMm: number;\n}\n\nexport interface CalculateLensProjectionFieldOfViewBoundsInput {\n projectionModel: LensProjectionModel;\n focalLengthMm: number;\n /** Signed minimum sensor coordinate along one axis through the optical axis, in mm. */\n minimumSensorCoordinateMm: number;\n /** Signed maximum sensor coordinate along the same axis, in mm. */\n maximumSensorCoordinateMm: number;\n}\n\nexport interface LensProjectionFieldOfView {\n projectionModel: LensProjectionModel;\n degrees: number;\n radians: number;\n /** Projection scale used by the mapping, in millimetres. */\n projectionDistanceMm: number;\n}\n\nexport interface LensProjectionFieldOfViewBounds {\n projectionModel: LensProjectionModel;\n minimumRadians: number;\n maximumRadians: number;\n radians: number;\n minimumDegrees: number;\n maximumDegrees: number;\n degrees: number;\n projectionDistanceMm: number;\n}\n\nconst HALF_PI = Math.PI / 2;\n\n/** Runtime parser for the projection discriminator. Rejects (fails closed on) unknown values. */\nexport function parseLensProjectionModel(\n value: unknown\n): LensProjectionModel {\n if (\n typeof value !== \"string\" ||\n !(LENS_PROJECTION_MODELS as readonly string[]).includes(value)\n ) {\n throw new InvalidConfigurationError(\n \"projectionModel must be one of \" +\n LENS_PROJECTION_MODELS.join(\", \") +\n \".\"\n );\n }\n return value as LensProjectionModel;\n}\n\nfunction requireRecord(name: string, value: unknown): void {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new InvalidConfigurationError(`${name} must be an object.`);\n }\n}\n\nfunction requireFinite(name: string, value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new InvalidScientificInputError(`${name} must be a finite number.`);\n }\n return value;\n}\n\n/**\n * Largest field angle (radians) a radial model maps, and whether the model can reach it.\n * Rectilinear, stereographic, equidistant and equisolid exclude the limit: the radius there is\n * infinite, or the azimuth of the backward direction is undefined. Orthographic reaches 90 degrees.\n */\nfunction radialFieldAngleLimit(model: RadialLensProjectionModel): {\n radians: number;\n inclusive: boolean;\n} {\n switch (model) {\n case \"rectilinear\":\n return { radians: HALF_PI, inclusive: false };\n case \"fisheye-orthographic\":\n return { radians: HALF_PI, inclusive: true };\n default:\n return { radians: Math.PI, inclusive: false };\n }\n}\n\n/** Image radius r(theta) for one radial model. */\nexport function radialProjectionImageRadius(\n model: RadialLensProjectionModel,\n focalLengthMm: number,\n fieldAngleRadians: number\n): number {\n switch (model) {\n case \"rectilinear\":\n return focalLengthMm * Math.tan(fieldAngleRadians);\n case \"fisheye-equidistant\":\n return focalLengthMm * fieldAngleRadians;\n case \"fisheye-equisolid-angle\":\n return 2 * focalLengthMm * Math.sin(fieldAngleRadians / 2);\n case \"fisheye-stereographic\":\n return 2 * focalLengthMm * Math.tan(fieldAngleRadians / 2);\n case \"fisheye-orthographic\":\n return focalLengthMm * Math.sin(fieldAngleRadians);\n }\n}\n\n/**\n * Largest image radius a radial model reaches inside its field-angle domain, in mm\n * (Infinity for rectilinear and stereographic).\n */\nfunction radialImageRadiusLimit(\n model: RadialLensProjectionModel,\n focalLengthMm: number\n): { radiusMm: number; inclusive: boolean } {\n switch (model) {\n case \"rectilinear\":\n case \"fisheye-stereographic\":\n return { radiusMm: Number.POSITIVE_INFINITY, inclusive: false };\n case \"fisheye-equidistant\":\n return { radiusMm: Math.PI * focalLengthMm, inclusive: false };\n case \"fisheye-equisolid-angle\":\n return { radiusMm: 2 * focalLengthMm, inclusive: false };\n case \"fisheye-orthographic\":\n return { radiusMm: focalLengthMm, inclusive: true };\n }\n}\n\n/** Inverse theta(r) for one radial model; throws outside the image circle. */\nexport function radialProjectionFieldAngle(\n model: RadialLensProjectionModel,\n focalLengthMm: number,\n imageRadiusMm: number\n): number {\n const limit = radialImageRadiusLimit(model, focalLengthMm);\n if (\n imageRadiusMm < 0 ||\n imageRadiusMm > limit.radiusMm ||\n (!limit.inclusive && imageRadiusMm === limit.radiusMm)\n ) {\n throw new InvalidScientificInputError(\n `Image radius lies outside the ${model} image circle.`\n );\n }\n switch (model) {\n case \"rectilinear\":\n return Math.atan(imageRadiusMm / focalLengthMm);\n case \"fisheye-equidistant\":\n return imageRadiusMm / focalLengthMm;\n case \"fisheye-equisolid-angle\":\n return 2 * Math.asin(imageRadiusMm / (2 * focalLengthMm));\n case \"fisheye-stereographic\":\n return 2 * Math.atan(imageRadiusMm / (2 * focalLengthMm));\n case \"fisheye-orthographic\":\n return Math.asin(Math.min(1, imageRadiusMm / focalLengthMm));\n }\n}\n\nfunction assumptions(model: LensProjectionModel): readonly string[] {\n const common = [\n \"Ideal mapping function of a perfect projection; real lenses deviate from it and need calibrated distortion data\",\n \"Camera frame +X right, +Y up, +Z toward the scene; image plane +X right, +Y up (non-inverted)\"\n ];\n if (model === \"equirectangular\") {\n return [\n \"Equirectangular (spherical) projection: x = f * longitude, y = f * latitude, longitude measured about +Y from +Z\",\n ...common\n ];\n }\n return [\n `Radial projection ${model}; image azimuth equals scene azimuth about the optical axis`,\n ...common\n ];\n}\n\nfunction validateCommon(\n input: { projectionModel: unknown; focalLengthMm: number },\n name: string\n): LensProjectionModel {\n requireRecord(name, input);\n const model = parseLensProjectionModel(input.projectionModel);\n requirePositiveFinite(\"focalLengthMm\", input.focalLengthMm);\n return model;\n}\n\n/**\n * Maps one camera-frame scene direction to its ideal image-plane point under the declared\n * projection model.\n */\nexport function calculateLensProjectionImagePoint(\n input: CalculateLensProjectionImagePointInput\n): CalculationResult<LensProjectionImagePoint> {\n requireInputKeys(input, \"calculateLensProjectionImagePoint\", [\"projectionModel\", \"focalLengthMm\", \"direction\"]);\n const model = validateCommon(input, \"input\");\n requireRecord(\"direction\", input.direction);\n rejectUnknownFields(input.direction, [\"x\", \"y\", \"z\"], \"direction\");\n const x = requireFinite(\"direction.x\", input.direction.x);\n const y = requireFinite(\"direction.y\", input.direction.y);\n const z = requireFinite(\"direction.z\", input.direction.z);\n const length = Math.hypot(x, y, z);\n if (!(length > 0) || !Number.isFinite(length)) {\n throw new InvalidScientificInputError(\n \"direction must be a finite non-zero vector.\"\n );\n }\n const { imagePointMm, fieldAngleRadians } = lensProjectionImagePointValue(\n model,\n input.focalLengthMm,\n x,\n y,\n z\n );\n\n return calculatedResult(\n {\n projectionModel: model,\n imagePointMm,\n fieldAngleRadians,\n imageRadiusMm: Math.hypot(imagePointMm.x, imagePointMm.y)\n },\n `${model}-lens-projection`,\n \"1.0.0\",\n assumptions(model)\n );\n}\n\n/**\n * Inverse-maps one image-plane point to its unit camera-frame scene direction under the declared\n * projection model. It rejects points outside the model's image circle. For equirectangular, it\n * rejects points outside +/-pi longitude and +/-pi/2 latitude.\n */\nexport function calculateLensProjectionDirection(\n input: CalculateLensProjectionDirectionInput\n): CalculationResult<LensProjectionDirection> {\n requireInputKeys(input, \"calculateLensProjectionDirection\", [\"projectionModel\", \"focalLengthMm\", \"imagePointMm\"]);\n const model = validateCommon(input, \"input\");\n requireRecord(\"imagePointMm\", input.imagePointMm);\n rejectUnknownFields(input.imagePointMm, [\"x\", \"y\"], \"imagePointMm\");\n const px = requireFinite(\"imagePointMm.x\", input.imagePointMm.x);\n const py = requireFinite(\"imagePointMm.y\", input.imagePointMm.y);\n const imageRadiusMm = Math.hypot(px, py);\n const direction = lensProjectionDirectionValue(\n model,\n input.focalLengthMm,\n px,\n py\n );\n\n return calculatedResult(\n {\n projectionModel: model,\n direction,\n fieldAngleRadians: Math.atan2(\n Math.hypot(direction.x, direction.y),\n direction.z\n ),\n imageRadiusMm\n },\n `inverse-${model}-lens-projection`,\n \"1.0.0\",\n assumptions(model)\n );\n}\n\n/** Signed angle subtended along one axis through the optical axis for a signed coordinate. */\nfunction signedAxisAngle(\n model: LensProjectionModel,\n focalLengthMm: number,\n coordinateMm: number\n): number {\n if (model === \"equirectangular\") {\n // Along a horizontal or vertical line through the centre the angle is linear in position.\n return coordinateMm / focalLengthMm;\n }\n const theta = radialProjectionFieldAngle(\n model,\n focalLengthMm,\n Math.abs(coordinateMm)\n );\n return coordinateMm < 0 ? -theta : theta;\n}\n\n/**\n * Angular field of view across a centred sensor dimension along one axis through the optical\n * axis: 2 * theta(d / 2) for radial models and d / f for equirectangular.\n *\n * For equirectangular, the result is the longitude span for a horizontal dimension and the latitude\n * span for a vertical one. A sensor dimension larger than a fisheye's image circle is rejected, not\n * clipped.\n */\nexport function calculateLensProjectionFieldOfView(\n input: CalculateLensProjectionFieldOfViewInput\n): CalculationResult<LensProjectionFieldOfView> {\n requireInputKeys(input, \"calculateLensProjectionFieldOfView\", [\"projectionModel\", \"focalLengthMm\", \"sensorDimensionMm\"]);\n const model = validateCommon(input, \"input\");\n requirePositiveFinite(\"sensorDimensionMm\", input.sensorDimensionMm);\n const radians =\n 2 *\n signedAxisAngle(model, input.focalLengthMm, input.sensorDimensionMm / 2);\n if (model === \"equirectangular\" && radians > 2 * Math.PI) {\n throw new InvalidScientificInputError(\n \"sensorDimensionMm exceeds the full 360-degree equirectangular span.\"\n );\n }\n return calculatedResult(\n {\n projectionModel: model,\n radians,\n degrees: (radians * 180) / Math.PI,\n projectionDistanceMm: input.focalLengthMm\n },\n `${model}-field-of-view`,\n \"1.0.0\",\n [\n ...assumptions(model),\n \"Field of view is measured along one axis through the optical axis\"\n ]\n );\n}\n\n/**\n * Asymmetric angular span between two signed sensor coordinates on one axis through the optical\n * axis, for off-centre crops.\n */\nexport function calculateLensProjectionFieldOfViewBounds(\n input: CalculateLensProjectionFieldOfViewBoundsInput\n): CalculationResult<LensProjectionFieldOfViewBounds> {\n requireInputKeys(input, \"calculateLensProjectionFieldOfViewBounds\", [\"projectionModel\", \"focalLengthMm\", \"minimumSensorCoordinateMm\", \"maximumSensorCoordinateMm\"]);\n const model = validateCommon(input, \"input\");\n const minimum = requireFinite(\n \"minimumSensorCoordinateMm\",\n input.minimumSensorCoordinateMm\n );\n const maximum = requireFinite(\n \"maximumSensorCoordinateMm\",\n input.maximumSensorCoordinateMm\n );\n if (maximum <= minimum) {\n throw new InvalidScientificInputError(\n \"maximumSensorCoordinateMm must be greater than minimumSensorCoordinateMm.\"\n );\n }\n const minimumRadians = signedAxisAngle(model, input.focalLengthMm, minimum);\n const maximumRadians = signedAxisAngle(model, input.focalLengthMm, maximum);\n const radians = maximumRadians - minimumRadians;\n if (model === \"equirectangular\" && radians > 2 * Math.PI) {\n throw new InvalidScientificInputError(\n \"Sensor bounds exceed the full 360-degree equirectangular span.\"\n );\n }\n return calculatedResult(\n {\n projectionModel: model,\n minimumRadians,\n maximumRadians,\n radians,\n minimumDegrees: (minimumRadians * 180) / Math.PI,\n maximumDegrees: (maximumRadians * 180) / Math.PI,\n degrees: (radians * 180) / Math.PI,\n projectionDistanceMm: input.focalLengthMm\n },\n `asymmetric-${model}-field-of-view`,\n \"1.0.0\",\n [\n ...assumptions(model),\n \"Signed sensor-plane bounds are measured relative to the optical axis along one axis through it\"\n ]\n );\n}\n\n/**\n * @internal Image point of a non-zero camera-frame direction under one model (no envelope).\n * Shared by the public mapping, geometric remaps and the environment ray query.\n */\nexport function lensProjectionImagePointValue(\n model: LensProjectionModel,\n focalLengthMm: number,\n x: number,\n y: number,\n z: number\n): { imagePointMm: { x: number; y: number }; fieldAngleRadians: number } {\n const length = Math.hypot(x, y, z);\n const f = focalLengthMm;\n const transverse = Math.hypot(x, y);\n const fieldAngleRadians = Math.atan2(transverse, z);\n\n let imagePointMm: { x: number; y: number };\n if (model === \"equirectangular\") {\n const latitude = Math.asin(Math.max(-1, Math.min(1, y / length)));\n const horizontal = Math.hypot(x, z);\n if (horizontal === 0) {\n // Poles have an undefined longitude; the projection is singular there.\n throw new InvalidScientificInputError(\n \"Equirectangular projection is undefined at the poles (direction parallel to +/-Y).\"\n );\n }\n const longitude = Math.atan2(x, z);\n imagePointMm = { x: f * longitude, y: f * latitude };\n } else {\n const limit = radialFieldAngleLimit(model);\n if (\n fieldAngleRadians > limit.radians ||\n (!limit.inclusive && fieldAngleRadians === limit.radians)\n ) {\n throw new InvalidScientificInputError(\n `direction lies outside the ${model} projection domain.`\n );\n }\n const radius = radialProjectionImageRadius(model, f, fieldAngleRadians);\n imagePointMm =\n transverse === 0\n ? { x: 0, y: 0 }\n : { x: (radius * x) / transverse, y: (radius * y) / transverse };\n }\n\n return { imagePointMm, fieldAngleRadians };\n}\n\n/** @internal Unit camera-frame direction of one image point under one model (no envelope). */\nexport function lensProjectionDirectionValue(\n model: LensProjectionModel,\n focalLengthMm: number,\n px: number,\n py: number\n): LensDirection {\n const f = focalLengthMm;\n const imageRadiusMm = Math.hypot(px, py);\n\n let direction: LensDirection;\n if (model === \"equirectangular\") {\n const longitude = px / f;\n const latitude = py / f;\n if (Math.abs(longitude) > Math.PI || Math.abs(latitude) > HALF_PI) {\n throw new InvalidScientificInputError(\n \"imagePointMm lies outside the equirectangular longitude/latitude domain.\"\n );\n }\n const cosLatitude = Math.cos(latitude);\n direction = {\n x: cosLatitude * Math.sin(longitude),\n y: Math.sin(latitude),\n z: cosLatitude * Math.cos(longitude)\n };\n } else {\n const theta = radialProjectionFieldAngle(model, f, imageRadiusMm);\n const sinTheta = Math.sin(theta);\n direction =\n imageRadiusMm === 0\n ? { x: 0, y: 0, z: 1 }\n : {\n x: (sinTheta * px) / imageRadiusMm,\n y: (sinTheta * py) / imageRadiusMm,\n z: Math.cos(theta)\n };\n }\n\n return direction;\n}\n\n/** d r / d theta of a radial model. */\nfunction radialDerivative(\n model: RadialLensProjectionModel,\n f: number,\n theta: number\n): number {\n switch (model) {\n case \"rectilinear\":\n return f / Math.cos(theta) ** 2;\n case \"fisheye-equidistant\":\n return f;\n case \"fisheye-equisolid-angle\":\n return f * Math.cos(theta / 2);\n case \"fisheye-stereographic\":\n return f / Math.cos(theta / 2) ** 2;\n case \"fisheye-orthographic\":\n return f * Math.cos(theta);\n }\n}\n\ntype Matrix32 = readonly [readonly [number, number], readonly [number, number], readonly [number, number]];\n\n/** Unit direction of an image point and its derivative d(direction)/d(point) (3 x 2). */\nfunction directionWithDerivative(\n model: LensProjectionModel,\n f: number,\n px: number,\n py: number\n): { direction: LensDirection; derivative: Matrix32 } {\n const direction = lensProjectionDirectionValue(model, f, px, py);\n if (model === \"equirectangular\") {\n const lambda = px / f;\n const phi = py / f;\n const cp = Math.cos(phi);\n const sp = Math.sin(phi);\n const cl = Math.cos(lambda);\n const sl = Math.sin(lambda);\n return {\n direction,\n derivative: [\n [(cp * cl) / f, (-sp * sl) / f],\n [0, cp / f],\n [(-cp * sl) / f, (-sp * cl) / f]\n ]\n };\n }\n const rho = Math.hypot(px, py);\n if (rho === 0) {\n const a = 1 / radialDerivative(model, f, 0);\n return { direction, derivative: [[a, 0], [0, a], [0, 0]] };\n }\n const theta = radialProjectionFieldAngle(model, f, rho);\n const dTheta = 1 / radialDerivative(model, f, theta);\n const a = Math.sin(theta) / rho;\n const aPrime = (Math.cos(theta) * dTheta * rho - Math.sin(theta)) / (rho * rho);\n const ux = px / rho;\n const uy = py / rho;\n const dz = -Math.sin(theta) * dTheta;\n return {\n direction,\n derivative: [\n [a + aPrime * px * ux, aPrime * px * uy],\n [aPrime * py * ux, a + aPrime * py * uy],\n [dz * ux, dz * uy]\n ]\n };\n}\n\n/** Image point of a unit direction and its derivative d(point)/d(direction) (2 x 3). */\nfunction imagePointWithDerivative(\n model: LensProjectionModel,\n f: number,\n v: LensDirection\n): { point: { x: number; y: number }; derivative: readonly [readonly [number, number, number], readonly [number, number, number]] } {\n const point = lensProjectionImagePointValue(model, f, v.x, v.y, v.z).imagePointMm;\n if (model === \"equirectangular\") {\n const h2 = v.x * v.x + v.z * v.z;\n const c = Math.sqrt(Math.max(0, 1 - v.y * v.y));\n return {\n point,\n derivative: [\n [(f * v.z) / h2, 0, (-f * v.x) / h2],\n [0, f / c, 0]\n ]\n };\n }\n const t = Math.hypot(v.x, v.y);\n const theta = Math.atan2(t, v.z);\n if (theta < 1e-9) {\n const g = radialDerivative(model, f, 0);\n return { point, derivative: [[g, 0, 0], [0, g, 0]] };\n }\n const s = Math.sin(theta);\n const g = radialProjectionImageRadius(model, f, theta);\n const b = g / s;\n const bPrime = (radialDerivative(model, f, theta) * s - g * Math.cos(theta)) / (s * s);\n // q = b(theta) (vx, vy); on the unit sphere d theta = -d vz / sin theta.\n const k = -bPrime / s;\n return {\n point,\n derivative: [\n [b, 0, k * v.x],\n [0, b, k * v.y]\n ]\n };\n}\n\n/**\n * @internal Destination-to-source remap between two ideal projections: the destination point is\n * unprojected with the destination model and reprojected with the source model. Returns the\n * source point and the 2 x 2 Jacobian d(source)/d(destination), row-major.\n */\nexport function lensProjectionRemapValue(\n destinationModel: LensProjectionModel,\n destinationFocalLengthMm: number,\n sourceModel: LensProjectionModel,\n sourceFocalLengthMm: number,\n px: number,\n py: number\n): { sourcePointMm: { x: number; y: number }; jacobian: readonly [number, number, number, number] } {\n const inverse = directionWithDerivative(destinationModel, destinationFocalLengthMm, px, py);\n const forward = imagePointWithDerivative(sourceModel, sourceFocalLengthMm, inverse.direction);\n const d = inverse.derivative;\n const q = forward.derivative;\n const j = (row: 0 | 1, column: 0 | 1): number =>\n q[row][0] * d[0][column] + q[row][1] * d[1][column] + q[row][2] * d[2][column];\n return {\n sourcePointMm: forward.point,\n jacobian: [j(0, 0), j(0, 1), j(1, 0), j(1, 1)]\n };\n}\n\n/** @internal Signed angle along one axis through the optical axis (exported for sampling plans). */\nexport function lensProjectionSignedAxisAngle(\n model: LensProjectionModel,\n focalLengthMm: number,\n coordinateMm: number\n): number {\n return signedAxisAngle(model, focalLengthMm, coordinateMm);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lens-projection.js","sourceRoot":"","sources":["../../src/camera/lens-projection.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Ideal lens projection (mapping) functions. They convert between a scene direction and a point on the\n * image plane.\n *\n * A radial projection maps the field angle theta (the angle from the optical axis) to the image\n * radius r:\n * rectilinear r = f tan(theta) (gnomonic / pinhole)\n * equidistant r = f theta (\"f-theta\" fisheye)\n * equisolid-angle r = 2 f sin(theta / 2) (equal-area fisheye)\n * stereographic r = 2 f tan(theta / 2) (conformal fisheye)\n * orthographic r = f sin(theta)\n * The image point keeps the direction's azimuth about the optical axis, so every radial projection is\n * rotationally symmetric. Equirectangular (spherical, or cylindrical equidistant) is not radial:\n * x = f * longitude, y = f * latitude, where f is the image distance per radian.\n *\n * Sources: Kannala & Brandt 2006 (IEEE TPAMI 28:1335, generic camera model listing these\n * projections, eqs. 1-5; free author copy); Miyamoto 1964 (JOSA 54:1060, fish-eye projection\n * functions); Snyder 1987 (Map Projections - A Working Manual, USGS PP 1395, free) for the\n * equirectangular and stereographic projections. Also see: Kingslake 1989 (A History of the\n * Photographic Lens, ch. on fish-eye lenses); Ray 2002 (Applied Photographic Optics, 3rd ed., ch. 6\n * and 47).\n *\n * These are ideal mapping functions. Real fisheye lenses deviate from them. Modelling a real lens\n * would need a calibrated polynomial (for example Kannala-Brandt) with defensible provenance.\n *\n * Camera frame: +X right, +Y up, +Z along the optical axis toward the scene. Image-plane coordinates\n * use the same non-inverted +X right / +Y up convention as the rest of the engine.\n * @see docs/PHYSICS_FOUNDATION.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport {\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveFinite\n} from \"../core/validation.js\";\nimport { rejectUnknownFields, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\n/** Ideal projection model discriminator. `rectilinear` is the engine default. */\nexport type LensProjectionModel =\n | \"rectilinear\"\n | \"fisheye-equidistant\"\n | \"fisheye-equisolid-angle\"\n | \"fisheye-stereographic\"\n | \"fisheye-orthographic\"\n | \"equirectangular\";\n\n/** Radial (rotationally symmetric) projection models. */\nexport type RadialLensProjectionModel = Exclude<\n LensProjectionModel,\n \"equirectangular\"\n>;\n\nexport const LENS_PROJECTION_MODELS: readonly LensProjectionModel[] =\n /* @__PURE__ */ Object.freeze([\n \"rectilinear\",\n \"fisheye-equidistant\",\n \"fisheye-equisolid-angle\",\n \"fisheye-stereographic\",\n \"fisheye-orthographic\",\n \"equirectangular\"\n ]);\n\nexport interface LensDirection {\n /** Camera-frame direction component, +X right. Any positive length. */\n x: number;\n /** Camera-frame direction component, +Y up. */\n y: number;\n /** Camera-frame direction component along the optical axis toward the scene. */\n z: number;\n}\n\nexport interface CalculateLensProjectionImagePointInput {\n projectionModel: LensProjectionModel;\n /**\n * Projection scale in millimetres: the focal length f of the mapping function, or for\n * equirectangular the image distance per radian.\n */\n focalLengthMm: number;\n /** Scene direction in the camera frame. Need not be normalized; must be non-zero. */\n direction: LensDirection;\n}\n\nexport interface LensProjectionImagePoint {\n projectionModel: LensProjectionModel;\n imagePointMm: { x: number; y: number };\n /** Angle between the direction and the optical axis, in radians. */\n fieldAngleRadians: number;\n /** Distance of the image point from the optical axis, in millimetres. */\n imageRadiusMm: number;\n}\n\nexport interface CalculateLensProjectionDirectionInput {\n projectionModel: LensProjectionModel;\n focalLengthMm: number;\n /** Image-plane point relative to the optical axis, in millimetres. */\n imagePointMm: { x: number; y: number };\n}\n\nexport interface LensProjectionDirection {\n projectionModel: LensProjectionModel;\n /** Unit direction in the camera frame. */\n direction: LensDirection;\n fieldAngleRadians: number;\n imageRadiusMm: number;\n}\n\nexport interface CalculateLensProjectionFieldOfViewInput {\n projectionModel: LensProjectionModel;\n focalLengthMm: number;\n /** Centred sensor/capture dimension along one axis through the optical axis, in mm. */\n sensorDimensionMm: number;\n}\n\nexport interface CalculateLensProjectionFieldOfViewBoundsInput {\n projectionModel: LensProjectionModel;\n focalLengthMm: number;\n /** Signed minimum sensor coordinate along one axis through the optical axis, in mm. */\n minimumSensorCoordinateMm: number;\n /** Signed maximum sensor coordinate along the same axis, in mm. */\n maximumSensorCoordinateMm: number;\n}\n\nexport interface LensProjectionFieldOfView {\n projectionModel: LensProjectionModel;\n degrees: number;\n radians: number;\n /** Projection scale used by the mapping, in millimetres. */\n projectionDistanceMm: number;\n}\n\nexport interface LensProjectionFieldOfViewBounds {\n projectionModel: LensProjectionModel;\n minimumRadians: number;\n maximumRadians: number;\n radians: number;\n minimumDegrees: number;\n maximumDegrees: number;\n degrees: number;\n projectionDistanceMm: number;\n}\n\nconst HALF_PI = Math.PI / 2;\n\n/** Runtime parser for the projection discriminator. Rejects (fails closed on) unknown values. */\nexport function parseLensProjectionModel(\n value: unknown\n): LensProjectionModel {\n if (\n typeof value !== \"string\" ||\n !(LENS_PROJECTION_MODELS as readonly string[]).includes(value)\n ) {\n throw new InvalidConfigurationError(\n \"projectionModel must be one of \" +\n LENS_PROJECTION_MODELS.join(\", \") +\n \".\"\n );\n }\n return value as LensProjectionModel;\n}\n\nfunction requireRecord(name: string, value: unknown): void {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new InvalidConfigurationError(`${name} must be an object.`);\n }\n}\n\nfunction requireFinite(name: string, value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new InvalidScientificInputError(`${name} must be a finite number.`);\n }\n return value;\n}\n\n/**\n * Largest field angle (radians) a radial model maps, and whether the model can reach it.\n * Rectilinear, stereographic, equidistant and equisolid exclude the limit: the radius there is\n * infinite, or the azimuth of the backward direction is undefined. Orthographic reaches 90 degrees.\n */\nfunction radialFieldAngleLimit(model: RadialLensProjectionModel): {\n radians: number;\n inclusive: boolean;\n} {\n switch (model) {\n case \"rectilinear\":\n return { radians: HALF_PI, inclusive: false };\n case \"fisheye-orthographic\":\n return { radians: HALF_PI, inclusive: true };\n default:\n return { radians: Math.PI, inclusive: false };\n }\n}\n\n/** Image radius r(theta) for one radial model. */\nexport function radialProjectionImageRadius(\n model: RadialLensProjectionModel,\n focalLengthMm: number,\n fieldAngleRadians: number\n): number {\n switch (model) {\n case \"rectilinear\":\n return focalLengthMm * Math.tan(fieldAngleRadians);\n case \"fisheye-equidistant\":\n return focalLengthMm * fieldAngleRadians;\n case \"fisheye-equisolid-angle\":\n return 2 * focalLengthMm * Math.sin(fieldAngleRadians / 2);\n case \"fisheye-stereographic\":\n return 2 * focalLengthMm * Math.tan(fieldAngleRadians / 2);\n case \"fisheye-orthographic\":\n return focalLengthMm * Math.sin(fieldAngleRadians);\n }\n}\n\n/**\n * Largest image radius a radial model reaches inside its field-angle domain, in mm\n * (Infinity for rectilinear and stereographic).\n */\nfunction radialImageRadiusLimit(\n model: RadialLensProjectionModel,\n focalLengthMm: number\n): { radiusMm: number; inclusive: boolean } {\n switch (model) {\n case \"rectilinear\":\n case \"fisheye-stereographic\":\n return { radiusMm: Number.POSITIVE_INFINITY, inclusive: false };\n case \"fisheye-equidistant\":\n return { radiusMm: Math.PI * focalLengthMm, inclusive: false };\n case \"fisheye-equisolid-angle\":\n return { radiusMm: 2 * focalLengthMm, inclusive: false };\n case \"fisheye-orthographic\":\n return { radiusMm: focalLengthMm, inclusive: true };\n }\n}\n\n/** Inverse theta(r) for one radial model; throws outside the image circle. */\nexport function radialProjectionFieldAngle(\n model: RadialLensProjectionModel,\n focalLengthMm: number,\n imageRadiusMm: number\n): number {\n const limit = radialImageRadiusLimit(model, focalLengthMm);\n if (\n imageRadiusMm < 0 ||\n imageRadiusMm > limit.radiusMm ||\n (!limit.inclusive && imageRadiusMm === limit.radiusMm)\n ) {\n throw new InvalidScientificInputError(\n `Image radius lies outside the ${model} image circle.`\n );\n }\n switch (model) {\n case \"rectilinear\":\n return Math.atan(imageRadiusMm / focalLengthMm);\n case \"fisheye-equidistant\":\n return imageRadiusMm / focalLengthMm;\n case \"fisheye-equisolid-angle\":\n return 2 * Math.asin(imageRadiusMm / (2 * focalLengthMm));\n case \"fisheye-stereographic\":\n return 2 * Math.atan(imageRadiusMm / (2 * focalLengthMm));\n case \"fisheye-orthographic\":\n return Math.asin(Math.min(1, imageRadiusMm / focalLengthMm));\n }\n}\n\nfunction assumptions(model: LensProjectionModel): readonly string[] {\n const common = [\n \"Ideal mapping function of a perfect projection; real lenses deviate from it and need calibrated distortion data\",\n \"Camera frame +X right, +Y up, +Z toward the scene; image plane +X right, +Y up (non-inverted)\"\n ];\n if (model === \"equirectangular\") {\n return [\n \"Equirectangular (spherical) projection: x = f * longitude, y = f * latitude, longitude measured about +Y from +Z\",\n ...common\n ];\n }\n return [\n `Radial projection ${model}; image azimuth equals scene azimuth about the optical axis`,\n ...common\n ];\n}\n\nfunction validateCommon(\n input: { projectionModel: unknown; focalLengthMm: number },\n name: string\n): LensProjectionModel {\n requireRecord(name, input);\n const model = parseLensProjectionModel(input.projectionModel);\n requirePositiveFinite(\"focalLengthMm\", input.focalLengthMm);\n return model;\n}\n\n/**\n * Maps one camera-frame scene direction to its ideal image-plane point under the declared\n * projection model.\n */\nexport function calculateLensProjectionImagePoint(\n input: CalculateLensProjectionImagePointInput\n): CalculationResult<LensProjectionImagePoint> {\n requireInputKeys(input, \"calculateLensProjectionImagePoint\", [\"projectionModel\", \"focalLengthMm\", \"direction\"]);\n const model = validateCommon(input, \"input\");\n requireRecord(\"direction\", input.direction);\n rejectUnknownFields(input.direction, [\"x\", \"y\", \"z\"], \"direction\");\n const x = requireFinite(\"direction.x\", input.direction.x);\n const y = requireFinite(\"direction.y\", input.direction.y);\n const z = requireFinite(\"direction.z\", input.direction.z);\n const length = Math.hypot(x, y, z);\n if (!(length > 0) || !Number.isFinite(length)) {\n throw new InvalidScientificInputError(\n \"direction must be a finite non-zero vector.\"\n );\n }\n const { imagePointMm, fieldAngleRadians } = lensProjectionImagePointValue(\n model,\n input.focalLengthMm,\n x,\n y,\n z\n );\n\n return calculatedResult(\n {\n projectionModel: model,\n imagePointMm,\n fieldAngleRadians,\n imageRadiusMm: Math.hypot(imagePointMm.x, imagePointMm.y)\n },\n `${model}-lens-projection`,\n \"1.0.0\",\n assumptions(model)\n );\n}\n\n/**\n * Inverse-maps one image-plane point to its unit camera-frame scene direction under the declared\n * projection model. It rejects points outside the model's image circle. For equirectangular, it\n * rejects points outside +/-pi longitude and +/-pi/2 latitude.\n */\nexport function calculateLensProjectionDirection(\n input: CalculateLensProjectionDirectionInput\n): CalculationResult<LensProjectionDirection> {\n requireInputKeys(input, \"calculateLensProjectionDirection\", [\"projectionModel\", \"focalLengthMm\", \"imagePointMm\"]);\n const model = validateCommon(input, \"input\");\n requireRecord(\"imagePointMm\", input.imagePointMm);\n rejectUnknownFields(input.imagePointMm, [\"x\", \"y\"], \"imagePointMm\");\n const px = requireFinite(\"imagePointMm.x\", input.imagePointMm.x);\n const py = requireFinite(\"imagePointMm.y\", input.imagePointMm.y);\n const imageRadiusMm = Math.hypot(px, py);\n const direction = lensProjectionDirectionValue(\n model,\n input.focalLengthMm,\n px,\n py\n );\n\n return calculatedResult(\n {\n projectionModel: model,\n direction,\n fieldAngleRadians: Math.atan2(\n Math.hypot(direction.x, direction.y),\n direction.z\n ),\n imageRadiusMm\n },\n `inverse-${model}-lens-projection`,\n \"1.0.0\",\n assumptions(model)\n );\n}\n\n/** Signed angle subtended along one axis through the optical axis for a signed coordinate. */\nfunction signedAxisAngle(\n model: LensProjectionModel,\n focalLengthMm: number,\n coordinateMm: number\n): number {\n if (model === \"equirectangular\") {\n // Along a horizontal or vertical line through the centre the angle is linear in position.\n return coordinateMm / focalLengthMm;\n }\n const theta = radialProjectionFieldAngle(\n model,\n focalLengthMm,\n Math.abs(coordinateMm)\n );\n return coordinateMm < 0 ? -theta : theta;\n}\n\n/**\n * Angular field of view across a centred sensor dimension along one axis through the optical\n * axis: 2 * theta(d / 2) for radial models and d / f for equirectangular.\n *\n * For equirectangular, the result is the longitude span for a horizontal dimension and the latitude\n * span for a vertical one. A sensor dimension larger than a fisheye's image circle is rejected, not\n * clipped.\n */\nexport function calculateLensProjectionFieldOfView(\n input: CalculateLensProjectionFieldOfViewInput\n): CalculationResult<LensProjectionFieldOfView> {\n requireInputKeys(input, \"calculateLensProjectionFieldOfView\", [\"projectionModel\", \"focalLengthMm\", \"sensorDimensionMm\"]);\n const model = validateCommon(input, \"input\");\n requirePositiveFinite(\"sensorDimensionMm\", input.sensorDimensionMm);\n const radians =\n 2 *\n signedAxisAngle(model, input.focalLengthMm, input.sensorDimensionMm / 2);\n if (model === \"equirectangular\" && radians > 2 * Math.PI) {\n throw new InvalidScientificInputError(\n \"sensorDimensionMm exceeds the full 360-degree equirectangular span.\"\n );\n }\n return calculatedResult(\n {\n projectionModel: model,\n radians,\n degrees: (radians * 180) / Math.PI,\n projectionDistanceMm: input.focalLengthMm\n },\n `${model}-field-of-view`,\n \"1.0.0\",\n [\n ...assumptions(model),\n \"Field of view is measured along one axis through the optical axis\"\n ]\n );\n}\n\n/**\n * Asymmetric angular span between two signed sensor coordinates on one axis through the optical\n * axis, for off-centre crops.\n */\nexport function calculateLensProjectionFieldOfViewBounds(\n input: CalculateLensProjectionFieldOfViewBoundsInput\n): CalculationResult<LensProjectionFieldOfViewBounds> {\n requireInputKeys(input, \"calculateLensProjectionFieldOfViewBounds\", [\"projectionModel\", \"focalLengthMm\", \"minimumSensorCoordinateMm\", \"maximumSensorCoordinateMm\"]);\n const model = validateCommon(input, \"input\");\n const minimum = requireFinite(\n \"minimumSensorCoordinateMm\",\n input.minimumSensorCoordinateMm\n );\n const maximum = requireFinite(\n \"maximumSensorCoordinateMm\",\n input.maximumSensorCoordinateMm\n );\n if (maximum <= minimum) {\n throw new InvalidScientificInputError(\n \"maximumSensorCoordinateMm must be greater than minimumSensorCoordinateMm.\"\n );\n }\n const minimumRadians = signedAxisAngle(model, input.focalLengthMm, minimum);\n const maximumRadians = signedAxisAngle(model, input.focalLengthMm, maximum);\n const radians = maximumRadians - minimumRadians;\n if (model === \"equirectangular\" && radians > 2 * Math.PI) {\n throw new InvalidScientificInputError(\n \"Sensor bounds exceed the full 360-degree equirectangular span.\"\n );\n }\n return calculatedResult(\n {\n projectionModel: model,\n minimumRadians,\n maximumRadians,\n radians,\n minimumDegrees: (minimumRadians * 180) / Math.PI,\n maximumDegrees: (maximumRadians * 180) / Math.PI,\n degrees: (radians * 180) / Math.PI,\n projectionDistanceMm: input.focalLengthMm\n },\n `asymmetric-${model}-field-of-view`,\n \"1.0.0\",\n [\n ...assumptions(model),\n \"Signed sensor-plane bounds are measured relative to the optical axis along one axis through it\"\n ]\n );\n}\n\n/**\n * @internal Image point of a non-zero camera-frame direction under one model (no envelope).\n * Shared by the public mapping, geometric remaps and the environment ray query.\n */\nexport function lensProjectionImagePointValue(\n model: LensProjectionModel,\n focalLengthMm: number,\n x: number,\n y: number,\n z: number\n): { imagePointMm: { x: number; y: number }; fieldAngleRadians: number } {\n const length = Math.hypot(x, y, z);\n const f = focalLengthMm;\n const transverse = Math.hypot(x, y);\n const fieldAngleRadians = Math.atan2(transverse, z);\n\n let imagePointMm: { x: number; y: number };\n if (model === \"equirectangular\") {\n const latitude = Math.asin(Math.max(-1, Math.min(1, y / length)));\n const horizontal = Math.hypot(x, z);\n if (horizontal === 0) {\n // Poles have an undefined longitude; the projection is singular there.\n throw new InvalidScientificInputError(\n \"Equirectangular projection is undefined at the poles (direction parallel to +/-Y).\"\n );\n }\n const longitude = Math.atan2(x, z);\n imagePointMm = { x: f * longitude, y: f * latitude };\n } else {\n const limit = radialFieldAngleLimit(model);\n if (\n fieldAngleRadians > limit.radians ||\n (!limit.inclusive && fieldAngleRadians === limit.radians)\n ) {\n throw new InvalidScientificInputError(\n `direction lies outside the ${model} projection domain.`\n );\n }\n const radius = radialProjectionImageRadius(model, f, fieldAngleRadians);\n imagePointMm =\n transverse === 0\n ? { x: 0, y: 0 }\n : { x: (radius * x) / transverse, y: (radius * y) / transverse };\n }\n\n return { imagePointMm, fieldAngleRadians };\n}\n\n/** @internal Unit camera-frame direction of one image point under one model (no envelope). */\nexport function lensProjectionDirectionValue(\n model: LensProjectionModel,\n focalLengthMm: number,\n px: number,\n py: number\n): LensDirection {\n const f = focalLengthMm;\n const imageRadiusMm = Math.hypot(px, py);\n\n let direction: LensDirection;\n if (model === \"equirectangular\") {\n const longitude = px / f;\n const latitude = py / f;\n if (Math.abs(longitude) > Math.PI || Math.abs(latitude) > HALF_PI) {\n throw new InvalidScientificInputError(\n \"imagePointMm lies outside the equirectangular longitude/latitude domain.\"\n );\n }\n const cosLatitude = Math.cos(latitude);\n direction = {\n x: cosLatitude * Math.sin(longitude),\n y: Math.sin(latitude),\n z: cosLatitude * Math.cos(longitude)\n };\n } else {\n const theta = radialProjectionFieldAngle(model, f, imageRadiusMm);\n const sinTheta = Math.sin(theta);\n direction =\n imageRadiusMm === 0\n ? { x: 0, y: 0, z: 1 }\n : {\n x: (sinTheta * px) / imageRadiusMm,\n y: (sinTheta * py) / imageRadiusMm,\n z: Math.cos(theta)\n };\n }\n\n return direction;\n}\n\n/** d r / d theta of a radial model. */\nfunction radialDerivative(\n model: RadialLensProjectionModel,\n f: number,\n theta: number\n): number {\n switch (model) {\n case \"rectilinear\":\n return f / Math.cos(theta) ** 2;\n case \"fisheye-equidistant\":\n return f;\n case \"fisheye-equisolid-angle\":\n return f * Math.cos(theta / 2);\n case \"fisheye-stereographic\":\n return f / Math.cos(theta / 2) ** 2;\n case \"fisheye-orthographic\":\n return f * Math.cos(theta);\n }\n}\n\ntype Matrix32 = readonly [readonly [number, number], readonly [number, number], readonly [number, number]];\n\n/** Unit direction of an image point and its derivative d(direction)/d(point) (3 x 2). */\nfunction directionWithDerivative(\n model: LensProjectionModel,\n f: number,\n px: number,\n py: number\n): { direction: LensDirection; derivative: Matrix32 } {\n const direction = lensProjectionDirectionValue(model, f, px, py);\n if (model === \"equirectangular\") {\n const lambda = px / f;\n const phi = py / f;\n const cp = Math.cos(phi);\n const sp = Math.sin(phi);\n const cl = Math.cos(lambda);\n const sl = Math.sin(lambda);\n return {\n direction,\n derivative: [\n [(cp * cl) / f, (-sp * sl) / f],\n [0, cp / f],\n [(-cp * sl) / f, (-sp * cl) / f]\n ]\n };\n }\n const rho = Math.hypot(px, py);\n if (rho === 0) {\n const a = 1 / radialDerivative(model, f, 0);\n return { direction, derivative: [[a, 0], [0, a], [0, 0]] };\n }\n const theta = radialProjectionFieldAngle(model, f, rho);\n const dTheta = 1 / radialDerivative(model, f, theta);\n const a = Math.sin(theta) / rho;\n const aPrime = (Math.cos(theta) * dTheta * rho - Math.sin(theta)) / (rho * rho);\n const ux = px / rho;\n const uy = py / rho;\n const dz = -Math.sin(theta) * dTheta;\n return {\n direction,\n derivative: [\n [a + aPrime * px * ux, aPrime * px * uy],\n [aPrime * py * ux, a + aPrime * py * uy],\n [dz * ux, dz * uy]\n ]\n };\n}\n\n/** Image point of a unit direction and its derivative d(point)/d(direction) (2 x 3). */\nfunction imagePointWithDerivative(\n model: LensProjectionModel,\n f: number,\n v: LensDirection\n): { point: { x: number; y: number }; derivative: readonly [readonly [number, number, number], readonly [number, number, number]] } {\n const point = lensProjectionImagePointValue(model, f, v.x, v.y, v.z).imagePointMm;\n if (model === \"equirectangular\") {\n const h2 = v.x * v.x + v.z * v.z;\n const c = Math.sqrt(Math.max(0, 1 - v.y * v.y));\n return {\n point,\n derivative: [\n [(f * v.z) / h2, 0, (-f * v.x) / h2],\n [0, f / c, 0]\n ]\n };\n }\n const t = Math.hypot(v.x, v.y);\n const theta = Math.atan2(t, v.z);\n if (theta < 1e-9) {\n const g = radialDerivative(model, f, 0);\n return { point, derivative: [[g, 0, 0], [0, g, 0]] };\n }\n const s = Math.sin(theta);\n const g = radialProjectionImageRadius(model, f, theta);\n const b = g / s;\n const bPrime = (radialDerivative(model, f, theta) * s - g * Math.cos(theta)) / (s * s);\n // q = b(theta) (vx, vy); on the unit sphere d theta = -d vz / sin theta.\n const k = -bPrime / s;\n return {\n point,\n derivative: [\n [b, 0, k * v.x],\n [0, b, k * v.y]\n ]\n };\n}\n\n/**\n * @internal Destination-to-source remap between two ideal projections: the destination point is\n * unprojected with the destination model and reprojected with the source model. Returns the\n * source point and the 2 x 2 Jacobian d(source)/d(destination), row-major.\n */\nexport function lensProjectionRemapValue(\n destinationModel: LensProjectionModel,\n destinationFocalLengthMm: number,\n sourceModel: LensProjectionModel,\n sourceFocalLengthMm: number,\n px: number,\n py: number\n): { sourcePointMm: { x: number; y: number }; jacobian: readonly [number, number, number, number] } {\n const inverse = directionWithDerivative(destinationModel, destinationFocalLengthMm, px, py);\n const forward = imagePointWithDerivative(sourceModel, sourceFocalLengthMm, inverse.direction);\n const d = inverse.derivative;\n const q = forward.derivative;\n const j = (row: 0 | 1, column: 0 | 1): number =>\n q[row][0] * d[0][column] + q[row][1] * d[1][column] + q[row][2] * d[2][column];\n return {\n sourcePointMm: forward.point,\n jacobian: [j(0, 0), j(0, 1), j(1, 0), j(1, 1)]\n };\n}\n\n/** @internal Signed angle along one axis through the optical axis (exported for sampling plans). */\nexport function lensProjectionSignedAxisAngle(\n model: LensProjectionModel,\n focalLengthMm: number,\n coordinateMm: number\n): number {\n return signedAxisAngle(model, focalLengthMm, coordinateMm);\n}\n"]}
|
|
@@ -22,7 +22,7 @@ import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
|
22
22
|
import { parseLosslessJpeg } from "./lossless-jpeg.js";
|
|
23
23
|
export const DNG_IMPORT_SCHEMA_VERSION = "1.0.0";
|
|
24
24
|
/** Upper bound for `maximumPixels`. 200 MP (megapixels) of 16-bit codes is 400 MB. */
|
|
25
|
-
export const DNG_IMPORT_LIMITS = Object.freeze({ defaultMaximumPixels: 64_000_000, maximumPixels: 200_000_000, defaultMaximumIfds: 64, maximumIfds: 1024 });
|
|
25
|
+
export const DNG_IMPORT_LIMITS = /* @__PURE__ */ Object.freeze({ defaultMaximumPixels: 64_000_000, maximumPixels: 200_000_000, defaultMaximumIfds: 64, maximumIfds: 1024 });
|
|
26
26
|
const TYPE_SIZES = [0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4];
|
|
27
27
|
function fail(message) { throw new InvalidConfigurationError("DNG import: " + message); }
|
|
28
28
|
function limit(value, fallback, max, name) {
|