@photivra/engine 2.13.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +7 -7
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +2 -0
  6. package/dist/camera/lens-projection.js +2 -1
  7. package/dist/camera/lens-projection.js.map +1 -1
  8. package/dist/capture/dng-import.js +1 -1
  9. package/dist/capture/dng-import.js.map +1 -1
  10. package/dist/capture/host-yield-cadence.js +1 -1
  11. package/dist/capture/host-yield-cadence.js.map +1 -1
  12. package/dist/capture/native-environment-optical-stage.js +1 -1
  13. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  14. package/dist/capture/native-raw.js +1 -1
  15. package/dist/capture/native-raw.js.map +1 -1
  16. package/dist/capture/packed-sensor-raw.js +3 -3
  17. package/dist/capture/packed-sensor-raw.js.map +1 -1
  18. package/dist/capture/sensor-raw-producer.js +1 -1
  19. package/dist/capture/sensor-raw-producer.js.map +1 -1
  20. package/dist/color/capture-color.js +3 -3
  21. package/dist/color/capture-color.js.map +1 -1
  22. package/dist/color/chromatic-adaptation.js +2 -2
  23. package/dist/color/chromatic-adaptation.js.map +1 -1
  24. package/dist/color/color-matching-functions.js +1 -1
  25. package/dist/color/color-matching-functions.js.map +1 -1
  26. package/dist/color/colorimetry-math.js +2 -2
  27. package/dist/color/colorimetry-math.js.map +1 -1
  28. package/dist/color/illuminants.js +3 -3
  29. package/dist/color/illuminants.js.map +1 -1
  30. package/dist/color/rgb-color-spaces.js +1 -1
  31. package/dist/color/rgb-color-spaces.js.map +1 -1
  32. package/dist/color/sensor-color-development.js +1 -1
  33. package/dist/color/sensor-color-development.js.map +1 -1
  34. package/dist/core/record-validation.js +3 -3
  35. package/dist/core/record-validation.js.map +1 -1
  36. package/dist/core/version.d.ts +1 -1
  37. package/dist/core/version.js +1 -1
  38. package/dist/core/version.js.map +1 -1
  39. package/dist/equipment/generic-prime-lenses.js +3 -3
  40. package/dist/equipment/generic-prime-lenses.js.map +1 -1
  41. package/dist/equipment/generic-tier-assets.d.ts +23 -1
  42. package/dist/equipment/generic-tier-assets.js +58 -30
  43. package/dist/equipment/generic-tier-assets.js.map +1 -1
  44. package/dist/equipment/generic-tier-presets.d.ts +6 -3
  45. package/dist/equipment/generic-tier-presets.js +26 -13
  46. package/dist/equipment/generic-tier-presets.js.map +1 -1
  47. package/dist/exposure/auto-iso-slowest-shutter.js +2 -1
  48. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  49. package/dist/exposure/flash-ttl.js +1 -1
  50. package/dist/exposure/flash-ttl.js.map +1 -1
  51. package/dist/exposure/flash.js +1 -1
  52. package/dist/exposure/flash.js.map +1 -1
  53. package/dist/facade/chart-material-layers.js +2 -2
  54. package/dist/facade/chart-material-layers.js.map +1 -1
  55. package/dist/facade/chart-radiance-batch.js +1 -1
  56. package/dist/facade/chart-radiance-batch.js.map +1 -1
  57. package/dist/facade/chart-scenes.d.ts +45 -8
  58. package/dist/facade/chart-scenes.js +101 -44
  59. package/dist/facade/chart-scenes.js.map +1 -1
  60. package/dist/facade/color-patches.d.ts +129 -0
  61. package/dist/facade/color-patches.js +209 -0
  62. package/dist/facade/color-patches.js.map +1 -0
  63. package/dist/facade/display-view-gpu.js +1 -1
  64. package/dist/facade/display-view-gpu.js.map +1 -1
  65. package/dist/facade/scene-radiance.js +2 -2
  66. package/dist/facade/scene-radiance.js.map +1 -1
  67. package/dist/facade/simulate-capture.d.ts +47 -3
  68. package/dist/facade/simulate-capture.js +71 -22
  69. package/dist/facade/simulate-capture.js.map +1 -1
  70. package/dist/facade/simulate-display-view.js +2 -2
  71. package/dist/facade/simulate-display-view.js.map +1 -1
  72. package/dist/gpu/chart-composite-gpu.js +2 -2
  73. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  74. package/dist/gpu/display-view-composite-material-module.js +1 -1
  75. package/dist/gpu/display-view-composite-material-module.js.map +1 -1
  76. package/dist/gpu/image-kernel-execution.js +2 -2
  77. package/dist/gpu/image-kernel-execution.js.map +1 -1
  78. package/dist/gpu/temporal-sum-gpu.js +23 -23
  79. package/dist/gpu/webgpu-runtime.js +1 -1
  80. package/dist/gpu/webgpu-runtime.js.map +1 -1
  81. package/dist/gpu/wgsl-chart-composite-kernel.js +318 -318
  82. package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -1
  83. package/dist/gpu/wgsl-chart-kernel.js +117 -117
  84. package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
  85. package/dist/gpu/wgsl-display-view-quality-kernel.js +505 -505
  86. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  87. package/dist/gpu/wgsl-display-view-speed-kernel.js +248 -248
  88. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  89. package/dist/gpu/wgsl-kernels.js +203 -203
  90. package/dist/gpu/wgsl-kernels.js.map +1 -1
  91. package/dist/gpu/wgsl-sensor-kernel.js +302 -302
  92. package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
  93. package/dist/index.d.ts +5 -3
  94. package/dist/index.js +4 -2
  95. package/dist/index.js.map +1 -1
  96. package/dist/motion/temporal-exposure-sum.js +1 -1
  97. package/dist/motion/temporal-exposure-sum.js.map +1 -1
  98. package/dist/optics/diffraction-psf-profile.js +4 -4
  99. package/dist/optics/diffraction-psf-profile.js.map +1 -1
  100. package/dist/optics/front-of-lens-filter.js +1 -1
  101. package/dist/optics/front-of-lens-filter.js.map +1 -1
  102. package/dist/optics/irradiance-convolution.js +1 -1
  103. package/dist/optics/irradiance-convolution.js.map +1 -1
  104. package/dist/output/native-capture-sdr.js +1 -1
  105. package/dist/output/native-capture-sdr.js.map +1 -1
  106. package/dist/output/png-encoder.js +2 -2
  107. package/dist/output/png-encoder.js.map +1 -1
  108. package/dist/output/print-jpeg.js +1 -1
  109. package/dist/output/print-jpeg.js.map +1 -1
  110. package/dist/sensor/fixed-pattern-noise.js +1 -1
  111. package/dist/sensor/fixed-pattern-noise.js.map +1 -1
  112. package/dist/sensor/readout-exposure-linkage.js +1 -1
  113. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  114. package/docs/API_REFERENCE.md +29 -1
  115. package/docs/QUICK_START.md +40 -5
  116. package/docs/USAGE.md +35 -1
  117. package/docs/api/core-version.md +1 -1
  118. package/docs/api/equipment-generic-tier-assets.md +41 -1
  119. package/docs/api/equipment-generic-tier-presets.md +11 -3
  120. package/docs/api/exports.json +107 -2
  121. package/docs/api/facade-chart-scenes.md +36 -5
  122. package/docs/api/facade-color-patches.md +150 -0
  123. package/docs/api/facade-simulate-capture.md +52 -2
  124. package/llms.txt +1 -0
  125. package/package.json +2 -2
