@yagejs/core 0.2.0 → 0.3.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/dist/index.cjs CHANGED
@@ -99,6 +99,7 @@ __export(index_exports, {
99
99
  QueryCache: () => QueryCache,
100
100
  QueryCacheKey: () => QueryCacheKey,
101
101
  QueryResult: () => QueryResult,
102
+ RendererAdapterKey: () => RendererAdapterKey,
102
103
  SERIALIZABLE_KEY: () => SERIALIZABLE_KEY,
103
104
  Scene: () => Scene,
104
105
  SceneHookRegistry: () => SceneHookRegistry,
@@ -265,14 +266,52 @@ var Vec2 = class _Vec2 {
265
266
  static lerp(a, b, t) {
266
267
  return new _Vec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
267
268
  }
269
+ /** Move current toward target by at most maxDelta without overshooting. */
270
+ static moveTowards(current, target, maxDelta) {
271
+ const dx = target.x - current.x;
272
+ const dy = target.y - current.y;
273
+ const distanceSq = dx * dx + dy * dy;
274
+ if (distanceSq < EPSILON * EPSILON) {
275
+ return new _Vec2(target.x, target.y);
276
+ }
277
+ if (maxDelta <= 0) {
278
+ return new _Vec2(current.x, current.y);
279
+ }
280
+ const distance = Math.sqrt(distanceSq);
281
+ if (distance <= maxDelta) {
282
+ return new _Vec2(target.x, target.y);
283
+ }
284
+ const scale = maxDelta / distance;
285
+ return new _Vec2(current.x + dx * scale, current.y + dy * scale);
286
+ }
268
287
  };
269
288
 
270
289
  // src/MathUtils.ts
290
+ var TAU = Math.PI * 2;
291
+ var MIN_SMOOTH_TIME = 1e-4;
292
+ function normalizeAngle(radians) {
293
+ const wrapped = ((radians + Math.PI) % TAU + TAU) % TAU - Math.PI;
294
+ return wrapped === -Math.PI && radians > 0 ? Math.PI : wrapped;
295
+ }
296
+ __name(normalizeAngle, "normalizeAngle");
271
297
  var MathUtils = {
272
298
  /** Linear interpolation between a and b. */
273
299
  lerp(a, b, t) {
274
300
  return a + (b - a) * t;
275
301
  },
302
+ /** Return the clamped interpolation factor that produces v between a and b. */
303
+ inverseLerp(a, b, v) {
304
+ if (a === b) return 0;
305
+ return MathUtils.clamp((v - a) / (b - a), 0, 1);
306
+ },
307
+ /** Interpolate between angles in radians along the shortest path. */
308
+ lerpAngle(a, b, t) {
309
+ return normalizeAngle(a + MathUtils.shortestAngleBetween(a, b) * t);
310
+ },
311
+ /** Signed shortest angular delta from a to b, in radians. */
312
+ shortestAngleBetween(a, b) {
313
+ return normalizeAngle(b - a);
314
+ },
276
315
  /** Clamp a value between min and max. */
277
316
  clamp(value, min, max) {
278
317
  return Math.max(min, Math.min(max, value));
@@ -282,6 +321,12 @@ var MathUtils = {
282
321
  const t = (value - inMin) / (inMax - inMin);
283
322
  return outMin + (outMax - outMin) * t;
284
323
  },
324
+ /** Bounce t between 0 and length. */
325
+ pingPong(t, length) {
326
+ if (length <= 0) return 0;
327
+ const wrapped = MathUtils.wrap(t, 0, length * 2);
328
+ return length - Math.abs(wrapped - length);
329
+ },
285
330
  /** Random float in [min, max). */
286
331
  randomRange(min, max) {
287
332
  return min + Math.random() * (max - min);
@@ -305,6 +350,34 @@ var MathUtils = {
305
350
  }
306
351
  return Math.max(current - step, target);
307
352
  },
353
+ /**
354
+ * Smoothly damp current toward target without overshooting.
355
+ * Pass the returned velocity back into the next call.
356
+ */
357
+ smoothDamp(current, target, velocity, smoothTime, deltaTime, maxSpeed = Infinity) {
358
+ if (deltaTime <= 0) {
359
+ return { value: current, velocity };
360
+ }
361
+ const safeSmoothTime = Math.max(MIN_SMOOTH_TIME, smoothTime);
362
+ const omega = 2 / safeSmoothTime;
363
+ const x = omega * deltaTime;
364
+ const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
365
+ const originalTarget = target;
366
+ const maxChange = maxSpeed * safeSmoothTime;
367
+ const change = MathUtils.clamp(current - target, -maxChange, maxChange);
368
+ const adjustedTarget = current - change;
369
+ const temp = (velocity + omega * change) * deltaTime;
370
+ const nextVelocity = (velocity - omega * temp) * exp;
371
+ let value = adjustedTarget + (change + temp) * exp;
372
+ let resultVelocity = nextVelocity;
373
+ const targetIsAboveCurrent = originalTarget - current > 0;
374
+ const valuePassedTarget = targetIsAboveCurrent ? value > originalTarget : value < originalTarget;
375
+ if (valuePassedTarget) {
376
+ value = originalTarget;
377
+ resultVelocity = 0;
378
+ }
379
+ return { value, velocity: resultVelocity };
380
+ },
308
381
  /** Wrap value into the range [min, max). */
309
382
  wrap(value, min, max) {
310
383
  const range = max - min;
@@ -2069,6 +2142,29 @@ var SceneManager = class {
2069
2142
  _pendingChain = Promise.resolve();
2070
2143
  _mutationDepth = 0;
2071
2144
  _destroyed = false;
2145
+ _autoPauseOnBlur = false;
2146
+ _isBlurred = false;
2147
+ _visibilityPausedScenes = /* @__PURE__ */ new Set();
2148
+ _visibilityListenerCleanup;
2149
+ /**
2150
+ * Pause all non-paused scenes when `document.hidden` becomes `true`; restore
2151
+ * them on focus. Default: `false`. Only scenes paused by this mechanism are
2152
+ * restored — user-paused scenes (manual `scene.paused = true` or `pauseBelow`
2153
+ * cascade) are never touched.
2154
+ */
2155
+ get autoPauseOnBlur() {
2156
+ return this._autoPauseOnBlur;
2157
+ }
2158
+ set autoPauseOnBlur(value) {
2159
+ if (this._autoPauseOnBlur === value) return;
2160
+ this._autoPauseOnBlur = value;
2161
+ if (!this._isBlurred) return;
2162
+ if (value) {
2163
+ this._applyBlurPause();
2164
+ } else if (this._visibilityPausedScenes.size > 0) {
2165
+ this._restoreBlurPause();
2166
+ }
2167
+ }
2072
2168
  /**
2073
2169
  * Set the engine context.
2074
2170
  * @internal
@@ -2079,6 +2175,40 @@ var SceneManager = class {
2079
2175
  this.assetManager = context.tryResolve(AssetManagerKey);
2080
2176
  this.hookRegistry = context.tryResolve(SceneHookRegistryKey);
2081
2177
  this.logger = context.tryResolve(LoggerKey);
2178
+ if (this._visibilityListenerCleanup || typeof document === "undefined") {
2179
+ return;
2180
+ }
2181
+ const onVisibilityChange = /* @__PURE__ */ __name(() => {
2182
+ this._handleVisibilityChange(document.hidden);
2183
+ }, "onVisibilityChange");
2184
+ document.addEventListener("visibilitychange", onVisibilityChange);
2185
+ this._visibilityListenerCleanup = () => document.removeEventListener("visibilitychange", onVisibilityChange);
2186
+ }
2187
+ /**
2188
+ * React to a visibility change. Parameterised on `hidden` so unit tests can
2189
+ * drive it without a real `document`.
2190
+ * @internal
2191
+ */
2192
+ _handleVisibilityChange(hidden) {
2193
+ if (hidden && !this._isBlurred) {
2194
+ this._isBlurred = true;
2195
+ if (this._autoPauseOnBlur) this._applyBlurPause();
2196
+ } else if (!hidden && this._isBlurred) {
2197
+ this._isBlurred = false;
2198
+ if (this._visibilityPausedScenes.size > 0) this._restoreBlurPause();
2199
+ }
2200
+ }
2201
+ _applyBlurPause() {
2202
+ for (const scene of this.activeScenes) {
2203
+ scene.paused = true;
2204
+ this._visibilityPausedScenes.add(scene);
2205
+ }
2206
+ }
2207
+ _restoreBlurPause() {
2208
+ for (const scene of this._visibilityPausedScenes) {
2209
+ scene.paused = false;
2210
+ }
2211
+ this._visibilityPausedScenes.clear();
2082
2212
  }
2083
2213
  /** The topmost (active) scene. */
2084
2214
  get active() {
@@ -2205,6 +2335,9 @@ var SceneManager = class {
2205
2335
  this._cleanupRun(this._currentRun);
2206
2336
  }
2207
2337
  this._pendingChain = Promise.resolve();
2338
+ this._visibilityListenerCleanup?.();
2339
+ this._visibilityListenerCleanup = void 0;
2340
+ this._visibilityPausedScenes.clear();
2208
2341
  this._withMutationSync(() => {
2209
2342
  while (this.stack.length > 0) {
2210
2343
  const scene = this.stack.pop();
@@ -2324,6 +2457,7 @@ var SceneManager = class {
2324
2457
  scene._destroyAllEntities();
2325
2458
  this.hookRegistry?.runAfterExit(scene);
2326
2459
  scene._clearScopedServices();
2460
+ this._visibilityPausedScenes.delete(scene);
2327
2461
  }
2328
2462
  async _runTransition(kind, transition, fromScene, toScene) {
2329
2463
  if (this._destroyed) return;
@@ -3445,6 +3579,11 @@ var Engine = class {
3445
3579
  }
3446
3580
  };
3447
3581
 
3582
+ // src/RendererAdapter.ts
3583
+ var RendererAdapterKey = new ServiceKey(
3584
+ "rendererAdapter"
3585
+ );
3586
+
3448
3587
  // src/test-utils.ts
3449
3588
  var _TestScene = class extends Scene {
3450
3589
  static {
@@ -3529,6 +3668,7 @@ var VERSION = "0.0.0";
3529
3668
  QueryCache,
3530
3669
  QueryCacheKey,
3531
3670
  QueryResult,
3671
+ RendererAdapterKey,
3532
3672
  SERIALIZABLE_KEY,
3533
3673
  Scene,
3534
3674
  SceneHookRegistry,