@photivra/engine 2.2.0 → 2.2.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 +10 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +1 -1
- package/dist/facade/simulate-capture.js +6 -2
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/optics/lens-psf-kernels.d.ts +3 -1
- package/dist/optics/lens-psf-kernels.js +7 -2
- package/dist/optics/lens-psf-kernels.js.map +1 -1
- package/docs/API_REFERENCE.md +1 -1
- package/docs/QUICK_START.md +5 -1
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +2 -2
- package/docs/api/facade-simulate-capture.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 2.2.
|
|
3
|
+
## 2.2.1 — unreleased (prepared October 18, 2026)
|
|
4
|
+
|
|
5
|
+
A patch release: `simulateCapture` `lens.diffraction` no longer fails for about a third of the combinations of real pixel pitches and f-numbers because of a floating-point rounding error in the kernel-to-site mapping, and the diffraction assumption states when its default window is capped at 32 sites. Captures that 2.2.0 completed are byte-identical (RAW codes, images, kernels and assumption values); only the explanation text of capped windows changes. No root export is added or removed (1,680 symbols).
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`simulateCapture` `lens.diffraction` failed for about a third of real pixel pitch and f-number combinations.** For pitches such as 3.808864265927978 µm (44 mm / 11 552 px) or 1.6 µm, a capture with `lens.diffraction` threw "Diffraction kernels must sample the site pitch exactly." or "The kernel radius is too small for the PSF support; no tap is dropped.": 182 of the 545 combinations of the app's 109 pitches with f/4, 5.6, 8, 11 and 16 (19, 24, 41, 42 and 56 per f-number). `resolveLensPsfConvolutionKernels` mapped kernel tap n to the site displacement (n·p)/p with the kernel's sample pitch p equal to the site pitch, and that rounded quotient misses n by one unit in the last place for some pitches (3 · 1.6 / 1.6 = 3.0000000000000004). Just below n the tap was split bilinearly, so the kernel was no longer exactly site-sampled; just above n a weight of about 10⁻¹⁶ landed one site outside the window, so the outer ring needed a radius of R + 1. The facade's radius was never too small for the real support. When the two pitches are the same number, the displacement is now the tap offset itself. Captures that 2.2.0 completed are byte-identical (RAW codes, images, kernels and assumption values; 86 of 86 compared at 27 pitches × 5 f-numbers); the other 49 now complete. `resolveLensPsfConvolutionKernels` called directly with equal pitches now reports exact site sampling where it split a tap by such a rounding error. A test sweeps the app's pitches and 200 seeded pitches in 0.5–100 µm at five f-numbers, both through the kernels and through the facade (aborted after the first RAW tile).
|
|
10
|
+
- **The diffraction assumption claimed the third dark ring when the window was capped.** The default window is capped at 32 sites; for small pixels at large f-numbers (for example 0.5 µm at f/16, which needs 81) the `lens.diffraction` explanation still said the window reached the third dark ring. It now says the window is capped and how many sites the ring would need; the renormalized tail, already listed per wavelength, is larger there (15 % at 0.5 µm and f/16). The quick start documents the cap. Values, kernels and images are unchanged.
|
|
11
|
+
|
|
12
|
+
## 2.2.0 — published October 17, 2026
|
|
4
13
|
|
|
5
14
|
A minor release: opt-in pixel limits for full-resolution images up to 2^27 pixels (about 100 MP sensors) with a measured cost table (#337), and two `simulateCapture` fixes: pixel pitches that failed the aperture fit by a rounding error now work, and image brightness no longer depends on pixel size (#336). Without the new inputs, and at the default 6 µm pitch, every request, plan, RAW, image and encoded file is byte-identical to 2.1.0; at other pitches the brightness fix changes the output by design. Root exports grow from 1,677 to 1,680 symbols; none is removed.
|
|
6
15
|
|
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.2.
|
|
12
|
+
version: "2.2.1"
|
|
13
13
|
keywords:
|
|
14
14
|
- photography
|
|
15
15
|
- camera optics
|
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.2.
|
|
35
|
+
Install the published ESM package below. The [2.2.1 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_2_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.2.
|
|
38
|
+
npm install @photivra/engine@2.2.1
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
Simulate a photograph of a test chart with a generic camera, then read the developed sRGB pixels:
|
|
@@ -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.2.
|
|
78
|
+
Repository `main` may describe newer, unreleased work. For the stable contract, use the docs bundled in the published package, or the exact `v2.2.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.2.
|
|
367
|
-
- Engine API contract: `2.2.
|
|
366
|
+
- Repository package version: `2.2.1` (release candidate prepared October 18, 2026; publication is separate)
|
|
367
|
+
- Engine API contract: `2.2.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.2.
|
|
372
|
+
From 1.0.1, the package version and `ENGINE_API_VERSION` are equal. New captures/plans record 2.2.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
|
|
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.2.0**, published on October 17, 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/dist/core/version.d.ts
CHANGED
package/dist/core/version.js
CHANGED
package/dist/core/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAgB,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Version of the engine release. Since 1.0.1 it equals the npm package version.\n *\n * New captures and plans record this version as their creator. Archived records keep the\n * version they were made with. Proof-of-concept (POC), schema and model contracts have their\n * own versions. Release checks confirm it matches package.json and the packed package.\n * @see docs/RELEASE_1_0_1.md\n */\nexport const ENGINE_API_VERSION = \"2.2.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAgB,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Version of the engine release. Since 1.0.1 it equals the npm package version.\n *\n * New captures and plans record this version as their creator. Archived records keep the\n * version they were made with. Proof-of-concept (POC), schema and model contracts have their\n * own versions. Release checks confirm it matches package.json and the packed package.\n * @see docs/RELEASE_1_0_1.md\n */\nexport const ENGINE_API_VERSION = \"2.2.1\" as const;\n"]}
|
|
@@ -129,7 +129,7 @@ export interface SimulateCaptureOptions {
|
|
|
129
129
|
* Opt-in (2.1) diffraction blur of an ideal, aberration-free, in-focus lens with a circular pupil at the
|
|
130
130
|
* capture's resolved f-number: the engine builds the Airy kernels (`createDiffractionLimitedPsfProfile`)
|
|
131
131
|
* on the site pitch. `kernelRadiusSamples` (0–32) sets the kernel window; default: the third dark ring at
|
|
132
|
-
* the longest spectral node. Not combinable with `psf` or `pupilSampling`. Default off.
|
|
132
|
+
* the longest spectral node, capped at 32 sites. Not combinable with `psf` or `pupilSampling`. Default off.
|
|
133
133
|
*/
|
|
134
134
|
diffraction?: SimulateCaptureLensDiffraction;
|
|
135
135
|
};
|
|
@@ -1029,7 +1029,10 @@ export async function simulateCapture(options) {
|
|
|
1029
1029
|
const lambdaN = wavelengths[wavelengths.length - 1] / 1000 * fNumber * imageDistanceMm / focalLengthMm;
|
|
1030
1030
|
// The environment resolves the PSF at each pre-filter source point, up to the largest split offset outside the imaging area.
|
|
1031
1031
|
const aaReachMm = antiAliasing ? Math.max(...antiAliasing.components.map((c) => Math.max(Math.abs(c.offsetMicrometers.x), Math.abs(c.offsetMicrometers.y)))) / 1000 : 0;
|
|
1032
|
-
|
|
1032
|
+
// The default reach is capped at the 32-site convolution limit; a capped window ends inside the third dark ring, and the
|
|
1033
|
+
// larger tail energy outside it is listed per wavelength like any other (2.2.1 states the cap in the explanation).
|
|
1034
|
+
const thirdRingSites = Math.max(1, Math.ceil(AIRY_THIRD_ZERO_PER_LAMBDA_N * lambdaN / pitch));
|
|
1035
|
+
const kernelRadiusSamples = diffractionRadiusOption ?? Math.min(32, thirdRingSites);
|
|
1033
1036
|
diffraction = createDiffractionLimitedPsfProfile({ aperture: "circular", focalLengthMm, apertureFNumber: fNumber, focus: { kind: "finite", distanceM: focusDistanceM },
|
|
1034
1037
|
wavelengthsNm: wavelengths, samplePitchMicrometers: pitch, kernelRadiusSamples, fieldHalfExtentMm: aaReachMm === 0 ? { x: imagingArea.widthMm / 2, y: imagingArea.heightMm / 2 } :
|
|
1035
1038
|
{ x: imagingArea.widthMm / 2 + aaReachMm, y: imagingArea.heightMm / 2 + aaReachMm },
|
|
@@ -1042,7 +1045,8 @@ export async function simulateCapture(options) {
|
|
|
1042
1045
|
truncatedEnergyByWavelength: diffraction.wavelengths.map((w) => ({ wavelengthNm: w.wavelengthNm, truncatedEnergyFraction: w.truncatedEnergyFraction })),
|
|
1043
1046
|
execution: diffractionObjectSpace ? "object-space-convolution" : "environment-psf-quadrature" }, "Scalar Fraunhofer diffraction of an ideal, aberration-free circular pupil (Airy pattern, createDiffractionLimitedPsfProfile) at the resolved f-number, in focus, with D = f/N and the thin-lens image distance (working f-number N·v/f). " +
|
|
1044
1047
|
"One kernel per spectral node, binned into site-sized cells over a " + (2 * kernelRadiusSamples + 1) + " × " + (2 * kernelRadiusSamples + 1) + " window" +
|
|
1045
|
-
(diffractionRadiusOption
|
|
1048
|
+
(diffractionRadiusOption !== null ? "" : thirdRingSites > 32 ? " (capped at 32 sites; the third dark ring at the longest node needs " + thirdRingSites + ")" :
|
|
1049
|
+
" (reaching the third dark ring at the longest node)") + "; the Airy tail energy outside it (listed per wavelength) is put back by unit-sum renormalization. " +
|
|
1046
1050
|
(diffractionObjectSpace ? "Applied as an object-space convolution: the scene radiance on the site lattice is convolved once per spectral node (calculateIrradianceConvolution) and the provider answers each site query with it, so the site inputs declare no PSF; this equals the per-tap environment PSF quadrature for this still, distortion-free planar scene (request.diffraction.psf). " :
|
|
1047
1051
|
"Applied through the environment PSF quadrature (one scene query per kernel tap), because camera rotation, a lens field mapping or vignetting moves the taps off the site lattice. ") +
|
|
1048
1052
|
"No defocus, aberration or pupil clipping; the sensor pixel aperture is still point-sampled at the site centre.");
|