@woosh/meep-engine 3.60.2 → 3.60.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts +5 -5
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js +35 -6
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts +19 -0
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts.map +1 -1
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.js +52 -2
- package/src/view/elements/progress/segmented/segment_frame_transform.d.ts +15 -0
- package/src/view/elements/progress/segmented/segment_frame_transform.d.ts.map +1 -0
- package/src/view/elements/progress/segmented/segment_frame_transform.js +69 -0
package/package.json
CHANGED
package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Samples per texel. Each reads the average whose texel matches the solid angle it stands for, so
|
|
3
|
-
*
|
|
4
|
-
* single bright texel of a 1024 map at 48 places, level 1 over
|
|
5
|
-
* 32 → 0.995 ± 0.014, 64 → 0.998 ± 0.008 (at most 1.005),
|
|
6
|
-
*
|
|
7
|
-
* roughness 0.1 and 64 above.
|
|
3
|
+
* fewer samples read coarser averages and more read finer ones, back toward point taps that catch or
|
|
4
|
+
* miss a bright texel. Measured on a single bright texel of a 1024 map at 48 places, level 1 over
|
|
5
|
+
* level 0 in solid-angle energy: 32 → 0.995 ± 0.014, 64 → 0.998 ± 0.008 (at most 1.005),
|
|
6
|
+
* 196 → 1.001 ± 0.019 — at 196 the lod is 0.5 .. 1.7, and every read below 1 blends in level 0's
|
|
7
|
+
* point taps. UE filters with 32 below roughness 0.1 and 64 above.
|
|
8
8
|
* @type {number}
|
|
9
9
|
*/
|
|
10
10
|
export const CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT: number;
|
package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader_convolve_reflection_map_from_averages.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shader_convolve_reflection_map_from_averages.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js"],"names":[],"mappings":"AAqCA;;;;;;;;GAQG;AACH,kDAFU,MAAM,CAEsC;AAmJtD,yEAII;8BAlL0B,+BAA+B"}
|
package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
chunk_uv_octahedral_unit_encode
|
|
10
10
|
} from "../../shader/chunk/geometry/octahedron/chunk_uv_octahedral_unit_encode.js";
|
|
11
11
|
import { chunk_uniform_sample_cone } from "../../shader/chunk/geometry/ray/chunk_uniform_sample_cone.js";
|
|
12
|
+
import { chunk_pow2 } from "../../shader/chunk/math/chunk_pow2.js";
|
|
12
13
|
import { chunk_safe_rcp } from "../../shader/chunk/math/chunk_safe_rcp.js";
|
|
13
14
|
import { chunk_hammersley_2d } from "../../shader/chunk/noise/chunk_hammersley_2d.js";
|
|
14
15
|
import {
|
|
@@ -36,15 +37,31 @@ resources.createGroup()
|
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Samples per texel. Each reads the average whose texel matches the solid angle it stands for, so
|
|
39
|
-
*
|
|
40
|
-
* single bright texel of a 1024 map at 48 places, level 1 over
|
|
41
|
-
* 32 → 0.995 ± 0.014, 64 → 0.998 ± 0.008 (at most 1.005),
|
|
42
|
-
*
|
|
43
|
-
* roughness 0.1 and 64 above.
|
|
40
|
+
* fewer samples read coarser averages and more read finer ones, back toward point taps that catch or
|
|
41
|
+
* miss a bright texel. Measured on a single bright texel of a 1024 map at 48 places, level 1 over
|
|
42
|
+
* level 0 in solid-angle energy: 32 → 0.995 ± 0.014, 64 → 0.998 ± 0.008 (at most 1.005),
|
|
43
|
+
* 196 → 1.001 ± 0.019 — at 196 the lod is 0.5 .. 1.7, and every read below 1 blends in level 0's
|
|
44
|
+
* point taps. UE filters with 32 below roughness 0.1 and 64 above.
|
|
44
45
|
* @type {number}
|
|
45
46
|
*/
|
|
46
47
|
export const CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT = 64;
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* How much a read of the averages widens the lobe, as mean squared angle per steradian a sample
|
|
51
|
+
* stands for. The lobe exp(2 (N·H - 1) / r^4) is a Gaussian of mean squared angle 4 r^4 (N·H =
|
|
52
|
+
* cos(θ/2), so N·H - 1 ≈ -θ²/8), and convolving adds variances, so the lobe is narrowed to
|
|
53
|
+
* r^4 - k Ω_s / 4 and lands at the width it is asked for.
|
|
54
|
+
*
|
|
55
|
+
* A box over the texel plus the bilinear tent across its neighbours predicts 1/2; 1 is measured. On
|
|
56
|
+
* a 1024 map's sun against level 1 from 8192 point taps (1.315° rms): k = 0 → 1.436°, 1/2 → 1.380°,
|
|
57
|
+
* 1 → 1.317°; the sun's tail error 110% → 49%. The two reads a lod between levels blends, and an
|
|
58
|
+
* average's footprint 2 + 2/n texels of the one above rather than 2, are the likely difference.
|
|
59
|
+
* Matching the width leaves the shape: the core is flatter than a Gaussian, and the sky's rms error
|
|
60
|
+
* against the reference goes 0.166 → 0.222.
|
|
61
|
+
* @type {number}
|
|
62
|
+
*/
|
|
63
|
+
const AVERAGES_BLUR_PER_SOLID_ANGLE = 1;
|
|
64
|
+
|
|
48
65
|
/**
|
|
49
66
|
* Level 1 of the prefilter, by filtered importance sampling: each sample reads the plain average of
|
|
50
67
|
* level 0 whose texel is the solid angle the sample stands for, so a texel brighter than its
|
|
@@ -53,6 +70,11 @@ export const CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT = 64;
|
|
|
53
70
|
* one bright texel's share of level 1 anywhere from 0.79 to 1.21 of its energy, and an HDR sun
|
|
54
71
|
* came out 0.66% brighter than it went in.
|
|
55
72
|
*
|
|
73
|
+
* Samples are uniform in the cone, so each stands for the same solid angle and reads the same
|
|
74
|
+
* average: the averages blur the lobe by one fixed amount, which the lobe gives back by narrowing
|
|
75
|
+
* (see AVERAGES_BLUR_PER_SOLID_ANGLE). UE's importance sampling read fine averages in the lobe's core
|
|
76
|
+
* and coarse ones in its tail, and the sun came out in blocks.
|
|
77
|
+
*
|
|
56
78
|
* Level 1 only. The levels after it read the level above, already smooth at their sample spacing
|
|
57
79
|
* (see shader_convolve_reflection_map). Read from level 0 instead, their samples land on averages
|
|
58
80
|
* 8 to 16 texels a side, too coarse an octahedral grid to keep a bright texel's energy: ±11% at
|
|
@@ -112,6 +134,12 @@ fn main(
|
|
|
112
134
|
// uniform in the cone, so every sample stands for the same solid angle
|
|
113
135
|
let sample_solid_angle = 2.0 * PI * (1.0 - cone_cos) / f32(sample_count);
|
|
114
136
|
|
|
137
|
+
// narrowed by the blur the averages add, so the lobe comes out at the roughness asked for
|
|
138
|
+
let lobe_roughness = sqrt(sqrt(max(
|
|
139
|
+
pow2(pow2(roughness)) - ${AVERAGES_BLUR_PER_SOLID_ANGLE.toFixed(6)} * sample_solid_angle / 4.0,
|
|
140
|
+
pow2(pow2(${BSDF_ROUGHNESS_MINIMUM}))
|
|
141
|
+
)));
|
|
142
|
+
|
|
115
143
|
// level 0's octahedral square, n texels a side, spans 2 in octahedron coordinates; a texel there
|
|
116
144
|
// covers (2/n)^2 of it, and the octahedron's solid angle per unit area is |d|_1^3 along unit d
|
|
117
145
|
let level_0_interior = f32(textureDimensions(level_0).x) - 2.0;
|
|
@@ -137,7 +165,7 @@ fn main(
|
|
|
137
165
|
lod - lod_floor
|
|
138
166
|
);
|
|
139
167
|
|
|
140
|
-
let weight = reflection_sample_weight(out_direction, in_direction,
|
|
168
|
+
let weight = reflection_sample_weight(out_direction, in_direction, lobe_roughness);
|
|
141
169
|
|
|
142
170
|
radiance_accum += radiance * weight;
|
|
143
171
|
weight_accum += weight;
|
|
@@ -152,6 +180,7 @@ fn main(
|
|
|
152
180
|
chunk_hammersley_2d,
|
|
153
181
|
chunk_build_orthonormal_matrix_n,
|
|
154
182
|
chunk_safe_rcp,
|
|
183
|
+
chunk_pow2,
|
|
155
184
|
chunk_uniform_sample_cone,
|
|
156
185
|
chunk_texture_octahedral_skirt_texel_uv,
|
|
157
186
|
chunk_texture_octahedral_skirt_uv,
|
|
@@ -140,6 +140,25 @@ export class SegmentedResourceBarView extends View {
|
|
|
140
140
|
* @private
|
|
141
141
|
*/
|
|
142
142
|
private __columns;
|
|
143
|
+
/**
|
|
144
|
+
* scratch for {@link segment_frame_transform}: [scale, offset]
|
|
145
|
+
* @type {number[]}
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
148
|
+
private __frame;
|
|
149
|
+
/**
|
|
150
|
+
* The timer that re-checks the placement while linked; 0 when none
|
|
151
|
+
* @type {number}
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
private __recheck_timer;
|
|
155
|
+
/**
|
|
156
|
+
* Place again if anything moved that no event reports: an ancestor's transform, the transform of an iframe the
|
|
157
|
+
* bar sits in, a move by a fraction of a pixel. Cheap when nothing did — a computed width and a few rects —
|
|
158
|
+
* and {@link updateSegments} rebuilds only on a change.
|
|
159
|
+
* @private
|
|
160
|
+
*/
|
|
161
|
+
private __recheck;
|
|
143
162
|
/**
|
|
144
163
|
*
|
|
145
164
|
* @type {List<NumericInterval>}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedResourceBarView.d.ts","sourceRoot":"","sources":["../../../../../../src/view/elements/progress/segmented/SegmentedResourceBarView.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SegmentedResourceBarView.d.ts","sourceRoot":"","sources":["../../../../../../src/view/elements/progress/segmented/SegmentedResourceBarView.js"],"names":[],"mappings":"AAoCA;;;;;;;;;;;;;;;;;GAiBG;AACH;IACI;;;;;;;;;;;;OAYG;IACH;QAVgD,MAAM,EAA3C,CAAC,OAAO,kBAAgB,CAAC,EAAE;QAC0B,GAAG,GAAxD,OAAO,uCAAmC;QACvB,SAAS,GAA5B,MAAM,EAAE;QAC6E,KAAK,GAA1F;YAAC,IAAI,EAAC,MAAM,CAAC;YAAC,UAAU,EAAC,MAAM,CAAC;YAAC,QAAQ,EAAC,MAAM,CAAC;YAAC,cAAc,EAAC,MAAM,CAAA;SAAC,EAAE;QAC9B,KAAK,GAAjD;YAAC,KAAK,EAAC,MAAM,CAAC;YAAC,KAAK,EAAC,MAAM,CAAA;SAAC,GAAC,IAAI;QAChB,cAAc,GAA/B,MAAM;QACW,qBAAqB,GAAtC,MAAM;QACW,qBAAqB,GAAtC,MAAM;OAyThB;IA5RG;;;;OAIG;IACH,yBAA8B;IAE9B;;;;OAIG;IACH,oBAAsB;IAEtB;;;OAGG;IACH,gBAAoB;IAEpB;;;OAGG;IACH,gBAAoB;IAEpB;;;OAGG;IACH,gBAA+B;IAG/B;;;OAGG;IACH,gCAAoD;IAEpD;;;OAGG;IACH,gCAAoD;IAEpD;;;;OAIG;IACH,yBAAyB;IAEzB;;;OAGG;IACH,yBAAgH;IAEhH;;;;OAIG;IACH,oBAAuB;IA0kB3B;;OAEG;IACH,4BASC;IAllBG,yBAA4B;IAC5B,mBAAmB;IAuiBvB;;OAEG;IACH,wBAQC;IA/iBG;;;;OAIG;IACH,oBAAwB;IAGxB,oBAAsB;IACtB,sBAAwB;IACxB,sBAAwB;IACxB,6BAA+B;IAE/B;;;;OAIG;IACH,sBAAyB;IAEzB;;;;OAIG;IACH,qBAAqB;IAErB;;;;OAIG;IACH,kBAAmB;IAEnB;;;;OAIG;IACH,gBAAqB;IAErB;;;;OAIG;IACH,wBAAwB;IA0e5B;;;;;OAKG;IACH,kBAIC;IAhfG;;;OAGG;IACH,YAFU,qBAAqB,CAEH;IAG5B,mBAAuC;IASvC;;;OAGG;IACH,mBAA+C;IAG/C;;;OAGG;IACH,sBAAuB;IAEvB;;;;OAIG;IACH,sBAAuB;IAEvB;;;;OAIG;IACH,mBAAsB;IACtB,gCAA2B;IAC3B,+BAA0B;IAkBtB;;;;OAIG;IACH,oBAAgD;IAoCpD;;;OAGG;IACH,gBAFU,QAAQ,CAsChB;IAWF;;;;OAIG;IACH,uBAA2C;IAgD/C,eAGC;IAED,wBAMC;IAED;;;;;;;OAOG;IACH,uBAiEC;IAED;;;;;;OAMG;IACH,sBAKC;IAED;;;;;;OAMG;IACH,wBA2CC;IAED;;;;;OAKG;IACH,sBAoBC;IAED,mBAiDC;IAED;;;;OAIG;IACH,eAaC;IAED;;;;OAIG;IACH,qBAoBC;IA2BD;;;;OAIG;IACH,mCAUC;CAeJ;iBA9uBgB,kBAAkB;iBALlB,0CAA0C;qBAGtC,6BAA6B;oBAF9B,kCAAkC"}
|
|
@@ -10,6 +10,7 @@ import { GhostTrail } from "./GhostTrail.js";
|
|
|
10
10
|
import { SEGMENT_TIERS_DEFAULT } from "./SEGMENT_TIERS_DEFAULT.js";
|
|
11
11
|
import { segment_cell_extent } from "./segment_cell_extent.js";
|
|
12
12
|
import { segment_cell_layout } from "./segment_cell_layout.js";
|
|
13
|
+
import { segment_frame_transform } from "./segment_frame_transform.js";
|
|
13
14
|
import { segment_mask_apply, segment_mask_url } from "./segment_mask_url.js";
|
|
14
15
|
import { segment_mask_width } from "./segment_mask_width.js";
|
|
15
16
|
import { segment_tick_positions } from "./segment_tick_positions.js";
|
|
@@ -26,6 +27,13 @@ const REBUILD_WIDTH_THRESHOLD_PX = 0.5;
|
|
|
26
27
|
*/
|
|
27
28
|
const UNSCALED_TOLERANCE = 2e-3;
|
|
28
29
|
|
|
30
|
+
/**
|
|
31
|
+
* How often a linked bar checks that its notches or cells still sit on the screen's pixels, in milliseconds. A
|
|
32
|
+
* transform on an ancestor or on a containing iframe moves them without any event the bar hears.
|
|
33
|
+
* @type {number}
|
|
34
|
+
*/
|
|
35
|
+
const RECHECK_INTERVAL_MS = 250;
|
|
36
|
+
|
|
29
37
|
/**
|
|
30
38
|
* Resource bar in the style of a MOBA health bar: the values fill it end to end, notches mark fixed amounts, and
|
|
31
39
|
* a ghost behind the fill trails a drop so the loss stays readable for a moment.
|
|
@@ -189,6 +197,22 @@ export class SegmentedResourceBarView extends View {
|
|
|
189
197
|
*/
|
|
190
198
|
this.__columns = [];
|
|
191
199
|
|
|
200
|
+
/**
|
|
201
|
+
* scratch for {@link segment_frame_transform}: [scale, offset]
|
|
202
|
+
* @type {number[]}
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
this.__frame = [1, 0];
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The timer that re-checks the placement while linked; 0 when none
|
|
209
|
+
* @type {number}
|
|
210
|
+
* @private
|
|
211
|
+
*/
|
|
212
|
+
this.__recheck_timer = 0;
|
|
213
|
+
|
|
214
|
+
this.__recheck = this.__recheck.bind(this);
|
|
215
|
+
|
|
192
216
|
/**
|
|
193
217
|
*
|
|
194
218
|
* @type {List<NumericInterval>}
|
|
@@ -364,6 +388,10 @@ export class SegmentedResourceBarView extends View {
|
|
|
364
388
|
this.__sizeObserver.start();
|
|
365
389
|
this.__watch_device_pixel_ratio();
|
|
366
390
|
this.update();
|
|
391
|
+
|
|
392
|
+
if (typeof setInterval === 'function') {
|
|
393
|
+
this.__recheck_timer = setInterval(this.__recheck, RECHECK_INTERVAL_MS);
|
|
394
|
+
}
|
|
367
395
|
}
|
|
368
396
|
|
|
369
397
|
unlink() {
|
|
@@ -373,6 +401,11 @@ export class SegmentedResourceBarView extends View {
|
|
|
373
401
|
|
|
374
402
|
this.__sizeObserver.stop();
|
|
375
403
|
|
|
404
|
+
if (this.__recheck_timer !== 0) {
|
|
405
|
+
clearInterval(this.__recheck_timer);
|
|
406
|
+
this.__recheck_timer = 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
376
409
|
if (this.__dpr_query !== null) {
|
|
377
410
|
this.__dpr_query.removeEventListener('change', this.__handle_dpr_change);
|
|
378
411
|
this.__dpr_query = null;
|
|
@@ -430,16 +463,21 @@ export class SegmentedResourceBarView extends View {
|
|
|
430
463
|
|
|
431
464
|
const dpr = typeof devicePixelRatio === 'number' && devicePixelRatio > 0 ? devicePixelRatio : 1;
|
|
432
465
|
|
|
466
|
+
// how the iframes this bar sits in are drawn by the pages around them — invisible from in here
|
|
467
|
+
const frame = segment_frame_transform(el.ownerDocument.defaultView, this.__frame);
|
|
468
|
+
const frame_scale = frame[0];
|
|
469
|
+
const frame_offset = frame[1];
|
|
470
|
+
|
|
433
471
|
// a CSS scale on an ancestor shows each CSS pixel of the bar as `scale` page pixels. The computed width is
|
|
434
472
|
// serialised to a few decimals (175.844 for 175.84375), so an unscaled bar measures a hair off 1 — enough to
|
|
435
473
|
// put a width that should be whole in device pixels just past it — and is read as unscaled
|
|
436
|
-
const measured_scale = rect.width > 0 ? rect.width / layout_width : 1;
|
|
474
|
+
const measured_scale = (rect.width > 0 ? rect.width / layout_width : 1) * frame_scale;
|
|
437
475
|
const scale = Math.abs(measured_scale - 1) < UNSCALED_TOLERANCE ? 1 : measured_scale;
|
|
438
476
|
|
|
439
477
|
// device pixels per CSS pixel of the bar
|
|
440
478
|
const ratio = dpr * scale;
|
|
441
479
|
|
|
442
|
-
const origin_device = rect.left * dpr;
|
|
480
|
+
const origin_device = (frame_offset + rect.left * frame_scale) * dpr;
|
|
443
481
|
const origin_phase = origin_device - Math.round(origin_device);
|
|
444
482
|
|
|
445
483
|
const max = this.__value_max.getValue();
|
|
@@ -661,6 +699,18 @@ export class SegmentedResourceBarView extends View {
|
|
|
661
699
|
ghost.width = `${segment_cell_extent(layout, ghost_fraction * count) / ratio}px`;
|
|
662
700
|
}
|
|
663
701
|
|
|
702
|
+
/**
|
|
703
|
+
* Place again if anything moved that no event reports: an ancestor's transform, the transform of an iframe the
|
|
704
|
+
* bar sits in, a move by a fraction of a pixel. Cheap when nothing did — a computed width and a few rects —
|
|
705
|
+
* and {@link updateSegments} rebuilds only on a change.
|
|
706
|
+
* @private
|
|
707
|
+
*/
|
|
708
|
+
__recheck() {
|
|
709
|
+
if (this.isLinked) {
|
|
710
|
+
this.updateSegments();
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
664
714
|
/**
|
|
665
715
|
* @private
|
|
666
716
|
*/
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a point in a window's CSS pixels lands in the top-most window this one can see: `top = offset + local * scale`,
|
|
3
|
+
* through every same-origin iframe it sits in, each frame's own transform included.
|
|
4
|
+
*
|
|
5
|
+
* Nothing inside an iframe can see how the page around it draws it: an iframe the host scales to fit (a preview, a
|
|
6
|
+
* playground) reports `devicePixelRatio` and every rect as though unscaled, so something drawn for that pixel grid
|
|
7
|
+
* is painted on another. Walking out through `frameElement` is the one way to find out. A cross-origin frame stops
|
|
8
|
+
* the walk (its `frameElement` reads null, or throws), and is taken as unscaled from there out.
|
|
9
|
+
*
|
|
10
|
+
* @param {Window|null|undefined} win the window the element is in
|
|
11
|
+
* @param {number[]} result receives [scale, offset]
|
|
12
|
+
* @returns {number[]} result
|
|
13
|
+
*/
|
|
14
|
+
export function segment_frame_transform(win: Window | null | undefined, result: number[]): number[];
|
|
15
|
+
//# sourceMappingURL=segment_frame_transform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment_frame_transform.d.ts","sourceRoot":"","sources":["../../../../../../src/view/elements/progress/segmented/segment_frame_transform.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,6CAJW,MAAM,GAAC,IAAI,GAAC,SAAS,UACrB,MAAM,EAAE,GACN,MAAM,EAAE,CAyDpB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a point in a window's CSS pixels lands in the top-most window this one can see: `top = offset + local * scale`,
|
|
3
|
+
* through every same-origin iframe it sits in, each frame's own transform included.
|
|
4
|
+
*
|
|
5
|
+
* Nothing inside an iframe can see how the page around it draws it: an iframe the host scales to fit (a preview, a
|
|
6
|
+
* playground) reports `devicePixelRatio` and every rect as though unscaled, so something drawn for that pixel grid
|
|
7
|
+
* is painted on another. Walking out through `frameElement` is the one way to find out. A cross-origin frame stops
|
|
8
|
+
* the walk (its `frameElement` reads null, or throws), and is taken as unscaled from there out.
|
|
9
|
+
*
|
|
10
|
+
* @param {Window|null|undefined} win the window the element is in
|
|
11
|
+
* @param {number[]} result receives [scale, offset]
|
|
12
|
+
* @returns {number[]} result
|
|
13
|
+
*/
|
|
14
|
+
export function segment_frame_transform(win, result) {
|
|
15
|
+
let scale = 1;
|
|
16
|
+
let offset = 0;
|
|
17
|
+
|
|
18
|
+
let w = win;
|
|
19
|
+
|
|
20
|
+
for (let depth = 0; depth < 16 && w; depth++) {
|
|
21
|
+
let frame = null;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
frame = w.frameElement;
|
|
25
|
+
} catch (e) {
|
|
26
|
+
frame = null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!frame) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const host = frame.ownerDocument && frame.ownerDocument.defaultView;
|
|
34
|
+
|
|
35
|
+
if (!host) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const style = host.getComputedStyle(frame);
|
|
40
|
+
|
|
41
|
+
const border_left = parseFloat(style.borderLeftWidth) || 0;
|
|
42
|
+
const border_right = parseFloat(style.borderRightWidth) || 0;
|
|
43
|
+
const padding_left = parseFloat(style.paddingLeft) || 0;
|
|
44
|
+
const padding_right = parseFloat(style.paddingRight) || 0;
|
|
45
|
+
|
|
46
|
+
let layout_width = parseFloat(style.width);
|
|
47
|
+
|
|
48
|
+
if (style.boxSizing !== 'border-box') {
|
|
49
|
+
layout_width += border_left + border_right + padding_left + padding_right;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const rect = frame.getBoundingClientRect();
|
|
53
|
+
|
|
54
|
+
const frame_scale = layout_width > 0 && rect.width > 0 ? rect.width / layout_width : 1;
|
|
55
|
+
|
|
56
|
+
// where the frame's content starts, in the host window's CSS pixels
|
|
57
|
+
const content_left = rect.left + (border_left + padding_left) * frame_scale;
|
|
58
|
+
|
|
59
|
+
offset = content_left + offset * frame_scale;
|
|
60
|
+
scale *= frame_scale;
|
|
61
|
+
|
|
62
|
+
w = host;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
result[0] = scale;
|
|
66
|
+
result[1] = offset;
|
|
67
|
+
|
|
68
|
+
return result;
|
|
69
|
+
}
|