@photivra/engine 2.21.0 → 2.23.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 (172) hide show
  1. package/CHANGELOG.md +150 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +11 -0
  6. package/dist/capture/c2pa-embed.d.ts +116 -0
  7. package/dist/capture/c2pa-embed.js +493 -0
  8. package/dist/capture/c2pa-embed.js.map +1 -0
  9. package/dist/capture/dng-import.d.ts +73 -8
  10. package/dist/capture/dng-import.js +315 -80
  11. package/dist/capture/dng-import.js.map +1 -1
  12. package/dist/capture/dng-opcodes.d.ts +96 -0
  13. package/dist/capture/dng-opcodes.js +391 -0
  14. package/dist/capture/dng-opcodes.js.map +1 -0
  15. package/dist/capture/dng-profile-maps.d.ts +99 -0
  16. package/dist/capture/dng-profile-maps.js +381 -0
  17. package/dist/capture/dng-profile-maps.js.map +1 -0
  18. package/dist/capture/dng-stage-image.d.ts +23 -0
  19. package/dist/capture/dng-stage-image.js +65 -0
  20. package/dist/capture/dng-stage-image.js.map +1 -0
  21. package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
  22. package/dist/capture/jpeg-xl-tiles.js +151 -0
  23. package/dist/capture/jpeg-xl-tiles.js.map +1 -0
  24. package/dist/capture/lossy-jpeg.d.ts +17 -0
  25. package/dist/capture/lossy-jpeg.js +369 -0
  26. package/dist/capture/lossy-jpeg.js.map +1 -0
  27. package/dist/capture/native-raw-development.d.ts +2 -1
  28. package/dist/capture/native-raw-development.js.map +1 -1
  29. package/dist/color/neighbor-highlight-reconstruction.d.ts +76 -0
  30. package/dist/color/neighbor-highlight-reconstruction.js +257 -0
  31. package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
  32. package/dist/color/sensor-color-development.d.ts +5 -0
  33. package/dist/color/sensor-color-development.js +7 -1
  34. package/dist/color/sensor-color-development.js.map +1 -1
  35. package/dist/color/spectral-spikiness.d.ts +11 -0
  36. package/dist/color/spectral-spikiness.js +23 -0
  37. package/dist/color/spectral-spikiness.js.map +1 -0
  38. package/dist/core/execution-error.d.ts +21 -0
  39. package/dist/core/execution-error.js +12 -0
  40. package/dist/core/execution-error.js.map +1 -0
  41. package/dist/core/version.d.ts +1 -1
  42. package/dist/core/version.js +1 -1
  43. package/dist/core/version.js.map +1 -1
  44. package/dist/facade/catalogue-lens-signal.d.ts +26 -1
  45. package/dist/facade/catalogue-lens-signal.js +25 -7
  46. package/dist/facade/catalogue-lens-signal.js.map +1 -1
  47. package/dist/facade/diffraction-provider.d.ts +1 -1
  48. package/dist/facade/diffraction-provider.js +4 -4
  49. package/dist/facade/diffraction-provider.js.map +1 -1
  50. package/dist/facade/display-view-gpu-session.js +38 -6
  51. package/dist/facade/display-view-gpu-session.js.map +1 -1
  52. package/dist/facade/display-view-gpu.d.ts +13 -5
  53. package/dist/facade/display-view-gpu.js.map +1 -1
  54. package/dist/facade/display-view-normal.d.ts +15 -0
  55. package/dist/facade/display-view-normal.js +44 -0
  56. package/dist/facade/display-view-normal.js.map +1 -0
  57. package/dist/facade/display-view-rendering.d.ts +102 -0
  58. package/dist/facade/display-view-rendering.js +633 -0
  59. package/dist/facade/display-view-rendering.js.map +1 -0
  60. package/dist/facade/display-view.d.ts +37 -65
  61. package/dist/facade/display-view.js +147 -476
  62. package/dist/facade/display-view.js.map +1 -1
  63. package/dist/facade/expected-signal-setup.js +2 -2
  64. package/dist/facade/expected-signal-setup.js.map +1 -1
  65. package/dist/facade/fast-methods.d.ts +1 -1
  66. package/dist/facade/fast-methods.js +37 -8
  67. package/dist/facade/fast-methods.js.map +1 -1
  68. package/dist/facade/generic-lens-capture.d.ts +1 -1
  69. package/dist/facade/generic-lens-capture.js +1 -1
  70. package/dist/facade/generic-lens-capture.js.map +1 -1
  71. package/dist/facade/image-plane.js +41 -10
  72. package/dist/facade/image-plane.js.map +1 -1
  73. package/dist/facade/on-demand-kits.d.ts +14 -0
  74. package/dist/facade/on-demand-kits.js +36 -0
  75. package/dist/facade/on-demand-kits.js.map +1 -0
  76. package/dist/facade/opt-in-kit.d.ts +9 -0
  77. package/dist/facade/opt-in-kit.js +11 -0
  78. package/dist/facade/opt-in-kit.js.map +1 -0
  79. package/dist/facade/quality-kit.d.ts +21 -0
  80. package/dist/facade/quality-kit.js +23 -0
  81. package/dist/facade/quality-kit.js.map +1 -0
  82. package/dist/facade/quality-paths.d.ts +22 -0
  83. package/dist/facade/quality-paths.js +44 -0
  84. package/dist/facade/quality-paths.js.map +1 -0
  85. package/dist/facade/scene-radiance.d.ts +18 -2
  86. package/dist/facade/scene-radiance.js +6 -4
  87. package/dist/facade/scene-radiance.js.map +1 -1
  88. package/dist/facade/simulate-capture.d.ts +32 -13
  89. package/dist/facade/simulate-capture.js +162 -69
  90. package/dist/facade/simulate-capture.js.map +1 -1
  91. package/dist/facade/simulate-display-view.d.ts +10 -12
  92. package/dist/facade/simulate-display-view.js +57 -48
  93. package/dist/facade/simulate-display-view.js.map +1 -1
  94. package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
  95. package/dist/gpu/display-view-gpu-renderer.js +134 -0
  96. package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
  97. package/dist/gpu/display-view-image-material-module.d.ts +3 -2
  98. package/dist/gpu/display-view-image-material-module.js +25 -3
  99. package/dist/gpu/display-view-image-material-module.js.map +1 -1
  100. package/dist/gpu/display-view-image-quality.d.ts +43 -1
  101. package/dist/gpu/display-view-image-quality.js +256 -11
  102. package/dist/gpu/display-view-image-quality.js.map +1 -1
  103. package/dist/gpu/display-view-quality-gpu.d.ts +13 -64
  104. package/dist/gpu/display-view-quality-gpu.js +55 -240
  105. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  106. package/dist/gpu/display-view-speed-gpu.d.ts +16 -16
  107. package/dist/gpu/display-view-speed-gpu.js +62 -19
  108. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  109. package/dist/gpu/image-kernel-accelerator.js +3 -0
  110. package/dist/gpu/image-kernel-accelerator.js.map +1 -1
  111. package/dist/gpu/image-kernel-execution.d.ts +2 -13
  112. package/dist/gpu/image-kernel-execution.js +2 -10
  113. package/dist/gpu/image-kernel-execution.js.map +1 -1
  114. package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +13 -0
  115. package/dist/gpu/wgsl-display-view-coupled-stub.js +29 -0
  116. package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
  117. package/dist/gpu/wgsl-display-view-coupled.d.ts +91 -0
  118. package/dist/gpu/wgsl-display-view-coupled.js +365 -0
  119. package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
  120. package/dist/gpu/wgsl-display-view-neighbors.d.ts +46 -0
  121. package/dist/gpu/wgsl-display-view-neighbors.js +114 -0
  122. package/dist/gpu/wgsl-display-view-neighbors.js.map +1 -0
  123. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +6 -9
  124. package/dist/gpu/wgsl-display-view-quality-kernel.js +113 -113
  125. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  126. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +1 -41
  127. package/dist/gpu/wgsl-display-view-speed-kernel.js +73 -10
  128. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  129. package/dist/index.d.ts +11 -3
  130. package/dist/index.js +9 -1
  131. package/dist/index.js.map +1 -1
  132. package/dist/output/camera-channel-response.d.ts +50 -0
  133. package/dist/output/camera-channel-response.js +69 -0
  134. package/dist/output/camera-channel-response.js.map +1 -0
  135. package/dist/output/native-capture-sdr.js +5 -2
  136. package/dist/output/native-capture-sdr.js.map +1 -1
  137. package/dist/output/sdr-rendering.d.ts +18 -1
  138. package/dist/output/sdr-rendering.js +39 -2
  139. package/dist/output/sdr-rendering.js.map +1 -1
  140. package/dist/sensor/native-sensor-report.d.ts +10 -0
  141. package/dist/sensor/native-sensor-report.js +16 -0
  142. package/dist/sensor/native-sensor-report.js.map +1 -0
  143. package/dist/sensor/sensor-characteristics.d.ts +81 -13
  144. package/dist/sensor/sensor-characteristics.js +10 -6
  145. package/dist/sensor/sensor-characteristics.js.map +1 -1
  146. package/docs/API_REFERENCE.md +117 -4
  147. package/docs/QUICK_START.md +37 -8
  148. package/docs/USAGE.md +105 -22
  149. package/docs/api/capture-c2pa-embed.md +177 -0
  150. package/docs/api/capture-dng-import.md +86 -10
  151. package/docs/api/capture-dng-opcodes.md +93 -0
  152. package/docs/api/capture-dng-profile-maps.md +141 -0
  153. package/docs/api/capture-dng-stage-image.md +43 -0
  154. package/docs/api/capture-jpeg-xl-tiles.md +33 -0
  155. package/docs/api/capture-lossy-jpeg.md +41 -0
  156. package/docs/api/capture-native-raw-development.md +2 -1
  157. package/docs/api/core-execution-error.md +43 -0
  158. package/docs/api/core-version.md +1 -1
  159. package/docs/api/exports.json +255 -5
  160. package/docs/api/facade-display-view-gpu.md +5 -2
  161. package/docs/api/facade-display-view.md +11 -15
  162. package/docs/api/facade-fast-methods.md +38 -9
  163. package/docs/api/facade-quality-paths.md +24 -0
  164. package/docs/api/facade-scene-radiance.md +1 -1
  165. package/docs/api/facade-simulate-capture.md +24 -7
  166. package/docs/api/facade-simulate-display-view.md +10 -12
  167. package/docs/api/gpu-image-kernel-execution.md +0 -36
  168. package/docs/api/output-camera-channel-response.md +55 -0
  169. package/docs/api/output-sdr-rendering.md +6 -1
  170. package/docs/api/sensor-sensor-characteristics.md +54 -2
  171. package/llms.txt +1 -0
  172. package/package.json +6 -5
@@ -25,6 +25,15 @@
25
25
  * point is flagged: on the print's edge, near a nearest-pixel edge, or where a bilinear weight moved by the point's error could