@@ -16,10 +16,18 @@ This call is synchronous; any supplied provider must follow its explicit synchro
16
16
 
17
17
  ## GENERIC_EQUIPMENT_TIER_PRESET_VERSION
18
18
 
19
- The exact contract is declared below; use the domain guide for assumptions and staged integration.
19
+ The current preset version (2.14: 2.1.0, third-stop ISO settings).
20
+
21
+ ```ts
22
+ GENERIC_EQUIPMENT_TIER_PRESET_VERSION = "2.1.0" as const
23
+ ```
24
+
25
+ ## GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS
26
+
27
+ Every exact preset version that resolves, oldest first. A version is never retuned in place (`test/generic-tier-presets.test.ts`).
20
28
 
21
29
  ```ts
22
- GENERIC_EQUIPMENT_TIER_PRESET_VERSION = "2.0.0" as const
30
+ GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS = Object.freeze(["2.0.0", "2.1.0"] as const)
23
31
  ```
24
32
 
25
33
  ## GENERIC_EQUIPMENT_TIERS
@@ -44,7 +52,7 @@ The exact contract is declared below; use the domain guide for assumptions and s
44
52
 
45
53
  ```ts
46
54
  export interface GenericEquipmentTierSelection {
47
- schemaVersion: "0.1.0"; presetVersion: typeof GENERIC_EQUIPMENT_TIER_PRESET_VERSION;
55
+ schemaVersion: "0.1.0"; presetVersion: GenericTierPresetVersion;
48
56
  bodyTier: GenericEquipmentTier; lensTier: GenericEquipmentTier;
49
57
  bodyPresetId: string; lensPresetId: string;
50
58
  profileReferences: readonly GenericEquipmentTierProfileReference[];
@@ -1,6 +1,6 @@
1
1
  {
2
- "packageVersion": "2.13.0",
3
- "engineApiVersion": "2.13.0",
2
+ "packageVersion": "2.14.0",
3
+ "engineApiVersion": "2.14.0",
4
4
  "exports": [
5
5
  {
6
6
  "name": "AbsoluteUncertaintyEstimate",
@@ -2077,6 +2077,26 @@
2077
2077
  "source": "src/color/colorimetry-math.ts",
2078
2078
  "kind": "type"
2079
2079
  },
2080
+ {
2081
+ "name": "COLOR_PATCH_IDS",
2082
+ "source": "src/facade/color-patches.ts",
2083
+ "kind": "runtime"
2084
+ },
2085
+ {
2086
+ "name": "COLOR_PATCH_SET_ID",
2087
+ "source": "src/facade/color-patches.ts",
2088
+ "kind": "runtime"
2089
+ },
2090
+ {
2091
+ "name": "COLOR_PATCH_SET_VERSION",
2092
+ "source": "src/facade/color-patches.ts",
2093
+ "kind": "runtime"
2094
+ },
2095
+ {
2096
+ "name": "COLOR_PATCH_WAVELENGTH_RANGE_NANOMETERS",
2097
+ "source": "src/facade/color-patches.ts",
2098
+ "kind": "runtime"
2099
+ },
2080
2100
  {
2081
2101
  "name": "COLOR_TEMPERATURE_WHITE_BALANCE_INTENT_VERSION",
2082
2102
  "source": "src/color/white-balance.ts",
@@ -2122,6 +2142,41 @@
2122
2142
  "source": "src/color/colorimetry-math.ts",
2123
2143
  "kind": "type"
2124
2144
  },
2145
+ {
2146
+ "name": "ColorPatch",
2147
+ "source": "src/facade/color-patches.ts",
2148
+ "kind": "type"
2149
+ },
2150
+ {
2151
+ "name": "ColorPatchesChartScene",
2152
+ "source": "src/facade/chart-scenes.ts",
2153
+ "kind": "type"
2154
+ },
2155
+ {
2156
+ "name": "ColorPatchGroup",
2157
+ "source": "src/facade/color-patches.ts",
2158
+ "kind": "type"
2159
+ },
2160
+ {
2161
+ "name": "ColorPatchId",
2162
+ "source": "src/facade/color-patches.ts",
2163
+ "kind": "type"
2164
+ },
2165
+ {
2166
+ "name": "ColorPatchReflectance",
2167
+ "source": "src/facade/chart-scenes.ts",
2168
+ "kind": "type"
2169
+ },
2170
+ {
2171
+ "name": "ColorPatchSet",
2172
+ "source": "src/facade/color-patches.ts",
2173
+ "kind": "type"
2174
+ },
2175
+ {
2176
+ "name": "ColorPatchSpectralTerm",
2177
+ "source": "src/facade/color-patches.ts",
2178
+ "kind": "type"
2179
+ },
2125
2180
  {
2126
2181
  "name": "ColorSamplingChannelComposition",
2127
2182
  "source": "src/sensor/capture-color-sampling-binding.ts",
@@ -2922,6 +2977,16 @@
2922
2977
  "source": "src/color/rgb-color-spaces.ts",
2923
2978
  "kind": "runtime"
2924
2979
  },
2980
+ {
2981
+ "name": "evaluateColorPatchReflectance",
2982
+ "source": "src/facade/color-patches.ts",
2983
+ "kind": "runtime"
2984
+ },
2985
+ {
2986
+ "name": "EvaluateColorPatchReflectanceInput",
2987
+ "source": "src/facade/color-patches.ts",
2988
+ "kind": "type"
2989
+ },
2925
2990
  {
2926
2991
  "name": "evaluateSceneIlluminationTemporalMultiplier",
2927
2992
  "source": "src/schema/illumination-temporal.ts",
@@ -3367,6 +3432,11 @@
3367
3432
  "source": "src/equipment/generic-tier-presets.ts",
3368
3433
  "kind": "runtime"
3369
3434
  },
3435
+ {
3436
+ "name": "GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS",
3437
+ "source": "src/equipment/generic-tier-presets.ts",
3438
+ "kind": "runtime"
3439
+ },
3370
3440
  {
3371
3441
  "name": "GENERIC_EQUIPMENT_TIERS",
3372
3442
  "source": "src/equipment/generic-tier-presets.ts",
@@ -3407,6 +3477,21 @@
3407
3477
  "source": "src/equipment/release-capabilities.ts",
3408
3478
  "kind": "runtime"
3409
3479
  },
3480
+ {
3481
+ "name": "GENERIC_THIRD_STOP_ISO_SETTINGS",
3482
+ "source": "src/equipment/generic-tier-assets.ts",
3483
+ "kind": "runtime"
3484
+ },
3485
+ {
3486
+ "name": "GENERIC_THIRD_STOP_SHUTTER_SECONDS",
3487
+ "source": "src/equipment/generic-tier-assets.ts",
3488
+ "kind": "runtime"
3489
+ },
3490
+ {
3491
+ "name": "GENERIC_WHOLE_STOP_ISO_SETTINGS",
3492
+ "source": "src/equipment/generic-tier-assets.ts",
3493
+ "kind": "runtime"
3494
+ },
3410
3495
  {
3411
3496
  "name": "GenericBodyExposureCapabilityProfile",
3412
3497
  "source": "src/equipment/exposure-capabilities.ts",
@@ -3527,6 +3612,11 @@
3527
3612
  "source": "src/equipment/generic-tier-assets.ts",
3528
3613
  "kind": "type"
3529
3614
  },
3615
+ {
3616
+ "name": "GenericTierPresetVersion",
3617
+ "source": "src/equipment/generic-tier-assets.ts",
3618
+ "kind": "type"
3619
+ },
3530
3620
  {
3531
3621
  "name": "GenericVersionedAsset",
3532
3622
  "source": "src/equipment/generic-tier-assets.ts",
@@ -3582,6 +3672,11 @@
3582
3672
  "source": "src/output/geometric-transforms.ts",
3583
3673
  "kind": "type"
3584
3674
  },
3675
+ {
3676
+ "name": "getColorPatchSet",
3677
+ "source": "src/facade/color-patches.ts",
3678
+ "kind": "runtime"
3679
+ },
3585
3680
  {
3586
3681
  "name": "getImageFormationContract",
3587
3682
  "source": "src/core/image-formation.ts",
@@ -8167,6 +8262,11 @@
8167
8262
  "source": "src/facade/simulate-capture.ts",
8168
8263
  "kind": "runtime"
8169
8264
  },
8265
+ {
8266
+ "name": "SIMULATE_CAPTURE_METERING_MODES",
8267
+ "source": "src/facade/simulate-capture.ts",
8268
+ "kind": "runtime"
8269
+ },
8170
8270
  {
8171
8271
  "name": "SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS",
8172
8272
  "source": "src/facade/simulate-capture.ts",
@@ -8252,6 +8352,11 @@
8252
8352
  "source": "src/facade/simulate-capture.ts",
8253
8353
  "kind": "type"
8254
8354
  },
8355
+ {
8356
+ "name": "SimulateCaptureMetering",
8357
+ "source": "src/facade/simulate-capture.ts",
8358
+ "kind": "type"
8359
+ },
8255
8360
  {
8256
8361
  "name": "SimulateCaptureMotion",
8257
8362
  "source": "src/facade/simulate-capture.ts",
@@ -97,6 +97,7 @@ CHART_SCENE_DEFAULTS = Object.freeze({
97
97
  "slanted-squares": Object.freeze({ columns: 3, rows: 2, sizeFraction: 0.5, angleDegrees: 5, darkReflectance: 0.15, lightReflectance: 0.6 }),
98
98
  "line-grid": Object.freeze({ spacing: 1 / 3, lineWidth: 0.01, lineReflectance: 0.42, backgroundReflectance: 0.6 }),
99
99
  fiducial: Object.freeze({ radiusFraction: 0.8, ringFraction: 0.18, inverted: false, darkReflectance: 0.05, lightReflectance: 0.8, backgroundReflectance: 0.18 }),
100
+ "color-patches": Object.freeze({ patchIds: COLOR_PATCH_IDS, gapFraction: 0.1, backgroundReflectance: 0.18 }),
100
101
  composite: Object.freeze({ background: Object.freeze({ preset: "uniform" as const, reflectance: 0.18 }) })
101
102
  })
102
103
  ```
