@photivra/engine 2.5.0 → 2.7.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 (87) hide show
  1. package/CHANGELOG.md +129 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +1 -0
  6. package/dist/capture/native-environment-lean.d.ts +1 -1
  7. package/dist/capture/native-environment-lean.js +17 -3
  8. package/dist/capture/native-environment-lean.js.map +1 -1
  9. package/dist/capture/native-environment-raw.d.ts +0 -4
  10. package/dist/capture/native-environment-raw.js +6 -2
  11. package/dist/capture/native-environment-raw.js.map +1 -1
  12. package/dist/color/illuminants.d.ts +15 -0
  13. package/dist/color/illuminants.js +81 -24
  14. package/dist/color/illuminants.js.map +1 -1
  15. package/dist/color/srgb-transfer.d.ts +6 -0
  16. package/dist/color/srgb-transfer.js +9 -0
  17. package/dist/color/srgb-transfer.js.map +1 -1
  18. package/dist/color/white-balance.d.ts +6 -0
  19. package/dist/color/white-balance.js +15 -2
  20. package/dist/color/white-balance.js.map +1 -1
  21. package/dist/core/internal-radiance-evaluator.d.ts +6 -0
  22. package/dist/core/internal-radiance-evaluator.js +33 -0
  23. package/dist/core/internal-radiance-evaluator.js.map +1 -0
  24. package/dist/core/portable-math.d.ts +33 -0
  25. package/dist/core/portable-math.js +342 -0
  26. package/dist/core/portable-math.js.map +1 -1
  27. package/dist/core/version.d.ts +1 -1
  28. package/dist/core/version.js +1 -1
  29. package/dist/core/version.js.map +1 -1
  30. package/dist/facade/chart-radiance-batch.js +3 -2
  31. package/dist/facade/chart-radiance-batch.js.map +1 -1
  32. package/dist/facade/chart-scenes.d.ts +11 -0
  33. package/dist/facade/chart-scenes.js +21 -7
  34. package/dist/facade/chart-scenes.js.map +1 -1
  35. package/dist/facade/diffraction-provider.d.ts +10 -0
  36. package/dist/facade/diffraction-provider.js +38 -17
  37. package/dist/facade/diffraction-provider.js.map +1 -1
  38. package/dist/facade/display-view.d.ts +213 -0
  39. package/dist/facade/display-view.js +1543 -0
  40. package/dist/facade/display-view.js.map +1 -0
  41. package/dist/facade/scene-radiance.d.ts +45 -5
  42. package/dist/facade/scene-radiance.js +97 -28
  43. package/dist/facade/scene-radiance.js.map +1 -1
  44. package/dist/facade/simulate-capture.d.ts +233 -5
  45. package/dist/facade/simulate-capture.js +120 -37
  46. package/dist/facade/simulate-capture.js.map +1 -1
  47. package/dist/facade/simulate-display-view.d.ts +45 -0
  48. package/dist/facade/simulate-display-view.js +214 -0
  49. package/dist/facade/simulate-display-view.js.map +1 -0
  50. package/dist/facade/subject-framing.d.ts +16 -0
  51. package/dist/facade/subject-framing.js +8 -2
  52. package/dist/facade/subject-framing.js.map +1 -1
  53. package/dist/index.d.ts +3 -2
  54. package/dist/index.js +3 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/optics/diffraction-psf-profile.d.ts +9 -2
  57. package/dist/optics/diffraction-psf-profile.js +33 -4
  58. package/dist/optics/diffraction-psf-profile.js.map +1 -1
  59. package/dist/optics/irradiance-convolution.js +1 -1
  60. package/dist/optics/irradiance-convolution.js.map +1 -1
  61. package/dist/optics/scene-to-sensor-irradiance.d.ts +7 -0
  62. package/dist/optics/scene-to-sensor-irradiance.js +10 -5
  63. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  64. package/dist/output/print-plan.d.ts +8 -1
  65. package/dist/output/print-plan.js +8 -1
  66. package/dist/output/print-plan.js.map +1 -1
  67. package/dist/schema/scene-radiance.d.ts +16 -0
  68. package/dist/schema/scene-radiance.js +25 -4
  69. package/dist/schema/scene-radiance.js.map +1 -1
  70. package/dist/sensor/raw-readout.d.ts +12 -0
  71. package/dist/sensor/raw-readout.js +131 -9
  72. package/dist/sensor/raw-readout.js.map +1 -1
  73. package/dist/sensor/stochastic-seed.d.ts +13 -0
  74. package/dist/sensor/stochastic-seed.js +42 -8
  75. package/dist/sensor/stochastic-seed.js.map +1 -1
  76. package/docs/AGENT_GUIDE.md +1 -0
  77. package/docs/API_REFERENCE.md +19 -1
  78. package/docs/QUICK_START.md +203 -4
  79. package/docs/USAGE.md +15 -0
  80. package/docs/api/core-version.md +1 -1
  81. package/docs/api/exports.json +52 -2
  82. package/docs/api/facade-scene-radiance.md +34 -1
  83. package/docs/api/facade-simulate-capture.md +66 -2
  84. package/docs/api/facade-simulate-display-view.md +57 -0
  85. package/docs/api/facade-subject-framing.md +16 -0
  86. package/docs/api/output-print-plan.md +8 -1
  87. package/package.json +3 -2
