@moku-labs/game 0.15.4 → 0.15.6
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/dist/app/page.d.mts +1 -1
- package/dist/app/system.d.mts +3 -3
- package/dist/app.d.mts +3 -3
- package/dist/app.mjs +1 -1
- package/dist/assets.d.mts +1 -1
- package/dist/{commands-CvWBaoEi.d.mts → commands-CAEwtEAy.d.mts} +5 -5
- package/dist/control.d.mts +4 -4
- package/dist/{define-DSC-Q0yU.d.mts → define-CNctRrea.d.mts} +2 -2
- package/dist/{headless-oykCMFBV.d.mts → headless-DDkRwLnJ.d.mts} +1 -1
- package/dist/{index-Cv5r0M9Y.d.mts → index-DytzVJFz.d.mts} +15 -8
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/inspect.d.mts +6 -6
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/{runtime-BVVlgcMv.d.mts → runtime-BGsDXKuO.d.mts} +1 -1
- package/dist/{src-tkKl-oCn.mjs → src-DWJabPi4.mjs} +464 -110
- package/dist/testing.d.mts +4 -4
- package/dist/testing.mjs +1 -1
- package/dist/{types-CiTr21gu.d.mts → types-C2LZJIxH.d.mts} +25 -5
- package/dist/{types-VNoG7shY.d.mts → types-CRgEt4wq.d.mts} +4 -4
- package/dist/{types-B6YgPkY8.d.mts → types-CWSMo70I.d.mts} +13 -2
- package/dist/{types-DY_wOvUF.d.mts → types-DFoxDq-g.d.mts} +2 -2
- package/dist/{types-vjWgmPqS.d.mts → types-DnttyLeu.d.mts} +25 -6
- package/dist/{visual-B8ve4Qll.d.mts → visual-gTuXlmvH.d.mts} +6 -6
- package/dist/visual.d.mts +1 -1
- package/package.json +1 -1
|
@@ -9719,6 +9719,44 @@ function collectView(pctx, spec, item) {
|
|
|
9719
9719
|
return values;
|
|
9720
9720
|
}
|
|
9721
9721
|
/**
|
|
9722
|
+
* Tells whether a view is a screen root that draws at the place of its projection in a list: its
|
|
9723
|
+
* view described a node, so `collectView` gave it a `Tree` and the `Order` of that place. A view
|
|
9724
|
+
* that returned its own components keeps whatever `Order` it gave itself.
|
|
9725
|
+
*
|
|
9726
|
+
* @param pctx - Domain context of the projection module.
|
|
9727
|
+
* @param view - A live view of the projection.
|
|
9728
|
+
* @param order - The `Order` of the place the projection had.
|
|
9729
|
+
* @returns True when the view draws at that place.
|
|
9730
|
+
*/
|
|
9731
|
+
function drawsAtPlace(pctx, view, order) {
|
|
9732
|
+
const { Order, Tree } = pctx.deps.components;
|
|
9733
|
+
const rest = view.rest.get(Order.componentName)?.value;
|
|
9734
|
+
return view.rest.has(Tree.componentName) && typeof rest === "object" && "value" in rest && rest.value === order;
|
|
9735
|
+
}
|
|
9736
|
+
/**
|
|
9737
|
+
* Keeps a mounted projection for the scene that comes, as a scene switch does for a projection
|
|
9738
|
+
* both scenes list: it stays mounted into the new layer list, and its screen roots take the
|
|
9739
|
+
* `Order` of their place in the new list, on the entity and in the rest pose, so the next node of
|
|
9740
|
+
* a root compares equal and writes none. Nothing else is touched: no view is spawned or
|
|
9741
|
+
* despawned, no motion is cut, no hook runs. A view on its way out keeps the order it leaves with.
|
|
9742
|
+
*
|
|
9743
|
+
* @param pctx - Domain context of the projection module.
|
|
9744
|
+
* @param mounted - The mounted projection the new list names again.
|
|
9745
|
+
* @param order - The `Order` of its place in the new list.
|
|
9746
|
+
*/
|
|
9747
|
+
function placeMounted(pctx, mounted, order) {
|
|
9748
|
+
const previous = mounted.order;
|
|
9749
|
+
mounted.layers = pctx.ctx.state.projection.layers;
|
|
9750
|
+
if (previous === order) return;
|
|
9751
|
+
mounted.order = order;
|
|
9752
|
+
for (const view of mounted.live.values()) {
|
|
9753
|
+
if (!drawsAtPlace(pctx, view, previous)) continue;
|
|
9754
|
+
const value = pctx.deps.components.Order({ value: order });
|
|
9755
|
+
view.rest.set(value.type.componentName, value);
|
|
9756
|
+
writeValue(pctx, view.entity, value);
|
|
9757
|
+
}
|
|
9758
|
+
}
|
|
9759
|
+
/**
|
|
9722
9760
|
* Writes the layer of a view.
|
|
9723
9761
|
*
|
|
9724
9762
|
* @param pctx - Domain context of the projection module.
|
|
@@ -11037,6 +11075,155 @@ function reconcile(pctx, options) {
|
|
|
11037
11075
|
});
|
|
11038
11076
|
}
|
|
11039
11077
|
//#endregion
|
|
11078
|
+
//#region src/plugins/world/projection/mount.ts
|
|
11079
|
+
/**
|
|
11080
|
+
* Creates the empty view tables of a freshly mounted projection. These live in their own
|
|
11081
|
+
* non-exported functions because lint rule L5 refuses a collection built inside an exported
|
|
11082
|
+
* declaration.
|
|
11083
|
+
*
|
|
11084
|
+
* @param owner - Who owns the entities of this projection.
|
|
11085
|
+
* @param order - The `Order` its screen roots draw at.
|
|
11086
|
+
* @param layers - The layer list of the scene it is mounted into.
|
|
11087
|
+
* @returns A mounted projection with nothing in it yet.
|
|
11088
|
+
*/
|
|
11089
|
+
function emptyMounted(owner, order, layers) {
|
|
11090
|
+
return {
|
|
11091
|
+
owner,
|
|
11092
|
+
live: /* @__PURE__ */ new Map(),
|
|
11093
|
+
queue: [],
|
|
11094
|
+
items: /* @__PURE__ */ new Map(),
|
|
11095
|
+
order,
|
|
11096
|
+
layers
|
|
11097
|
+
};
|
|
11098
|
+
}
|
|
11099
|
+
/**
|
|
11100
|
+
* Checks that every layer a projection names is declared by the scene.
|
|
11101
|
+
*
|
|
11102
|
+
* @param pctx - Domain context of the projection module.
|
|
11103
|
+
* @param spec - The projection spec.
|
|
11104
|
+
* @throws {Error} When `layer` or `lift` is not in the layer list.
|
|
11105
|
+
*/
|
|
11106
|
+
function checkLayers(pctx, spec) {
|
|
11107
|
+
const declared = new Set(pctx.ctx.state.projection.layers.map((layer) => layer.name));
|
|
11108
|
+
for (const name of [spec.layer, spec.lift]) {
|
|
11109
|
+
if (name === void 0 || declared.has(name)) continue;
|
|
11110
|
+
throw new Error(`[game] Projection "${spec.name}" names layer "${name}", which the scene does not declare.\n Add it to the layers of the scene.`);
|
|
11111
|
+
}
|
|
11112
|
+
}
|
|
11113
|
+
/**
|
|
11114
|
+
* Tells whether two layer lists declare one layer alike: every field of its entry (`sort`,
|
|
11115
|
+
* `above`, whatever a layer declares) the same in both. The renderer keeps the container of a
|
|
11116
|
+
* layer that stays, but sorts a view again only when the view changes, so a view kept under
|
|
11117
|
+
* another rule would stand at its old place.
|
|
11118
|
+
*
|
|
11119
|
+
* @param before - The layer list the projection was mounted under.
|
|
11120
|
+
* @param after - The layer list of the coming scene.
|
|
11121
|
+
* @param name - The layer name.
|
|
11122
|
+
* @returns True when both lists hold the layer with the same fields.
|
|
11123
|
+
* @example
|
|
11124
|
+
* ```ts
|
|
11125
|
+
* sameLayer([{ name: "ui", sort: "none" }], [{ name: "ui", sort: "order" }], "ui"); // false
|
|
11126
|
+
* ```
|
|
11127
|
+
*/
|
|
11128
|
+
function sameLayer(before, after, name) {
|
|
11129
|
+
const first = before.find((layer) => layer.name === name);
|
|
11130
|
+
const second = after.find((layer) => layer.name === name);
|
|
11131
|
+
if (first === void 0 || second === void 0) return false;
|
|
11132
|
+
const fields = Object.entries(first);
|
|
11133
|
+
return fields.length === Object.keys(second).length && fields.every(([field, value]) => Object.getOwnPropertyDescriptor(second, field)?.value === value);
|
|
11134
|
+
}
|
|
11135
|
+
/**
|
|
11136
|
+
* Tells whether a projection draws under the same rules in two layer lists: its `layer`, and its
|
|
11137
|
+
* `lift` when it has one, declared alike in both.
|
|
11138
|
+
*
|
|
11139
|
+
* @param before - The layer list the projection was mounted under.
|
|
11140
|
+
* @param after - The layer list of the coming scene.
|
|
11141
|
+
* @param spec - The projection spec.
|
|
11142
|
+
* @returns True when the projection can stay mounted.
|
|
11143
|
+
*/
|
|
11144
|
+
function drawsAlike(before, after, spec) {
|
|
11145
|
+
return sameLayer(before, after, spec.layer) && (spec.lift === void 0 || sameLayer(before, after, spec.lift));
|
|
11146
|
+
}
|
|
11147
|
+
/**
|
|
11148
|
+
* Unmounts projections: every motion of their views is finished, and the live views and the
|
|
11149
|
+
* despawn queue are despawned in the same call. An unknown or unmounted name is skipped.
|
|
11150
|
+
*
|
|
11151
|
+
* @param pctx - Domain context of the projection module.
|
|
11152
|
+
* @param names - Names of the projections to unmount.
|
|
11153
|
+
*/
|
|
11154
|
+
function unmountNames(pctx, names) {
|
|
11155
|
+
const state = pctx.ctx.state.projection;
|
|
11156
|
+
for (const name of names) {
|
|
11157
|
+
const mounted = state.mounted.get(name);
|
|
11158
|
+
if (mounted === void 0) continue;
|
|
11159
|
+
clearMounted(pctx, mounted);
|
|
11160
|
+
state.mounted.delete(name);
|
|
11161
|
+
}
|
|
11162
|
+
}
|
|
11163
|
+
/**
|
|
11164
|
+
* The first pass of a mount, which touches nothing: every name is checked, a projection mounted
|
|
11165
|
+
* into this scene already is reported and passed over, and the rest is sorted into the ones that
|
|
11166
|
+
* come and the ones a scene before mounted that have to go and come back.
|
|
11167
|
+
*
|
|
11168
|
+
* @param pctx - Domain context of the projection module.
|
|
11169
|
+
* @param names - Names of the projections to mount.
|
|
11170
|
+
* @returns The specs to mount and reconcile, and the names to unmount first.
|
|
11171
|
+
* @throws {Error} For an unknown name, or a `layer` or `lift` the scene does not declare.
|
|
11172
|
+
*/
|
|
11173
|
+
function sortMount(pctx, names) {
|
|
11174
|
+
const state = pctx.ctx.state.projection;
|
|
11175
|
+
const fresh = [];
|
|
11176
|
+
const again = [];
|
|
11177
|
+
for (const name of names) {
|
|
11178
|
+
const spec = state.specs.get(name);
|
|
11179
|
+
if (spec === void 0) throw new Error(`[game] Projection "${name}" is not registered.\n Register it before a scene mounts it.`);
|
|
11180
|
+
const mounted = state.mounted.get(name);
|
|
11181
|
+
if (mounted?.layers === state.layers) {
|
|
11182
|
+
pctx.ctx.log.warn("world:already-mounted", { projection: name });
|
|
11183
|
+
continue;
|
|
11184
|
+
}
|
|
11185
|
+
checkLayers(pctx, spec);
|
|
11186
|
+
if (mounted !== void 0 && drawsAlike(mounted.layers, state.layers, spec)) continue;
|
|
11187
|
+
if (mounted !== void 0) again.push(name);
|
|
11188
|
+
fresh.push(spec);
|
|
11189
|
+
}
|
|
11190
|
+
return {
|
|
11191
|
+
fresh,
|
|
11192
|
+
again
|
|
11193
|
+
};
|
|
11194
|
+
}
|
|
11195
|
+
/**
|
|
11196
|
+
* Mounts the projections of a list under an owner. A new one is reconciled at once, direct. One
|
|
11197
|
+
* a scene before mounted stays when the current layer list declares its layers alike, and takes
|
|
11198
|
+
* its place in this list; one whose layer is declared otherwise is unmounted and mounted again.
|
|
11199
|
+
* One mounted into this scene already is reported and changes nothing. Nothing is mounted when
|
|
11200
|
+
* the call throws.
|
|
11201
|
+
*
|
|
11202
|
+
* @param pctx - Domain context of the projection module.
|
|
11203
|
+
* @param names - Names of the projections to mount.
|
|
11204
|
+
* @param owner - Who owns the entities of the new ones.
|
|
11205
|
+
* @throws {Error} For an unknown name, or a `layer` or `lift` the scene does not declare.
|
|
11206
|
+
*/
|
|
11207
|
+
function mountNames(pctx, names, owner) {
|
|
11208
|
+
const state = pctx.ctx.state.projection;
|
|
11209
|
+
const { fresh, again } = sortMount(pctx, names);
|
|
11210
|
+
unmountNames(pctx, again);
|
|
11211
|
+
for (const name of names) {
|
|
11212
|
+
const order = names.indexOf(name) - names.length;
|
|
11213
|
+
const mounted = state.mounted.get(name);
|
|
11214
|
+
if (mounted === void 0) state.mounted.set(name, emptyMounted(owner, order, state.layers));
|
|
11215
|
+
else if (mounted.layers !== state.layers) placeMounted(pctx, mounted, order);
|
|
11216
|
+
}
|
|
11217
|
+
if (fresh.length === 0) return;
|
|
11218
|
+
reconcile(pctx, {
|
|
11219
|
+
names: fresh.map((spec) => spec.name),
|
|
11220
|
+
direct: true,
|
|
11221
|
+
force: true,
|
|
11222
|
+
converge: false,
|
|
11223
|
+
settle: false
|
|
11224
|
+
});
|
|
11225
|
+
}
|
|
11226
|
+
//#endregion
|
|
11040
11227
|
//#region src/plugins/world/projection/api.ts
|
|
11041
11228
|
/**
|
|
11042
11229
|
* Tells whether a cause sets the picture instead of playing it.
|
|
@@ -11069,25 +11256,8 @@ function isConvergeCause(cause) {
|
|
|
11069
11256
|
return cause === "load" || cause === "restore";
|
|
11070
11257
|
}
|
|
11071
11258
|
/**
|
|
11072
|
-
* Creates the empty
|
|
11073
|
-
*
|
|
11074
|
-
* declaration.
|
|
11075
|
-
*
|
|
11076
|
-
* @param owner - Who owns the entities of this projection.
|
|
11077
|
-
* @param order - The `Order` its screen roots draw at.
|
|
11078
|
-
* @returns A mounted projection with nothing in it yet.
|
|
11079
|
-
*/
|
|
11080
|
-
function emptyMounted(owner, order) {
|
|
11081
|
-
return {
|
|
11082
|
-
owner,
|
|
11083
|
-
live: /* @__PURE__ */ new Map(),
|
|
11084
|
-
queue: [],
|
|
11085
|
-
items: /* @__PURE__ */ new Map(),
|
|
11086
|
-
order
|
|
11087
|
-
};
|
|
11088
|
-
}
|
|
11089
|
-
/**
|
|
11090
|
-
* Creates the empty mute table of one entity.
|
|
11259
|
+
* Creates the empty mute table of one entity. It lives in its own non-exported function because
|
|
11260
|
+
* lint rule L5 refuses a collection built inside an exported declaration.
|
|
11091
11261
|
*
|
|
11092
11262
|
* @returns An empty table of component name to owned fields.
|
|
11093
11263
|
*/
|
|
@@ -11173,20 +11343,6 @@ function noPeers() {
|
|
|
11173
11343
|
};
|
|
11174
11344
|
}
|
|
11175
11345
|
/**
|
|
11176
|
-
* Checks that every layer a projection names is declared by the scene.
|
|
11177
|
-
*
|
|
11178
|
-
* @param pctx - Domain context of the projection module.
|
|
11179
|
-
* @param spec - The projection spec.
|
|
11180
|
-
* @throws {Error} When `layer` or `lift` is not in the layer list.
|
|
11181
|
-
*/
|
|
11182
|
-
function checkLayers(pctx, spec) {
|
|
11183
|
-
const declared = new Set(pctx.ctx.state.projection.layers.map((layer) => layer.name));
|
|
11184
|
-
for (const name of [spec.layer, spec.lift]) {
|
|
11185
|
-
if (name === void 0 || declared.has(name)) continue;
|
|
11186
|
-
throw new Error(`[game] Projection "${spec.name}" names layer "${name}", which the scene does not declare.\n Add it to the layers of the scene.`);
|
|
11187
|
-
}
|
|
11188
|
-
}
|
|
11189
|
-
/**
|
|
11190
11346
|
* Creates the projection module: the public API `scenes`, `input` and `i18n` call, plus the
|
|
11191
11347
|
* methods the plugin root drives from the frame and the `model:committed` hook.
|
|
11192
11348
|
*
|
|
@@ -11251,33 +11407,10 @@ function createProjectionApi(ctx, deps) {
|
|
|
11251
11407
|
},
|
|
11252
11408
|
layers: () => state.layers,
|
|
11253
11409
|
mount: (names, owner) => {
|
|
11254
|
-
|
|
11255
|
-
for (const name of names) {
|
|
11256
|
-
const spec = state.specs.get(name);
|
|
11257
|
-
if (spec === void 0) throw new Error(`[game] Projection "${name}" is not registered.\n Register it before a scene mounts it.`);
|
|
11258
|
-
if (state.mounted.has(name)) {
|
|
11259
|
-
ctx.log.warn("world:already-mounted", { projection: name });
|
|
11260
|
-
continue;
|
|
11261
|
-
}
|
|
11262
|
-
checkLayers(pctx, spec);
|
|
11263
|
-
fresh.push(spec);
|
|
11264
|
-
}
|
|
11265
|
-
for (const spec of fresh) state.mounted.set(spec.name, emptyMounted(owner, names.indexOf(spec.name) - names.length));
|
|
11266
|
-
if (fresh.length > 0) reconcile(pctx, {
|
|
11267
|
-
names: fresh.map((spec) => spec.name),
|
|
11268
|
-
direct: true,
|
|
11269
|
-
force: true,
|
|
11270
|
-
converge: false,
|
|
11271
|
-
settle: false
|
|
11272
|
-
});
|
|
11410
|
+
mountNames(pctx, names, owner);
|
|
11273
11411
|
},
|
|
11274
11412
|
unmount: (names) => {
|
|
11275
|
-
|
|
11276
|
-
const mounted = state.mounted.get(name);
|
|
11277
|
-
if (mounted === void 0) continue;
|
|
11278
|
-
clearMounted(pctx, mounted);
|
|
11279
|
-
state.mounted.delete(name);
|
|
11280
|
-
}
|
|
11413
|
+
unmountNames(pctx, names);
|
|
11281
11414
|
},
|
|
11282
11415
|
restOf: (entity, component) => {
|
|
11283
11416
|
const stored = (state.byEntity.get(entity)?.rest ?? state.rests.get(entity))?.get(component.componentName);
|
|
@@ -12184,7 +12317,7 @@ function withinRatio(differing, bytes) {
|
|
|
12184
12317
|
* @param host - The host module (`resolution()`).
|
|
12185
12318
|
* @returns Device pixels per CSS pixel; `undefined` for the half resolution of `host.pixels`.
|
|
12186
12319
|
*/
|
|
12187
|
-
function readResolution(ctx, host) {
|
|
12320
|
+
function readResolution$1(ctx, host) {
|
|
12188
12321
|
return hasPictures(ctx.state.kept) ? host.resolution() : void 0;
|
|
12189
12322
|
}
|
|
12190
12323
|
/**
|
|
@@ -12208,7 +12341,7 @@ function hasPictures(kept) {
|
|
|
12208
12341
|
* @param decided - The decision of this frame, changed in place when a missed frame is drawn.
|
|
12209
12342
|
*/
|
|
12210
12343
|
function verifyFrame(ctx, state, host, decided) {
|
|
12211
|
-
const resolution = readResolution(ctx, host);
|
|
12344
|
+
const resolution = readResolution$1(ctx, host);
|
|
12212
12345
|
if (decided.reason !== "none") {
|
|
12213
12346
|
state.verify.reference = host.pixels(resolution);
|
|
12214
12347
|
return;
|
|
@@ -15224,7 +15357,7 @@ const BEFORE = {
|
|
|
15224
15357
|
* @param resolution - The target's resolution.
|
|
15225
15358
|
* @returns Texels per CSS px.
|
|
15226
15359
|
*/
|
|
15227
|
-
function resolutionOf(filter, resolution) {
|
|
15360
|
+
function resolutionOf$1(filter, resolution) {
|
|
15228
15361
|
const own = filter.resolution;
|
|
15229
15362
|
return own === "inherit" ? resolution : own;
|
|
15230
15363
|
}
|
|
@@ -15244,7 +15377,7 @@ function readStack(slots, resolution, into) {
|
|
|
15244
15377
|
const filter = slots[index]?.filter;
|
|
15245
15378
|
if (filter === void 0 || !filter.enabled) continue;
|
|
15246
15379
|
enabled += 1;
|
|
15247
|
-
rs = Math.min(rs, resolutionOf(filter, resolution));
|
|
15380
|
+
rs = Math.min(rs, resolutionOf$1(filter, resolution));
|
|
15248
15381
|
padding += filter.padding;
|
|
15249
15382
|
}
|
|
15250
15383
|
into.enabled = enabled;
|
|
@@ -21564,7 +21697,7 @@ const NO_DETAIL = Object.freeze({});
|
|
|
21564
21697
|
/** The fewest device pixels per CSS pixel `setResolution` draws at. */
|
|
21565
21698
|
const MIN_RESOLUTION = .5;
|
|
21566
21699
|
/** The most device pixels per CSS pixel `setResolution` draws at. */
|
|
21567
|
-
const MAX_RESOLUTION = 4;
|
|
21700
|
+
const MAX_RESOLUTION$1 = 4;
|
|
21568
21701
|
/**
|
|
21569
21702
|
* Message of a resolution `setResolution` refuses.
|
|
21570
21703
|
*
|
|
@@ -21572,7 +21705,7 @@ const MAX_RESOLUTION = 4;
|
|
|
21572
21705
|
* @returns The message.
|
|
21573
21706
|
*/
|
|
21574
21707
|
function resolutionMessage(value) {
|
|
21575
|
-
return `[game] renderer.monitor.setResolution(${String(value)}) needs a number from ${MIN_RESOLUTION} to ${MAX_RESOLUTION}.\n Pass undefined to go back to maxResolution.`;
|
|
21708
|
+
return `[game] renderer.monitor.setResolution(${String(value)}) needs a number from ${MIN_RESOLUTION} to ${MAX_RESOLUTION$1}.\n Pass undefined to go back to maxResolution.`;
|
|
21576
21709
|
}
|
|
21577
21710
|
/**
|
|
21578
21711
|
* Tells whether `setResolution` takes a value: a number from `MIN_RESOLUTION` to `MAX_RESOLUTION`, or `undefined` to
|
|
@@ -21586,7 +21719,7 @@ function resolutionMessage(value) {
|
|
|
21586
21719
|
* ```
|
|
21587
21720
|
*/
|
|
21588
21721
|
function takesResolution(value) {
|
|
21589
|
-
return value === void 0 || typeof value === "number" && value >= MIN_RESOLUTION && value <= MAX_RESOLUTION;
|
|
21722
|
+
return value === void 0 || typeof value === "number" && value >= MIN_RESOLUTION && value <= MAX_RESOLUTION$1;
|
|
21590
21723
|
}
|
|
21591
21724
|
/**
|
|
21592
21725
|
* Creates the monitor module: the counters of the renderer, the capture of a frame, and the
|
|
@@ -32094,14 +32227,14 @@ function cancelTracksOf(actx, entity) {
|
|
|
32094
32227
|
//#endregion
|
|
32095
32228
|
//#region src/plugins/anim/tween/driver.ts
|
|
32096
32229
|
/** The empty field set of every track nobody muted: one frozen set, shared, read every frame. */
|
|
32097
|
-
const NO_FIELDS = Object.freeze(/* @__PURE__ */ new Set());
|
|
32230
|
+
const NO_FIELDS$1 = Object.freeze(/* @__PURE__ */ new Set());
|
|
32098
32231
|
/**
|
|
32099
32232
|
* The muted fields of a timeline step's track: none.
|
|
32100
32233
|
*
|
|
32101
32234
|
* @returns The shared empty set; a frame builds no set.
|
|
32102
32235
|
*/
|
|
32103
32236
|
function noFields() {
|
|
32104
|
-
return NO_FIELDS;
|
|
32237
|
+
return NO_FIELDS$1;
|
|
32105
32238
|
}
|
|
32106
32239
|
/**
|
|
32107
32240
|
* Creates the tween engine `world` runs `ViewHandle.tween`, `toRest` and `all` on. Installed with
|
|
@@ -35732,9 +35865,22 @@ function overTargetOf(ctx, node, run) {
|
|
|
35732
35865
|
return takePending(ctx);
|
|
35733
35866
|
}
|
|
35734
35867
|
/**
|
|
35735
|
-
*
|
|
35736
|
-
*
|
|
35737
|
-
*
|
|
35868
|
+
* The projections of the scene that leaves which the scene that comes does not list. A projection
|
|
35869
|
+
* both list stays mounted: the layers of a projection are the ones its spec names, and both scenes
|
|
35870
|
+
* declare them, so its views stay in the same layer containers.
|
|
35871
|
+
*
|
|
35872
|
+
* @param previous - The scene that leaves.
|
|
35873
|
+
* @param scene - The scene that comes.
|
|
35874
|
+
* @returns The names to unmount, in the order the leaving scene lists them.
|
|
35875
|
+
*/
|
|
35876
|
+
function leavingOf(previous, scene) {
|
|
35877
|
+
return previous.projections.filter((name) => !scene.projections.includes(name));
|
|
35878
|
+
}
|
|
35879
|
+
/**
|
|
35880
|
+
* Builds the scene in one synchronous block: the layers, then the projections only the old scene
|
|
35881
|
+
* lists out, then the list of the new scene in (a projection both list stays and takes its new
|
|
35882
|
+
* place), the new scene to the world's systems, then the list the scene names to `assets`, then
|
|
35883
|
+
* the frame loop out of its idle cap, and only then the event.
|
|
35738
35884
|
*
|
|
35739
35885
|
* @param ctx - Domain context of the plugin.
|
|
35740
35886
|
* @param scene - The scene whose bundles are there.
|
|
@@ -35746,7 +35892,7 @@ function apply(ctx, scene, named) {
|
|
|
35746
35892
|
const from = state.current;
|
|
35747
35893
|
const previous = from === void 0 ? void 0 : state.scenes.get(from);
|
|
35748
35894
|
projection.setLayers(scene.layers);
|
|
35749
|
-
if (previous !== void 0) projection.unmount(previous
|
|
35895
|
+
if (previous !== void 0) projection.unmount(leavingOf(previous, scene));
|
|
35750
35896
|
projection.mount(scene.projections, state.owner);
|
|
35751
35897
|
state.current = scene.id;
|
|
35752
35898
|
ctx.deps.world.ecs.setScene(scene.id);
|
|
@@ -37867,6 +38013,19 @@ function readArea(id, area) {
|
|
|
37867
38013
|
return area;
|
|
37868
38014
|
}
|
|
37869
38015
|
/**
|
|
38016
|
+
* Reads the declared resolution: texels per CSS px, or 0 for the canvas's. A JavaScript caller may pass anything.
|
|
38017
|
+
*
|
|
38018
|
+
* @param id - The filter id.
|
|
38019
|
+
* @param resolution - What the game declared.
|
|
38020
|
+
* @returns The resolution; 0 when it was left out.
|
|
38021
|
+
* @throws {Error} When it is given and is not a number from 0 to `MAX_RESOLUTION`.
|
|
38022
|
+
*/
|
|
38023
|
+
function readResolution(id, resolution) {
|
|
38024
|
+
if (resolution === void 0) return 0;
|
|
38025
|
+
if (typeof resolution === "number" && resolution >= 0 && resolution <= 4) return resolution;
|
|
38026
|
+
throw new Error(`[game] Filter "${id}": resolution must be from 0 to 4, 0 for the resolution of the canvas.\n Got ${String(resolution)}.`);
|
|
38027
|
+
}
|
|
38028
|
+
/**
|
|
37870
38029
|
* Tells whether a value is a list of 2 to 4 finite numbers.
|
|
37871
38030
|
*
|
|
37872
38031
|
* @param value - A uniform declaration.
|
|
@@ -38157,18 +38316,20 @@ function cacheWithInputs(cache, inputs) {
|
|
|
38157
38316
|
* `enabled: true` and `order: 0`. An input `blur` gives the body `sampleBlur(uv)`: the last texture
|
|
38158
38317
|
* of its chain, read at the body's `uv`. A `fallback` draws instead when the filter is broken.
|
|
38159
38318
|
* An `area` answers, from the value, the rectangle in the entity's own units outside of which the
|
|
38160
|
-
* filter draws nothing: Pixi then renders and fills only that box, not the view's bounds.
|
|
38319
|
+
* filter draws nothing: Pixi then renders and fills only that box, not the view's bounds. A
|
|
38320
|
+
* `resolution` draws the view and its filters at that many texels per CSS px instead of the
|
|
38321
|
+
* canvas's.
|
|
38161
38322
|
* Register it through the `filters` key of a feature. The README shows a filter with a blur input
|
|
38162
38323
|
* and one with an `area`.
|
|
38163
38324
|
*
|
|
38164
38325
|
* @param id - The filter id, also the component name; unique, and not one of the built-ins.
|
|
38165
38326
|
* @param spec - Both bodies, the uniforms, the passes one apply costs, the padding, the inputs, the fallback, the
|
|
38166
|
-
* cache category and the
|
|
38327
|
+
* cache category, the area and the resolution.
|
|
38167
38328
|
* @returns The component type, with the frozen `filter` definition attached.
|
|
38168
38329
|
* @throws {Error} When the id is empty, the WGSL has no `mainFragment`, the GLSL no `void main(`,
|
|
38169
38330
|
* a uniform is reserved, badly named or of no known form, the passes or the padding break
|
|
38170
38331
|
* their rules, an input or the fallback breaks one of its rules, `passes` is given with inputs,
|
|
38171
|
-
* or `
|
|
38332
|
+
* `area` is not a function, or `resolution` is not a number from 0 to 4.
|
|
38172
38333
|
* @example
|
|
38173
38334
|
* ```ts
|
|
38174
38335
|
* // features/board/effects.ts: a gold tint a motion hook tweens on a merge, on WebGPU and WebGL.
|
|
@@ -38190,6 +38351,7 @@ function defineFilter(id, spec) {
|
|
|
38190
38351
|
checkCost(id, passes, padding, uniforms);
|
|
38191
38352
|
checkCache(id, spec.cache);
|
|
38192
38353
|
const area = readArea(id, spec.area);
|
|
38354
|
+
const resolution = readResolution(id, spec.resolution);
|
|
38193
38355
|
const origin = callsCanvasPixel(spec.wgsl, spec.glsl) ? KEPT_ORIGIN : void 0;
|
|
38194
38356
|
const cache = cacheWithInputs(cacheOf(spec.wgsl, spec.glsl, spec.cache), inputs);
|
|
38195
38357
|
const filter = Object.freeze({
|
|
@@ -38203,6 +38365,7 @@ function defineFilter(id, spec) {
|
|
|
38203
38365
|
fallback,
|
|
38204
38366
|
cache,
|
|
38205
38367
|
origin,
|
|
38368
|
+
resolution,
|
|
38206
38369
|
...area === void 0 ? {} : { area }
|
|
38207
38370
|
});
|
|
38208
38371
|
if (spec.cache !== false && (readsScreenIn(spec.wgsl) || readsScreenIn(spec.glsl))) screenReaders.add(filter);
|
|
@@ -38423,6 +38586,7 @@ const displacementDefaults = {
|
|
|
38423
38586
|
};
|
|
38424
38587
|
const alphaDefaults = {
|
|
38425
38588
|
alpha: 1,
|
|
38589
|
+
resolution: 0,
|
|
38426
38590
|
enabled: true,
|
|
38427
38591
|
order: 0
|
|
38428
38592
|
};
|
|
@@ -38519,11 +38683,16 @@ const Noise = /*#__PURE__*/ component("effects.noise", noiseDefaults);
|
|
|
38519
38683
|
const Displacement = /*#__PURE__*/ component("effects.displacement", displacementDefaults);
|
|
38520
38684
|
/**
|
|
38521
38685
|
* Pixi's alpha over a view and its subtree as one layer, so overlapping children do not show
|
|
38522
|
-
* through each other.
|
|
38686
|
+
* through each other. `resolution: 0` draws at the resolution of the canvas; a number draws the
|
|
38687
|
+
* subtree at that many texels per CSS px, at most 4, and every other filter of the view with it
|
|
38688
|
+
* (Pixi's smallest resolution wins). At `alpha: 1` and `resolution: 0` the filter is switched off
|
|
38689
|
+
* after 8 frames there: it would copy the subtree unchanged through a pass of its own, and a
|
|
38690
|
+
* keyframe that only touches 1 switches nothing. One render pass per apply.
|
|
38523
38691
|
*
|
|
38524
38692
|
* @example
|
|
38525
38693
|
* ```ts
|
|
38526
|
-
*
|
|
38694
|
+
* // The fog of a memory screen, drawn at half a texel per CSS px through one pass.
|
|
38695
|
+
* Alpha({ resolution: 0.5 }).value; // { alpha: 1, resolution: 0.5, enabled: true, order: 0 }
|
|
38527
38696
|
* ```
|
|
38528
38697
|
*/
|
|
38529
38698
|
const Alpha = /*#__PURE__*/ component("effects.alpha", alphaDefaults);
|
|
@@ -38648,6 +38817,20 @@ function brokenMark() {
|
|
|
38648
38817
|
//#endregion
|
|
38649
38818
|
//#region src/plugins/effects/filters/instance.ts
|
|
38650
38819
|
/**
|
|
38820
|
+
* @file effects/filters — the only file that builds, writes and destroys Pixi filters. Ours: a
|
|
38821
|
+
* `Filter` over a `GpuProgram` of the assembled WGSL and a `GlProgram` of the assembled GLSL, with
|
|
38822
|
+
* one `UniformGroup` both read. The Pixi-core kinds: their own class, written through their setters
|
|
38823
|
+
* when a field differs from what they were last given. Every kind but `Blur` renders at
|
|
38824
|
+
* `resolution: "inherit"`, the resolution of the canvas, so a filtered view stays sharp on DPR 2-3
|
|
38825
|
+
* (decision 32), unless its value (`Alpha`) or its definition (`defineFilter`) gives a resolution of
|
|
38826
|
+
* its own. An `Alpha` at 1 drawn at the canvas's is switched off once it rested there: it would copy
|
|
38827
|
+
* the view unchanged through a pass. Every write compares first: a value the filter holds already writes nothing and
|
|
38828
|
+
* asks the renderer for no frame. A destroy frees the uniform buffers first: `Filter.destroy()`
|
|
38829
|
+
* leaves them to a GC that runs after a minute (P10). A filter with inputs draws each place of its
|
|
38830
|
+
* chains through Pixi's public `applyFilter` from inside its own `apply`, into pooled textures given
|
|
38831
|
+
* back in a `finally`; a stand-in draws in place of a broken kind or instance.
|
|
38832
|
+
*/
|
|
38833
|
+
/**
|
|
38651
38834
|
* The resolution of every filter but `Blur`: the render target's, so a filter draws at the
|
|
38652
38835
|
* resolution of the canvas. Pixi's default of 1 would draw it at half the pixels on DPR 2.
|
|
38653
38836
|
*/
|
|
@@ -38662,6 +38845,20 @@ const PROBE_VALUE = Object.freeze({
|
|
|
38662
38845
|
/** The numbers of a vector field that holds none. */
|
|
38663
38846
|
const NO_NUMBERS = Object.freeze([]);
|
|
38664
38847
|
/**
|
|
38848
|
+
* The resolution Pixi is given for a resolution of a value or a definition: the number, at most `MAX_RESOLUTION`, or
|
|
38849
|
+
* the canvas's for 0 (or less, or not a number).
|
|
38850
|
+
*
|
|
38851
|
+
* @param resolution - Texels per CSS px; 0 for the canvas's.
|
|
38852
|
+
* @returns What a filter's `resolution` takes.
|
|
38853
|
+
* @example
|
|
38854
|
+
* ```ts
|
|
38855
|
+
* resolutionOf(50); // 4
|
|
38856
|
+
* ```
|
|
38857
|
+
*/
|
|
38858
|
+
function resolutionOf(resolution) {
|
|
38859
|
+
return resolution > 0 ? Math.min(resolution, 4) : CANVAS_RESOLUTION;
|
|
38860
|
+
}
|
|
38861
|
+
/**
|
|
38665
38862
|
* Tells whether two numbers are the same value for the GPU: equal, or both NaN.
|
|
38666
38863
|
*
|
|
38667
38864
|
* @param a - One number.
|
|
@@ -38822,7 +39019,7 @@ function createOurs(pixi, definition, value) {
|
|
|
38822
39019
|
glProgram,
|
|
38823
39020
|
resources,
|
|
38824
39021
|
padding: paddingOf$1(definition, value),
|
|
38825
|
-
resolution:
|
|
39022
|
+
resolution: resolutionOf(definition.resolution)
|
|
38826
39023
|
}),
|
|
38827
39024
|
group,
|
|
38828
39025
|
definition,
|
|
@@ -38864,6 +39061,43 @@ function blurResolution(ectx, value) {
|
|
|
38864
39061
|
return ectx.state.phone ? ectx.config.blur.phoneResolution : 1;
|
|
38865
39062
|
}
|
|
38866
39063
|
/**
|
|
39064
|
+
* Tells whether an `Alpha` value draws its view unchanged: switched on, at alpha 1, at the canvas's resolution. Its
|
|
39065
|
+
* filter would copy the view through a pass of its own. The fields are read by name: a fade reads them every frame.
|
|
39066
|
+
*
|
|
39067
|
+
* @param value - The stored `Alpha` value.
|
|
39068
|
+
* @returns True when the filter may be switched off.
|
|
39069
|
+
* @example
|
|
39070
|
+
* ```ts
|
|
39071
|
+
* drawsUnchanged({ enabled: true, order: 0, alpha: 1, resolution: 0 }); // true
|
|
39072
|
+
* ```
|
|
39073
|
+
*/
|
|
39074
|
+
function drawsUnchanged(value) {
|
|
39075
|
+
const { alpha, resolution } = value;
|
|
39076
|
+
return value.enabled && alpha === 1 && !(typeof resolution === "number" && resolution > 0);
|
|
39077
|
+
}
|
|
39078
|
+
/**
|
|
39079
|
+
* The `enabled` switch of an instance for the stored value of this sync: the value's own, at once, but an `Alpha` that
|
|
39080
|
+
* draws its view unchanged is switched off only at its `CALM_AFTER`-th sync in a row so while its filter is on, counted
|
|
39081
|
+
* in `calm`: keyframes that touch 1 keep the pass and switch nothing, a fade that ends at 1 lets it go. The filter
|
|
39082
|
+
* sync syncs the view again while `calm` counts.
|
|
39083
|
+
*
|
|
39084
|
+
* @param instance - The instance.
|
|
39085
|
+
* @param value - The stored filter value.
|
|
39086
|
+
* @returns True when Pixi is to draw the filter.
|
|
39087
|
+
*/
|
|
39088
|
+
function switchOf(instance, value) {
|
|
39089
|
+
if (instance.kind !== "alpha") return value.enabled;
|
|
39090
|
+
if (!drawsUnchanged(value)) {
|
|
39091
|
+
instance.calm = 0;
|
|
39092
|
+
return value.enabled;
|
|
39093
|
+
}
|
|
39094
|
+
if (!instance.filter.enabled) return false;
|
|
39095
|
+
instance.calm += 1;
|
|
39096
|
+
if (instance.calm < 8) return true;
|
|
39097
|
+
instance.calm = 0;
|
|
39098
|
+
return false;
|
|
39099
|
+
}
|
|
39100
|
+
/**
|
|
38867
39101
|
* Composes a colour matrix from the identity: brightness, saturation, contrast, hue, and the
|
|
38868
39102
|
* grayscale as a negative saturation. `ColorMatrixFilter` multiplies, so it starts from `reset()`.
|
|
38869
39103
|
*
|
|
@@ -39005,10 +39239,11 @@ function createCore(ectx, pixi, core, value, probeMap) {
|
|
|
39005
39239
|
kind: "alpha",
|
|
39006
39240
|
filter: new pixi.AlphaFilter({
|
|
39007
39241
|
alpha: numberOf$1(value, "alpha"),
|
|
39008
|
-
resolution:
|
|
39242
|
+
resolution: resolutionOf(numberOf$1(value, "resolution"))
|
|
39009
39243
|
}),
|
|
39010
39244
|
...appliedOf(value),
|
|
39011
|
-
passes: 1
|
|
39245
|
+
passes: 1,
|
|
39246
|
+
calm: 0
|
|
39012
39247
|
};
|
|
39013
39248
|
}
|
|
39014
39249
|
}
|
|
@@ -39030,7 +39265,7 @@ function createCore(ectx, pixi, core, value, probeMap) {
|
|
|
39030
39265
|
function createFilter(ectx, pixi, kind, value, probeMap) {
|
|
39031
39266
|
const body = kind.source === "wgsl" ? createOurs(pixi, kind.definition, value) : createCore(ectx, pixi, kind.core, value, probeMap);
|
|
39032
39267
|
if (body === void 0) return void 0;
|
|
39033
|
-
body.filter.enabled = value.enabled;
|
|
39268
|
+
body.filter.enabled = value.enabled && !(body.kind === "alpha" && drawsUnchanged(value));
|
|
39034
39269
|
if (body.kind === "wgsl") body.areaStale = body.definition.area !== void 0;
|
|
39035
39270
|
const instance = Object.assign(body, {
|
|
39036
39271
|
order: value.order,
|
|
@@ -39255,7 +39490,8 @@ function wireInputs(ectx, pixi, instance) {
|
|
|
39255
39490
|
};
|
|
39256
39491
|
instance.inputs = inputs;
|
|
39257
39492
|
instance.filter.apply = (manager, input, output, clearMode) => applyInputs(ectx, pixi, instance, inputs, manager, input, output, clearMode);
|
|
39258
|
-
|
|
39493
|
+
const { resolution } = instance.definition;
|
|
39494
|
+
evaluate(ectx, pixi, instance, inputs, resolution > 0 ? resolution : ectx.deps.renderer.host.resolution());
|
|
39259
39495
|
}
|
|
39260
39496
|
/**
|
|
39261
39497
|
* Reads one property of a value a chain returned, whatever its type.
|
|
@@ -39730,6 +39966,7 @@ function sameField(a, b) {
|
|
|
39730
39966
|
*/
|
|
39731
39967
|
function coreDiffers(instance, value) {
|
|
39732
39968
|
const { fields, applied } = instance;
|
|
39969
|
+
if (instance.kind === "alpha") return !sameField(applied.alpha, value.alpha) || !sameField(applied.resolution, value.resolution);
|
|
39733
39970
|
for (const field of fields) if (instance.kind === "displacement" && field === "map") {
|
|
39734
39971
|
if (mapOf(value) !== instance.map) return true;
|
|
39735
39972
|
} else if (!sameField(applied[field], value[field])) return true;
|
|
@@ -39759,6 +39996,12 @@ function mapOf(value) {
|
|
|
39759
39996
|
function writeCore(ectx, instance, value) {
|
|
39760
39997
|
setCore(ectx, instance, value);
|
|
39761
39998
|
const { fields, applied } = instance;
|
|
39999
|
+
if (instance.kind === "alpha") {
|
|
40000
|
+
const { alpha, resolution } = value;
|
|
40001
|
+
if (alpha !== void 0) applied.alpha = alpha;
|
|
40002
|
+
if (resolution !== void 0 && resolution !== applied.resolution) applied.resolution = resolution;
|
|
40003
|
+
return;
|
|
40004
|
+
}
|
|
39762
40005
|
for (const field of fields) {
|
|
39763
40006
|
const next = value[field];
|
|
39764
40007
|
if (next !== void 0) applied[field] = next;
|
|
@@ -39800,7 +40043,11 @@ function setCore(ectx, instance, value) {
|
|
|
39800
40043
|
instance.map = map;
|
|
39801
40044
|
return;
|
|
39802
40045
|
}
|
|
39803
|
-
default:
|
|
40046
|
+
default: {
|
|
40047
|
+
const { alpha, resolution } = value;
|
|
40048
|
+
instance.filter.alpha = typeof alpha === "number" ? alpha : 0;
|
|
40049
|
+
if (resolution !== instance.applied.resolution) instance.filter.resolution = resolutionOf(typeof resolution === "number" ? resolution : 0);
|
|
40050
|
+
}
|
|
39804
40051
|
}
|
|
39805
40052
|
}
|
|
39806
40053
|
/**
|
|
@@ -39826,8 +40073,9 @@ function switchOffBroken(instance, before) {
|
|
|
39826
40073
|
}
|
|
39827
40074
|
/**
|
|
39828
40075
|
* Brings one instance up to the stored value for a frame, comparing before anything is written.
|
|
39829
|
-
* A broken instance stays off whatever its component says
|
|
39830
|
-
*
|
|
40076
|
+
* A broken instance stays off whatever its component says, and an `Alpha` at 1 drawn at the
|
|
40077
|
+
* canvas's resolution is switched off at its `CALM_AFTER`-th sync there. Switching on or off is a
|
|
40078
|
+
* visible change; so is a new value of a filter that is on. A filter that is off and stays off still gets
|
|
39831
40079
|
* its new values written, so it is current when it comes back on, but asks for nothing: an off
|
|
39832
40080
|
* filter draws no pixel. Ours compare on every sync of their view (a view syncs when something of it changed); a core
|
|
39833
40081
|
* kind only when its component changed.
|
|
@@ -39844,13 +40092,14 @@ function writeFilter(ectx, instance, value, changed, before) {
|
|
|
39844
40092
|
switchOffBroken(instance, before);
|
|
39845
40093
|
return false;
|
|
39846
40094
|
}
|
|
39847
|
-
const
|
|
40095
|
+
const on = switchOf(instance, value);
|
|
40096
|
+
const toggled = instance.filter.enabled !== on;
|
|
39848
40097
|
const differs = instance.kind === "wgsl" ? oursDiffer(instance, instance.definition, value) : changed && coreDiffers(instance, value);
|
|
39849
40098
|
if (!toggled && !differs) return false;
|
|
39850
40099
|
staleArea(instance);
|
|
39851
|
-
const visible = toggled ||
|
|
40100
|
+
const visible = toggled || on;
|
|
39852
40101
|
if (visible) before();
|
|
39853
|
-
if (toggled) instance.filter.enabled =
|
|
40102
|
+
if (toggled) instance.filter.enabled = on;
|
|
39854
40103
|
if (differs) {
|
|
39855
40104
|
if (instance.kind === "wgsl") {
|
|
39856
40105
|
writeOurs(instance, instance.definition, value);
|
|
@@ -40595,8 +40844,10 @@ function judgeArea(ectx, instance) {
|
|
|
40595
40844
|
}
|
|
40596
40845
|
/**
|
|
40597
40846
|
* The union of the areas of a view's enabled filters, into the scratch box: none while no filter is enabled, and none
|
|
40598
|
-
* when an enabled filter has no area (every built-in kind, or a definition that answered `undefined`) — it draws
|
|
40599
|
-
* the view's bounds.
|
|
40847
|
+
* when an enabled filter has no area (every other built-in kind, or a definition that answered `undefined`) — it draws
|
|
40848
|
+
* over the view's bounds. An `Alpha` does not count: it only scales the pixels the others draw, and makes none where
|
|
40849
|
+
* they draw nothing; alone it leaves the view without an area. A disabled filter, and a kind a debug switch turned off
|
|
40850
|
+
* (it is not placed), do not count either.
|
|
40600
40851
|
*
|
|
40601
40852
|
* @param view - The view's record, its instances placed.
|
|
40602
40853
|
*/
|
|
@@ -40607,7 +40858,7 @@ function unionOf(view) {
|
|
|
40607
40858
|
let bottom = Number.NEGATIVE_INFINITY;
|
|
40608
40859
|
union.some = false;
|
|
40609
40860
|
for (const instance of view.placed) {
|
|
40610
|
-
if (!instance.filter.enabled) continue;
|
|
40861
|
+
if (!instance.filter.enabled || instance.kind === "alpha") continue;
|
|
40611
40862
|
if (instance.kind !== "wgsl" || !instance.area.some) {
|
|
40612
40863
|
union.some = false;
|
|
40613
40864
|
return;
|
|
@@ -40619,6 +40870,7 @@ function unionOf(view) {
|
|
|
40619
40870
|
right = Math.max(right, area.x + area.width);
|
|
40620
40871
|
bottom = Math.max(bottom, area.y + area.height);
|
|
40621
40872
|
}
|
|
40873
|
+
if (!union.some) return;
|
|
40622
40874
|
union.x = left;
|
|
40623
40875
|
union.y = top;
|
|
40624
40876
|
union.width = right - left;
|
|
@@ -40834,7 +41086,7 @@ function syncKind(ectx, pixi, entity, view, kind, changed, off) {
|
|
|
40834
41086
|
writeFilter(ectx, instance, value, changed, off ? NO_SIGNAL : view.signal);
|
|
40835
41087
|
instance.order = value.order;
|
|
40836
41088
|
refreshInputs(ectx, pixi, instance, off);
|
|
40837
|
-
if (instance.kind === "wgsl" && (instance.inputs?.retired.length ?? 0) > 0) markDirty$1(ectx, view);
|
|
41089
|
+
if (instance.kind === "wgsl" && (instance.inputs?.retired.length ?? 0) > 0 || instance.kind === "alpha" && instance.calm > 0) markDirty$1(ectx, view);
|
|
40838
41090
|
return installReplacement(view, kind, instance);
|
|
40839
41091
|
}
|
|
40840
41092
|
instance = firstInstance(ectx, pixi, kind, value);
|
|
@@ -41101,7 +41353,8 @@ function dropProbe(ectx, probe) {
|
|
|
41101
41353
|
}
|
|
41102
41354
|
/**
|
|
41103
41355
|
* A probe of one Pixi-core kind at the frozen defaults of its component; a `Displacement` draws over Pixi's white
|
|
41104
|
-
* texture, since its program does not depend on the map.
|
|
41356
|
+
* texture, since its program does not depend on the map. A probe is switched on whatever its defaults: an `Alpha` at 1
|
|
41357
|
+
* would be off, and Pixi would warm nothing. Its `apply` is guarded like any instance's.
|
|
41105
41358
|
*
|
|
41106
41359
|
* @param ectx - Domain context of the effects plugin.
|
|
41107
41360
|
* @param pixi - The module the renderer loaded.
|
|
@@ -41112,6 +41365,7 @@ function coreProbe(ectx, pixi, id) {
|
|
|
41112
41365
|
const kind = ectx.state.kinds.get(id);
|
|
41113
41366
|
const instance = kind === void 0 ? void 0 : createFilter(ectx, pixi, kind, kind.component.defaults, pixi.Texture.WHITE);
|
|
41114
41367
|
if (instance === void 0) return void 0;
|
|
41368
|
+
instance.filter.enabled = true;
|
|
41115
41369
|
return {
|
|
41116
41370
|
filter: instance.filter,
|
|
41117
41371
|
destroy: () => destroyFilter(instance)
|
|
@@ -47773,20 +48027,68 @@ function resolveContent(ctx, entity, text) {
|
|
|
47773
48027
|
return resolved;
|
|
47774
48028
|
}
|
|
47775
48029
|
/**
|
|
47776
|
-
* The
|
|
47777
|
-
* label is visited every frame: `ui` writes `Text` after this system and change sets clear in
|
|
47778
|
-
* phase `signals`, so `changed(Text)` cannot tell which moved. The locale is read once per run,
|
|
47779
|
-
* the clock at most once and only when a countdown label asks.
|
|
48030
|
+
* The frame the system and the step after the layout flush share, made once per plugin.
|
|
47780
48031
|
*
|
|
47781
48032
|
* @param ctx - Domain context of the text plugin.
|
|
47782
|
-
* @returns
|
|
48033
|
+
* @returns A frame with no locale and no moment read yet.
|
|
47783
48034
|
*/
|
|
47784
|
-
function
|
|
47785
|
-
|
|
48035
|
+
function createTextFrame(ctx) {
|
|
48036
|
+
return {
|
|
47786
48037
|
now: NaN,
|
|
47787
48038
|
locale: "",
|
|
47788
48039
|
duration: (ms) => ctx.deps.i18n.duration(ms)
|
|
47789
48040
|
};
|
|
48041
|
+
}
|
|
48042
|
+
/**
|
|
48043
|
+
* Empties a list of labels in place: popped, so it keeps its store for the next batch.
|
|
48044
|
+
*
|
|
48045
|
+
* @param labels - The list.
|
|
48046
|
+
*/
|
|
48047
|
+
function forgetAdded(labels) {
|
|
48048
|
+
while (labels.length > 0) labels.pop();
|
|
48049
|
+
}
|
|
48050
|
+
/** A patch with no field: the write marks `Text` changed and changes nothing. Frozen, so no caller fills it. */
|
|
48051
|
+
const NO_FIELDS = Object.freeze({});
|
|
48052
|
+
/**
|
|
48053
|
+
* Writes `Text` once more, with no field, for the labels the step after the last layout flush
|
|
48054
|
+
* resolved. `ui` measured them in its solve, before they were resolved, and the change of that
|
|
48055
|
+
* frame is gone; this one reaches its remeasure in the same layout phase, so a label whose string
|
|
48056
|
+
* measures otherwise than what `ui` measured gets its size. A label whose `Text` left is skipped.
|
|
48057
|
+
*
|
|
48058
|
+
* @param ctx - Domain context of the text plugin.
|
|
48059
|
+
*/
|
|
48060
|
+
function writeOnceMore(ctx) {
|
|
48061
|
+
const ecs = ctx.deps.world.ecs;
|
|
48062
|
+
for (const entity of ctx.state.rewrite) if (ecs.get(entity, Text) !== void 0) ecs.set(entity, Text, NO_FIELDS);
|
|
48063
|
+
forgetAdded(ctx.state.rewrite);
|
|
48064
|
+
}
|
|
48065
|
+
/**
|
|
48066
|
+
* Notes a label whose `Text` came, as the world's `onAdded` hook calls it: it is marked for the
|
|
48067
|
+
* next layout phase, and while the world is live it is listed for the step after the layout
|
|
48068
|
+
* flush. In another mode no layout phase runs and the list would only grow: the label waits for
|
|
48069
|
+
* the next live run of the system, which resolves every marked label.
|
|
48070
|
+
*
|
|
48071
|
+
* @param ctx - Domain context of the text plugin.
|
|
48072
|
+
* @param entity - The entity that got a `Text`.
|
|
48073
|
+
*/
|
|
48074
|
+
function noteAdded(ctx, entity) {
|
|
48075
|
+
ctx.state.dirty.add(entity);
|
|
48076
|
+
if (ctx.deps.world.ecs.mode() === "live") ctx.state.added.push(entity);
|
|
48077
|
+
}
|
|
48078
|
+
/**
|
|
48079
|
+
* The one system of the plugin: phase `layout`, before the two of `ui`, over every label. Every
|
|
48080
|
+
* label is visited every frame: `ui` writes `Text` after this system and change sets clear in
|
|
48081
|
+
* phase `signals`, so `changed(Text)` cannot tell which moved. The locale is read once per run,
|
|
48082
|
+
* the clock at most once and only when a countdown label asks. The labels that came before the run
|
|
48083
|
+
* are in its walk, so the list of the labels that came starts again empty: the step after the
|
|
48084
|
+
* layout flush takes only the ones the flush brings. The labels that step resolved last get their
|
|
48085
|
+
* `Text` written once more first, so `ui` measures the string they show.
|
|
48086
|
+
*
|
|
48087
|
+
* @param ctx - Domain context of the text plugin.
|
|
48088
|
+
* @param frame - What the runs share with the step after the layout flush.
|
|
48089
|
+
* @returns The system definition `world.ecs.system` takes.
|
|
48090
|
+
*/
|
|
48091
|
+
function createTextSystem(ctx, frame) {
|
|
47790
48092
|
return system({
|
|
47791
48093
|
name: TEXT_SYSTEM_NAME,
|
|
47792
48094
|
phase: "layout",
|
|
@@ -47794,11 +48096,42 @@ function createTextSystem(ctx) {
|
|
|
47794
48096
|
run: (entities) => {
|
|
47795
48097
|
frame.now = NaN;
|
|
47796
48098
|
frame.locale = ctx.deps.i18n.locale();
|
|
48099
|
+
forgetAdded(ctx.state.added);
|
|
48100
|
+
if (ctx.state.rewrite.length > 0) writeOnceMore(ctx);
|
|
47797
48101
|
for (const row of entities) stepText(ctx, row[0], row[1], frame);
|
|
47798
48102
|
}
|
|
47799
48103
|
});
|
|
47800
48104
|
}
|
|
47801
48105
|
/**
|
|
48106
|
+
* Resolves the labels whose `Text` came after the layout system ran. `ui` adds a label in its
|
|
48107
|
+
* solve, and the flush at the end of phase `layout` attaches it with the rest of its batch, a bound
|
|
48108
|
+
* component included; this runs right after that flush and before phase `sync`, so the renderer
|
|
48109
|
+
* builds the label with its string, and lists it, so the next system run writes its `Text` once
|
|
48110
|
+
* more for the remeasure of `ui`. A label whose content changed is not among them: it resolves in
|
|
48111
|
+
* the next layout phase, as before. While the world is not live its layout phase did not run: the
|
|
48112
|
+
* list is dropped, and the labels, marked, wait for the next live run, as every label does.
|
|
48113
|
+
*
|
|
48114
|
+
* @param ctx - Domain context of the text plugin.
|
|
48115
|
+
* @param frame - What the system's runs share.
|
|
48116
|
+
*/
|
|
48117
|
+
function resolveAdded(ctx, frame) {
|
|
48118
|
+
const added = ctx.state.added;
|
|
48119
|
+
if (added.length === 0) return;
|
|
48120
|
+
if (ctx.deps.world.ecs.mode() !== "live") {
|
|
48121
|
+
added.length = 0;
|
|
48122
|
+
return;
|
|
48123
|
+
}
|
|
48124
|
+
frame.now = NaN;
|
|
48125
|
+
frame.locale = ctx.deps.i18n.locale();
|
|
48126
|
+
for (const entity of added) {
|
|
48127
|
+
const text = ctx.deps.world.ecs.get(entity, Text);
|
|
48128
|
+
if (text === void 0) continue;
|
|
48129
|
+
stepText(ctx, entity, text, frame);
|
|
48130
|
+
ctx.state.rewrite.push(entity);
|
|
48131
|
+
}
|
|
48132
|
+
forgetAdded(added);
|
|
48133
|
+
}
|
|
48134
|
+
/**
|
|
47802
48135
|
* Marks labels for the next layout phase: every one of them, or only the ones `keep` names. A
|
|
47803
48136
|
* locale change names the labels whose content is a message; a dev hot swap names the labels
|
|
47804
48137
|
* drawn with a replaced font or icon.
|
|
@@ -48544,9 +48877,12 @@ function replaceStyles(ctx, styles) {
|
|
|
48544
48877
|
ctx.deps.time.wake();
|
|
48545
48878
|
}
|
|
48546
48879
|
/**
|
|
48547
|
-
* Opens what the plugin owns: the style table, the font keys, the layout system, the
|
|
48548
|
-
* hooks of `Text` and the display adapter. The
|
|
48549
|
-
*
|
|
48880
|
+
* Opens what the plugin owns: the style table, the font keys, the layout system, the step after
|
|
48881
|
+
* the layout flush, the two world hooks of `Text` and the display adapter. The step is a frame
|
|
48882
|
+
* callback of phase `layout`: `world` registered its own in its `onStart`, which runs before this
|
|
48883
|
+
* one (`text` depends on `world`), so the step runs after the world's layout phase and its flush,
|
|
48884
|
+
* and before phase `sync`. The fonts of a bundle that is already loaded are read at once, because
|
|
48885
|
+
* the boot bundle lands before this runs.
|
|
48550
48886
|
*
|
|
48551
48887
|
* @param ctx - Kernel context of the text plugin.
|
|
48552
48888
|
* @throws {Error} When two features define the same style name.
|
|
@@ -48554,18 +48890,18 @@ function replaceStyles(ctx, styles) {
|
|
|
48554
48890
|
function startText(ctx) {
|
|
48555
48891
|
const tctx = withDeps$1(ctx);
|
|
48556
48892
|
const ecs = tctx.deps.world.ecs;
|
|
48893
|
+
const frame = createTextFrame(tctx);
|
|
48557
48894
|
buildStyles(tctx);
|
|
48558
48895
|
collectFonts(tctx);
|
|
48559
|
-
tctx.state.removers.push(ecs.system(createTextSystem(tctx)), ecs.onAdded(Text, (entity) => {
|
|
48560
|
-
tctx.state.dirty.add(entity);
|
|
48561
|
-
}), ecs.onRemoved(Text, (entity) => {
|
|
48896
|
+
tctx.state.removers.push(ecs.system(createTextSystem(tctx, frame)), tctx.deps.time.onFrame("layout", () => resolveAdded(tctx, frame)), ecs.onAdded(Text, (entity) => noteAdded(tctx, entity)), ecs.onRemoved(Text, (entity) => {
|
|
48562
48897
|
forgetText(tctx.state, entity);
|
|
48563
48898
|
}), tctx.deps.renderer.sync.displays.provide(Text, createTextAdapter(tctx)));
|
|
48564
48899
|
installFonts(tctx);
|
|
48565
48900
|
}
|
|
48566
48901
|
/**
|
|
48567
|
-
* Closes what the plugin opened: the system, the
|
|
48568
|
-
* objects the adapter built are the renderer's, and it frees them in its
|
|
48902
|
+
* Closes what the plugin opened: the system, the step after the layout flush, the two world hooks
|
|
48903
|
+
* and the adapter. The Pixi objects the adapter built are the renderer's, and it frees them in its
|
|
48904
|
+
* own teardown.
|
|
48569
48905
|
*
|
|
48570
48906
|
* @param state - The plugin state, the only thing a teardown context carries.
|
|
48571
48907
|
*/
|
|
@@ -48582,6 +48918,8 @@ function stopText(state) {
|
|
|
48582
48918
|
state.measured.clear();
|
|
48583
48919
|
state.cache.clear();
|
|
48584
48920
|
state.dirty.clear();
|
|
48921
|
+
state.added.length = 0;
|
|
48922
|
+
state.rewrite.length = 0;
|
|
48585
48923
|
state.warned.clear();
|
|
48586
48924
|
}
|
|
48587
48925
|
//#endregion
|
|
@@ -48731,6 +49069,8 @@ function createTextState$1() {
|
|
|
48731
49069
|
cache: emptyMap$2(),
|
|
48732
49070
|
drawn: emptyWeakMap(),
|
|
48733
49071
|
dirty: emptySet$1(),
|
|
49072
|
+
added: [],
|
|
49073
|
+
rewrite: [],
|
|
48734
49074
|
generation: 0,
|
|
48735
49075
|
warned: emptySet$1(),
|
|
48736
49076
|
removers: []
|
|
@@ -53462,6 +53802,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
53462
53802
|
};
|
|
53463
53803
|
let slotsVersion = -1;
|
|
53464
53804
|
let slotsRoots = -1;
|
|
53805
|
+
let placedUnder;
|
|
53465
53806
|
/**
|
|
53466
53807
|
* Registers the key of an element under its root, so `entityOf` and `find` answer for it. A key
|
|
53467
53808
|
* `ui` gave a node of its own, a spacer's, is never registered.
|
|
@@ -54452,6 +54793,18 @@ function createReconciler(ctx, modules, fields) {
|
|
|
54452
54793
|
if (element?.live === true && root.slot === void 0) ecs.set(element.entity, Order, { value: order });
|
|
54453
54794
|
}
|
|
54454
54795
|
/**
|
|
54796
|
+
* Gives every root the order its entity holds once a new scene stands: a scene that keeps a
|
|
54797
|
+
* projection both scenes list places it again (F23), and its tree stands, so no root is dirty.
|
|
54798
|
+
* The world places it only under a new layer list, so the walk runs once per new list, in the
|
|
54799
|
+
* first layout phase that runs after it, also when the switch came while the world was paused.
|
|
54800
|
+
*/
|
|
54801
|
+
function reorderRoots() {
|
|
54802
|
+
const layers = ctx.deps.world.projection.layers();
|
|
54803
|
+
if (layers === placedUnder) return;
|
|
54804
|
+
placedUnder = layers;
|
|
54805
|
+
for (const root of state.roots.values()) if (!state.removing.has(root.entity)) refreshRootOrder(root);
|
|
54806
|
+
}
|
|
54807
|
+
/**
|
|
54455
54808
|
* Reconciles one dirty root: reads its current tree and projection name, then diffs it. The root
|
|
54456
54809
|
* stays dirty while the layout engine is not loaded, and a failure is logged for this root only.
|
|
54457
54810
|
*
|
|
@@ -54473,14 +54826,15 @@ function createReconciler(ctx, modules, fields) {
|
|
|
54473
54826
|
}
|
|
54474
54827
|
/**
|
|
54475
54828
|
* The first of the two systems of phase `layout`: the sweep, the roots a restore asked to build
|
|
54476
|
-
* again, the popups that may leave, the pull of the text being
|
|
54477
|
-
* windowed ranges, the bound texts that show a string of
|
|
54478
|
-
* roots and the hosted views.
|
|
54829
|
+
* again, the roots a scene placed again, the popups that may leave, the pull of the text being
|
|
54830
|
+
* typed, the scroll offsets and the windowed ranges, the bound texts that show a string of
|
|
54831
|
+
* another size, the viewport, the dirty roots and the hosted views.
|
|
54479
54832
|
*/
|
|
54480
54833
|
function reconcile() {
|
|
54481
54834
|
sweep();
|
|
54482
54835
|
state.reconciles += 1;
|
|
54483
54836
|
if (state.remount) remountRoots();
|
|
54837
|
+
reorderRoots();
|
|
54484
54838
|
settlePopups(ctx, state, unmountRoot);
|
|
54485
54839
|
fields.pull();
|
|
54486
54840
|
const containers = scrollContainers();
|