@@ -108,7 +109,7 @@ The exact contract is declared below; use the domain guide for assumptions and s
108
109
  ```ts
109
110
  CHART_SCENE_PRESETS: readonly ChartScenePresetId[] = Object.freeze(["uniform", "slanted-edge", "siemens-star",
110
111
  "step-wedge", "grey-ramp", "checkerboard", "point-grid", "patches", "dead-leaves", "bar-groups", "hyperbolic-wedge", "zone-plate",
111
- "slanted-squares", "line-grid", "fiducial", "composite"] as const)
112
+ "slanted-squares", "line-grid", "fiducial", "color-patches", "composite"] as const)
112
113
  ```
113
114
 
114
115
  ## CHART_SPECTRAL_RANGE_NANOMETERS
@@ -144,7 +145,7 @@ Any chart preset except `composite`: a composite's background or element.
144
145
 
145
146
  ```ts
146
147
  export type ChartElementScene = UniformChartScene | SlantedEdgeChartScene | SiemensStarChartScene | StepWedgeChartScene | GreyRampChartScene |
147
- CheckerboardChartScene | PointGridChartScene | PatchChartScene | DeadLeavesChartScene | BarGroupsChartScene | HyperbolicWedgeChartScene |
148
+ CheckerboardChartScene | PointGridChartScene | PatchChartScene | ColorPatchesChartScene | DeadLeavesChartScene | BarGroupsChartScene | HyperbolicWedgeChartScene |
148
149
  ZonePlateChartScene | SlantedSquaresChartScene | LineGridChartScene | FiducialChartScene;
149
150
  ```
