@photivra/engine 2.9.0 → 2.11.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.
Files changed (147) hide show
  1. package/CHANGELOG.md +223 -1
  2. package/CITATION.cff +1 -1
  3. package/NODE_SUPPORT.md +21 -3
  4. package/README.md +6 -6
  5. package/SECURITY.md +1 -1
  6. package/THIRD_PARTY.md +2 -0
  7. package/dist/capture/native-environment-lean.js +89 -5
  8. package/dist/capture/native-environment-lean.js.map +1 -1
  9. package/dist/capture/native-environment-tabulated.d.ts +37 -0
  10. package/dist/capture/native-environment-tabulated.js +42 -6
  11. package/dist/capture/native-environment-tabulated.js.map +1 -1
  12. package/dist/core/portable-math.d.ts +19 -0
  13. package/dist/core/portable-math.js +82 -0
  14. package/dist/core/portable-math.js.map +1 -1
  15. package/dist/core/version.d.ts +1 -1
  16. package/dist/core/version.js +1 -1
  17. package/dist/core/version.js.map +1 -1
  18. package/dist/facade/channel-response.d.ts +15 -0
  19. package/dist/facade/channel-response.js +33 -0
  20. package/dist/facade/channel-response.js.map +1 -0
  21. package/dist/facade/chart-material-layers.d.ts +115 -0
  22. package/dist/facade/chart-material-layers.js +224 -0
  23. package/dist/facade/chart-material-layers.js.map +1 -0
  24. package/dist/facade/chart-radiance-batch.d.ts +8 -2
  25. package/dist/facade/chart-radiance-batch.js +11 -4
  26. package/dist/facade/chart-radiance-batch.js.map +1 -1
  27. package/dist/facade/chart-scenes.d.ts +134 -0
  28. package/dist/facade/chart-scenes.js +32 -22
  29. package/dist/facade/chart-scenes.js.map +1 -1
  30. package/dist/facade/diffraction-provider.d.ts +8 -2
  31. package/dist/facade/diffraction-provider.js +120 -16
  32. package/dist/facade/diffraction-provider.js.map +1 -1
  33. package/dist/facade/display-view-gpu-session.d.ts +18 -0
  34. package/dist/facade/display-view-gpu-session.js +280 -0
  35. package/dist/facade/display-view-gpu-session.js.map +1 -0
  36. package/dist/facade/display-view-gpu.d.ts +146 -0
  37. package/dist/facade/display-view-gpu.js +59 -0
  38. package/dist/facade/display-view-gpu.js.map +1 -0
  39. package/dist/facade/display-view.d.ts +123 -1
  40. package/dist/facade/display-view.js +478 -116
  41. package/dist/facade/display-view.js.map +1 -1
  42. package/dist/facade/expected-signal-setup.d.ts +5 -0
  43. package/dist/facade/expected-signal-setup.js +18 -1
  44. package/dist/facade/expected-signal-setup.js.map +1 -1
  45. package/dist/facade/expected-signal.d.ts +80 -6
  46. package/dist/facade/expected-signal.js +200 -24
  47. package/dist/facade/expected-signal.js.map +1 -1
  48. package/dist/facade/scene-radiance.d.ts +4 -2
  49. package/dist/facade/scene-radiance.js +7 -4
  50. package/dist/facade/scene-radiance.js.map +1 -1
  51. package/dist/facade/simulate-capture.d.ts +81 -7
  52. package/dist/facade/simulate-capture.js +276 -53
  53. package/dist/facade/simulate-capture.js.map +1 -1
  54. package/dist/facade/simulate-display-view.d.ts +54 -3
  55. package/dist/facade/simulate-display-view.js +120 -27
  56. package/dist/facade/simulate-display-view.js.map +1 -1
  57. package/dist/gpu/chart-composite-gpu.d.ts +119 -0
  58. package/dist/gpu/chart-composite-gpu.js +572 -0
  59. package/dist/gpu/chart-composite-gpu.js.map +1 -0
  60. package/dist/gpu/chart-radiance-gpu.d.ts +6 -6
  61. package/dist/gpu/chart-radiance-gpu.js +9 -11
  62. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  63. package/dist/gpu/display-view-chart-material-module.d.ts +10 -0
  64. package/dist/gpu/display-view-chart-material-module.js +58 -0
  65. package/dist/gpu/display-view-chart-material-module.js.map +1 -0
  66. package/dist/gpu/display-view-composite-material-module.d.ts +68 -0
  67. package/dist/gpu/display-view-composite-material-module.js +248 -0
  68. package/dist/gpu/display-view-composite-material-module.js.map +1 -0
  69. package/dist/gpu/display-view-quality-gpu.d.ts +185 -0
  70. package/dist/gpu/display-view-quality-gpu.js +1000 -0
  71. package/dist/gpu/display-view-quality-gpu.js.map +1 -0
  72. package/dist/gpu/display-view-speed-gpu.d.ts +156 -0
  73. package/dist/gpu/display-view-speed-gpu.js +524 -0
  74. package/dist/gpu/display-view-speed-gpu.js.map +1 -0
  75. package/dist/gpu/image-kernel-accelerator.d.ts +23 -4
  76. package/dist/gpu/image-kernel-accelerator.js +58 -10
  77. package/dist/gpu/image-kernel-accelerator.js.map +1 -1
  78. package/dist/gpu/image-kernel-execution.d.ts +60 -3
  79. package/dist/gpu/image-kernel-execution.js +2 -2
  80. package/dist/gpu/image-kernel-execution.js.map +1 -1
  81. package/dist/gpu/sensor-stage-gpu.d.ts +11 -0
  82. package/dist/gpu/sensor-stage-gpu.js +42 -13
  83. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  84. package/dist/gpu/webgpu-runtime.d.ts +4 -2
  85. package/dist/gpu/webgpu-runtime.js +13 -4
  86. package/dist/gpu/webgpu-runtime.js.map +1 -1
  87. package/dist/gpu/webgpu-switch.d.ts +4 -0
  88. package/dist/gpu/webgpu-switch.js +29 -0
  89. package/dist/gpu/webgpu-switch.js.map +1 -0
  90. package/dist/gpu/wgsl-chart-composite-kernel.d.ts +52 -0
  91. package/dist/gpu/wgsl-chart-composite-kernel.js +515 -0
  92. package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -0
  93. package/dist/gpu/wgsl-chart-kernel.d.ts +11 -9
  94. package/dist/gpu/wgsl-chart-kernel.js +33 -27
  95. package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
  96. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +95 -0
  97. package/dist/gpu/wgsl-display-view-quality-kernel.js +769 -0
  98. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -0
  99. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +75 -0
  100. package/dist/gpu/wgsl-display-view-speed-kernel.js +386 -0
  101. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -0
  102. package/dist/gpu/wgsl-sensor-kernel.d.ts +28 -9
  103. package/dist/gpu/wgsl-sensor-kernel.js +94 -50
  104. package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
  105. package/dist/index.d.ts +6 -3
  106. package/dist/index.js +4 -2
  107. package/dist/index.js.map +1 -1
  108. package/dist/optics/defocus-disc-kernel.d.ts +61 -0
  109. package/dist/optics/defocus-disc-kernel.js +191 -0
  110. package/dist/optics/defocus-disc-kernel.js.map +1 -0
  111. package/dist/optics/irradiance-convolution.d.ts +11 -0
  112. package/dist/optics/irradiance-convolution.js +64 -24
  113. package/dist/optics/irradiance-convolution.js.map +1 -1
  114. package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
  115. package/dist/optics/scene-to-sensor-irradiance.js +58 -24
  116. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  117. package/dist/optics/sensor-aperture-rays.js +2 -1
  118. package/dist/optics/sensor-aperture-rays.js.map +1 -1
  119. package/dist/optics/sensor-environment-query.js +4 -3
  120. package/dist/optics/sensor-environment-query.js.map +1 -1
  121. package/dist/optics/site-diffraction-kernel.d.ts +16 -0
  122. package/dist/optics/site-diffraction-kernel.js +60 -8
  123. package/dist/optics/site-diffraction-kernel.js.map +1 -1
  124. package/dist/processing/demosaic.js +114 -37
  125. package/dist/processing/demosaic.js.map +1 -1
  126. package/dist/schema/scene-radiance.d.ts +8 -0
  127. package/dist/schema/scene-radiance.js +28 -17
  128. package/dist/schema/scene-radiance.js.map +1 -1
  129. package/dist/sensor/environment-photo-signal.d.ts +21 -0
  130. package/dist/sensor/environment-photo-signal.js +68 -2
  131. package/dist/sensor/environment-photo-signal.js.map +1 -1
  132. package/docs/API_REFERENCE.md +60 -1
  133. package/docs/QUICK_START.md +120 -11
  134. package/docs/USAGE.md +82 -2
  135. package/docs/api/core-version.md +1 -1
  136. package/docs/api/exports.json +157 -2
  137. package/docs/api/facade-chart-material-layers.md +137 -0
  138. package/docs/api/facade-chart-radiance-batch.md +7 -2
  139. package/docs/api/facade-display-view-gpu.md +94 -0
  140. package/docs/api/facade-display-view.md +50 -0
  141. package/docs/api/facade-scene-radiance.md +3 -2
  142. package/docs/api/facade-simulate-capture.md +81 -12
  143. package/docs/api/facade-simulate-display-view.md +39 -2
  144. package/docs/api/gpu-image-kernel-accelerator.md +10 -3
  145. package/docs/api/gpu-image-kernel-execution.md +76 -4
  146. package/docs/api/gpu-webgpu-switch.md +23 -0
  147. package/package.json +3 -1
