react-x11 2.38.0 → 2.39.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.
- package/package.json +1 -1
- package/src/cocoa/sprites.js +95 -18
- package/src/node.d.ts +21 -3
- package/src/nodes/sprites.js +6 -3
package/package.json
CHANGED
package/src/cocoa/sprites.js
CHANGED
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
// `@windowkit/appkit` 0.19 (`transformForms()`) along with the negative
|
|
28
28
|
// delay a part joined half way through needs, so that is the bridge sprites
|
|
29
29
|
// ask for: on an older one nothing is asked and every part is drawn by its
|
|
30
|
-
// element.
|
|
30
|
+
// element. Or it is CSS's `matrix3d()`, a part turned out of the plane and
|
|
31
|
+
// seen in a perspective, which Core Animation draws through the whole
|
|
32
|
+
// matrix on the GPU: a part none of whose corners is behind the viewer, on a
|
|
33
|
+
// bridge that takes one (`matrix3d`).
|
|
31
34
|
//
|
|
32
35
|
// A part none of which shows — scrolled out of its pane, or outside its own
|
|
33
36
|
// clip — keeps its layer, cut to show nothing, up to a budget of raster for
|
|
@@ -68,6 +71,14 @@ const AFTER_CHILDREN = Number.MAX_SAFE_INTEGER;
|
|
|
68
71
|
|
|
69
72
|
const IDENTITY = Object.freeze([1, 0, 0, 1, 0, 0]);
|
|
70
73
|
|
|
74
|
+
// what a part with no bounds may reach (`_underDrawn`)
|
|
75
|
+
const EVERYWHERE = Object.freeze({
|
|
76
|
+
x: -1e9,
|
|
77
|
+
y: -1e9,
|
|
78
|
+
width: 2e9,
|
|
79
|
+
height: 2e9,
|
|
80
|
+
});
|
|
81
|
+
|
|
71
82
|
const ORIGIN = Object.freeze({ x: 0, y: 0 });
|
|
72
83
|
|
|
73
84
|
// what a part with no part lifted inside it is handed
|
|
@@ -98,7 +109,20 @@ const isRect = (r) =>
|
|
|
98
109
|
r.width > 0 &&
|
|
99
110
|
r.height > 0;
|
|
100
111
|
|
|
101
|
-
|
|
112
|
+
// CSS's `matrix(a, b, c, d, e, f)`, or its `matrix3d()`'s sixteen numbers
|
|
113
|
+
const isMatrix = (m) =>
|
|
114
|
+
Array.isArray(m) && (m.length === 6 || m.length === 16) && m.every(finite);
|
|
115
|
+
|
|
116
|
+
const isSolid = (m) => m.length === 16;
|
|
117
|
+
|
|
118
|
+
/** Does `sprite` turn out of the plane, at rest or in an animation? */
|
|
119
|
+
function solidSprite(sprite) {
|
|
120
|
+
if (sprite.transform && isSolid(sprite.transform)) return true;
|
|
121
|
+
for (const a of sprite.animations ?? []) {
|
|
122
|
+
if (a.property === 'transform' && a.values.some(isSolid)) return true;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
102
126
|
|
|
103
127
|
const isCurve = (t) =>
|
|
104
128
|
typeof t === 'string' ||
|
|
@@ -203,12 +227,15 @@ const originOf = (sprite) =>
|
|
|
203
227
|
y: sprite.rect.y + sprite.rect.height / 2,
|
|
204
228
|
};
|
|
205
229
|
|
|
206
|
-
/** `rect` through `m` about `origin`: the bounds of its four corners
|
|
230
|
+
/** `rect` through `m` about `origin`: the bounds of its four corners, or
|
|
231
|
+
* null where `m` is a matrix3d that puts a corner behind the viewer, past
|
|
232
|
+
* which the plane's picture has no bounds. */
|
|
207
233
|
function mapRect(rect, m, origin) {
|
|
208
234
|
let x0 = Infinity;
|
|
209
235
|
let y0 = Infinity;
|
|
210
236
|
let x1 = -Infinity;
|
|
211
237
|
let y1 = -Infinity;
|
|
238
|
+
const solid = isSolid(m);
|
|
212
239
|
for (const [px, py] of [
|
|
213
240
|
[rect.x, rect.y],
|
|
214
241
|
[rect.x + rect.width, rect.y],
|
|
@@ -217,8 +244,18 @@ function mapRect(rect, m, origin) {
|
|
|
217
244
|
]) {
|
|
218
245
|
const dx = px - origin.x;
|
|
219
246
|
const dy = py - origin.y;
|
|
220
|
-
|
|
221
|
-
|
|
247
|
+
let x;
|
|
248
|
+
let y;
|
|
249
|
+
if (solid) {
|
|
250
|
+
// column-major, as `matrix3d()` writes it: the point (dx, dy, 0, 1)
|
|
251
|
+
const w = m[3] * dx + m[7] * dy + m[15];
|
|
252
|
+
if (!(w > W_NEAR)) return null;
|
|
253
|
+
x = (m[0] * dx + m[4] * dy + m[12]) / w + origin.x;
|
|
254
|
+
y = (m[1] * dx + m[5] * dy + m[13]) / w + origin.y;
|
|
255
|
+
} else {
|
|
256
|
+
x = m[0] * dx + m[2] * dy + m[4] + origin.x;
|
|
257
|
+
y = m[1] * dx + m[3] * dy + m[5] + origin.y;
|
|
258
|
+
}
|
|
222
259
|
if (x < x0) x0 = x;
|
|
223
260
|
if (y < y0) y0 = y;
|
|
224
261
|
if (x > x1) x1 = x;
|
|
@@ -227,6 +264,10 @@ function mapRect(rect, m, origin) {
|
|
|
227
264
|
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
228
265
|
}
|
|
229
266
|
|
|
267
|
+
// A corner whose w is this close to the viewer's plane is as good as behind
|
|
268
|
+
// it: projected that near, it lands off any window.
|
|
269
|
+
const W_NEAR = 1e-6;
|
|
270
|
+
|
|
230
271
|
function unionRect(a, b) {
|
|
231
272
|
if (!a) return b;
|
|
232
273
|
const x = Math.min(a.x, b.x);
|
|
@@ -242,7 +283,8 @@ function unionRect(a, b) {
|
|
|
242
283
|
/**
|
|
243
284
|
* Everywhere `sprite` can draw while its animations run: its reach through
|
|
244
285
|
* its own transform and every transform it is animated through, out to
|
|
245
|
-
* whole pixels and the antialiasing a raster is padded for
|
|
286
|
+
* whole pixels and the antialiasing a raster is padded for — or null, where
|
|
287
|
+
* one of those puts a corner of it behind the viewer. Between two
|
|
246
288
|
* keyframes Core Animation interpolates the matrices by their parts, which
|
|
247
289
|
* stays within a pixel of the two for the densely sampled keyframes a CSS
|
|
248
290
|
* transform arrives as; an element whose keyframes are far apart, or whose
|
|
@@ -271,12 +313,18 @@ function extentOf(sprite) {
|
|
|
271
313
|
if (about?.key !== key) {
|
|
272
314
|
let box = mapRect(rel, m0, ORIGIN);
|
|
273
315
|
for (const a of sprite.animations ?? []) {
|
|
274
|
-
if (a.property !== 'transform') continue;
|
|
275
|
-
for (const m of a.values)
|
|
316
|
+
if (a.property !== 'transform' || !box) continue;
|
|
317
|
+
for (const m of a.values) {
|
|
318
|
+
const at = mapRect(rel, m, ORIGIN);
|
|
319
|
+
box = at && unionRect(box, at);
|
|
320
|
+
if (!box) break;
|
|
321
|
+
}
|
|
276
322
|
}
|
|
277
323
|
about = { key, box };
|
|
278
324
|
if (turns) EXTENTS.set(turns, about);
|
|
279
325
|
}
|
|
326
|
+
// a corner behind the viewer, at rest or on the way: nowhere bounded
|
|
327
|
+
if (!about.box) return null;
|
|
280
328
|
const box = {
|
|
281
329
|
x: about.box.x + origin.x,
|
|
282
330
|
y: about.box.y + origin.y,
|
|
@@ -311,7 +359,31 @@ function rasterRectOf(sprite) {
|
|
|
311
359
|
}
|
|
312
360
|
|
|
313
361
|
const sameMatrix = (a, b) =>
|
|
314
|
-
a === b ||
|
|
362
|
+
a === b ||
|
|
363
|
+
(a != null &&
|
|
364
|
+
b != null &&
|
|
365
|
+
a.length === b.length &&
|
|
366
|
+
a.every((v, i) => v === b[i]));
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* A part's matrix in device pixels as the bridge takes it, in points: its
|
|
370
|
+
* translation over the scale and, for a matrix3d, its perspective row times
|
|
371
|
+
* it — S⁻¹·M·S, with S scaling x, y and z, so that a turn and a perspective
|
|
372
|
+
* of so many CSS pixels look the same at any scale.
|
|
373
|
+
*/
|
|
374
|
+
function inPoints(m, s) {
|
|
375
|
+
if (!isSolid(m)) {
|
|
376
|
+
return { matrix: [m[0], m[1], m[2], m[3], m[4] / s, m[5] / s] };
|
|
377
|
+
}
|
|
378
|
+
const p = m.slice();
|
|
379
|
+
p[12] /= s;
|
|
380
|
+
p[13] /= s;
|
|
381
|
+
p[14] /= s;
|
|
382
|
+
p[3] *= s;
|
|
383
|
+
p[7] *= s;
|
|
384
|
+
p[11] *= s;
|
|
385
|
+
return { matrix3d: p };
|
|
386
|
+
}
|
|
315
387
|
|
|
316
388
|
/**
|
|
317
389
|
* The lifted parts of every element in one window: a layer each, its raster,
|
|
@@ -326,9 +398,10 @@ export class SpriteLayers {
|
|
|
326
398
|
this.app = promotion.app;
|
|
327
399
|
// a matrix and a negative delay are what a CSS animation needs from the
|
|
328
400
|
// bridge, and @windowkit/appkit 0.19 is where both are
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
401
|
+
const forms = this.native.transformForms?.();
|
|
402
|
+
this.supported = Boolean(forms?.includes?.('matrix'));
|
|
403
|
+
// and a part turned out of the plane, its matrix3d, the same release
|
|
404
|
+
this.solid = Boolean(forms?.includes?.('matrix3d'));
|
|
332
405
|
// and a video's frames on a layer need its video surfaces
|
|
333
406
|
this.videos = canLiftVideo(this.native);
|
|
334
407
|
this.hosts = new Map(); // element -> Map(key -> state)
|
|
@@ -429,7 +502,12 @@ export class SpriteLayers {
|
|
|
429
502
|
radius: sprite.clip ? (sprite.clipRadius ?? 0) : 0,
|
|
430
503
|
bounds: null,
|
|
431
504
|
};
|
|
505
|
+
// turned out of the plane, on a bridge that takes no matrix3d
|
|
506
|
+
if (!this.solid && solidSprite(sprite)) continue;
|
|
432
507
|
const extent = extentOf(sprite);
|
|
508
|
+
// a corner behind the viewer: the element draws it, which it can cut
|
|
509
|
+
// where the plane meets the viewer's
|
|
510
|
+
if (!extent) continue;
|
|
433
511
|
const bounds = cut.clip ? intersectRects(extent, cut.clip) : extent;
|
|
434
512
|
if (parent) {
|
|
435
513
|
// the parent was asked about everywhere it can be, and its layer
|
|
@@ -544,8 +622,9 @@ export class SpriteLayers {
|
|
|
544
622
|
drawn.push(state.extent);
|
|
545
623
|
continue;
|
|
546
624
|
}
|
|
547
|
-
// the element draws it, where its own clip lets it show
|
|
548
|
-
|
|
625
|
+
// the element draws it, where its own clip lets it show — anywhere,
|
|
626
|
+
// for one with a corner behind the viewer and no clip
|
|
627
|
+
const extent = extentOf(sprite) ?? EVERYWHERE;
|
|
549
628
|
const shows = sprite.clip ? intersectRects(extent, sprite.clip) : extent;
|
|
550
629
|
if (shows) drawn.push(shows);
|
|
551
630
|
}
|
|
@@ -776,7 +855,7 @@ export class SpriteLayers {
|
|
|
776
855
|
}
|
|
777
856
|
}
|
|
778
857
|
if (!sameMatrix(prev.matrix, m)) {
|
|
779
|
-
out.transform =
|
|
858
|
+
out.transform = inPoints(m, s);
|
|
780
859
|
any = true;
|
|
781
860
|
}
|
|
782
861
|
if (any) this.native.setLayerProps(state.layer, out);
|
|
@@ -843,9 +922,7 @@ export class SpriteLayers {
|
|
|
843
922
|
const opts = {
|
|
844
923
|
values:
|
|
845
924
|
a.property === 'transform'
|
|
846
|
-
? a.values.map((m) => (
|
|
847
|
-
matrix: [m[0], m[1], m[2], m[3], m[4] / s, m[5] / s],
|
|
848
|
-
}))
|
|
925
|
+
? a.values.map((m) => inPoints(m, s))
|
|
849
926
|
: a.values,
|
|
850
927
|
duration: a.duration / 1000,
|
|
851
928
|
id: caId,
|
package/src/node.d.ts
CHANGED
|
@@ -237,6 +237,23 @@ export interface PaintCachePlan {
|
|
|
237
237
|
*/
|
|
238
238
|
export type SpriteMatrix = [number, number, number, number, number, number];
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* CSS's `matrix3d()`: sixteen numbers, column by column, a point
|
|
242
|
+
* (x, y, z, 1) going to the first three rows over the fourth — a turn out
|
|
243
|
+
* of the plane and the perspective it is seen in, about the sprite's
|
|
244
|
+
* `origin`. Its translation (the 13th to 15th) is in device pixels, and so
|
|
245
|
+
* is the distance its perspective row divides by. A presenter lifts a
|
|
246
|
+
* sprite with one only where none of its corners is behind the viewer, at
|
|
247
|
+
* rest or in any keyframe, and where its bridge takes one.
|
|
248
|
+
*/
|
|
249
|
+
// prettier-ignore
|
|
250
|
+
export type SpriteMatrix3D = [
|
|
251
|
+
number, number, number, number,
|
|
252
|
+
number, number, number, number,
|
|
253
|
+
number, number, number, number,
|
|
254
|
+
number, number, number, number,
|
|
255
|
+
];
|
|
256
|
+
|
|
240
257
|
/** A cubic-bezier's control points `[x1, y1, x2, y2]`, `x1` and `x2` in
|
|
241
258
|
* 0..1, or one of Core Animation's curve names. */
|
|
242
259
|
export type SpriteTiming = [number, number, number, number] | string;
|
|
@@ -252,8 +269,9 @@ export interface SpriteAnimation {
|
|
|
252
269
|
id: string;
|
|
253
270
|
property: 'opacity' | 'transform';
|
|
254
271
|
/** At least two: opacities, or matrices for a transform — sampled densely
|
|
255
|
-
* where the element interpolates in a way the render server would not.
|
|
256
|
-
|
|
272
|
+
* where the element interpolates in a way the render server would not.
|
|
273
|
+
* A transform's may be `matrix3d`s, in any mix with `matrix`es. */
|
|
274
|
+
values: number[] | (SpriteMatrix | SpriteMatrix3D)[];
|
|
257
275
|
/** One per value, 0 to 1, never decreasing; evenly spaced when left out. */
|
|
258
276
|
keyTimes?: number[];
|
|
259
277
|
/** One curve per segment, so one fewer than `values`; linear when left
|
|
@@ -321,7 +339,7 @@ export interface Sprite {
|
|
|
321
339
|
/** What the layer shows when no animation runs on it; 1 when left out. */
|
|
322
340
|
opacity?: number;
|
|
323
341
|
/** …and the transform, about `origin`; the identity when left out. */
|
|
324
|
-
transform?: SpriteMatrix;
|
|
342
|
+
transform?: SpriteMatrix | SpriteMatrix3D;
|
|
325
343
|
/** Device pixels, window coordinates; `rect`'s centre when left out. */
|
|
326
344
|
origin?: { x: number; y: number };
|
|
327
345
|
animations?: SpriteAnimation[];
|
package/src/nodes/sprites.js
CHANGED
|
@@ -25,9 +25,12 @@ export class NodeSprites {
|
|
|
25
25
|
* - `version`: compared with `===` from frame to frame; a change paints
|
|
26
26
|
* the part again. Left out, it is painted once.
|
|
27
27
|
* - `opacity`, and `transform` — CSS's `matrix(a, b, c, d, e, f)` as an
|
|
28
|
-
* array, `e` and `f` in device pixels
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* array, `e` and `f` in device pixels, or `matrix3d()`'s sixteen
|
|
29
|
+
* numbers for a part turned out of the plane and seen in a perspective
|
|
30
|
+
* — about `origin`, a point in device pixels that defaults to `rect`'s
|
|
31
|
+
* centre: the values the layer shows when no animation is running on
|
|
32
|
+
* it. A matrix3d part is lifted only where none of its corners is behind
|
|
33
|
+
* the viewer, at rest or in any keyframe.
|
|
31
34
|
* - `animations`: what the render server runs on it, each `{ id,
|
|
32
35
|
* property: 'opacity' | 'transform', values, keyTimes, timings,
|
|
33
36
|
* duration, delay, repeat, autoreverse, hold }`, with times in
|