150
151
 
@@ -158,10 +159,11 @@ export interface ChartRectangle { u0: number; v0: number; u1: number; v1: number
158
159
 
159
160
  ## ChartReflectance
160
161
 
161
- A neutral (spectrally flat) reflectance in [0, 1], or a caller reflectance spectrum (values in [0, 1]).
162
+ A neutral (spectrally flat) reflectance in [0, 1], a caller reflectance spectrum (values in [0, 1]) or, from 2.14, a
163
+ built-in color patch `{ kind: "color-patch", patchId }`.
162
164
 
163
165
  ```ts
164
- export type ChartReflectance = number | ColorimetricSpectrum;
166
+ export type ChartReflectance = number | ColorimetricSpectrum | ColorPatchReflectance;
165
167
  ```
166
168
 
167
169
  ## ChartScene
@@ -213,6 +215,35 @@ export interface CheckerboardChartScene {
213
215
  }
214
216
  ```
215
217
 
218
+ ## ColorPatchesChartScene
219
+
220
+ 2.14: the built-in Photivra color patches (`getColorPatchSet()`) in a grid, drawn like `patches`: row-major, each
221
+ patch centred in an equal cell with a neutral gap around it. The spectra are the engine's own analytic designs; no
222
+ third-party chart data, names, order or layout are used.
223
+
224
+ ```ts
225
+ export interface ColorPatchesChartScene {
226
+ preset: "color-patches";
227
+ /** Patches to draw, row-major, distinct. Default: the whole set in `COLOR_PATCH_IDS` order (30 patches, six per row). */
228
+ patchIds?: readonly ColorPatchId[];
229
+ /** Patches per row, 1 to the patch count. Default min(6, patch count). */
230
+ columns?: number;
231
+ /** Gap between patches as a fraction of the cell size, [0, 0.5). Default 0.1. */
232
+ gapFraction?: number;
233
+ /** Neutral reflectance of the gaps and empty cells. Default 0.18. */
234
+ backgroundReflectance?: number;
235
+ }
236
+ ```
237
+
238
+ ## ColorPatchReflectance
239
+
240
+ 2.14: one of the engine's built-in color patches (see `getColorPatchSet()`): its reflectance spectrum, Photivra's own
241
+ analytic design, rendered through the same spectral path as a caller spectrum.
242
+
243
+ ```ts
244
+ export interface ColorPatchReflectance { kind: "color-patch"; patchId: ColorPatchId }
245
+ ```
246
+
216
247
  ## CompositeChartElement
217
248
 
218
249
  One element of a composite chart: a preset drawn inside its placement rectangle (frame units of the
@@ -346,7 +377,7 @@ The exact contract is declared below; use the domain guide for assumptions and s
346
377
  ```ts
347
378
  export interface PatchChartScene {
348
379
  preset: "patches";
349
- /** Caller reflectance per patch, row-major; the facade bundles no colour-chart data. 1..1024 patches. */
380
+ /** Caller reflectance per patch, row-major; the facade bundles no color-chart data. 1..1024 patches. */
350
381
  patches: readonly { patchId: string; reflectance: ChartReflectance }[];
351
382
  /** Patches per row. Default ceil(sqrt(patch count)). */
352
383
  columns?: number;
@@ -0,0 +1,150 @@
1
+ # facade/color-patches.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## COLOR_PATCH_IDS
6
+
7
+ The ids of the built-in patches, in the order of the `color-patches` chart (row-major, six per row).
8
+
9
+ ```ts
10
+ COLOR_PATCH_IDS = Object.freeze([
11
+ "neutral-1", "neutral-2", "neutral-3", "neutral-4", "neutral-5", "neutral-6",
12
+ "red", "yellow", "green", "cyan", "blue", "magenta",
13
+ "red-soft", "yellow-soft", "green-soft", "cyan-soft", "blue-soft", "magenta-soft",
14
+ "skin-pale", "skin-medium", "skin-tan", "skin-deep", "sky-blue", "foliage-green",
15
+ "brick-red", "sand", "teal", "lavender", "deep-orange", "deep-violet"
16
+ ] as const)
17
+ ```
18
+
19
+ ## COLOR_PATCH_SET_ID
20
+
21
+ Identity of the built-in patch set.
22
+
23
+ ```ts
24
+ COLOR_PATCH_SET_ID = "photivra-color-patches"
25
+ ```
26
+
27
+ ## COLOR_PATCH_SET_VERSION
28
+
29
+ Version of the patch set's spectra and reference values; a change to any spectrum changes it.
30
+
31
+ ```ts
32
+ COLOR_PATCH_SET_VERSION = "1.0.0"
33
+ ```
34
+
35
+ ## COLOR_PATCH_WAVELENGTH_RANGE_NANOMETERS
36
+
37
+ Wavelengths on which every patch spectrum is defined, nanometers, inclusive.
38
+
39
+ ```ts
40
+ COLOR_PATCH_WAVELENGTH_RANGE_NANOMETERS = Object.freeze({ minimum: 360, maximum: 830 })
41
+ ```
42
+
43
+ ## ColorPatch
44
+
45
+ One built-in patch: its id, short code, name, reason for being on the chart, spectrum definition and reference
46
+ colorimetry.
47
+
48
+ ```ts
49
+ export interface ColorPatch {
50
+ /** Stable API id, e.g. `"skin-medium"`. */
51
+ patchId: ColorPatchId;
52
+ /**
53
+ * Stable short code for labels and printed charts: `PV-` and a group letter and number, e.g. `"PV-N4"`. The letters are
54
+ * N neutral, S strong, T soft (tint), P skin (portrait), M memory and G gamut-edge, numbered in chart order.
55
+ */
56
+ code: string;
57
+ /** Photivra's own descriptive name, in US English, e.g. `"Studio Gray 4"`; not a third-party color name. */
58
+ name: string;
59
+ group: ColorPatchGroup;
60
+ /** One sentence: why the patch is on the chart. */
61
+ reason: string;
62
+ /** The constant b of the formula. */
63
+ base: number;
64
+ terms: readonly ColorPatchSpectralTerm[];
65
+ /**
66
+ * Reference colorimetry of the reflectance (not of a rendered capture): CIELAB under CIE illuminant D65 with the CIE
67
+ * 1931 2° observer, the CIE tables at 1 nm over 360–830 nm, white = the perfect diffuser under D65 (Y = 100); and the
68
+ * sRGB color of that XYZ (CSS Color 4 matrix, after XYZ scaling of the D65 table white to the sRGB white),
69
+ * 8-bit hex. Rounded for publication: L*, a*, b* to 0.01.
70
+ */
71
+ reference: { cielabD65: readonly [number, number, number]; srgbHex: string; inSrgbGamut: boolean };
72
+ }
73
+ ```
74
+
75
+ ## ColorPatchGroup
76
+
77
+ Groups of the patch set, in chart order.
78
+
79
+ ```ts
80
+ export type ColorPatchGroup = "neutral" | "strong" | "soft" | "skin" | "memory" | "gamut-edge";
81
+ ```
82
+
83
+ ## ColorPatchId
84
+
85
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
86
+
87
+ ```ts
88
+ export type ColorPatchId = (typeof COLOR_PATCH_IDS)[number];
89
+ ```
90
+
91
+ ## ColorPatchSet
92
+
93
+ The built-in patch set (see {@link getColorPatchSet}).
94
+
95
+ ```ts
96
+ export interface ColorPatchSet {
97
+ setId: typeof COLOR_PATCH_SET_ID;
98
+ version: typeof COLOR_PATCH_SET_VERSION;
99
+ wavelengthRangeNanometers: { minimum: number; maximum: number };
100
+ /** Patches per row of the `color-patches` chart's default layout. */
101
+ columns: number;
102
+ patches: readonly ColorPatch[];
103
+ }
104
+ ```
105
+
106
+ ## ColorPatchSpectralTerm
107
+
108
+ One term of a patch spectrum (see the module formula).
109
+
110
+ ```ts
111
+ export type ColorPatchSpectralTerm =
112
+ | { kind: "gaussian"; amplitude: number; centerNanometers: number; standardDeviationNanometers: number }
113
+ | { kind: "logistic-rise" | "logistic-fall"; amplitude: number; midpointNanometers: number; scaleNanometers: number };
114
+ ```
115
+
116
+ ## evaluateColorPatchReflectance
117
+
118
+ The reflectance (dimensionless, in [0, 1]) of a built-in color patch at one wavelength, from its analytic formula.
119
+ Labeled `approximation`: the spectra are Photivra's own smooth designs, not measurements of a physical sample.
120
+
121
+ ```ts
122
+ export function evaluateColorPatchReflectance(input: EvaluateColorPatchReflectanceInput): CalculationResult<{ reflectance: number }>;
123
+ ```
124
+
125
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
126
+
127
+ ## EvaluateColorPatchReflectanceInput
128
+
129
+ Input of {@link evaluateColorPatchReflectance}.
130
+
131
+ ```ts
132
+ export interface EvaluateColorPatchReflectanceInput {
133
+ patchId: ColorPatchId;
134
+ /** Vacuum wavelength, nanometers, in 360–830. */
135
+ wavelengthNanometers: number;
136
+ }
137
+ ```
138
+
139
+ ## getColorPatchSet
140
+
141
+ The built-in Photivra color-patch set: every patch's id, code, name, group, reason, spectrum parameters and reference CIELAB (D65,
142
+ CIE 1931 2°) and sRGB values. A static, deeply frozen engine contract. Pass a patch to a chart as
143
+ `{ kind: "color-patch", patchId }` wherever a chart reflectance is accepted, or draw the whole set with the
144
+ `color-patches` chart preset.
145
+
146
+ ```ts
147
+ export function getColorPatchSet(): ColorPatchSet;
148
+ ```
149
+
150
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
@@ -21,10 +21,17 @@ export interface DisplayViewResult {
21
21
  */
22
22
  view: { width: number; height: number; frameWidth: number; frameHeight: number; scaleX: number; scaleY: number; samplesPerAxis: number;
23
23
  region?: { x: number; y: number; width: number; height: number } };
24
- /** The resolved exposure of the capture this view shows; `standardExposureDeviationStops` and `standardExposureIlluminanceLux` as in {@link SimulateCaptureResult}. */
24
+ /**
25
+ * The resolved exposure of the capture this view shows; `standardExposureDeviationStops`, `standardExposureIlluminanceLux`
26
+ * and `greyCardDeviationStops` as in {@link SimulateCaptureResult}.
27
+ */
25
28
  exposure: { mode: SimulateCaptureExposureMode; settings: { fNumber: number; shutterSeconds: number; iso: number }; ev100: number; standardExposureDeviationStops: number | null;
26
29
  /** As in the capture result. */
27
30
  standardExposureIlluminanceLux: number | null;
31
+ /** 2.14: as in the capture result. */
32
+ greyCardDeviationStops: number | null;
33
+ /** 2.14: as in the capture result. */
34
+ metering: SimulateCaptureMetering;
28
35
  /** #516: as in the capture result. */
29
36
  isoControl: "manual" | "automatic"; autoIso?: SimulateCaptureAutoIsoReport };
30
37
  scene: SimulateCaptureResult["scene"];
@@ -136,6 +143,14 @@ whether or not it has a fast method yet; one without runs exact and reports `fal
136
143
  SIMULATE_CAPTURE_FAST_OPTIONS = Object.freeze(["lens.diffraction", "sensor.antiAliasing", "sensor.pixelAperture.box", "output.demosaic.ahd"] as const)
137
144
  ```
138
145
 
146
+ ## SIMULATE_CAPTURE_METERING_MODES
147
+
148
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
149
+
150
+ ```ts
151
+ SIMULATE_CAPTURE_METERING_MODES: readonly SimulateCaptureMetering[] = Object.freeze(["scene-average", "grey-card"] as const)
152
+ ```
153
+
139
154
  ## SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS
140
155
 
141
156
  Default stratified samples per axis of a display view (`view.samplesPerAxis`): 16 × 16 sampled real pixels per display pixel with detail, 4 × 4 where it is uniform.
@@ -221,6 +236,8 @@ export interface SimulateCaptureAutoIsoReport {
221
236
  slowestPreferredShutterApplied?: boolean;
222
237
  /** #504: present only with `slowestPreferredShutterSeconds: "auto"`: how the engine derived it (`estimateAutoIsoSlowestShutter`). */
223
238
  automaticSlowestShutter?: SimulateCaptureAutomaticSlowestShutterReport;
239
+ /** 2.14: present when `exposure.autoIso.isoSteps` is given: the ISO steps Auto ISO chose from. */
240
+ isoSteps?: "whole-stop" | "third-stop";
224
241
  }
225
242
  ```
226
243
 
@@ -369,6 +386,17 @@ export type SimulateCaptureLensDistortion =
369
386
  | { kind: "brown-conrady"; profile: BrownConradyDistortionProfile };
370
387
  ```
371
388
 
389
+ ## SimulateCaptureMetering
390
+
391
+ 2.14: what the meter reads. `scene-average` (the default): the body's 16 × 16-zone meter reads the scene's luminance.
392
+ `grey-card`: incident metering; every zone reads the luminance 0.18·E/π of an 18 % Lambertian grey card under the
393
+ request's illuminance E, so the automatic modes choose settings whose `greyCardDeviationStops` is 0 (up to their steps
394
+ and limits), whatever the scene's reflectances.
395
+
396
+ ```ts
397
+ export type SimulateCaptureMetering = "scene-average" | "grey-card";
398
+ ```
399
+
372
400
  ## SimulateCaptureMotion
373
401
 
374
402
  The exact contract is declared below; use the domain guide for assumptions and staged integration.
@@ -467,13 +495,22 @@ export interface SimulateCaptureOptions {
467
495
  * `maximum` (default: the body's minimum). Outside the body's range or off its ISO grid the request fails; the
468
496
  * engine never clips a limit. When the ISO stops at a limit strictly inside the body's range, the report's
469
497
  * `limitingConstraint` is `"user-iso-maximum"` or `"user-iso-minimum"`.
498
+ * - `isoSteps` (2.14): `"whole-stop"` (default) lets Auto ISO choose only the whole-stop ISOs of the body's grid
499
+ * (100, 200, 400 …), as before the generic bodies gained third-stop settings (preset 2.1.0); `"third-stop"` lets it
500
+ * choose every setting (100, 125, 160 …). Manual ISO always takes any setting of the grid. Limits must lie on the
501
+ * grid Auto ISO uses.
470
502
  */
471
503
  autoIso?: { slowestPreferredShutterSeconds?: number | "auto"; afterMaximumIso?: SimulateCaptureAutoIsoAfterMaximum; limits?: AutoIsoLimits;
472
- automaticSlowestShutter?: SimulateCaptureAutomaticSlowestShutterOptions };
504
+ automaticSlowestShutter?: SimulateCaptureAutomaticSlowestShutterOptions; isoSteps?: "whole-stop" | "third-stop" };
473
505
  /** Shutter for manual and shutter-priority modes. Default 1/125 s. */
474
506
  shutterSeconds?: number;
475
507
  /** Exposure compensation applied to the meter target, in stops. Default 0. */
476
508
  compensationStops?: number;
509
+ /**
510
+ * 2.14: `"scene-average"` (default) or `"grey-card"` (incident metering, see {@link SimulateCaptureMetering}). Manual
511
+ * exposure does not use the meter's reading, so the setting changes only the automatic modes and Auto ISO.
512
+ */
513
+ metering?: SimulateCaptureMetering;
477
514
  };
478
515
  /**
479
516
  * Required: an engine chart (for example `{ preset: "uniform" }`, an 18 % grey card), a declared `material-layers` scene (#491:
@@ -682,6 +719,9 @@ export interface SimulateCaptureResult {
682
719
  isoControl: "manual" | "automatic";
683
720
  /** #516: present with `iso: "auto"`: how Auto ISO chose `settings.iso` and what limited it. */
684
721
  autoIso?: SimulateCaptureAutoIsoReport;
722
+ /** 2.14: the metering the exposure used (`exposure.metering`; `"scene-average"` when omitted). */
723
+ metering: SimulateCaptureMetering;
724
+ /** The scene's average luminance on the meter's zones, cd/m², with either metering. */
685
725
  meteredSceneLuminanceCdPerSquareMeter: number;
686
726
  /**
687
727
  * #515: deviation, in stops, of the exposure used from the CIPA DC-004-2020 standard exposure for the metered luminance L:
@@ -697,6 +737,16 @@ export interface SimulateCaptureResult {
697
737
  * radiance does not depend on the illuminance. It may exceed the 1e7 lux that `illuminanceLux` accepts.
698
738
  */
699
739
  standardExposureIlluminanceLux: number | null;
740
+ /**
741
+ * 2.14: deviation, in stops, of the exposure used from the CIPA DC-004-2020 standard exposure of an 18 % Lambertian grey
742
+ * card under the request's illuminance E (incident metering: the light falling on the scene, as a grey card or a
743
+ * hand-held incident meter reads it): log2(L_ref(N, t, S) / (0.18·E/π)), with L_ref = 10·N²/(0.65·t·S). Same sign as
744
+ * `standardExposureDeviationStops` (positive is darker, under). It does not depend on the scene's reflectances; with the
745
+ * default settings and illuminance it is exactly 0. The exposure modes still meter the scene (`meteredSceneLuminanceCdPerSquareMeter`);
746
+ * this only reports where the settings used sit against the grey card. Null for a radiance-function scene, whose radiance
747
+ * does not depend on the illuminance.
748
+ */
749
+ greyCardDeviationStops: number | null;
700
750
  /** Full output of the existing exposure-mode resolver. */
701
751
  resolution: unknown;
702
752
  };