@@ -1,5 +1,6 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  import { InvalidConfigurationError } from "../core/configuration-error.js";
3
+ import { hypot3 } from "../core/portable-math.js";
3
4
  import { apertureRadianceQuery } from "../sensor/environment-photo-signal.js";
4
5
  import { createSensorEqeTemporalPhotoSignal } from "../sensor/temporal-photo-signal.js";
5
6
  import { isDeeplyFrozen } from "../core/internal-radiance-evaluator.js";
@@ -273,7 +274,7 @@ function geometry(t, k, frame, environment, index, access) {
273
274
  if (!Number.isFinite(reference.z) || reference.z <= 0)
274
275
  return undefined;
275
276
  const rx = pd * (reference.x / reference.z), ry = pd * (reference.y / reference.z);
276
- const length = Math.hypot(rx, ry, pd);
277
+ const length = hypot3(rx, ry, pd);
277
278
  const ray = { x: rx / length, y: ry / length, z: pd / length };
278
279
  // Build the parseSceneRadianceEvaluationRequest output directly. Every other field is a template constant already
279
280
  // parsed for the template site. The sample ID is a public opaque ID (trimming cannot change it) and the time is
@@ -281,7 +282,7 @@ function geometry(t, k, frame, environment, index, access) {
281
282
  const direction = { x: -ray.x, y: -ray.y, z: -ray.z };
282
283
  if (!Number.isFinite(direction.x) || !Number.isFinite(direction.y) || !Number.isFinite(direction.z))
283
284
  return undefined;
284
- const norm = Math.hypot(direction.x, direction.y, direction.z);
285
+ const norm = hypot3(direction.x, direction.y, direction.z);
285
286
  if (!Number.isFinite(norm) || Math.abs(norm - 1) > 1e-9)
286
287
  return undefined;
287
288
  // #394: the key the evaluator would read for this request (undefined where it would throw: the full path decides).
@@ -1011,6 +1012,84 @@ function leanSensorSite(t, h, index, seed) {
1011
1012
  flags: Number(physicalSaturation) | Number(preAdcSaturation) << 1 | Number(quantized >= digitalSaturationCode) << 2,
1012
1013
  whiteCode: t.whiteCode, photoRealized, darkRealized };
1013
1014
  }
1015
+ function flatSensorStage(t) {
1016
+ const regime = t.readout.regime;
1017
+ return { darkRate: t.darkRate, capacity: t.capacity, pattern: t.pattern, whiteCode: t.whiteCode, regime, adcMaximumCode: t.readout.adcMaximumCode,
1018
+ inlDefined: regime.adc.integralNonLinearity !== undefined, gain: regime.systemConversionGainElectronsPerCode.value, black: regime.adc.blackLevelCode,
1019
+ digitalGain: regime.adc.digitalGain, digitalSaturationCode: regime.adc.digitalSaturationCode, threshold: regime.preAdcSaturationElectronEquivalent.value,
1020
+ rms: Float64Array.from(regime.readNoiseComponents, (component) => component.rmsElectrons.value), darkMean: NaN, darkThreshold: NaN,
1021
+ out: { rawCode: 0, blackLevelCode: 0, digitalSaturationCode: 0, flags: 0, whiteCode: 0 } };
1022
+ }
1023
+ /** Writes the site's outputs into `f.out`; false where leanSensorSite returns undefined. */
1024
+ function flatSensorSite(f, photoE, darkE, total, x, y, index, seed) {
1025
+ const ratio = total / f.capacity, headroom = f.capacity - total;
1026
+ if (!Number.isFinite(ratio) || !Number.isFinite(headroom))
1027
+ return false;
1028
+ const pattern = f.pattern === undefined ? null : evaluateParsedFixedPatternSite(f.pattern, { x, y });
1029
+ const photoMean = pattern === null ? photoE : photoE * pattern.photoResponseGainFactor;
1030
+ const darkMean = pattern === null ? darkE : darkE * pattern.darkCurrentFactor;
1031
+ if (!Number.isFinite(photoMean) || photoMean < 0 || !Number.isFinite(darkMean) || darkMean < 0)
1032
+ return false;
1033
+ const random = chargeStream;
1034
+ random.reset(deriveNativeSiteStreamSeedUint32(seed, index, NATIVE_SITE_CHARGE_STREAM_ID));
1035
+ const photoRealized = samplePoisson(photoMean, random);
1036
+ let darkRealized;
1037
+ if (darkMean > 0 && darkMean < 30) {
1038
+ // samplePoisson's branch for 0 < mean < 30 (Knuth's product of uniforms) with its threshold Math.exp(−mean) kept while the
1039
+ // mean repeats: the same draws from the same stream. (createPoissonSampler would also do this, but it brings the
1040
+ // portable-math functions into capture-only bundles.)
1041
+ if (darkMean !== f.darkMean) {
1042
+ f.darkMean = darkMean;
1043
+ f.darkThreshold = Math.exp(-darkMean);
1044
+ }
1045
+ const threshold = f.darkThreshold;
1046
+ let product = 1, count = 0;
1047
+ do {
1048
+ count += 1;
1049
+ product *= random.next();
1050
+ } while (product > threshold);
1051
+ darkRealized = count - 1;
1052
+ }
1053
+ else
1054
+ darkRealized = samplePoisson(darkMean, random);
1055
+ const realized = photoRealized + darkRealized + 0;
1056
+ if (!Number.isFinite(realized) || realized < 0)
1057
+ return false;
1058
+ const readRandom = readNoiseStream;
1059
+ readRandom.reset(deriveNativeSiteStreamSeedUint32(seed, index, NATIVE_SITE_READ_NOISE_STREAM_ID));
1060
+ const capacity = f.capacity;
1061
+ const afterPhysical = Math.min(realized, capacity), physicalSaturation = afterPhysical < realized;
1062
+ const rmsAll = f.rms;
1063
+ let totalReadNoise = 0;
1064
+ for (let k = 0; k < rmsAll.length; k++) {
1065
+ const rms = rmsAll[k];
1066
+ totalReadNoise = totalReadNoise + (rms === 0 ? 0 : sampleDeterministicStandardNormal(readRandom) * rms);
1067
+ }
1068
+ const afterReadNoise = afterPhysical + totalReadNoise + (pattern === null ? 0 : pattern.readoutOffsetElectrons);
1069
+ const threshold = f.threshold;
1070
+ const afterPreAdc = Math.min(afterReadNoise, threshold), preAdcSaturation = afterReadNoise > threshold;
1071
+ let quantized;
1072
+ if (f.inlDefined)
1073
+ quantized = adcTransfer(f.regime, f.adcMaximumCode, afterPreAdc).quantized;
1074
+ else {
1075
+ // adcTransfer without integral non-linearity: ideal = e/G + black, plus the zero INL offset, rounded half up (Math.round + 0),
1076
+ // then with a digital gain the ADC clamp and the gain's own rounding about the black level.
1077
+ const ideal = afterPreAdc / f.gain + f.black, quantizedAdc = Math.round(ideal + 0) + 0, digitalGain = f.digitalGain;
1078
+ if (digitalGain === undefined)
1079
+ quantized = quantizedAdc;
1080
+ else {
1081
+ const adcCode = Math.max(0, Math.min(quantizedAdc, f.adcMaximumCode));
1082
+ quantized = Math.round((adcCode - f.black) * digitalGain + f.black) + 0;
1083
+ }
1084
+ }
1085
+ const digitalSaturationCode = f.digitalSaturationCode, out = f.out;
1086
+ out.rawCode = Math.max(0, Math.min(quantized, digitalSaturationCode));
1087
+ out.blackLevelCode = f.black;
1088
+ out.digitalSaturationCode = digitalSaturationCode;
1089
+ out.flags = Number(physicalSaturation) | Number(preAdcSaturation) << 1 | Number(quantized >= digitalSaturationCode) << 2;
1090
+ out.whiteCode = f.whiteCode;
1091
+ return true;
1092
+ }
1014
1093
  /** The Kahan (compensated) total that composeSensorAccumulatedCharge computes, for photo and dark charge only. */
1015
1094
  function chargeTotal(photoE, darkE) {
1016
1095
  let sum = 0, correction = 0;
@@ -1039,17 +1118,22 @@ export function createVerifiedNativeSiteReadout(parsed, result, whiteCode, photo
1039
1118
  realization.additionalComponents.length !== 0 || out.rawCode !== r.rawCode || out.blackLevelCode !== r.blackLevelCode || out.digitalSaturationCode !== r.digitalSaturationCode ||
1040
1119
  out.flags !== (Number(r.physicalScalarSaturationApplied) | Number(r.preAdcSaturationApplied) << 1 | Number(r.digitalSaturationApplied) << 2))
1041
1120
  return undefined;
1121
+ // #449: the flat stage must reproduce the verification site too.
1122
+ const flat = flatSensorStage(t), fo = flat.out;
1123
+ if (!flatSensorSite(flat, photo.expectedGeneratedElectronCount, darkE, total, x, y, index, seed) || fo.rawCode !== out.rawCode || fo.blackLevelCode !== out.blackLevelCode ||
1124
+ fo.digitalSaturationCode !== out.digitalSaturationCode || fo.flags !== out.flags || fo.whiteCode !== out.whiteCode)
1125
+ return undefined;
1042
1126
  return {
1043
1127
  compute(photoE, duration, sx, sy, siteIndex, siteSeed) {
1044
1128
  // The parse-phase predicates of the full path (claim()): finite non-negative dark and total charge.
1045
- const siteDark = t.darkRate * duration;
1129
+ const siteDark = flat.darkRate * duration;
1046
1130
  if (!Number.isFinite(photoE) || photoE < 0 || !Number.isFinite(siteDark) || siteDark < 0)
1047
1131
  return undefined;
1048
1132
  const siteTotal = chargeTotal(photoE, siteDark);
1049
1133
  if (!Number.isFinite(siteTotal) || siteTotal < 0)
1050
1134
  return undefined;
1051
- // The site's own record: the caller reads only the five RAW fields, so no copy without the realizations (#418).
1052
- return leanSensorSite(t, { photo: { expectedGeneratedElectronCount: photoE }, darkE: siteDark, total: siteTotal, x: sx, y: sy }, siteIndex, siteSeed);
1135
+ // One record for every site (#449): the caller copies the five RAW fields before its next call.
1136
+ return flatSensorSite(flat, photoE, siteDark, siteTotal, sx, sy, siteIndex, siteSeed) ? fo : undefined;
1053
1137
  }
1054
1138
  };
1055
1139
  }