26
26
  * change the value by more than E of it (the Speed module's rule, per node).
27
27
  *
28
+ * **Mixed light (#597).** Under `illumination` W_n is replaced by the irradiance / π at the point, E_n(p) = Σ_i w_i(p)·T_i,n +
29
+ * Σ_j c_j,n·h_j/d_j³ (src/facade/mixed-illumination.ts), from the light table (`imagePlaneLightTable`: the parts and the spectra at
30
+ * the nodes, no channel value). The blend's position t is formed in double-float32 with te, its error beyond its own low part; each
31
+ * weight comes from its own distance to the segment's ends (rounded once: E relative, plus te times the segment's slope); a regions
32
+ * edge, or a hard edge of equal stops, within the point's bound flags it; a choice of the neighbouring segment moves the weights by
33
+ * at most te times the steepest slope; each point source's h/d³ is a bounded value (B arithmetic). The node value's computed bound
34
+ * (the tables' rounding, the weights, the products and the sums) must stay within (IMAGE_PLANE_LIGHT_U − 1)·E of it, else the point
35
+ * is flagged, so a lit point carries (IMAGE_PLANE_LATTICE_U + IMAGE_PLANE_LIGHT_U)·E relative.
36
+ *
28
37
  * **Flagged points.** With site kernels the reference evaluates the lattice formula itself, so the host replaces a flagged
29
38
  * point's values with the reference's own `radianceAt` there and clears its flag (as #492 resolves uncertain chart materials).
30
39
  * Without kernels the reference reads the environment's own query points, so a site that reads a flagged point (with a nonzero
@@ -43,17 +52,237 @@ import { DISPLAY_VIEW_QUALITY_COMMON_WGSL, DISPLAY_VIEW_QUALITY_GROUP_WORDS, DIS
43
52
  import { latticeCoordinate } from "./display-view-quality-gpu.js";
44
53
  import { queryGeometry } from "./display-view-speed-gpu.js";
45
54
  import { displayViewSignalTables } from "../facade/expected-signal.js";
55
+ import { calculateIlluminantIrradianceScale } from "../facade/scene-radiance.js";
46
56
  const E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;
47
57
  /** Relative roundings of a lattice point's radiance (see the module comment). */
48
58
  export const IMAGE_PLANE_LATTICE_U = 32;
59
+ /**
60
+ * #597: relative roundings of the mixed light at a lattice point (a point whose computed bound exceeds IMAGE_PLANE_LIGHT_U − 1 is
61
+ * flagged; see the module comment), added to IMAGE_PLANE_LATTICE_U for a lit print.
62
+ */
63
+ export const IMAGE_PLANE_LIGHT_U = 40;
64
+ /** #597: layout of the light table (float32 / u32 words; see `imagePlaneLightTable`). */
65
+ export const IMAGE_PLANE_LIGHT = /* @__PURE__ */ Object.freeze({ kind: 0, sources: 1, points: 2, regions: 3, outside: 4, regionsAt: 6, pointsAt: 7, tablesAt: 8, slack: 9,
66
+ surround: 10, maximumInverse: 11, halfHi: 12, halfLo: 13, aU: 16, aV: 18, dU: 20, dV: 22, inverse: 24, stops: 26, regionWords: 9, pointWords: 6, maximumPoints: 8 });
49
67
  /** Lattice nodes of the print (5 × 5 × 5). */
50
68
  const NODES = 125;
51
69
  /** Words per pixel record. */
52
70
  const RECORD_WORDS = 4;
53
71
  /** Layout of the photo uniform (u32 / float32 words, 96 bytes). */
54
- export const IMAGE_PLANE_QUALITY_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, nodes: 3, row0: 4, zero: 5, suHi: 8, suLo: 9, svHi: 10, svLo: 11,
72
+ export const IMAGE_PLANE_QUALITY_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, nodes: 3, row0: 4, zero: 5, lit: 6, suHi: 8, suLo: 9, svHi: 10, svLo: 11,
55
73
  kHi: 12, kLo: 13, halfWHi: 14, halfWLo: 15, halfHHi: 16, halfHLo: 17, aspectHi: 18, aspectLo: 19, slackX: 20, slackY: 21, black: 22, span: 23, words: 24 });
56
74
  const FLOAT32_SAFE_MAGNITUDE = 2 ** 120, FLOAT32_MINIMUM_NORMAL = 2 ** -126;
75
+ const L = IMAGE_PLANE_LIGHT;
76
+ /**
77
+ * #597: the mixed light at a lattice point (see the module comment): `light_prepare` once per point (the blend's source and weights
78
+ * with their error bound, and each point source's h/d³ as a bounded value), then `light_at(n)` per node, flagging the point when the
79
+ * computed bound exceeds (IMAGE_PLANE_LIGHT_U − 1)·E of the value.
80
+ */
81
+ const LIGHT_WGSL = /* wgsl */ `
82
+ var<private> blend_i: u32;
83
+ var<private> blend_w0: f32;
84
+ var<private> blend_w1: f32;
85
+ var<private> blend_e0: f32;
86
+ var<private> blend_e1: f32;
87
+ var<private> blend_amb: f32;
88
+ var<private> fall_v: array<f32, ${L.maximumPoints}>;
89
+ var<private> fall_e: array<f32, ${L.maximumPoints}>;
90
+ fn lw(i: u32) -> u32 { return bitcast<u32>(light[i]); }
91
+ fn ld(i: u32) -> D { return D(light[i], light[i + 1u]); }
92
+
93
+ fn d_gt(a: D, b: D) -> bool { let d = d_sub(a, b); return d.h > 0.0 || (d.h == 0.0 && d.l > 0.0); }
94
+
95
+ fn bsqrt(a: B) -> B {
96
+ let v = sqrt(max(a.v, 0.0));
97
+ let low = a.v - SAFETY * a.e;
98
+ if (!(low > 0.0)) { material_uncertain = true; return B(v, 3.0e38); }
99
+ return B(v, a.e / (2.0 * sqrt(low)) + 5.0 * E * v);
100
+ }
101
+ fn light_prepare(u: D, v: D) {
102
+ let kind = lw(${L.kind}u);
103
+ let S = lw(${L.sources}u);
104
+ blend_i = 0u; blend_w0 = 1.0; blend_w1 = 0.0; blend_e0 = 0.0; blend_e1 = 0.0; blend_amb = 0.0;
105
+
106
+ let ce = 4.0 * DW * (abs(u.h) + abs(v.h) + 1.0) + light[${L.slack}u];
107
+ if (kind == 3u) {
108
+ var source = lw(${L.outside}u);
109
+ let bu = bd(u);
110
+ let bv = bd(v);
111
+ for (var r = 0u; r < lw(${L.regions}u); r = r + 1u) {
112
+ let b = lw(${L.regionsAt}u) + r * ${L.regionWords}u;
113
+ if (ge(bu, bd(ld(b + 1u))) && le(bu, bd(ld(b + 3u))) && ge(bv, bd(ld(b + 5u))) && le(bv, bd(ld(b + 7u)))) { source = lw(b); break; }
114
+ }
115
+ blend_i = source;
116
+ } else if (kind != 0u) {
117
+
118
+ var t: D;
119
+ var te: f32;
120
+ let a_u = d_sub(u, ld(${L.aU}u));
121
+ let a_v = d_sub(v, ld(${L.aV}u));
122
+ let inv = ld(${L.inverse}u);
123
+ let ce_t = ce + 4.0 * DW * (abs(light[${L.aU}u]) + abs(light[${L.aV}u]));
124
+ if (kind == 1u) {
125
+ let x = d_mul(a_u, ld(${L.dU}u));
126
+ let y = d_mul(a_v, ld(${L.dV}u));
127
+ let scale = (abs(x.h) + abs(y.h)) * abs(inv.h);
128
+ t = d_mul(d_add(x, y), inv);
129
+ te = 8.0 * DW * (scale + 1.0) + ce_t * (abs(light[${L.dU}u]) + abs(light[${L.dV}u])) * abs(inv.h);
130
+ if (t.h < 0.0) { t = D(0.0, 0.0); }
131
+ if (d_gt(t, D(1.0, 0.0))) { t = D(1.0, 0.0); }
132
+ } else {
133
+
134
+ let dd = d_add(d_mul(a_u, a_u), d_mul(a_v, a_v));
135
+ let r0 = sqrt(max(dd.h, 0.0));
136
+ var r = D(r0, 0.0);
137
+ if (r0 > 0.0) { let res = d_sub(dd, d_mul(D(r0, 0.0), D(r0, 0.0))); r = quick_two_sum(r0, ob(ob(res.h + res.l) / ob(2.0 * r0))); }
138
+ t = d_mul(r, inv);
139
+ te = 8.0 * DW * (abs(t.h) + 1.0) + 2.0 * ce_t * abs(inv.h);
140
+ if (d_gt(t, D(1.0, 0.0))) { t = D(1.0, 0.0); }
141
+ }
142
+
143
+ var i = 0u;
144
+ loop { if (i + 1u >= S) { break; } if (d_gt(ld(${L.stops}u + (i + 1u) * 4u), t)) { break; } i = i + 1u; }
145
+ for (var k = 0u; k + 1u < S; k = k + 1u) {
146
+ if (light[${L.stops}u + k * 4u + 2u] == 0.0 && abs(d_sub(t, ld(${L.stops}u + k * 4u)).h) <= SAFETY * te + DW * (abs(t.h) + 1.0)) { material_uncertain = true; }
147
+ }
148
+ let si = ld(${L.stops}u + i * 4u);
149
+
150
+ blend_amb = te * light[${L.maximumInverse}u];
151
+ if (i + 1u < S && d_gt(t, si)) {
152
+
153
+ let inv_i = ld(${L.stops}u + i * 4u + 2u);
154
+ let fd = d_mul(d_sub(t, si), inv_i);
155
+ let gd = d_mul(d_sub(ld(${L.stops}u + (i + 1u) * 4u), t), inv_i);
156
+ blend_w1 = clamp(ob(fd.h + fd.l), 0.0, 1.0);
157
+ blend_w0 = clamp(ob(gd.h + gd.l), 0.0, 1.0);
158
+ let shared_e = (te + 4.0 * DW * (abs(t.h) + abs(si.h) + 1.0)) * abs(inv_i.h);
159
+ blend_e1 = E * blend_w1 + shared_e;
160
+ blend_e0 = E * blend_w0 + shared_e;
161
+ }
162
+ blend_i = i;
163
+ }
164
+
165
+ let half = ld(${L.halfHi}u);
166
+ let x = d_mul(u, half);
167
+ let y = d_mul(v, half);
168
+ let cm = ce * abs(half.h) + 4.0 * DW * (abs(x.h) + abs(y.h));
169
+ for (var j = 0u; j < lw(${L.points}u); j = j + 1u) {
170
+ let b = lw(${L.pointsAt}u) + j * ${L.pointWords}u;
171
+ var dx = bds(d_sub(x, ld(b)), abs(x.h) + abs(light[b]));
172
+ dx.e = dx.e + cm;
173
+ var dy = bds(d_sub(y, ld(b + 2u)), abs(y.h) + abs(light[b + 2u]));
174
+ dy.e = dy.e + cm;
175
+ let h = bd(ld(b + 4u));
176
+ let d2 = badd(badd(bmul(dx, dx), bmul(dy, dy)), bmul(h, h));
177
+ let fall = bdiv(h, bmul(d2, bsqrt(d2)));
178
+ fall_v[j] = fall.v;
179
+ fall_e[j] = fall.e;
180
+ }
181
+ }
182
+
183
+ fn light_at(n: u32) -> f32 {
184
+ let N = photo.nodes;
185
+ let S = lw(${L.sources}u);
186
+ let P = lw(${L.points}u);
187
+ let tab = lw(${L.tablesAt}u);
188
+ var value = 0.0;
189
+ var err = 0.0;
190
+ if (S > 0u) {
191
+ let ti = light[tab + blend_i * N + n];
192
+ var top = ti;
193
+ for (var s = 0u; s < S; s = s + 1u) { top = max(top, light[tab + s * N + n]); }
194
+ var tj = 0.0;
195
+ if (blend_w1 == 0.0) { value = ti; } else { tj = light[tab + (blend_i + 1u) * N + n]; value = blend_w0 * ti + blend_w1 * tj; }
196
+ err = 3.0 * E * value + blend_e0 * ti + blend_e1 * tj + blend_amb * top;
197
+ }
198
+ for (var j = 0u; j < P; j = j + 1u) {
199
+ let c = light[tab + (S + j) * N + n];
200
+ let term = c * fall_v[j];
201
+ value = value + term;
202
+ err = err + c * fall_e[j] + 2.0 * E * term;
203
+ }
204
+ err = err + f32(P + 1u) * E * value;
205
+ if (!(err <= ${IMAGE_PLANE_LIGHT_U - 1}.0 * E * value)) { material_uncertain = true; }
206
+ return value;
207
+ }
208
+ `;
209
+ /**
210
+ * #597: the light table of a lit print at the spectral nodes (the layout `IMAGE_PLANE_LIGHT`): the blend (kind, sources, its geometry
211
+ * and stops with each segment's inverse width, 0 at a hard edge), the regions, the point sources (x, y, h in metres), then per node
212
+ * T_i,n = k_i·S_i(λ_n)/π for each blend source and c_j·S_j(λ_n)/π for each point source: spectra only, no channel value. `slack` is the
213
+ * reference's own rounding of a query point in print-local units.
214
+ */
215
+ export function imagePlaneLightTable(light, wavelengths, surroundReflectance, slack) {
216
+ const resolved = light.layout;
217
+ if (!resolved)
218
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The print's mixed light carries no parts for the GPU; use cpu-reference.");
219
+ // The blend's sources come first, then the point sources (their report carries the position and the intensity, c = I·k(1 lux)).
220
+ const blend = resolved.blend;
221
+ const sources = resolved.sources.filter((s) => s.report.position === undefined), points = resolved.sources.filter((s) => s.report.position !== undefined).map((s) => {
222
+ const position = s.report.position;
223
+ return { ...position, scale: calculateIlluminantIrradianceScale(s.illuminant.shape, 1) * s.report.intensityCandela, shape: s.illuminant.shape };
224
+ });
225
+ const S = sources.length, P = points.length, N = wavelengths.length, regions = blend?.regions ?? [];
226
+ if (P > L.maximumPoints)
227
+ throw new ImageKernelExecutionError("unsupported-on-backend", "More point sources than the GPU pass holds.");
228
+ const regionsAt = L.stops + 4 * S, pointsAt = regionsAt + L.regionWords * regions.length, tablesAt = pointsAt + L.pointWords * P;
229
+ const table = new Float32Array(tablesAt + (S + P) * N), words = new Uint32Array(table.buffer);
230
+ const pair = (at, x) => { [table[at], table[at + 1]] = dwSplit(x); };
231
+ words[L.kind] = blend === null ? 0 : blend.kind === "linear-gradient" ? 1 : blend.kind === "radial" ? 2 : 3;
232
+ words[L.sources] = S;
233
+ words[L.points] = P;
234
+ words[L.regions] = regions.length;
235
+ words[L.outside] = blend?.outsideSource ?? 0;
236
+ words[L.regionsAt] = regionsAt;
237
+ words[L.pointsAt] = pointsAt;
238
+ words[L.tablesAt] = tablesAt;
239
+ table[L.slack] = slack;
240
+ table[L.surround] = surroundReflectance;
241
+ pair(L.halfHi, light.halfWidthM);
242
+ if (blend?.kind === "linear-gradient") {
243
+ const from = blend.from, du = blend.to.u - from.u, dv = blend.to.v - from.v;
244
+ pair(L.aU, from.u);
245
+ pair(L.aV, from.v);
246
+ pair(L.dU, du);
247
+ pair(L.dV, dv);
248
+ pair(L.inverse, 1 / (du * du + dv * dv));
249
+ }
250
+ else if (blend?.kind === "radial") {
251
+ pair(L.aU, blend.center.u);
252
+ pair(L.aV, blend.center.v);
253
+ pair(L.inverse, 1 / blend.radius);
254
+ }
255
+ let maximumInverse = 0;
256
+ const stops = blend?.stops ?? [];
257
+ stops.forEach((stop, i) => {
258
+ const next = stops[i + 1], inverse = next !== undefined && next > stop ? 1 / (next - stop) : 0;
259
+ pair(L.stops + 4 * i, stop);
260
+ pair(L.stops + 4 * i + 2, inverse);
261
+ maximumInverse = Math.max(maximumInverse, inverse);
262
+ });
263
+ table[L.maximumInverse] = maximumInverse;
264
+ regions.forEach((r, i) => {
265
+ const at = regionsAt + L.regionWords * i;
266
+ words[at] = r.source;
267
+ pair(at + 1, r.uMin);
268
+ pair(at + 3, r.uMax);
269
+ pair(at + 5, r.vMin);
270
+ pair(at + 7, r.vMax);
271
+ });
272
+ points.forEach((p, j) => { const at = pointsAt + L.pointWords * j; pair(at, p.xM); pair(at + 2, p.yM); pair(at + 4, p.heightM); });
273
+ // The largest irradiance / π anywhere (the blend's largest source and every point at normal incidence, 1/h²), for the tiny-value bound.
274
+ let largestIrradiance = 0;
275
+ wavelengths.forEach((nm, n) => {
276
+ let top = 0, atNormal = 0;
277
+ sources.forEach((source, i) => { const value = source.irradianceScale * source.illuminant.shape.evaluate(nm) / Math.PI; table[tablesAt + i * N + n] = value; top = Math.max(top, value); });
278
+ points.forEach((p, j) => { const value = p.scale * p.shape.evaluate(nm) / Math.PI; table[tablesAt + (S + j) * N + n] = value; atNormal += value / (p.heightM * p.heightM); });
279
+ largestIrradiance = Math.max(largestIrradiance, top + atNormal);
280
+ });
281
+ for (let i = tablesAt; i < table.length; i++)
282
+ if (!(table[i] >= 0 && table[i] <= FLOAT32_SAFE_MAGNITUDE))
283
+ throw new ImageKernelExecutionError("unsupported-float32-domain", "A light's spectrum is outside the float32 domain.");
284
+ return { table, largestIrradiance };
285
+ }
57
286
  /** The display-view helpers of the composite kernel's common part, with the barrier word read from the photo uniform. */
58
287
  const photoCommon = (path) => displayViewModuleCommonWgsl(path).replace(/\blattice\.zero\b/gu, "photo.zero");
59
288
  /** Pass 1's WGSL (see the module comment). */
@@ -63,14 +292,16 @@ export function imagePlaneQualityLatticeWgsl(path) {
63
292
  @group(0) @binding(2) var<storage, read_write> lattice_values: array<f32>;
64
293
  @group(0) @binding(3) var<storage, read_write> lattice_flags: array<u32>;
65
294
  struct PhotoParams {
66
- width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32, pad0: u32, pad1: u32,
295
+ width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32, lit: u32, pad1: u32,
67
296
  su_hi: f32, su_lo: f32, sv_hi: f32, sv_lo: f32, k_hi: f32, k_lo: f32, hw_hi: f32, hw_lo: f32,
68
297
  hh_hi: f32, hh_lo: f32, aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32, black: f32, span: f32,
69
298
  };
70
299
  @group(1) @binding(0) var<storage, read> records: array<u32>;
71
300
  @group(1) @binding(1) var<uniform> photo: PhotoParams;
72
301
  @group(1) @binding(2) var<storage, read> node_table: array<f32>;
302
+ @group(1) @binding(3) var<storage, read> light: array<f32>;
73
303
  ${photoCommon(path)}
304
+ ${LIGHT_WGSL}
74
305
  fn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }
75
306
  struct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }
76
307
  fn bilinear_axis(s: D, n: u32) -> Axis {
@@ -96,6 +327,8 @@ fn pixel_at(i: u32, j: u32) -> Pixel {
96
327
  channel_weights(w, 7u, bitcast<f32>(records[o + 2u])), channel_weights(w, 8u, bitcast<f32>(records[o + 3u])));
97
328
  }
98
329
  fn node(k: u32, n: u32) -> f32 { return node_table[k * photo.nodes + n]; }
330
+ // The white radiance at node n, or (#597) under mixed light the irradiance / π at the point.
331
+ fn white_at(white: u32, n: u32) -> f32 { if (photo.lit != 0u) { return light_at(n); } return node_table[white + n]; }
99
332
  // One pixel's reflectance at node n: the reference's nested trilinear form, then the paper.
100
333
  fn pixel_value(px: Pixel, n: u32) -> f32 {
101
334
  let b = px.base;
@@ -122,8 +355,12 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
122
355
  let u = d_mul(d_mul(D(f32(p) + bitcast<f32>(groups[base + 6u]), 0.0), D(photo.su_hi, photo.su_lo)), k);
123
356
  let v = d_mul(d_mul(D(f32(q) + bitcast<f32>(groups[base + 7u]), 0.0), D(photo.sv_hi, photo.sv_lo)), k);
124
357
  let aspect = D(photo.aspect_hi, photo.aspect_lo);
358
+ // #597: under mixed light the irradiance at the point (its blend and point sources) replaces the white radiance per node.
359
+ if (photo.lit != 0u) { light_prepare(u, v); }
125
360
  if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) {
126
- for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = node_table[white + N + n]; }
361
+ for (var n = 0u; n < N; n = n + 1u) {
362
+ if (photo.lit != 0u) { lattice_values[out + n] = light[${L.surround}u] * light_at(n); } else { lattice_values[out + n] = node_table[white + N + n]; }
363
+ }
127
364
  lattice_flags[i] = select(0u, 1u, material_uncertain);
128
365
  return;
129
366
  }
