@photivra/engine 2.14.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -1
- package/CITATION.cff +1 -1
- package/NOTICE +5 -4
- package/README.md +9 -7
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +15 -11
- 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 +15 -4
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/optics/defocus-disc-kernel.d.ts +90 -1
- package/dist/optics/defocus-disc-kernel.js +58 -5
- package/dist/optics/defocus-disc-kernel.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.d.ts +9 -4
- package/dist/optics/diffraction-psf-profile.js +14 -6
- package/dist/optics/diffraction-psf-profile.js.map +1 -1
- package/dist/optics/lens-psf-kernels.d.ts +13 -2
- package/dist/optics/lens-psf-kernels.js +23 -6
- package/dist/optics/lens-psf-kernels.js.map +1 -1
- package/dist/optics/site-diffraction-kernel.d.ts +1 -1
- package/dist/optics/site-diffraction-kernel.js +37 -17
- package/dist/optics/site-diffraction-kernel.js.map +1 -1
- package/dist/output/print-contrast-reference.js +2 -2
- package/dist/output/print-contrast-reference.js.map +1 -1
- package/docs/AGENT_GUIDE.md +1 -0
- package/docs/API_REFERENCE.md +12 -1
- package/docs/QUICK_START.md +20 -4
- package/docs/USAGE.md +17 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +22 -2
- package/docs/api/facade-simulate-capture.md +1 -1
- package/docs/api/optics-defocus-disc-kernel.md +86 -0
- package/docs/api/optics-diffraction-psf-profile.md +6 -3
- package/docs/api/optics-lens-psf-kernels.md +3 -2
- package/llms.txt +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 2.
|
|
3
|
+
## 2.15.0 — unreleased (prepared November 7, 2026)
|
|
4
|
+
|
|
5
|
+
A minor release: `lens.diffraction` at every spectral step (a fix for the app's color chart), and the defocus size limit with a pre-check as root exports (#532, #560). Captures, RAW codes, files and assumptions are the same as 2.14.1 for every request that 2.14.1 renders.
|
|
6
|
+
- **Fixed:** `lens.diffraction` with `sensor.spectralStepNanometers` under 6.25 nm, for example the color chart at the recommended 5 nm.
|
|
7
|
+
- **New:** `DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES` and `calculateDefocusDiscKernelRadius` (#532).
|
|
8
|
+
- **Why minor:** 4 new root exports, and two public limits that accept more input (64 → 400 wavelengths). Nothing is removed and no default changes.
|
|
9
|
+
|
|
10
|
+
- **Fixed: `lens.diffraction` at every spectral step (owner report from the app, 2026-11-06).** The color chart with diffraction failed with "wavelengthsNm must list 1 to 64 wavelengths." The app renders the `color-patches` chart at `sensor.spectralStepNanometers: 5`, as docs/COLOR_PATCHES.md recommends for color work. That step gives the sensor quadrature 80 spectral nodes over 380–780 nm, and `lens.diffraction` builds one Airy kernel per node with `createDiffractionLimitedPsfProfile`, which took at most 64. So every step under 6.25 nm failed with diffraction, on every scene, in `simulateCapture`, `simulateDisplayView`, `prepareDisplayView` and `simulateIdealReference`.
|
|
11
|
+
- **Fix, exact:** `DIFFRACTION_LIMITED_PSF_PROFILE_LIMITS.maximumWavelengths` and `LENS_PSF_CONVOLUTION_KERNELS_MAXIMUM_WAVELENGTHS` are now 400, the node count of the finest allowed step (1 nm). Each node keeps its own kernel at its own wavelength: no kernel is shared, banded or interpolated, and each equals a one-wavelength profile's kernel bit for bit. The refusal messages now state the limit ("… 1 to 400 wavelengths.").
|
|
12
|
+
- **Cost made linear in the node count:** every node's site kernel used to clone and parse the whole profile, O(N²). Now one private clone, parsed once, serves a request's nodes, with the same kernels. At radius 32 that took 80 nodes from 5.1 s to 0.12 s and 400 from 124 s to 1.4 s. The Airy cell-table cache holds 800 tables (was 256), and the capture's profile cache is also limited to the kernel values it could hold before.
|
|
13
|
+
- **Measured** (Apple M5 Pro, Node.js 24.19.0): a 4000 × 2666 color chart capture at 6 µm and f/16 takes 3.9 s without diffraction. With diffraction it takes 4.4 s at 20 nm, 4.3 s at 5 nm (heap 146 MiB against 128 MiB) and 4.5 s at 1 nm (218 MiB). The worst case, `kernelRadiusSamples: 32` at 1 nm on 1200 × 800, takes 35.9 s and 675 MiB. The full table is in docs/PSF_FOUNDATION.md, "Spectral nodes (2.15.0)"; also see docs/QUICK_START.md ("Optics") and docs/COLOR_PATCHES.md.
|
|
14
|
+
- **Tests** (`test/diffraction-spectral-grid.test.ts`): the reproduction through all four entry points; node counts from 1 nm to 100 nm; kernels against one-wavelength profiles; every chart preset and both caller scene kinds at 1 nm and 5 nm on the tabulated and per-site paths; every option that changes how diffraction runs; the display view's GPU Quality kernels (float32 emulation) at 80 nodes; the limits.
|
|
15
|
+
- **Compatibility:** no new export. Two public limits accept more input (64 → 400). Requests that 2.14.1 renders give the same values, because the kernels are bit-identical and only the cache sizes changed.
|
|
16
|
+
|
|
17
|
+
- **The defocus size limit and a pre-check are root exports (#532, for Photivra/apps#154 and Photivra/apps#157).** `lens.defocus: { model: "geometric-disc" }` refuses a disc whose kernel radius is over 64 sites (129 sites across), for example 50 mm f/1.4 focused at 1 m on a plane at 2 m with 6 µm pixels. A caller can now check a request before rendering it, instead of reading the refusal afterwards.
|
|
18
|
+
- **API:** `DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES` (64) and `calculateDefocusDiscKernelRadius({ focalLengthMm, aperture, focusDistanceM, subjectDistanceM, pixelPitchMicrometers })`, which returns a `CalculationResult` (model `defocus-disc-kernel-radius` 1.0.0) with `diameterMicrometers`, `diameterSites`, `kernelRadiusSites` (0 in focus), `maximumKernelRadiusSites`, `maximumDiameterSites` (129), `withinLimit` and `excessKernelRadiusSites`. Pure: no kernel is built and nothing is rendered.
|
|
19
|
+
- **Same rule as the renderer:** it calls the capture's own disc resolution (`resolveDefocusDisc`, from `calculateDefocusCircle` 1.1.0) and the same limit test as `requireDefocusDiscKernelRadius`, which `simulateCapture`, `simulateDisplayView`, `prepareDisplayView` and `simulateIdealReference` run. Pass the f-number the capture uses (in an automatic mode, `exposure.settings.fNumber`). The limit is on the disc alone; with `lens.diffraction` the combined kernel has no further limit. The other refusals of `lens.defocus` (rotation, projection, distortion, vignetting, pupil sampling, a caller PSF) are not checked.
|
|
20
|
+
- **Validation:** unknown fields fail with `InvalidConfigurationError`; a missing, non-finite or non-positive input, or a distance inside the focal length, with `InvalidScientificInputError`.
|
|
21
|
+
- **Tests** (`test/defocus-disc-limit.test.ts`): the limit is 64 and a disc of 129 sites is the largest within it; the closed form and the capture's resolved disc; radius 0 in focus and for a disc inside one site; at two adjacent focus distances (bisected to neighbouring doubles), the inside one renders with radius 64 and the outside one (radius 65, 1 over) is refused by `simulateCapture`, `prepareDisplayView` and `simulateIdealReference`; the same disc as a program-mode capture at its resolved f-number; input validation. `test/root-export-input-errors.test.ts` lists it as a strict calculator.
|
|
22
|
+
- **Compatibility:** 4 new root exports (1,805; runtime exports 611 → 613): `DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES`, `calculateDefocusDiscKernelRadius` and the types `CalculateDefocusDiscKernelRadiusInput` and `DefocusDiscKernelRadius`. None removed or changed; captures, RAW codes, files, assumptions and error messages are unchanged. Docs: `docs/QUICK_START.md` ("Optics", `lens.defocus`), `docs/USAGE.md` ("Depth of field and defocus"), `docs/AGENT_GUIDE.md`, `llms.txt`; API reference regenerated.
|
|
23
|
+
|
|
24
|
+
## 2.14.1 — published November 3, 2026
|
|
25
|
+
|
|
26
|
+
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.
|
|
27
|
+
- **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.
|
|
28
|
+
- **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.
|
|
29
|
+
- **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.*`.
|
|
30
|
+
|
|
31
|
+
## 2.14.0 — published November 1, 2026
|
|
4
32
|
|
|
5
33
|
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`.
|
|
6
34
|
- **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).
|
|
@@ -391,6 +419,7 @@ A minor release: new functions for writing a whole-frame DNG from region parts a
|
|
|
391
419
|
- **Faster preview:** in the app's Quality preview (Chromium, four workers), the quick frame takes 0.7 s instead of 4.0 s, and the fully sharp preview 65 s instead of 170 s.
|
|
392
420
|
- **Faster captures:** a 24 MP chart capture takes 16.4 s instead of 18.5 s, and with AHD 28.5 s instead of 33.6 s (Node, one thread), with the same bytes.
|
|
393
421
|
- **Compatibility:** five new root exports (1,734); no option, default or result field changes. `createDisplayViewExpectedSignal`, which is internal, takes an optional span hint.
|
|
422
|
+
- **Known limit** (line added after release, #400): the Quality noise limit of 2.6.0 remains (#379), unchanged from 2.8.1; 2.9.0 did not fix it, and no release is scheduled for the fix.
|
|
394
423
|
|
|
395
424
|
### Added
|
|
396
425
|
|
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.15.0"
|
|
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.15.0 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_15_0.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.15.0
|
|
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.
|
|
78
|
+
Repository `main` may describe newer, unreleased work. For the stable contract, use the docs bundled in the published package, or the exact `v2.15.0` 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.15.0` (release candidate prepared November 7, 2026; publication is separate)
|
|
367
|
+
- Engine API contract: `2.15.0`
|
|
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.15.0; 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.1**, published on November 3, 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
|
|
|
@@ -93,7 +97,7 @@ The colorimetry modules in `src/color/` (see [docs/COLORIMETRY.md](https://githu
|
|
|
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). |
|
|
95
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`. |
|
|
96
|
-
| 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 Document License); 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 the closest reference color's name, set and ΔE00
|
|
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. |
|
|
97
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. |
|
|
98
102
|
| McCamy CCT cubic | McCamy (1992), Color Res. Appl. 17(2) | Published equation coefficients. |
|
|
99
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. |
|
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,QAAiB,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.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAiB,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.15.0\" as const;\n"]}
|
|
@@ -828,7 +828,7 @@ export interface SimulateDisplayViewOptions {
|
|
|
828
828
|
/** Display pixels, 1 to the capture's output width and height. */
|
|
829
829
|
width: number;
|
|
830
830
|
height: number;
|
|
831
|
-
/** Stratified Monte Carlo samples per axis for each display pixel's expected value (1–32, default
|
|
831
|
+
/** Stratified Monte Carlo samples per axis for each display pixel's expected value (1–32, default 16: 256 real pixels where the display pixel has detail). */
|
|
832
832
|
samplesPerAxis?: number;
|
|
833
833
|
/** "srgb-8" (default) or "srgb-16". */
|
|
834
834
|
format?: "srgb-8" | "srgb-16";
|
|
@@ -204,10 +204,15 @@ const channelResponseCache = new Map();
|
|
|
204
204
|
* DIFFRACTION_PROFILE_CACHE_SIZE, most recently used last). Every region part and detail tile of one request builds the same
|
|
205
205
|
* input. The kept record is private: each call gets its own copy, as mutable as a newly built one, so a caller's change
|
|
206
206
|
* never reaches another call. The copy is made of plain records and arrays (plainDataCopy): the kernel set-up structured-clones
|
|
207
|
-
* the profile once per spectral node, and in a CPU profile of the 96 × 64 Quality tile
|
|
208
|
-
* (24 instead of 12 ms per call) on a deep-frozen record or on a structuredClone copy as on a newly built record or this copy.
|
|
207
|
+
* the profile (once per spectral node before 2.15.0, once per request since), and in a CPU profile of the 96 × 64 Quality tile
|
|
208
|
+
* that clone took about twice as long (24 instead of 12 ms per call) on a deep-frozen record or on a structuredClone copy as on a newly built record or this copy.
|
|
209
|
+
* 2.15.0: profiles may hold up to 400 wavelengths (a 1 nm spectral step), so the cache also keeps at most
|
|
210
|
+
* DIFFRACTION_PROFILE_CACHE_KERNEL_VALUES kernel values in all (wavelengths × (2R + 1)², summed), the most it could hold before
|
|
211
|
+
* (8 profiles of 64 wavelengths at radius 32); the newest profile is always kept.
|
|
209
212
|
*/
|
|
210
213
|
const DIFFRACTION_PROFILE_CACHE_SIZE = 8;
|
|
214
|
+
const DIFFRACTION_PROFILE_CACHE_KERNEL_VALUES = 8 * 64 * 65 * 65;
|
|
215
|
+
const kernelValuesOf = (profile) => profile.wavelengths.length * (2 * profile.kernelRadiusSamples + 1) ** 2;
|
|
211
216
|
const diffractionProfileCache = new Map();
|
|
212
217
|
/** A copy of plain data (records, arrays, primitives; typed arrays are sliced), built from ordinary literals. */
|
|
213
218
|
function plainDataCopy(value) {
|
|
@@ -231,8 +236,14 @@ function cachedDiffractionProfile(input) {
|
|
|
231
236
|
else
|
|
232
237
|
kept = createDiffractionLimitedPsfProfile(input).value;
|
|
233
238
|
diffractionProfileCache.set(key, kept);
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
let kernelValues = 0;
|
|
240
|
+
for (const profile of diffractionProfileCache.values())
|
|
241
|
+
kernelValues += kernelValuesOf(profile);
|
|
242
|
+
while (diffractionProfileCache.size > 1 && (diffractionProfileCache.size > DIFFRACTION_PROFILE_CACHE_SIZE || kernelValues > DIFFRACTION_PROFILE_CACHE_KERNEL_VALUES)) {
|
|
243
|
+
const oldest = diffractionProfileCache.keys().next().value;
|
|
244
|
+
kernelValues -= kernelValuesOf(diffractionProfileCache.get(oldest));
|
|
245
|
+
diffractionProfileCache.delete(oldest);
|
|
246
|
+
}
|
|
236
247
|
// The same fields in the same order as the built record, frozen at the top level like it; `model` is the frozen constant.
|
|
237
248
|
return Object.freeze({ ...kept, profile: plainDataCopy(kept.profile), wavelengths: plainDataCopy(kept.wavelengths), quadrature: plainDataCopy(kept.quadrature) });
|
|
238
249
|
}
|