package/llms.txt CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  - [Agent guide](https://github.com/Photivra/photivra/blob/main/docs/AGENT_GUIDE.md): which function to call, units, reading results, errors and limits
8
8
  - [Quick start](https://github.com/Photivra/photivra/blob/main/docs/QUICK_START.md): `simulateCapture()` inputs, defaults, chart presets and output formats
9
+ - [Color patches](https://github.com/Photivra/photivra/blob/main/docs/COLOR_PATCHES.md): the built-in color chart patches (`color-patches` preset, `{ kind: "color-patch", patchId }`), their spectra and CIELAB
9
10
  - [Usage guide](https://github.com/Photivra/photivra/blob/main/docs/USAGE.md): worked examples for every public area
10
11
  - [API reference](https://github.com/Photivra/photivra/blob/main/docs/API_REFERENCE.md): every root export, with a task-to-function index
11
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photivra/engine",
3
- "version": "2.13.0",
3
+ "version": "2.14.0",
4
4
  "private": false,
5
5
  "description": "Scientific TypeScript engine for camera optics, exposure, focus, motion, sensor sampling, diffraction, and digital imaging calculations.",
6
6
  "type": "module",
@@ -73,7 +73,7 @@
73
73
  "node": ">=22.13.0"
74
74
  },
75
75
  "scripts": {
76
- "build": "tsc -p tsconfig.build.json",
76
+ "build": "tsc -p tsconfig.build.json && node scripts/minify-wgsl-dist.mjs",
77
77
  "hooks:install": "node scripts/install-git-hooks.mjs",
78
78
  "prepush": "node scripts/prepush.mjs",
79
79
  "check:static": "eslint . && tsc --noEmit && npm run check:licenses && npm run check:license-headers && npm run check:identity && npm run check:browser-surface",