@@ -133,7 +370,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
133
370
  let last_x = f32(photo.width - 1u);
134
371
  let last_y = f32(photo.height - 1u);
135
372
  let px = pixel_at(u32(clamp(bfloor(bd(x), 1.0, last_x), 0.0, last_x)), u32(clamp(bfloor(bd(y), 1.0, last_y), 0.0, last_y)));
136
- for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = node_table[white + n] * pixel_value(px, n); }
373
+ for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = white_at(white, n) * pixel_value(px, n); }
137
374
  } else {
138
375
  let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);
139
376
  let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);
@@ -149,7 +386,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
149
386
  let s11 = pixel_value(p11, n);
150
387
  let value = ay.w0 * (ax.w0 * s00 + ax.w1 * s10) + ay.w1 * (ax.w0 * s01 + ax.w1 * s11);
151
388
  if (!(slack * max(max(s00, s10), max(s01, s11)) <= E * value)) { material_uncertain = true; }
152
- lattice_values[out + n] = node_table[white + n] * value;
389
+ lattice_values[out + n] = white_at(white, n) * value;
153
390
  }
154
391
  }
155
392
  lattice_flags[i] = select(0u, 1u, material_uncertain);
@@ -214,7 +451,7 @@ write_site(i, c, e, params.site_gamma * e + params.site_tiny, select(0u, 1u, fla
214
451
  * members' span), the lattice extent beyond the site window and the point constants, and the table C_n,c(t) at (n·3 + c)·T + t,
215
452
  * the reference's float64 values rounded once.
216
453
  */
217
- export function imagePlaneQualityGather(signalInput) {
454
+ export function imagePlaneQualityGather(signalInput, largestLitRadiance) {
218
455
  const tables = displayViewSignalTables(signalInput), W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0;
219
456
  const N = signalInput.wavelengthsNm.length, A = signalInput.electronsPerRadiance, groups = [];
220
457
  const rects = tables.groups.map((group) => {
@@ -251,8 +488,11 @@ export function imagePlaneQualityGather(signalInput) {
251
488
  }
252
489
  });
253
490
  const image = signalInput.scene.image;
254
- for (const w of image.whiteRadiance)
255
- largestRadiance = Math.max(largestRadiance, w);
491
+ if (largestLitRadiance !== undefined)
492
+ largestRadiance = largestLitRadiance;
493
+ else
494
+ for (const w of image.whiteRadiance)
495
+ largestRadiance = Math.max(largestRadiance, w);
256
496
  let tinyMass = 0;
257
497
  for (let t = 0; t < T; t++)
258
498
  tinyMass += tiny[t] * largestRadiance * N;
@@ -263,7 +503,9 @@ export function imagePlaneQualityPasses(runtime, gpu, path) {
263
503
  const signalInput = gpu.signalInput, image = signalInput.scene.image;
264
504
  if (!image || !gpu.placement)
265
505
  throw new ImageKernelExecutionError("unsupported-on-backend", "The setup carries no image-plane print and placement; use cpu-reference.");
266
- const scene = image.scene, N = signalInput.wavelengthsNm.length, gather = imagePlaneQualityGather(signalInput);
506
+ // #597: under mixed light, the light's table (its parts and spectra at the nodes), which replaces the white radiance in pass 1.
507
+ const k0 = gpu.placement.chartUnitsPerFrameUnit, lit = image.light ? imagePlaneLightTable(image.light, signalInput.wavelengthsNm, image.surroundReflectance, 1e-14 * k0) : undefined;
508
+ const scene = image.scene, N = signalInput.wavelengthsNm.length, gather = imagePlaneQualityGather(signalInput, lit?.largestIrradiance);
267
509
  let cw = 0, ch = 0;
268
510
  for (const g of gather.groups) {
269
511
  cw = Math.max(cw, g.cw);
@@ -287,6 +529,8 @@ export function imagePlaneQualityPasses(runtime, gpu, path) {
287
529
  words[P.nearest] = scene.interpolation === "nearest" ? 1 : 0;
288
530
  words[P.nodes] = N;
289
531
  words[P.zero] = 0;
532
+ words[P.lit] = lit ? 1 : 0;
533
+ const lightTable = lit ? lit.table : new Float32Array(1);
290
534
  [floats[P.suHi], floats[P.suLo]] = dwSplit(geometry.scaleU);
291
535
  [floats[P.svHi], floats[P.svLo]] = dwSplit(geometry.scaleV);
292
536
  [floats[P.kHi], floats[P.kLo]] = dwSplit(k);
@@ -300,7 +544,7 @@ export function imagePlaneQualityPasses(runtime, gpu, path) {
300
544
  const label = path === "dekker" ? "image-plane" : "image-plane-fma";
301
545
  const fractions = new Float64Array(3);
302
546
  const passes = {
303
- gather, siteGamma: (IMAGE_PLANE_LATTICE_U + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,
547
+ gather, siteGamma: (IMAGE_PLANE_LATTICE_U + (lit ? IMAGE_PLANE_LIGHT_U : 0) + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,
304
548
  lattice: ["photivra-display-view-quality-photo-lattice:" + label, imagePlaneQualityLatticeWgsl(path)],
305
549
  sites: ["photivra-display-view-quality-photo-sites", IMAGE_PLANE_QUALITY_SITES_WGSL],
306
550
  async runLattice(band, pipeline, latticeParams, groupBuffer, groupWords, latticeTotal, signal) {
@@ -350,7 +594,8 @@ export function imagePlaneQualityPasses(runtime, gpu, path) {
350
594
  return runtime.device.createBindGroup({ layout, entries: [
351
595
  { binding: 0, resource: { buffer: scope.upload("photivra-display-view-photo-records", records, BUFFER_USAGE.STORAGE) } },
352
596
  { binding: 1, resource: { buffer: scope.upload("photivra-display-view-photo-params", new Uint8Array(bandParams), BUFFER_USAGE.UNIFORM) } },
353
- { binding: 2, resource: { buffer: scope.upload("photivra-display-view-photo-nodes", nodeTable, BUFFER_USAGE.STORAGE) } }
597
+ { binding: 2, resource: { buffer: scope.upload("photivra-display-view-photo-nodes", nodeTable, BUFFER_USAGE.STORAGE) } },
598
+ { binding: 3, resource: { buffer: scope.upload("photivra-display-view-photo-light", lightTable, BUFFER_USAGE.STORAGE) } }
354
599
  ] });
355
600
  }
356
601
  function resolve(values, flags, groupWords) {
@@ -1 +1 @@
1
- {"version":3,"file":"display-view-image-quality.js","sourceRoot":"","sources":["../../src/gpu/display-view-image-quality.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The image-plane print in the GPU display view's Quality passes (#581): diffraction kernels, the defocus disc, the anti-aliasing\n * filter's spots and AHD for a photo, loaded with the image-plane GPU module (only after `prepareImagePlaneScenes()`). Passes 1\n * and 2 are the print's own; pass 3 (the display pixels) and the host finishing are the Quality renderer's, unchanged.\n *\n * **What the reference computes** (expected-signal.ts, the radiance path): with site kernels, per site of channel c,\n * Σ_g Σ_d Σ_n C_n,c(d)·L_n(p_g + d), where L_n(p) is the print's spectral radiance at spectral node n at lattice point p (the\n * provider's `radianceAt` on the lattice formula) and C_n,c(d) the reference's combined kernel per node (`perNode` of its\n * tables: Σ_members w·A_c(λ_n)·K_n(t)); without kernels, per query offset w·Σ_n A_c(λ_n)·L_n(p_o), the same value as the direct\n * path's `signalAt`.\n *\n * **Pass 1 (lattice radiance).** One invocation per lattice point of each group: the exact point (p + c)·g in double-float32\n * (p + c is exact in float32, checked by the Quality host; the scale's hi + lo from float64), the print-local point, the print's\n * edge in the composite kernel's bound arithmetic, the pixel coordinates, the bilinear weights as the Speed module forms them,\n * and for every node n\n *\n * L_n = W_n·Σ_j b_j·(ρ_black + (ρ_white − ρ_black)·Σ_corners w_jk·s_k(λ_n)),\n *\n * the reference's arithmetic regrouped. Each pixel's lattice cell and fractions come from the host (float64, as the reference\n * decodes the pixel), per band for the rows its lattice windows reach: 16 bytes per pixel (the cell, and per channel the smaller\n * of f and 1 − f rounded once, with a bit for which), so every corner weight carries at most 2E relative per channel. All terms\n * are non-negative: L_n carries `IMAGE_PLANE_LATTICE_U`·E relative (s 1, three channel weights 6, the nested trilinear form 6,\n * the paper 3, the bilinear weights 4, their products and sums 4, the moved-weight budget 1, W_n 2: 27, declared 32), or the\n * point is flagged: on the print's edge, near a nearest-pixel edge, or where a bilinear weight moved by the point's error could\n * change the value by more than E of it (the Speed module's rule, per node).\n *\n * **Flagged points.** With site kernels the reference evaluates the lattice formula itself, so the host replaces a flagged\n * point's values with the reference's own `radianceAt` there and clears its flag (as #492 resolves uncertain chart materials).\n * Without kernels the reference reads the environment's own query points, so a site that reads a flagged point (with a nonzero\n * weight) is flagged and its display pixels are recomputed on the host.\n *\n * **Pass 2 (sites).** The blocked sum (#492's order) of Σ_n L_n·C_n,c(t) over each group's dense tap rectangle (without kernels the\n * members' span, zero where no member sits): a term carries L's bound, the table's rounding and the product, the node sum's N − 1,\n * and the row, group and total additions, so the electrons carry (IMAGE_PLANE_LATTICE_U + N + cw + ch + G + 4)·E relative.\n * Then the Quality site tail (readout moments, white clip, running bounds), unchanged.\n */\n\nimport { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF } from \"./image-kernel-execution.js\";\nimport { BUFFER_USAGE, linearDispatch, runComputePass, type GpuResourceScope, type WebGpuRuntime } from \"./webgpu-runtime.js\";\nimport { dwSplit } from \"./chart-composite-gpu.js\";\nimport type { ChartCompositeProductPath } from \"./wgsl-chart-composite-kernel.js\";\nimport { displayViewModuleCommonWgsl } from \"./display-view-composite-material-module.js\";\nimport { DISPLAY_VIEW_QUALITY_COMMON_WGSL, DISPLAY_VIEW_QUALITY_GROUP_WORDS, DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE } from \"./wgsl-display-view-quality-kernel.js\";\nimport { latticeCoordinate, type DisplayViewQualityImagePasses, type SignalGather } from \"./display-view-quality-gpu.js\";\nimport { queryGeometry } from \"./display-view-speed-gpu.js\";\nimport { displayViewSignalTables, type DisplayViewExpectedSignalInput } from \"../facade/expected-signal.js\";\nimport type { DisplayViewGpuSetup } from \"../facade/display-view.js\";\nimport type { ImagePlaneSignalScene } from \"../facade/scene-radiance.js\";\n\nconst E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;\n/** Relative roundings of a lattice point's radiance (see the module comment). */\nexport const IMAGE_PLANE_LATTICE_U = 32;\n/** Lattice nodes of the print (5 × 5 × 5). */\nconst NODES = 125;\n/** Words per pixel record. */\nconst RECORD_WORDS = 4;\n/** Layout of the photo uniform (u32 / float32 words, 96 bytes). */\nexport const IMAGE_PLANE_QUALITY_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, nodes: 3, row0: 4, zero: 5, suHi: 8, suLo: 9, svHi: 10, svLo: 11,\n kHi: 12, kLo: 13, halfWHi: 14, halfWLo: 15, halfHHi: 16, halfHLo: 17, aspectHi: 18, aspectLo: 19, slackX: 20, slackY: 21, black: 22, span: 23, words: 24 });\nconst FLOAT32_SAFE_MAGNITUDE = 2 ** 120, FLOAT32_MINIMUM_NORMAL = 2 ** -126;\n\n/** The display-view helpers of the composite kernel's common part, with the barrier word read from the photo uniform. */\nconst photoCommon = (path: ChartCompositeProductPath): string => displayViewModuleCommonWgsl(path).replace(/\\blattice\\.zero\\b/gu, \"photo.zero\");\n\n/** Pass 1's WGSL (see the module comment). */\nexport function imagePlaneQualityLatticeWgsl(path: ChartCompositeProductPath): string {\n return `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}\n@group(0) @binding(1) var<storage, read> groups: array<u32>;\n@group(0) @binding(2) var<storage, read_write> lattice_values: array<f32>;\n@group(0) @binding(3) var<storage, read_write> lattice_flags: array<u32>;\nstruct PhotoParams {\n width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32, pad0: u32, pad1: u32,\n su_hi: f32, su_lo: f32, sv_hi: f32, sv_lo: f32, k_hi: f32, k_lo: f32, hw_hi: f32, hw_lo: f32,\n hh_hi: f32, hh_lo: f32, aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32, black: f32, span: f32,\n};\n@group(1) @binding(0) var<storage, read> records: array<u32>;\n@group(1) @binding(1) var<uniform> photo: PhotoParams;\n@group(1) @binding(2) var<storage, read> node_table: array<f32>;\n${photoCommon(path)}\nfn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }\nstruct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }\nfn bilinear_axis(s: D, n: u32) -> Axis {\n var f0 = floor(s.h);\n if (f0 == s.h && s.l < 0.0) { f0 = f0 - 1.0; }\n let frac = d_sub(s, D(f0, 0.0));\n let comp = d_sub(D(ob(f0 + 1.0), 0.0), s);\n var w0 = 0.0; var w1 = 0.0;\n if (frac.h <= 0.5) { w1 = ob(frac.h + frac.l); w0 = ob(1.0 - w1); } else { w0 = ob(comp.h + comp.l); w1 = ob(1.0 - w0); }\n let last = i32(n) - 1;\n return Axis(u32(clamp(i32(f0), 0, last)), u32(clamp(i32(f0) + 1, 0, last)), w0, w1);\n}\n// A channel's two corner weights from its record: the stored smaller one and 1 minus it.\nfn channel_weights(word: u32, bit: u32, small: f32) -> vec2<f32> {\n if (((word >> bit) & 1u) == 0u) { return vec2<f32>(ob(1.0 - small), small); }\n return vec2<f32>(small, ob(1.0 - small));\n}\nstruct Pixel { base: u32, r: vec2<f32>, g: vec2<f32>, b: vec2<f32> }\nfn pixel_at(i: u32, j: u32) -> Pixel {\n let o = ((j - photo.row0) * photo.width + i) * ${RECORD_WORDS}u;\n let w = records[o];\n return Pixel((w & 3u) * 25u + ((w >> 2u) & 3u) * 5u + ((w >> 4u) & 3u), channel_weights(w, 6u, bitcast<f32>(records[o + 1u])),\n channel_weights(w, 7u, bitcast<f32>(records[o + 2u])), channel_weights(w, 8u, bitcast<f32>(records[o + 3u])));\n}\nfn node(k: u32, n: u32) -> f32 { return node_table[k * photo.nodes + n]; }\n// One pixel's reflectance at node n: the reference's nested trilinear form, then the paper.\nfn pixel_value(px: Pixel, n: u32) -> f32 {\n let b = px.base;\n let lat = px.r.x * (px.g.x * (px.b.x * node(b, n) + px.b.y * node(b + 1u, n)) + px.g.y * (px.b.x * node(b + 5u, n) + px.b.y * node(b + 6u, n))) +\n px.r.y * (px.g.x * (px.b.x * node(b + 25u, n) + px.b.y * node(b + 26u, n)) + px.g.y * (px.b.x * node(b + 30u, n) + px.b.y * node(b + 31u, n)));\n return photo.black + photo.span * lat;\n}\n@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {\n let i = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;\n if (i >= params.lattice_total) { return; }\n var g = 0u;\n for (var k = 1u; k < params.groups; k = k + 1u) { if (i >= groups[k * GROUP + 4u]) { g = k; } }\n let base = g * GROUP;\n let lw = groups[base + 2u];\n let local = i - groups[base + 4u];\n let q = bitcast<i32>(groups[base + 1u]) + i32(local / lw);\n let p = bitcast<i32>(groups[base]) + i32(local % lw);\n material_uncertain = false;\n let N = photo.nodes;\n let out = i * N;\n let white = ${NODES}u * N;\n let k = D(photo.k_hi, photo.k_lo);\n let u = d_mul(d_mul(D(f32(p) + bitcast<f32>(groups[base + 6u]), 0.0), D(photo.su_hi, photo.su_lo)), k);\n let v = d_mul(d_mul(D(f32(q) + bitcast<f32>(groups[base + 7u]), 0.0), D(photo.sv_hi, photo.sv_lo)), k);\n let aspect = D(photo.aspect_hi, photo.aspect_lo);\n if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) {\n for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = node_table[white + N + n]; }\n lattice_flags[i] = select(0u, 1u, material_uncertain);\n return;\n }\n let x = d_mul(d_add(u, D(1.0, 0.0)), D(photo.hw_hi, photo.hw_lo));\n let y = d_mul(d_sub(aspect, v), D(photo.hh_hi, photo.hh_lo));\n if (photo.nearest != 0u) {\n let last_x = f32(photo.width - 1u);\n let last_y = f32(photo.height - 1u);\n let px = pixel_at(u32(clamp(bfloor(bd(x), 1.0, last_x), 0.0, last_x)), u32(clamp(bfloor(bd(y), 1.0, last_y), 0.0, last_y)));\n for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = node_table[white + n] * pixel_value(px, n); }\n } else {\n let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);\n let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);\n let p00 = pixel_at(ax.i0, ay.i0);\n let p10 = pixel_at(ax.i1, ay.i0);\n let p01 = pixel_at(ax.i0, ay.i1);\n let p11 = pixel_at(ax.i1, ay.i1);\n let slack = (pixel_slack(x, photo.slack_x) + pixel_slack(y, photo.slack_y)) * 2.0;\n for (var n = 0u; n < N; n = n + 1u) {\n let s00 = pixel_value(p00, n);\n let s10 = pixel_value(p10, n);\n let s01 = pixel_value(p01, n);\n let s11 = pixel_value(p11, n);\n let value = ay.w0 * (ax.w0 * s00 + ax.w1 * s10) + ay.w1 * (ax.w0 * s01 + ax.w1 * s11);\n if (!(slack * max(max(s00, s10), max(s01, s11)) <= E * value)) { material_uncertain = true; }\n lattice_values[out + n] = node_table[white + n] * value;\n }\n }\n lattice_flags[i] = select(0u, 1u, material_uncertain);\n}\n`;\n}\n\n/** Pass 2's WGSL (see the module comment): the blocked sum over the nodes, then the Quality site tail. */\nexport const IMAGE_PLANE_QUALITY_SITES_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}\n@group(0) @binding(1) var<storage, read> groups: array<u32>;\n@group(0) @binding(3) var<storage, read> table: array<f32>;\n@group(0) @binding(4) var<storage, read> lattice: array<f32>;\n@group(0) @binding(5) var<storage, read_write> sites: array<f32>;\n@group(0) @binding(6) var<storage, read> lattice_flags: array<u32>;\n${DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL}\n@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {\n let local = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;\n if (local >= params.count) { return; }\n let i = params.site_first + local;\n let ii = i % params.site_w;\n let jj = i / params.site_w;\n let c = channel_of(params.site_x0 + i32(ii), params.site_y0 + i32(jj));\n let T = params.taps;\n let N = bitcast<u32>(params.pad_h);\n var sum = 0.0;\n var flagged = false;\n for (var k = 0u; k < params.groups; k = k + 1u) {\n let g = k * GROUP;\n let lw = groups[g + 2u];\n let cw = lw - params.site_w + 1u;\n let ch = groups[g + 3u] - params.site_h + 1u;\n let lattice0 = groups[g + 4u] + jj * lw + ii;\n let tap0 = groups[g + 5u];\n var group_sum = 0.0;\n for (var dy = 0u; dy < ch; dy = dy + 1u) {\n let row = lattice0 + dy * lw;\n let row_tap = tap0 + dy * cw;\n var row_sum = 0.0;\n for (var dx = 0u; dx < cw; dx = dx + 1u) {\n let point = row + dx;\n let t = row_tap + dx;\n var cell = 0.0;\n var weight = 0.0;\n for (var n = 0u; n < N; n = n + 1u) {\n let w = table[(n * 3u + c) * T + t];\n weight = weight + w;\n cell = cell + lattice[point * N + n] * w;\n }\n if (lattice_flags[point] != 0u && weight > 0.0) { flagged = true; }\n row_sum = row_sum + cell;\n }\n group_sum = group_sum + row_sum;\n }\n sum = sum + group_sum;\n }\n let e = sum + dark_of(c);\n write_site(i, c, e, params.site_gamma * e + params.site_tiny, select(0u, 1u, flagged));\n}\n`;\n\n/**\n * The print's gather: per group its dense tap rectangle (with site kernels the reference's combined window; without them the\n * members' span), the lattice extent beyond the site window and the point constants, and the table C_n,c(t) at (n·3 + c)·T + t,\n * the reference's float64 values rounded once.\n */\nexport function imagePlaneQualityGather(signalInput: DisplayViewExpectedSignalInput): SignalGather {\n const tables = displayViewSignalTables(signalInput), W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0;\n const N = signalInput.wavelengthsNm.length, A = signalInput.electronsPerRadiance, groups: SignalGather[\"groups\"] = [];\n const rects = tables.groups.map((group) => {\n const cw = signalInput.kernels ? group.cw : group.maxIx - group.minIx + 1, ch = signalInput.kernels ? group.ch : group.maxIy - group.minIy + 1;\n return { group, cw, ch, dpMin: group.minIx - R, dqMin: group.minIy - R };\n });\n let T = 0;\n for (const r of rects) {\n groups.push({ dpMin: r.dpMin, dqMin: r.dqMin, dpSpan: r.cw - 1, dqSpan: r.ch - 1, cu: 0.5 + r.group.fx - W / 2, cv: 0.5 + r.group.fy - H / 2, firstTap: T, cw: r.cw, ch: r.ch });\n T += r.cw * r.ch;\n }\n const table = new Float32Array(N * 3 * T), tiny = new Float64Array(T);\n let largestRadiance = 0;\n rects.forEach((r, g) => {\n const first = groups[g]!.firstTap;\n for (let n = 0; n < N; n++) for (let c = 0; c < 3; c++) {\n const values = new Float64Array(r.cw * r.ch);\n if (signalInput.kernels) values.set(r.group.perNode[n]![c]!);\n else for (const member of r.group.members) { const at = (member.iy - r.group.minIy) * r.cw + member.ix - r.group.minIx; values[at] = values[at]! + member.weight * A[c]![n]!; }\n for (let t = 0; t < values.length; t++) {\n const value = values[t]!;\n if (!(value >= 0 && value <= FLOAT32_SAFE_MAGNITUDE)) throw new ImageKernelExecutionError(\"unsupported-float32-domain\", \"A combined kernel value is outside the float32 domain.\");\n table[(n * 3 + c) * T + first + t] = value;\n if (value > 0 && value < FLOAT32_MINIMUM_NORMAL) tiny[first + t] = Math.max(tiny[first + t]!, value);\n }\n }\n });\n const image = (signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image!;\n for (const w of image.whiteRadiance) largestRadiance = Math.max(largestRadiance, w);\n let tinyMass = 0;\n for (let t = 0; t < T; t++) tinyMass += tiny[t]! * largestRadiance * N;\n return { groups, taps: new Uint32Array(0), tapCount: T, nodes: N, table, spread: new Float32Array(3 * Math.max(1, T)), materials: 0, tinyMass };\n}\n\n/** The print's Quality passes for a setup (see `DisplayViewQualityImagePasses`), with the double-word product path the device's probe chose. */\nexport function imagePlaneQualityPasses(runtime: WebGpuRuntime, gpu: DisplayViewGpuSetup, path: ChartCompositeProductPath): DisplayViewQualityImagePasses {\n const signalInput = gpu.signalInput, image = (signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image;\n if (!image || !gpu.placement) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The setup carries no image-plane print and placement; use cpu-reference.\");\n const scene = image.scene, N = signalInput.wavelengthsNm.length, gather = imagePlaneQualityGather(signalInput);\n let cw = 0, ch = 0;\n for (const g of gather.groups) { cw = Math.max(cw, g.cw); ch = Math.max(ch, g.ch); }\n // The lattice scale: the lattice formula's g with site kernels, else the environment's query lattice (checked within 2⁻⁴⁰ by queryGeometry).\n const g = 2 / signalInput.outputWidth, geometry = signalInput.kernels ? { scaleU: g, scaleV: -g } : queryGeometry(signalInput);\n const halfW = scene.width / 2, halfH = scene.height / (2 * scene.aspect), k = gpu.placement.chartUnitsPerFrameUnit;\n // The node table: s_k(λ_n) per node, then W_n, then the surround's radiance per node.\n const nodeTable = new Float32Array((NODES + 2) * N);\n signalInput.wavelengthsNm.forEach((nm, n) => {\n const reflectances = scene.nodeReflectances(nm);\n for (let node = 0; node < NODES; node++) nodeTable[node * N + n] = reflectances[node]!;\n nodeTable[NODES * N + n] = image.whiteRadiance[n]!;\n nodeTable[(NODES + 1) * N + n] = image.surroundReflectance * image.whiteRadiance[n]!;\n });\n const P = IMAGE_PLANE_QUALITY_PARAMS, params = new ArrayBuffer(4 * P.words), words = new Uint32Array(params), floats = new Float32Array(params);\n words[P.width] = scene.width; words[P.height] = scene.height; words[P.nearest] = scene.interpolation === \"nearest\" ? 1 : 0; words[P.nodes] = N; words[P.zero] = 0;\n [floats[P.suHi], floats[P.suLo]] = dwSplit(geometry.scaleU); [floats[P.svHi], floats[P.svLo]] = dwSplit(geometry.scaleV); [floats[P.kHi], floats[P.kLo]] = dwSplit(k);\n [floats[P.halfWHi], floats[P.halfWLo]] = dwSplit(halfW); [floats[P.halfHHi], floats[P.halfHLo]] = dwSplit(halfH); [floats[P.aspectHi], floats[P.aspectLo]] = dwSplit(scene.aspect);\n floats[P.slackX] = 1e-14 * k * halfW; floats[P.slackY] = 1e-14 * k * halfH; floats[P.black] = scene.paperBlackReflectance; floats[P.span] = scene.paperWhiteReflectance - scene.paperBlackReflectance;\n const label = path === \"dekker\" ? \"image-plane\" : \"image-plane-fma\";\n const fractions = new Float64Array(3);\n const passes: DisplayViewQualityImagePasses = {\n gather, siteGamma: (IMAGE_PLANE_LATTICE_U + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,\n lattice: [\"photivra-display-view-quality-photo-lattice:\" + label, imagePlaneQualityLatticeWgsl(path)],\n sites: [\"photivra-display-view-quality-photo-sites\", IMAGE_PLANE_QUALITY_SITES_WGSL],\n async runLattice(band, pipeline, latticeParams, groupBuffer, groupWords, latticeTotal, signal): Promise<{ values: GPUBuffer; flags: GPUBuffer; resolved: number }> {\n let values = band.buffer(\"photivra-display-view-photo-lattice\", latticeTotal * 4 * N, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);\n let flags = band.buffer(\"photivra-display-view-photo-lattice-flags\", latticeTotal * 4, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);\n const group0 = runtime.device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: latticeParams } },\n { binding: 1, resource: { buffer: groupBuffer } }, { binding: 2, resource: { buffer: values } }, { binding: 3, resource: { buffer: flags } }] });\n const [valueData, flagData] = await runComputePass(runtime, band, pipeline, [group0, bandGroup(band, pipeline.getBindGroupLayout(1), groupWords)],\n linearDispatch(latticeTotal, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension),\n [{ buffer: values, bytes: latticeTotal * 4 * N }, { buffer: flags, bytes: latticeTotal * 4 }], signal);\n const valueArray = new Float32Array(valueData!, 0, latticeTotal * N), flagArray = new Uint32Array(flagData!, 0, latticeTotal), resolved = resolve(valueArray, flagArray, groupWords);\n if (resolved > 0) { values = band.upload(\"photivra-display-view-photo-lattice-resolved\", valueArray, BUFFER_USAGE.STORAGE); flags = band.upload(\"photivra-display-view-photo-flags-resolved\", flagArray, BUFFER_USAGE.STORAGE); }\n return { values, flags, resolved };\n }\n };\n function bandGroup(scope: GpuResourceScope, layout: GPUBindGroupLayout, groupWords: Uint32Array): GPUBindGroup {\n // The pixel rows the band's lattice windows reach (the print-local v of each window's first and last rows, two rows of margin).\n const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupFloats = new Float32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length);\n let low = Infinity, high = -Infinity;\n for (let gi = 0; gi < gather.groups.length; gi++) {\n const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, cv = groupFloats[base + 7]!;\n for (const q of [ints[base + 1]!, ints[base + 1]! + groupWords[base + 3]! - 1]) {\n const Y = (scene.aspect - (q + cv) * geometry.scaleV * k) * halfH;\n low = Math.min(low, Y); high = Math.max(high, Y);\n }\n }\n const row0 = Math.max(0, Math.min(scene.height - 1, Math.floor(low) - 2)), row1 = Math.max(row0, Math.min(scene.height - 1, Math.floor(high) + 2));\n const records = new Uint32Array((row1 - row0 + 1) * scene.width * RECORD_WORDS), recordFloats = new Float32Array(records.buffer);\n for (let j = row0, o = 0; j <= row1; j++) for (let i = 0; i < scene.width; i++, o += RECORD_WORDS) {\n const cell = scene.pixelLattice(j * scene.width + i, fractions);\n let word = Math.floor(cell / 25) | ((Math.floor(cell / 5) % 5) << 2) | ((cell % 5) << 4);\n for (let c = 0; c < 3; c++) {\n const f = fractions[c]!, high1 = f > 0.5;\n if (high1) word |= 1 << (6 + c);\n recordFloats[o + 1 + c] = high1 ? 1 - f : f;\n }\n records[o] = word;\n }\n const bandParams = params.slice(0);\n new Uint32Array(bandParams)[P.row0] = row0;\n if (records.byteLength > runtime.maximumStorageBindingBytes) throw new ImageKernelExecutionError(\"insufficient-limits\", \"A band's pixel records exceed the device's storage binding limit.\");\n return runtime.device.createBindGroup({ layout, entries: [\n { binding: 0, resource: { buffer: scope.upload(\"photivra-display-view-photo-records\", records, BUFFER_USAGE.STORAGE) } },\n { binding: 1, resource: { buffer: scope.upload(\"photivra-display-view-photo-params\", new Uint8Array(bandParams), BUFFER_USAGE.UNIFORM) } },\n { binding: 2, resource: { buffer: scope.upload(\"photivra-display-view-photo-nodes\", nodeTable, BUFFER_USAGE.STORAGE) } }] });\n }\n function resolve(values: Float32Array, flags: Uint32Array, groupWords: Uint32Array): number {\n if (!signalInput.kernels) return 0;\n // The reference's lattice point (createDisplayViewExpectedSignal's latticeU / latticeV) and its radiance per node.\n const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupsOf = displayViewSignalTables(signalInput).groups;\n const W = signalInput.nativeWidth, H = signalInput.nativeHeight, scale = 2 / signalInput.outputWidth;\n let resolved = 0;\n groupsOf.forEach((group, gi) => {\n const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, lx0 = ints[base]!, ly0 = ints[base + 1]!, lw = groupWords[base + 2]!, lh = groupWords[base + 3]!, first = groupWords[base + 4]!;\n for (let local = 0; local < lw * lh; local++) {\n if (flags[first + local] === 0) continue;\n const p = lx0 + (local % lw), q = ly0 + Math.floor(local / lw);\n const u = latticeCoordinate(p, group.fx, W, scale), v = -latticeCoordinate(q, group.fy, H, scale);\n for (let n = 0; n < N; n++) values[(first + local) * N + n] = signalInput.scene.kind === \"radiance\" ? signalInput.scene.radianceAt(u, v, signalInput.wavelengthsNm[n]!) : 0;\n flags[first + local] = 0;\n resolved++;\n }\n });\n return resolved;\n }\n return passes;\n}\n"]}
1
+ {"version":3,"file":"display-view-image-quality.js","sourceRoot":"","sources":["../../src/gpu/display-view-image-quality.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAA6C,MAAM,qBAAqB,CAAC;AAC9H,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEnD,OAAO,EAAE,2BAA2B,EAAE,MAAM,6CAA6C,CAAC;AAC1F,OAAO,EAAE,gCAAgC,EAAE,gCAAgC,EAAE,mCAAmC,EAAE,mCAAmC,EAAE,MAAM,uCAAuC,CAAC;AACrM,OAAO,EAAE,iBAAiB,EAAyD,MAAM,+BAA+B,CAAC;AACzH,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAuC,MAAM,8BAA8B,CAAC;AAE5G,OAAO,EAAE,kCAAkC,EAA8B,MAAM,6BAA6B,CAAC;AAE7G,MAAM,CAAC,GAAG,CAAC,GAAG,wBAAwB,CAAC;AACvC,iFAAiF;AACjF,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACxC;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,yFAAyF;AACzF,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;IACvK,QAAQ,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC;AACvK,8CAA8C;AAC9C,MAAM,KAAK,GAAG,GAAG,CAAC;AAClB,8BAA8B;AAC9B,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,mEAAmE;AACnE,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;IACjL,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;AAC9J,MAAM,sBAAsB,GAAG,CAAC,IAAI,GAAG,EAAE,sBAAsB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AAE5E,MAAM,CAAC,GAAG,iBAAiB,CAAC;AAC5B;;;;GAIG;AACH,MAAM,UAAU,GAAG,UAAU,CAAC;;;;;;;kCAOI,CAAC,CAAC,aAAa;kCACf,CAAC,CAAC,aAAa;;;;;;;;;;;;;kBAa/B,CAAC,CAAC,IAAI;eACT,CAAC,CAAC,OAAO;;;4DAGoC,CAAC,CAAC,KAAK;;sBAE7C,CAAC,CAAC,OAAO;;;8BAGD,CAAC,CAAC,OAAO;mBACpB,CAAC,CAAC,SAAS,YAAY,CAAC,CAAC,WAAW;;;;;;;;4BAQ3B,CAAC,CAAC,EAAE;4BACJ,CAAC,CAAC,EAAE;mBACb,CAAC,CAAC,OAAO;4CACgB,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,EAAE;;8BAEzC,CAAC,CAAC,EAAE;8BACJ,CAAC,CAAC,EAAE;;;0DAGwB,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;qDAehC,CAAC,CAAC,KAAK;;kBAE1C,CAAC,CAAC,KAAK,8CAA8C,CAAC,CAAC,KAAK;;kBAE5D,CAAC,CAAC,KAAK;;6BAEI,CAAC,CAAC,cAAc;;;uBAGtB,CAAC,CAAC,KAAK;;gCAEE,CAAC,CAAC,KAAK;;;;;;;;;;kBAUrB,CAAC,CAAC,MAAM;;;;4BAIE,CAAC,CAAC,MAAM;iBACnB,CAAC,CAAC,QAAQ,YAAY,CAAC,CAAC,UAAU;;;;;;;;;;;;;;;eAepC,CAAC,CAAC,OAAO;eACT,CAAC,CAAC,MAAM;iBACN,CAAC,CAAC,QAAQ;;;;;;;;;;;;;;;;;;iBAkBV,mBAAmB,GAAG,CAAC;;;CAGvC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAA2D,EAAE,WAA8B,EAAE,mBAA2B,EAAE,KAAa;IAE1K,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC;IAC9B,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,yBAAyB,CAAC,wBAAwB,EAAE,0EAA0E,CAAC,CAAC;IACzJ,gIAAgI;IAChI,MAAM,KAAK,GAAG,QAAQ,CAAC,KACsI,CAAC;IAC9J,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAClK,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,QAAuD,CAAC;QAClF,OAAO,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE,kCAAkC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,GAAI,CAAC,CAAC,MAAM,CAAC,gBAA2B,EAAE,KAAK,EAAE,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC9J,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC;IACpG,IAAI,CAAC,GAAG,CAAC,CAAC,aAAa;QAAE,MAAM,IAAI,yBAAyB,CAAC,wBAAwB,EAAE,6CAA6C,CAAC,CAAC;IACtI,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,CAAC,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC;IACjI,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9F,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,CAAS,EAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5G,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,KAAK,EAAE,aAAa,IAAI,CAAC,CAAC;IAC3H,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;IAC3F,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,mBAAmB,CAAC;IAChE,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,KAAK,EAAE,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAK,EAAE,EAAE,GAAG,KAAK,CAAC,EAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,EAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAAC,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IACnH,CAAC;SAAM,IAAI,KAAK,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;QAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,MAAO,CAAC,CAAC,CAAC,CAAC;QAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,MAAO,CAAC,CAAC,CAAC,CAAC;QAAC,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,MAAO,CAAC,CAAC;IAAC,CAAC;IACtI,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;IACjC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/F,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;QAChE,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,EAAE,GAAG,SAAS,GAAG,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC;QACzC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/G,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,MAAM,EAAE,GAAG,QAAQ,GAAG,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnI,wIAAwI;IACxI,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;QAC5B,IAAI,GAAG,GAAG,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC;QAC1B,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5L,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9K,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IACH,KAAK,IAAI,CAAC,GAAG,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,IAAI,sBAAsB,CAAC;YAAE,MAAM,IAAI,yBAAyB,CAAC,4BAA4B,EAAE,mDAAmD,CAAC,CAAC;IACnO,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;AACtC,CAAC;AAED,yHAAyH;AACzH,MAAM,WAAW,GAAG,CAAC,IAA+B,EAAU,EAAE,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,YAAY,CAAC,CAAC;AAEhJ,8CAA8C;AAC9C,MAAM,UAAU,4BAA4B,CAAC,IAA+B;IAC1E,OAAO,GAAG,gCAAgC;;;;;;;;;;;;;EAa1C,WAAW,CAAC,IAAI,CAAC;EACjB,UAAU;;;;;;;;;;;;;;;;;;;;mDAoBuC,YAAY;;;;;;;;;;;;;;;2BAepC,mCAAmC;;mCAE3B,mCAAmC;;;;;;;;;;;;gBAYtD,KAAK;;;;;;;;;+DAS0C,CAAC,CAAC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCxE,CAAC;AACF,CAAC;AAED,0GAA0G;AAC1G,MAAM,CAAC,MAAM,8BAA8B,GAAG,UAAU,CAAC,GAAG,gCAAgC;;;;;;EAM1F,mCAAmC;2BACV,mCAAmC;;uCAEvB,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CzE,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,WAA2C,EAAE,kBAA2B;IAC9G,MAAM,MAAM,GAAG,uBAAuB,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC,GAAG,WAAW,CAAC,YAAY,EAAE,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACzK,MAAM,CAAC,GAAG,WAAW,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,GAAG,WAAW,CAAC,oBAAoB,EAAE,MAAM,GAA2B,EAAE,CAAC;IACtH,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACxC,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;QAC/I,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjL,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IACtE,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACrB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC;QAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YAAE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBACvD,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC7C,IAAI,WAAW,CAAC,OAAO;oBAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,CAAC,CAAE,CAAC,CAAC;;oBACxD,KAAK,MAAM,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;wBAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;wBAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,CAAE,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAE,CAAC;oBAAC,CAAC;gBAC/K,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;oBACzB,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,sBAAsB,CAAC;wBAAE,MAAM,IAAI,yBAAyB,CAAC,4BAA4B,EAAE,wDAAwD,CAAC,CAAC;oBAClL,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;oBAC3C,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,sBAAsB;wBAAE,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAE,EAAE,KAAK,CAAC,CAAC;gBACvG,CAAC;YACH,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,GAAI,WAAW,CAAC,KAAoD,CAAC,KAAM,CAAC;IACvF,IAAI,kBAAkB,KAAK,SAAS;QAAE,eAAe,GAAG,kBAAkB,CAAC;;QACtE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa;YAAE,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IACzF,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;QAAE,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAE,GAAG,eAAe,GAAG,CAAC,CAAC;IACvE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,YAAY,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;AAClJ,CAAC;AAED,gJAAgJ;AAChJ,MAAM,UAAU,uBAAuB,CAAC,OAAsB,EAAE,GAAwB,EAAE,IAA+B;IACvH,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,EAAE,KAAK,GAAI,WAAW,CAAC,KAAoD,CAAC,KAAK,CAAC;IACrH,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,SAAS;QAAE,MAAM,IAAI,yBAAyB,CAAC,wBAAwB,EAAE,0EAA0E,CAAC,CAAC;IACxK,gIAAgI;IAChI,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC,sBAAsB,EAAE,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,KAAK,EAAE,WAAW,CAAC,aAAa,EAAE,KAAK,CAAC,mBAAmB,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACrL,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,uBAAuB,CAAC,WAAW,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC;IACvI,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAAC,CAAC;IACpF,6IAA6I;IAC7I,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,WAAW,EAAE,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IAC/H,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,sBAAsB,CAAC;IACnH,sFAAsF;IACtF,MAAM,SAAS,GAAG,IAAI,YAAY,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,WAAW,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;QAC1C,MAAM,YAAY,GAAG,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;QAChD,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,KAAK,EAAE,IAAI,EAAE;YAAE,SAAS,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAE,CAAC;QACvF,SAAS,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAE,CAAC;QACnD,SAAS,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,mBAAmB,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAE,CAAC;IACvF,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,0BAA0B,EAAE,MAAM,GAAG,IAAI,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAChJ,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9L,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACnL,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,qBAAqB,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,qBAAqB,GAAG,KAAK,CAAC,qBAAqB,CAAC;IACtM,MAAM,KAAK,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,iBAAiB,CAAC;IACpE,MAAM,SAAS,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,MAAM,GAAkC;QAC5C,MAAM,EAAE,SAAS,EAAE,CAAC,qBAAqB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC;QACzI,OAAO,EAAE,CAAC,8CAA8C,GAAG,KAAK,EAAE,4BAA4B,CAAC,IAAI,CAAC,CAAC;QACrG,KAAK,EAAE,CAAC,2CAA2C,EAAE,8BAA8B,CAAC;QACpF,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM;YAC3F,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,qCAAqC,EAAE,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACpI,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,2CAA2C,EAAE,YAAY,GAAG,CAAC,EAAE,YAAY,CAAC,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACrI,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE;oBACnJ,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;YACnJ,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,EAC/I,cAAc,CAAC,YAAY,EAAE,mCAAmC,EAAE,OAAO,CAAC,6BAA6B,CAAC,EACxG,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;YACzG,MAAM,UAAU,GAAG,IAAI,YAAY,CAAC,SAAU,EAAE,CAAC,EAAE,YAAY,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,WAAW,CAAC,QAAS,EAAE,CAAC,EAAE,YAAY,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;YACrL,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;gBAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,8CAA8C,EAAE,UAAU,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;gBAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,4CAA4C,EAAE,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;YAAC,CAAC;YACjO,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;QACrC,CAAC;KACF,CAAC;IACF,SAAS,SAAS,CAAC,KAAuB,EAAE,MAA0B,EAAE,UAAuB;QAC3F,gIAAgI;QAChI,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,YAAY,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACtL,IAAI,GAAG,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC;QACrC,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,EAAE,GAAG,gCAAgC,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,GAAG,CAAC,CAAE,CAAC;YAChF,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAE,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,CAAE,GAAG,UAAU,CAAC,IAAI,GAAG,CAAC,CAAE,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC/E,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;gBAClE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACnJ,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjI,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE;YAAE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,YAAY,EAAE,CAAC;gBAClG,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;gBAChE,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;gBACzF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC3B,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAE,EAAE,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC;oBACzC,IAAI,KAAK;wBAAE,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBAChC,YAAY,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9C,CAAC;gBACD,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YACpB,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC3C,IAAI,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,0BAA0B;YAAE,MAAM,IAAI,yBAAyB,CAAC,qBAAqB,EAAE,mEAAmE,CAAC,CAAC;QAC7L,OAAO,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE;gBACvD,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,qCAAqC,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,EAAE;gBACxH,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,oCAAoC,EAAE,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,EAAE;gBAC1I,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,mCAAmC,EAAE,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,EAAE;gBACxH,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,mCAAmC,EAAE,UAAU,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,EAAE;aAAC,EAAE,CAAC,CAAC;IACpI,CAAC;IACD,SAAS,OAAO,CAAC,MAAoB,EAAE,KAAkB,EAAE,UAAuB;QAC9E,IAAI,CAAC,WAAW,CAAC,OAAO;YAAE,OAAO,CAAC,CAAC;QACnC,mHAAmH;QACnH,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,uBAAuB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC;QACjJ,MAAM,CAAC,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC,GAAG,WAAW,CAAC,YAAY,EAAE,KAAK,GAAG,CAAC,GAAG,WAAW,CAAC,WAAW,CAAC;QACrG,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YAC7B,MAAM,IAAI,GAAG,EAAE,GAAG,gCAAgC,EAAE,GAAG,GAAG,IAAI,CAAC,IAAI,CAAE,EAAE,GAAG,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC,CAAE,EAAE,EAAE,GAAG,UAAU,CAAC,IAAI,GAAG,CAAC,CAAE,EAAE,EAAE,GAAG,UAAU,CAAC,IAAI,GAAG,CAAC,CAAE,EAAE,KAAK,GAAG,UAAU,CAAC,IAAI,GAAG,CAAC,CAAE,CAAC;YACpL,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;gBAC7C,IAAI,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;oBAAE,SAAS;gBACzC,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;gBAC/D,MAAM,CAAC,GAAG,iBAAiB,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;gBAClG,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;oBAAE,MAAM,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5K,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;gBACzB,QAAQ,EAAE,CAAC;YACb,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IACpB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The image-plane print in the GPU display view's Quality passes (#581): diffraction kernels, the defocus disc, the anti-aliasing\n * filter's spots and AHD for a photo, loaded with the image-plane GPU module (only after `prepareImagePlaneScenes()`). Passes 1\n * and 2 are the print's own; pass 3 (the display pixels) and the host finishing are the Quality renderer's, unchanged.\n *\n * **What the reference computes** (expected-signal.ts, the radiance path): with site kernels, per site of channel c,\n * Σ_g Σ_d Σ_n C_n,c(d)·L_n(p_g + d), where L_n(p) is the print's spectral radiance at spectral node n at lattice point p (the\n * provider's `radianceAt` on the lattice formula) and C_n,c(d) the reference's combined kernel per node (`perNode` of its\n * tables: Σ_members w·A_c(λ_n)·K_n(t)); without kernels, per query offset w·Σ_n A_c(λ_n)·L_n(p_o), the same value as the direct\n * path's `signalAt`.\n *\n * **Pass 1 (lattice radiance).** One invocation per lattice point of each group: the exact point (p + c)·g in double-float32\n * (p + c is exact in float32, checked by the Quality host; the scale's hi + lo from float64), the print-local point, the print's\n * edge in the composite kernel's bound arithmetic, the pixel coordinates, the bilinear weights as the Speed module forms them,\n * and for every node n\n *\n * L_n = W_n·Σ_j b_j·(ρ_black + (ρ_white − ρ_black)·Σ_corners w_jk·s_k(λ_n)),\n *\n * the reference's arithmetic regrouped. Each pixel's lattice cell and fractions come from the host (float64, as the reference\n * decodes the pixel), per band for the rows its lattice windows reach: 16 bytes per pixel (the cell, and per channel the smaller\n * of f and 1 − f rounded once, with a bit for which), so every corner weight carries at most 2E relative per channel. All terms\n * are non-negative: L_n carries `IMAGE_PLANE_LATTICE_U`·E relative (s 1, three channel weights 6, the nested trilinear form 6,\n * the paper 3, the bilinear weights 4, their products and sums 4, the moved-weight budget 1, W_n 2: 27, declared 32), or the\n * point is flagged: on the print's edge, near a nearest-pixel edge, or where a bilinear weight moved by the point's error could\n * change the value by more than E of it (the Speed module's rule, per node).\n *\n * **Mixed light (#597).** Under `illumination` W_n is replaced by the irradiance / π at the point, E_n(p) = Σ_i w_i(p)·T_i,n +\n * Σ_j c_j,n·h_j/d_j³ (src/facade/mixed-illumination.ts), from the light table (`imagePlaneLightTable`: the parts and the spectra at\n * the nodes, no channel value). The blend's position t is formed in double-float32 with te, its error beyond its own low part; each\n * weight comes from its own distance to the segment's ends (rounded once: E relative, plus te times the segment's slope); a regions\n * edge, or a hard edge of equal stops, within the point's bound flags it; a choice of the neighbouring segment moves the weights by\n * at most te times the steepest slope; each point source's h/d³ is a bounded value (B arithmetic). The node value's computed bound\n * (the tables' rounding, the weights, the products and the sums) must stay within (IMAGE_PLANE_LIGHT_U − 1)·E of it, else the point\n * is flagged, so a lit point carries (IMAGE_PLANE_LATTICE_U + IMAGE_PLANE_LIGHT_U)·E relative.\n *\n * **Flagged points.** With site kernels the reference evaluates the lattice formula itself, so the host replaces a flagged\n * point's values with the reference's own `radianceAt` there and clears its flag (as #492 resolves uncertain chart materials).\n * Without kernels the reference reads the environment's own query points, so a site that reads a flagged point (with a nonzero\n * weight) is flagged and its display pixels are recomputed on the host.\n *\n * **Pass 2 (sites).** The blocked sum (#492's order) of Σ_n L_n·C_n,c(t) over each group's dense tap rectangle (without kernels the\n * members' span, zero where no member sits): a term carries L's bound, the table's rounding and the product, the node sum's N − 1,\n * and the row, group and total additions, so the electrons carry (IMAGE_PLANE_LATTICE_U + N + cw + ch + G + 4)·E relative.\n * Then the Quality site tail (readout moments, white clip, running bounds), unchanged.\n */\n\nimport { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF } from \"./image-kernel-execution.js\";\nimport { BUFFER_USAGE, linearDispatch, runComputePass, type GpuResourceScope, type WebGpuRuntime } from \"./webgpu-runtime.js\";\nimport { dwSplit } from \"./chart-composite-gpu.js\";\nimport type { ChartCompositeProductPath } from \"./wgsl-chart-composite-kernel.js\";\nimport { displayViewModuleCommonWgsl } from \"./display-view-composite-material-module.js\";\nimport { DISPLAY_VIEW_QUALITY_COMMON_WGSL, DISPLAY_VIEW_QUALITY_GROUP_WORDS, DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE } from \"./wgsl-display-view-quality-kernel.js\";\nimport { latticeCoordinate, type DisplayViewQualityImagePasses, type SignalGather } from \"./display-view-quality-gpu.js\";\nimport { queryGeometry } from \"./display-view-speed-gpu.js\";\nimport { displayViewSignalTables, type DisplayViewExpectedSignalInput } from \"../facade/expected-signal.js\";\nimport type { DisplayViewGpuSetup } from \"../facade/display-view.js\";\nimport { calculateIlluminantIrradianceScale, type ImagePlaneSignalScene } from \"../facade/scene-radiance.js\";\n\nconst E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;\n/** Relative roundings of a lattice point's radiance (see the module comment). */\nexport const IMAGE_PLANE_LATTICE_U = 32;\n/**\n * #597: relative roundings of the mixed light at a lattice point (a point whose computed bound exceeds IMAGE_PLANE_LIGHT_U − 1 is\n * flagged; see the module comment), added to IMAGE_PLANE_LATTICE_U for a lit print.\n */\nexport const IMAGE_PLANE_LIGHT_U = 40;\n/** #597: layout of the light table (float32 / u32 words; see `imagePlaneLightTable`). */\nexport const IMAGE_PLANE_LIGHT = /* @__PURE__ */ Object.freeze({ kind: 0, sources: 1, points: 2, regions: 3, outside: 4, regionsAt: 6, pointsAt: 7, tablesAt: 8, slack: 9,\n surround: 10, maximumInverse: 11, halfHi: 12, halfLo: 13, aU: 16, aV: 18, dU: 20, dV: 22, inverse: 24, stops: 26, regionWords: 9, pointWords: 6, maximumPoints: 8 });\n/** Lattice nodes of the print (5 × 5 × 5). */\nconst NODES = 125;\n/** Words per pixel record. */\nconst RECORD_WORDS = 4;\n/** Layout of the photo uniform (u32 / float32 words, 96 bytes). */\nexport const IMAGE_PLANE_QUALITY_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, nodes: 3, row0: 4, zero: 5, lit: 6, suHi: 8, suLo: 9, svHi: 10, svLo: 11,\n kHi: 12, kLo: 13, halfWHi: 14, halfWLo: 15, halfHHi: 16, halfHLo: 17, aspectHi: 18, aspectLo: 19, slackX: 20, slackY: 21, black: 22, span: 23, words: 24 });\nconst FLOAT32_SAFE_MAGNITUDE = 2 ** 120, FLOAT32_MINIMUM_NORMAL = 2 ** -126;\n\nconst L = IMAGE_PLANE_LIGHT;\n/**\n * #597: the mixed light at a lattice point (see the module comment): `light_prepare` once per point (the blend's source and weights\n * with their error bound, and each point source's h/d³ as a bounded value), then `light_at(n)` per node, flagging the point when the\n * computed bound exceeds (IMAGE_PLANE_LIGHT_U − 1)·E of the value.\n */\nconst LIGHT_WGSL = /* wgsl */ `\nvar<private> blend_i: u32;\nvar<private> blend_w0: f32;\nvar<private> blend_w1: f32;\nvar<private> blend_e0: f32;\nvar<private> blend_e1: f32;\nvar<private> blend_amb: f32;\nvar<private> fall_v: array<f32, ${L.maximumPoints}>;\nvar<private> fall_e: array<f32, ${L.maximumPoints}>;\nfn lw(i: u32) -> u32 { return bitcast<u32>(light[i]); }\nfn ld(i: u32) -> D { return D(light[i], light[i + 1u]); }\n// a > b for double-float32 values.\nfn d_gt(a: D, b: D) -> bool { let d = d_sub(a, b); return d.h > 0.0 || (d.h == 0.0 && d.l > 0.0); }\n// sqrt of a bounded value: |√x − √x̃| ≤ e / (2√(v − e)), and WGSL's sqrt within 5E (inherited from 1 / inverseSqrt).\nfn bsqrt(a: B) -> B {\n let v = sqrt(max(a.v, 0.0));\n let low = a.v - SAFETY * a.e;\n if (!(low > 0.0)) { material_uncertain = true; return B(v, 3.0e38); }\n return B(v, a.e / (2.0 * sqrt(low)) + 5.0 * E * v);\n}\nfn light_prepare(u: D, v: D) {\n let kind = lw(${L.kind}u);\n let S = lw(${L.sources}u);\n blend_i = 0u; blend_w0 = 1.0; blend_w1 = 0.0; blend_e0 = 0.0; blend_e1 = 0.0; blend_amb = 0.0;\n // A point's error beyond its double-float32 arithmetic: the recovery and the reference's own rounding (the host's slack).\n let ce = 4.0 * DW * (abs(u.h) + abs(v.h) + 1.0) + light[${L.slack}u];\n if (kind == 3u) {\n var source = lw(${L.outside}u);\n let bu = bd(u);\n let bv = bd(v);\n for (var r = 0u; r < lw(${L.regions}u); r = r + 1u) {\n let b = lw(${L.regionsAt}u) + r * ${L.regionWords}u;\n if (ge(bu, bd(ld(b + 1u))) && le(bu, bd(ld(b + 3u))) && ge(bv, bd(ld(b + 5u))) && le(bv, bd(ld(b + 7u)))) { source = lw(b); break; }\n }\n blend_i = source;\n } else if (kind != 0u) {\n // t in double-float32 and te, its error bound beyond its own low part (the arithmetic and the point's error).\n var t: D;\n var te: f32;\n let a_u = d_sub(u, ld(${L.aU}u));\n let a_v = d_sub(v, ld(${L.aV}u));\n let inv = ld(${L.inverse}u);\n let ce_t = ce + 4.0 * DW * (abs(light[${L.aU}u]) + abs(light[${L.aV}u]));\n if (kind == 1u) {\n let x = d_mul(a_u, ld(${L.dU}u));\n let y = d_mul(a_v, ld(${L.dV}u));\n let scale = (abs(x.h) + abs(y.h)) * abs(inv.h);\n t = d_mul(d_add(x, y), inv);\n te = 8.0 * DW * (scale + 1.0) + ce_t * (abs(light[${L.dU}u]) + abs(light[${L.dV}u])) * abs(inv.h);\n if (t.h < 0.0) { t = D(0.0, 0.0); }\n if (d_gt(t, D(1.0, 0.0))) { t = D(1.0, 0.0); }\n } else {\n // The distance in double-float32: the float32 root and one Newton step.\n let dd = d_add(d_mul(a_u, a_u), d_mul(a_v, a_v));\n let r0 = sqrt(max(dd.h, 0.0));\n var r = D(r0, 0.0);\n if (r0 > 0.0) { let res = d_sub(dd, d_mul(D(r0, 0.0), D(r0, 0.0))); r = quick_two_sum(r0, ob(ob(res.h + res.l) / ob(2.0 * r0))); }\n t = d_mul(r, inv);\n te = 8.0 * DW * (abs(t.h) + 1.0) + 2.0 * ce_t * abs(inv.h);\n if (d_gt(t, D(1.0, 0.0))) { t = D(1.0, 0.0); }\n }\n // The segment [s_i, s_i+1) that holds t (the reference's order); a hard edge (equal stops) within the bound of t flags the point.\n var i = 0u;\n loop { if (i + 1u >= S) { break; } if (d_gt(ld(${L.stops}u + (i + 1u) * 4u), t)) { break; } i = i + 1u; }\n for (var k = 0u; k + 1u < S; k = k + 1u) {\n if (light[${L.stops}u + k * 4u + 2u] == 0.0 && abs(d_sub(t, ld(${L.stops}u + k * 4u)).h) <= SAFETY * te + DW * (abs(t.h) + 1.0)) { material_uncertain = true; }\n }\n let si = ld(${L.stops}u + i * 4u);\n // A neighbouring segment's choice moves the weights by at most t's bound times the steepest segment's slope.\n blend_amb = te * light[${L.maximumInverse}u];\n if (i + 1u < S && d_gt(t, si)) {\n // Each weight from its own distance to the segment's ends (double-float32, rounded once), so a small weight keeps a small relative error.\n let inv_i = ld(${L.stops}u + i * 4u + 2u);\n let fd = d_mul(d_sub(t, si), inv_i);\n let gd = d_mul(d_sub(ld(${L.stops}u + (i + 1u) * 4u), t), inv_i);\n blend_w1 = clamp(ob(fd.h + fd.l), 0.0, 1.0);\n blend_w0 = clamp(ob(gd.h + gd.l), 0.0, 1.0);\n let shared_e = (te + 4.0 * DW * (abs(t.h) + abs(si.h) + 1.0)) * abs(inv_i.h);\n blend_e1 = E * blend_w1 + shared_e;\n blend_e0 = E * blend_w0 + shared_e;\n }\n blend_i = i;\n }\n // Point sources: h/d³ at the point in metres (print-local × the print's half width), as bounded values.\n let half = ld(${L.halfHi}u);\n let x = d_mul(u, half);\n let y = d_mul(v, half);\n let cm = ce * abs(half.h) + 4.0 * DW * (abs(x.h) + abs(y.h));\n for (var j = 0u; j < lw(${L.points}u); j = j + 1u) {\n let b = lw(${L.pointsAt}u) + j * ${L.pointWords}u;\n var dx = bds(d_sub(x, ld(b)), abs(x.h) + abs(light[b]));\n dx.e = dx.e + cm;\n var dy = bds(d_sub(y, ld(b + 2u)), abs(y.h) + abs(light[b + 2u]));\n dy.e = dy.e + cm;\n let h = bd(ld(b + 4u));\n let d2 = badd(badd(bmul(dx, dx), bmul(dy, dy)), bmul(h, h));\n let fall = bdiv(h, bmul(d2, bsqrt(d2)));\n fall_v[j] = fall.v;\n fall_e[j] = fall.e;\n }\n}\n// The irradiance / π at node n of the prepared point: the blend's sources and the point sources from their spectra at the nodes.\nfn light_at(n: u32) -> f32 {\n let N = photo.nodes;\n let S = lw(${L.sources}u);\n let P = lw(${L.points}u);\n let tab = lw(${L.tablesAt}u);\n var value = 0.0;\n var err = 0.0;\n if (S > 0u) {\n let ti = light[tab + blend_i * N + n];\n var top = ti;\n for (var s = 0u; s < S; s = s + 1u) { top = max(top, light[tab + s * N + n]); }\n var tj = 0.0;\n if (blend_w1 == 0.0) { value = ti; } else { tj = light[tab + (blend_i + 1u) * N + n]; value = blend_w0 * ti + blend_w1 * tj; }\n err = 3.0 * E * value + blend_e0 * ti + blend_e1 * tj + blend_amb * top;\n }\n for (var j = 0u; j < P; j = j + 1u) {\n let c = light[tab + (S + j) * N + n];\n let term = c * fall_v[j];\n value = value + term;\n err = err + c * fall_e[j] + 2.0 * E * term;\n }\n err = err + f32(P + 1u) * E * value;\n if (!(err <= ${IMAGE_PLANE_LIGHT_U - 1}.0 * E * value)) { material_uncertain = true; }\n return value;\n}\n`;\n\n/**\n * #597: the light table of a lit print at the spectral nodes (the layout `IMAGE_PLANE_LIGHT`): the blend (kind, sources, its geometry\n * and stops with each segment's inverse width, 0 at a hard edge), the regions, the point sources (x, y, h in metres), then per node\n * T_i,n = k_i·S_i(λ_n)/π for each blend source and c_j·S_j(λ_n)/π for each point source: spectra only, no channel value. `slack` is the\n * reference's own rounding of a query point in print-local units.\n */\nexport function imagePlaneLightTable(light: NonNullable<ImagePlaneSignalScene[\"image\"][\"light\"]>, wavelengths: readonly number[], surroundReflectance: number, slack: number):\n { table: Float32Array<ArrayBuffer>; largestIrradiance: number } {\n const resolved = light.layout;\n if (!resolved) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The print's mixed light carries no parts for the GPU; use cpu-reference.\");\n // The blend's sources come first, then the point sources (their report carries the position and the intensity, c = I·k(1 lux)).\n const blend = resolved.blend as { kind: string; from?: { u: number; v: number }; to?: { u: number; v: number }; center?: { u: number; v: number }; radius?: number;\n regions?: readonly { source: number; uMin: number; uMax: number; vMin: number; vMax: number }[]; outsideSource?: number; stops?: readonly number[] } | null;\n const sources = resolved.sources.filter((s) => s.report.position === undefined), points = resolved.sources.filter((s) => s.report.position !== undefined).map((s) => {\n const position = s.report.position as { xM: number; yM: number; heightM: number };\n return { ...position, scale: calculateIlluminantIrradianceScale(s.illuminant.shape, 1) * (s.report.intensityCandela as number), shape: s.illuminant.shape };\n });\n const S = sources.length, P = points.length, N = wavelengths.length, regions = blend?.regions ?? [];\n if (P > L.maximumPoints) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"More point sources than the GPU pass holds.\");\n const regionsAt = L.stops + 4 * S, pointsAt = regionsAt + L.regionWords * regions.length, tablesAt = pointsAt + L.pointWords * P;\n const table = new Float32Array(tablesAt + (S + P) * N), words = new Uint32Array(table.buffer);\n const pair = (at: number, x: number): void => { [table[at], table[at + 1]] = dwSplit(x); };\n words[L.kind] = blend === null ? 0 : blend.kind === \"linear-gradient\" ? 1 : blend.kind === \"radial\" ? 2 : 3;\n words[L.sources] = S; words[L.points] = P; words[L.regions] = regions.length; words[L.outside] = blend?.outsideSource ?? 0;\n words[L.regionsAt] = regionsAt; words[L.pointsAt] = pointsAt; words[L.tablesAt] = tablesAt;\n table[L.slack] = slack; table[L.surround] = surroundReflectance;\n pair(L.halfHi, light.halfWidthM);\n if (blend?.kind === \"linear-gradient\") {\n const from = blend.from!, du = blend.to!.u - from.u, dv = blend.to!.v - from.v;\n pair(L.aU, from.u); pair(L.aV, from.v); pair(L.dU, du); pair(L.dV, dv); pair(L.inverse, 1 / (du * du + dv * dv));\n } else if (blend?.kind === \"radial\") { pair(L.aU, blend.center!.u); pair(L.aV, blend.center!.v); pair(L.inverse, 1 / blend.radius!); }\n let maximumInverse = 0;\n const stops = blend?.stops ?? [];\n stops.forEach((stop, i) => {\n const next = stops[i + 1], inverse = next !== undefined && next > stop ? 1 / (next - stop) : 0;\n pair(L.stops + 4 * i, stop); pair(L.stops + 4 * i + 2, inverse);\n maximumInverse = Math.max(maximumInverse, inverse);\n });\n table[L.maximumInverse] = maximumInverse;\n regions.forEach((r, i) => {\n const at = regionsAt + L.regionWords * i;\n words[at] = r.source; pair(at + 1, r.uMin); pair(at + 3, r.uMax); pair(at + 5, r.vMin); pair(at + 7, r.vMax);\n });\n points.forEach((p, j) => { const at = pointsAt + L.pointWords * j; pair(at, p.xM); pair(at + 2, p.yM); pair(at + 4, p.heightM); });\n // The largest irradiance / π anywhere (the blend's largest source and every point at normal incidence, 1/h²), for the tiny-value bound.\n let largestIrradiance = 0;\n wavelengths.forEach((nm, n) => {\n let top = 0, atNormal = 0;\n sources.forEach((source, i) => { const value = source.irradianceScale * source.illuminant.shape.evaluate(nm) / Math.PI; table[tablesAt + i * N + n] = value; top = Math.max(top, value); });\n points.forEach((p, j) => { const value = p.scale * p.shape.evaluate(nm) / Math.PI; table[tablesAt + (S + j) * N + n] = value; atNormal += value / (p.heightM * p.heightM); });\n largestIrradiance = Math.max(largestIrradiance, top + atNormal);\n });\n for (let i = tablesAt; i < table.length; i++) if (!(table[i]! >= 0 && table[i]! <= FLOAT32_SAFE_MAGNITUDE)) throw new ImageKernelExecutionError(\"unsupported-float32-domain\", \"A light's spectrum is outside the float32 domain.\");\n return { table, largestIrradiance };\n}\n\n/** The display-view helpers of the composite kernel's common part, with the barrier word read from the photo uniform. */\nconst photoCommon = (path: ChartCompositeProductPath): string => displayViewModuleCommonWgsl(path).replace(/\\blattice\\.zero\\b/gu, \"photo.zero\");\n\n/** Pass 1's WGSL (see the module comment). */\nexport function imagePlaneQualityLatticeWgsl(path: ChartCompositeProductPath): string {\n return `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}\n@group(0) @binding(1) var<storage, read> groups: array<u32>;\n@group(0) @binding(2) var<storage, read_write> lattice_values: array<f32>;\n@group(0) @binding(3) var<storage, read_write> lattice_flags: array<u32>;\nstruct PhotoParams {\n width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32, lit: u32, pad1: u32,\n su_hi: f32, su_lo: f32, sv_hi: f32, sv_lo: f32, k_hi: f32, k_lo: f32, hw_hi: f32, hw_lo: f32,\n hh_hi: f32, hh_lo: f32, aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32, black: f32, span: f32,\n};\n@group(1) @binding(0) var<storage, read> records: array<u32>;\n@group(1) @binding(1) var<uniform> photo: PhotoParams;\n@group(1) @binding(2) var<storage, read> node_table: array<f32>;\n@group(1) @binding(3) var<storage, read> light: array<f32>;\n${photoCommon(path)}\n${LIGHT_WGSL}\nfn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }\nstruct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }\nfn bilinear_axis(s: D, n: u32) -> Axis {\n var f0 = floor(s.h);\n if (f0 == s.h && s.l < 0.0) { f0 = f0 - 1.0; }\n let frac = d_sub(s, D(f0, 0.0));\n let comp = d_sub(D(ob(f0 + 1.0), 0.0), s);\n var w0 = 0.0; var w1 = 0.0;\n if (frac.h <= 0.5) { w1 = ob(frac.h + frac.l); w0 = ob(1.0 - w1); } else { w0 = ob(comp.h + comp.l); w1 = ob(1.0 - w0); }\n let last = i32(n) - 1;\n return Axis(u32(clamp(i32(f0), 0, last)), u32(clamp(i32(f0) + 1, 0, last)), w0, w1);\n}\n// A channel's two corner weights from its record: the stored smaller one and 1 minus it.\nfn channel_weights(word: u32, bit: u32, small: f32) -> vec2<f32> {\n if (((word >> bit) & 1u) == 0u) { return vec2<f32>(ob(1.0 - small), small); }\n return vec2<f32>(small, ob(1.0 - small));\n}\nstruct Pixel { base: u32, r: vec2<f32>, g: vec2<f32>, b: vec2<f32> }\nfn pixel_at(i: u32, j: u32) -> Pixel {\n let o = ((j - photo.row0) * photo.width + i) * ${RECORD_WORDS}u;\n let w = records[o];\n return Pixel((w & 3u) * 25u + ((w >> 2u) & 3u) * 5u + ((w >> 4u) & 3u), channel_weights(w, 6u, bitcast<f32>(records[o + 1u])),\n channel_weights(w, 7u, bitcast<f32>(records[o + 2u])), channel_weights(w, 8u, bitcast<f32>(records[o + 3u])));\n}\nfn node(k: u32, n: u32) -> f32 { return node_table[k * photo.nodes + n]; }\n// The white radiance at node n, or (#597) under mixed light the irradiance / π at the point.\nfn white_at(white: u32, n: u32) -> f32 { if (photo.lit != 0u) { return light_at(n); } return node_table[white + n]; }\n// One pixel's reflectance at node n: the reference's nested trilinear form, then the paper.\nfn pixel_value(px: Pixel, n: u32) -> f32 {\n let b = px.base;\n let lat = px.r.x * (px.g.x * (px.b.x * node(b, n) + px.b.y * node(b + 1u, n)) + px.g.y * (px.b.x * node(b + 5u, n) + px.b.y * node(b + 6u, n))) +\n px.r.y * (px.g.x * (px.b.x * node(b + 25u, n) + px.b.y * node(b + 26u, n)) + px.g.y * (px.b.x * node(b + 30u, n) + px.b.y * node(b + 31u, n)));\n return photo.black + photo.span * lat;\n}\n@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {\n let i = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;\n if (i >= params.lattice_total) { return; }\n var g = 0u;\n for (var k = 1u; k < params.groups; k = k + 1u) { if (i >= groups[k * GROUP + 4u]) { g = k; } }\n let base = g * GROUP;\n let lw = groups[base + 2u];\n let local = i - groups[base + 4u];\n let q = bitcast<i32>(groups[base + 1u]) + i32(local / lw);\n let p = bitcast<i32>(groups[base]) + i32(local % lw);\n material_uncertain = false;\n let N = photo.nodes;\n let out = i * N;\n let white = ${NODES}u * N;\n let k = D(photo.k_hi, photo.k_lo);\n let u = d_mul(d_mul(D(f32(p) + bitcast<f32>(groups[base + 6u]), 0.0), D(photo.su_hi, photo.su_lo)), k);\n let v = d_mul(d_mul(D(f32(q) + bitcast<f32>(groups[base + 7u]), 0.0), D(photo.sv_hi, photo.sv_lo)), k);\n let aspect = D(photo.aspect_hi, photo.aspect_lo);\n // #597: under mixed light the irradiance at the point (its blend and point sources) replaces the white radiance per node.\n if (photo.lit != 0u) { light_prepare(u, v); }\n if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) {\n for (var n = 0u; n < N; n = n + 1u) {\n if (photo.lit != 0u) { lattice_values[out + n] = light[${L.surround}u] * light_at(n); } else { lattice_values[out + n] = node_table[white + N + n]; }\n }\n lattice_flags[i] = select(0u, 1u, material_uncertain);\n return;\n }\n let x = d_mul(d_add(u, D(1.0, 0.0)), D(photo.hw_hi, photo.hw_lo));\n let y = d_mul(d_sub(aspect, v), D(photo.hh_hi, photo.hh_lo));\n if (photo.nearest != 0u) {\n let last_x = f32(photo.width - 1u);\n let last_y = f32(photo.height - 1u);\n let px = pixel_at(u32(clamp(bfloor(bd(x), 1.0, last_x), 0.0, last_x)), u32(clamp(bfloor(bd(y), 1.0, last_y), 0.0, last_y)));\n for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = white_at(white, n) * pixel_value(px, n); }\n } else {\n let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);\n let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);\n let p00 = pixel_at(ax.i0, ay.i0);\n let p10 = pixel_at(ax.i1, ay.i0);\n let p01 = pixel_at(ax.i0, ay.i1);\n let p11 = pixel_at(ax.i1, ay.i1);\n let slack = (pixel_slack(x, photo.slack_x) + pixel_slack(y, photo.slack_y)) * 2.0;\n for (var n = 0u; n < N; n = n + 1u) {\n let s00 = pixel_value(p00, n);\n let s10 = pixel_value(p10, n);\n let s01 = pixel_value(p01, n);\n let s11 = pixel_value(p11, n);\n let value = ay.w0 * (ax.w0 * s00 + ax.w1 * s10) + ay.w1 * (ax.w0 * s01 + ax.w1 * s11);\n if (!(slack * max(max(s00, s10), max(s01, s11)) <= E * value)) { material_uncertain = true; }\n lattice_values[out + n] = white_at(white, n) * value;\n }\n }\n lattice_flags[i] = select(0u, 1u, material_uncertain);\n}\n`;\n}\n\n/** Pass 2's WGSL (see the module comment): the blocked sum over the nodes, then the Quality site tail. */\nexport const IMAGE_PLANE_QUALITY_SITES_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}\n@group(0) @binding(1) var<storage, read> groups: array<u32>;\n@group(0) @binding(3) var<storage, read> table: array<f32>;\n@group(0) @binding(4) var<storage, read> lattice: array<f32>;\n@group(0) @binding(5) var<storage, read_write> sites: array<f32>;\n@group(0) @binding(6) var<storage, read> lattice_flags: array<u32>;\n${DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL}\n@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {\n let local = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;\n if (local >= params.count) { return; }\n let i = params.site_first + local;\n let ii = i % params.site_w;\n let jj = i / params.site_w;\n let c = channel_of(params.site_x0 + i32(ii), params.site_y0 + i32(jj));\n let T = params.taps;\n let N = bitcast<u32>(params.pad_h);\n var sum = 0.0;\n var flagged = false;\n for (var k = 0u; k < params.groups; k = k + 1u) {\n let g = k * GROUP;\n let lw = groups[g + 2u];\n let cw = lw - params.site_w + 1u;\n let ch = groups[g + 3u] - params.site_h + 1u;\n let lattice0 = groups[g + 4u] + jj * lw + ii;\n let tap0 = groups[g + 5u];\n var group_sum = 0.0;\n for (var dy = 0u; dy < ch; dy = dy + 1u) {\n let row = lattice0 + dy * lw;\n let row_tap = tap0 + dy * cw;\n var row_sum = 0.0;\n for (var dx = 0u; dx < cw; dx = dx + 1u) {\n let point = row + dx;\n let t = row_tap + dx;\n var cell = 0.0;\n var weight = 0.0;\n for (var n = 0u; n < N; n = n + 1u) {\n let w = table[(n * 3u + c) * T + t];\n weight = weight + w;\n cell = cell + lattice[point * N + n] * w;\n }\n if (lattice_flags[point] != 0u && weight > 0.0) { flagged = true; }\n row_sum = row_sum + cell;\n }\n group_sum = group_sum + row_sum;\n }\n sum = sum + group_sum;\n }\n let e = sum + dark_of(c);\n write_site(i, c, e, params.site_gamma * e + params.site_tiny, select(0u, 1u, flagged));\n}\n`;\n\n/**\n * The print's gather: per group its dense tap rectangle (with site kernels the reference's combined window; without them the\n * members' span), the lattice extent beyond the site window and the point constants, and the table C_n,c(t) at (n·3 + c)·T + t,\n * the reference's float64 values rounded once.\n */\nexport function imagePlaneQualityGather(signalInput: DisplayViewExpectedSignalInput, largestLitRadiance?: number): SignalGather {\n const tables = displayViewSignalTables(signalInput), W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0;\n const N = signalInput.wavelengthsNm.length, A = signalInput.electronsPerRadiance, groups: SignalGather[\"groups\"] = [];\n const rects = tables.groups.map((group) => {\n const cw = signalInput.kernels ? group.cw : group.maxIx - group.minIx + 1, ch = signalInput.kernels ? group.ch : group.maxIy - group.minIy + 1;\n return { group, cw, ch, dpMin: group.minIx - R, dqMin: group.minIy - R };\n });\n let T = 0;\n for (const r of rects) {\n groups.push({ dpMin: r.dpMin, dqMin: r.dqMin, dpSpan: r.cw - 1, dqSpan: r.ch - 1, cu: 0.5 + r.group.fx - W / 2, cv: 0.5 + r.group.fy - H / 2, firstTap: T, cw: r.cw, ch: r.ch });\n T += r.cw * r.ch;\n }\n const table = new Float32Array(N * 3 * T), tiny = new Float64Array(T);\n let largestRadiance = 0;\n rects.forEach((r, g) => {\n const first = groups[g]!.firstTap;\n for (let n = 0; n < N; n++) for (let c = 0; c < 3; c++) {\n const values = new Float64Array(r.cw * r.ch);\n if (signalInput.kernels) values.set(r.group.perNode[n]![c]!);\n else for (const member of r.group.members) { const at = (member.iy - r.group.minIy) * r.cw + member.ix - r.group.minIx; values[at] = values[at]! + member.weight * A[c]![n]!; }\n for (let t = 0; t < values.length; t++) {\n const value = values[t]!;\n if (!(value >= 0 && value <= FLOAT32_SAFE_MAGNITUDE)) throw new ImageKernelExecutionError(\"unsupported-float32-domain\", \"A combined kernel value is outside the float32 domain.\");\n table[(n * 3 + c) * T + first + t] = value;\n if (value > 0 && value < FLOAT32_MINIMUM_NORMAL) tiny[first + t] = Math.max(tiny[first + t]!, value);\n }\n }\n });\n const image = (signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image!;\n if (largestLitRadiance !== undefined) largestRadiance = largestLitRadiance;\n else for (const w of image.whiteRadiance) largestRadiance = Math.max(largestRadiance, w);\n let tinyMass = 0;\n for (let t = 0; t < T; t++) tinyMass += tiny[t]! * largestRadiance * N;\n return { groups, taps: new Uint32Array(0), tapCount: T, nodes: N, table, spread: new Float32Array(3 * Math.max(1, T)), materials: 0, tinyMass };\n}\n\n/** The print's Quality passes for a setup (see `DisplayViewQualityImagePasses`), with the double-word product path the device's probe chose. */\nexport function imagePlaneQualityPasses(runtime: WebGpuRuntime, gpu: DisplayViewGpuSetup, path: ChartCompositeProductPath): DisplayViewQualityImagePasses {\n const signalInput = gpu.signalInput, image = (signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image;\n if (!image || !gpu.placement) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The setup carries no image-plane print and placement; use cpu-reference.\");\n // #597: under mixed light, the light's table (its parts and spectra at the nodes), which replaces the white radiance in pass 1.\n const k0 = gpu.placement.chartUnitsPerFrameUnit, lit = image.light ? imagePlaneLightTable(image.light, signalInput.wavelengthsNm, image.surroundReflectance, 1e-14 * k0) : undefined;\n const scene = image.scene, N = signalInput.wavelengthsNm.length, gather = imagePlaneQualityGather(signalInput, lit?.largestIrradiance);\n let cw = 0, ch = 0;\n for (const g of gather.groups) { cw = Math.max(cw, g.cw); ch = Math.max(ch, g.ch); }\n // The lattice scale: the lattice formula's g with site kernels, else the environment's query lattice (checked within 2⁻⁴⁰ by queryGeometry).\n const g = 2 / signalInput.outputWidth, geometry = signalInput.kernels ? { scaleU: g, scaleV: -g } : queryGeometry(signalInput);\n const halfW = scene.width / 2, halfH = scene.height / (2 * scene.aspect), k = gpu.placement.chartUnitsPerFrameUnit;\n // The node table: s_k(λ_n) per node, then W_n, then the surround's radiance per node.\n const nodeTable = new Float32Array((NODES + 2) * N);\n signalInput.wavelengthsNm.forEach((nm, n) => {\n const reflectances = scene.nodeReflectances(nm);\n for (let node = 0; node < NODES; node++) nodeTable[node * N + n] = reflectances[node]!;\n nodeTable[NODES * N + n] = image.whiteRadiance[n]!;\n nodeTable[(NODES + 1) * N + n] = image.surroundReflectance * image.whiteRadiance[n]!;\n });\n const P = IMAGE_PLANE_QUALITY_PARAMS, params = new ArrayBuffer(4 * P.words), words = new Uint32Array(params), floats = new Float32Array(params);\n words[P.width] = scene.width; words[P.height] = scene.height; words[P.nearest] = scene.interpolation === \"nearest\" ? 1 : 0; words[P.nodes] = N; words[P.zero] = 0; words[P.lit] = lit ? 1 : 0;\n const lightTable = lit ? lit.table : new Float32Array(1);\n [floats[P.suHi], floats[P.suLo]] = dwSplit(geometry.scaleU); [floats[P.svHi], floats[P.svLo]] = dwSplit(geometry.scaleV); [floats[P.kHi], floats[P.kLo]] = dwSplit(k);\n [floats[P.halfWHi], floats[P.halfWLo]] = dwSplit(halfW); [floats[P.halfHHi], floats[P.halfHLo]] = dwSplit(halfH); [floats[P.aspectHi], floats[P.aspectLo]] = dwSplit(scene.aspect);\n floats[P.slackX] = 1e-14 * k * halfW; floats[P.slackY] = 1e-14 * k * halfH; floats[P.black] = scene.paperBlackReflectance; floats[P.span] = scene.paperWhiteReflectance - scene.paperBlackReflectance;\n const label = path === \"dekker\" ? \"image-plane\" : \"image-plane-fma\";\n const fractions = new Float64Array(3);\n const passes: DisplayViewQualityImagePasses = {\n gather, siteGamma: (IMAGE_PLANE_LATTICE_U + (lit ? IMAGE_PLANE_LIGHT_U : 0) + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,\n lattice: [\"photivra-display-view-quality-photo-lattice:\" + label, imagePlaneQualityLatticeWgsl(path)],\n sites: [\"photivra-display-view-quality-photo-sites\", IMAGE_PLANE_QUALITY_SITES_WGSL],\n async runLattice(band, pipeline, latticeParams, groupBuffer, groupWords, latticeTotal, signal): Promise<{ values: GPUBuffer; flags: GPUBuffer; resolved: number }> {\n let values = band.buffer(\"photivra-display-view-photo-lattice\", latticeTotal * 4 * N, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);\n let flags = band.buffer(\"photivra-display-view-photo-lattice-flags\", latticeTotal * 4, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);\n const group0 = runtime.device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: latticeParams } },\n { binding: 1, resource: { buffer: groupBuffer } }, { binding: 2, resource: { buffer: values } }, { binding: 3, resource: { buffer: flags } }] });\n const [valueData, flagData] = await runComputePass(runtime, band, pipeline, [group0, bandGroup(band, pipeline.getBindGroupLayout(1), groupWords)],\n linearDispatch(latticeTotal, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension),\n [{ buffer: values, bytes: latticeTotal * 4 * N }, { buffer: flags, bytes: latticeTotal * 4 }], signal);\n const valueArray = new Float32Array(valueData!, 0, latticeTotal * N), flagArray = new Uint32Array(flagData!, 0, latticeTotal), resolved = resolve(valueArray, flagArray, groupWords);\n if (resolved > 0) { values = band.upload(\"photivra-display-view-photo-lattice-resolved\", valueArray, BUFFER_USAGE.STORAGE); flags = band.upload(\"photivra-display-view-photo-flags-resolved\", flagArray, BUFFER_USAGE.STORAGE); }\n return { values, flags, resolved };\n }\n };\n function bandGroup(scope: GpuResourceScope, layout: GPUBindGroupLayout, groupWords: Uint32Array): GPUBindGroup {\n // The pixel rows the band's lattice windows reach (the print-local v of each window's first and last rows, two rows of margin).\n const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupFloats = new Float32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length);\n let low = Infinity, high = -Infinity;\n for (let gi = 0; gi < gather.groups.length; gi++) {\n const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, cv = groupFloats[base + 7]!;\n for (const q of [ints[base + 1]!, ints[base + 1]! + groupWords[base + 3]! - 1]) {\n const Y = (scene.aspect - (q + cv) * geometry.scaleV * k) * halfH;\n low = Math.min(low, Y); high = Math.max(high, Y);\n }\n }\n const row0 = Math.max(0, Math.min(scene.height - 1, Math.floor(low) - 2)), row1 = Math.max(row0, Math.min(scene.height - 1, Math.floor(high) + 2));\n const records = new Uint32Array((row1 - row0 + 1) * scene.width * RECORD_WORDS), recordFloats = new Float32Array(records.buffer);\n for (let j = row0, o = 0; j <= row1; j++) for (let i = 0; i < scene.width; i++, o += RECORD_WORDS) {\n const cell = scene.pixelLattice(j * scene.width + i, fractions);\n let word = Math.floor(cell / 25) | ((Math.floor(cell / 5) % 5) << 2) | ((cell % 5) << 4);\n for (let c = 0; c < 3; c++) {\n const f = fractions[c]!, high1 = f > 0.5;\n if (high1) word |= 1 << (6 + c);\n recordFloats[o + 1 + c] = high1 ? 1 - f : f;\n }\n records[o] = word;\n }\n const bandParams = params.slice(0);\n new Uint32Array(bandParams)[P.row0] = row0;\n if (records.byteLength > runtime.maximumStorageBindingBytes) throw new ImageKernelExecutionError(\"insufficient-limits\", \"A band's pixel records exceed the device's storage binding limit.\");\n return runtime.device.createBindGroup({ layout, entries: [\n { binding: 0, resource: { buffer: scope.upload(\"photivra-display-view-photo-records\", records, BUFFER_USAGE.STORAGE) } },\n { binding: 1, resource: { buffer: scope.upload(\"photivra-display-view-photo-params\", new Uint8Array(bandParams), BUFFER_USAGE.UNIFORM) } },\n { binding: 2, resource: { buffer: scope.upload(\"photivra-display-view-photo-nodes\", nodeTable, BUFFER_USAGE.STORAGE) } },\n { binding: 3, resource: { buffer: scope.upload(\"photivra-display-view-photo-light\", lightTable, BUFFER_USAGE.STORAGE) } }] });\n }\n function resolve(values: Float32Array, flags: Uint32Array, groupWords: Uint32Array): number {\n if (!signalInput.kernels) return 0;\n // The reference's lattice point (createDisplayViewExpectedSignal's latticeU / latticeV) and its radiance per node.\n const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupsOf = displayViewSignalTables(signalInput).groups;\n const W = signalInput.nativeWidth, H = signalInput.nativeHeight, scale = 2 / signalInput.outputWidth;\n let resolved = 0;\n groupsOf.forEach((group, gi) => {\n const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, lx0 = ints[base]!, ly0 = ints[base + 1]!, lw = groupWords[base + 2]!, lh = groupWords[base + 3]!, first = groupWords[base + 4]!;\n for (let local = 0; local < lw * lh; local++) {\n if (flags[first + local] === 0) continue;\n const p = lx0 + (local % lw), q = ly0 + Math.floor(local / lw);\n const u = latticeCoordinate(p, group.fx, W, scale), v = -latticeCoordinate(q, group.fy, H, scale);\n for (let n = 0; n < N; n++) values[(first + local) * N + n] = signalInput.scene.kind === \"radiance\" ? signalInput.scene.radianceAt(u, v, signalInput.wavelengthsNm[n]!) : 0;\n flags[first + local] = 0;\n resolved++;\n }\n });\n return resolved;\n }\n return passes;\n}\n"]}