@@ -23,6 +23,7 @@
23
23
  * levels, like the grey ramp: the step (below 0.1 % of the range) is far below sensor noise.
24
24
  * @see docs/QUICK_START.md for the chart catalogue and their analytic definitions.
25
25
  */
26
+ import { portablePow } from "../core/portable-math.js";
26
27
  import { InvalidConfigurationError } from "../core/configuration-error.js";
27
28
  import { prepareColorimetricSpectrum } from "../color/color-matching-functions.js";
28
29
  import { requireInputObject, requireInputKeys } from "../core/record-validation.js";
@@ -69,7 +70,8 @@ function finiteIn(value, name, minimum, maximum, open = false) {
69
70
  }
70
71
  /** Wavelength range every chart spectrum must cover (the facade's sensor quadrature range). */
71
72
  export const CHART_SPECTRAL_RANGE_NANOMETERS = Object.freeze({ minimum: 380, maximum: 780 });
72
- function material(value, name) {
73
+ /** Internal: validates a chart reflectance input (a neutral number or a reflectance spectrum covering 380–780 nm) as a material. */
74
+ export function resolveChartMaterial(value, name) {
73
75
  if (typeof value === "number")
74
76
  return { kind: "neutral", reflectance: reflectance(value, name) };
75
77
  let spectrum;
@@ -124,6 +126,15 @@ export function resolveChartScene(input) {
124
126
  throw new InvalidConfigurationError("Chart aspect must be positive.");
125
127
  return resolveScene(scene, aspect, { pictureHeight: 2 * aspect, nested: false });
126
128
  }
129
+ /**
130
+ * Internal (not exported from the package): {@link resolveChartScene} with the step wedge's geometric levels computed
131
+ * with the portable power (`portablePow`, within 1 ulp) instead of `**`, whose last bit differs between JavaScript
132
+ * engines. Every other value, the geometry and the material order are the same. The display view uses its materials.
133
+ */
134
+ export function resolvePortableChartScene(input) {
135
+ requireInputObject(input, "resolveChartScene input");
136
+ return resolveScene(input.scene, input.aspect, { pictureHeight: 2 * input.aspect, nested: false, pow: portablePow });
137
+ }
127
138
  function resolveScene(scene, aspect, context) {
128
139
  requireInputObject(scene, "chart scene");
129
140
  const preset = scene.preset;
@@ -163,7 +174,7 @@ function resolveScene(scene, aspect, context) {
163
174
  };
164
175
  switch (preset) {
165
176
  case "uniform": {
166
- const raw = get("reflectance"), m = material(raw, "reflectance");
177
+ const raw = get("reflectance"), m = resolveChartMaterial(raw, "reflectance");
167
178
  return done({ reflectance: typeof raw === "number" ? raw : "caller-spectrum" }, [m], () => 0, [typeof raw === "number" ? undefined : raw]);
168
179
  }
169
180
  case "slanted-edge": {
@@ -209,7 +220,7 @@ function resolveScene(scene, aspect, context) {
209
220
  throw new InvalidConfigurationError("spacing must be equal-density or linear.");
210
221
  if (!(hi > lo) || (spacing === "equal-density" && lo <= 0))
211
222
  throw new InvalidConfigurationError("Step wedge needs 0 < minimum < maximum reflectance.");
212
- const values = calculateStepWedgeReflectances({ steps, minimumReflectance: lo, maximumReflectance: hi, spacing });
223
+ const values = stepWedgeReflectances(steps, lo, hi, spacing, context.pow);
213
224
  return done({ steps, minimumReflectance: lo, maximumReflectance: hi, spacing, stepReflectances: values }, values.map((reflectanceValue) => ({ kind: "neutral", reflectance: reflectanceValue })), (u) => Math.min(steps - 1, Math.max(0, Math.floor((u + 1) / 2 * steps))));
214
225
  }
215
226
  case "grey-ramp": {
@@ -250,7 +261,7 @@ function resolveScene(scene, aspect, context) {
250
261
  if (typeof p.patchId !== "string" || p.patchId.trim() === "" || ids.has(p.patchId))
251
262
  throw new InvalidConfigurationError("Each patch needs a unique non-empty patchId.");
252
263
  ids.add(p.patchId);
253
- materials.push(material(p.reflectance, "patches[" + index + "].reflectance"));
264
+ materials.push(resolveChartMaterial(p.reflectance, "patches[" + index + "].reflectance"));
254
265
  inputs.push(typeof p.reflectance === "number" ? undefined : p.reflectance);
255
266
  }
256
267
  const columnsRaw = record.columns === undefined ? Math.ceil(Math.sqrt(list.length)) : record.columns;
@@ -390,7 +401,7 @@ function resolveScene(scene, aspect, context) {
390
401
  const list = record.elements;
391
402
  if (!Array.isArray(list) || list.length < 1 || list.length > 256)
392
403
  throw new InvalidConfigurationError("elements must list 1 to 256 chart elements.");
393
- const nested = { pictureHeight: context.pictureHeight, nested: true };
404
+ const nested = { pictureHeight: context.pictureHeight, nested: true, ...(context.pow ? { pow: context.pow } : {}) };
394
405
  const background = resolveScene(get("background"), aspect, nested);
395
406
  const materials = [...background.materials], inputs = [...chartMaterialInputs(background)];
396
407
  for (const key of background.defaultedParameters)
@@ -410,7 +421,7 @@ function resolveScene(scene, aspect, context) {
410
421
  if (e.mask === undefined)
411
422
  defaulted.push("elements." + index + ".placement.mask");
412
423
  // The element's own frame: u = ±1 across its width; frequencies keep the composite's picture height.
413
- const chart = resolveScene(e.scene, height / width, { pictureHeight: context.pictureHeight * 2 / width, nested: true });
424
+ const chart = resolveScene(e.scene, height / width, { pictureHeight: context.pictureHeight * 2 / width, nested: true, ...(context.pow ? { pow: context.pow } : {}) });
414
425
  for (const key of chart.defaultedParameters)
415
426
  defaulted.push("elements." + index + ".parameters." + key);
416
427
  const offset = materials.length;
@@ -636,11 +647,14 @@ export function calculateStepWedgeReflectances(input) {
636
647
  throw new InvalidConfigurationError("spacing must be equal-density or linear.");
637
648
  if (!(hi > lo) || (input.spacing === "equal-density" && lo <= 0))
638
649
  throw new InvalidConfigurationError("Step wedge needs 0 < minimum < maximum reflectance.");
650
+ return stepWedgeReflectances(steps, lo, hi, input.spacing, undefined);
651
+ }
652
+ function stepWedgeReflectances(steps, lo, hi, spacing, pow) {
639
653
  return Array.from({ length: steps }, (_, k) => {
640
654
  if (k === steps - 1)
641
655
  return hi;
642
656
  const t = k / (steps - 1);
643
- return input.spacing === "linear" ? lo + (hi - lo) * t : lo * (hi / lo) ** t;
657
+ return spacing === "linear" ? lo + (hi - lo) * t : lo * (pow ? pow(hi / lo, t) : (hi / lo) ** t);
644
658
  });
645
659
  }
646
660
  /** Centres (frame units) of a point grid: one disk centred in each of columns × rows equal cells, row-major from the top-left. */