承 A0 引擎掌帧地基,按创始人「引擎↔插件边界模型」(引擎有→薄包装·引擎无→自研补层·
同一职不留两条并行路)接线 A1-A4。host.js 注入真 engineFactory,getEngine() 在 host-dev 真活。
A1 受控面+引擎工厂地基+easing 门面:
- host.js makeEngineCaps 一处构造 EngineCapabilities{particles,audio.synth,math};
- 新 host-dev/engine-math.js 包装引擎 lerp/smoothStep + Ease 曲线(11 条等价曲线门面);
- api.d.ts additive 扩 EngineEasing + EngineMath.easing?(不破已冻 lerp/smoothStep)+ 块级降级措辞校准为 null no-op;
- 受控面 random/time 保确定性(引擎粒子用全局 rand 不可种子对齐→random 留插件自管,真缺件)。
A2 particles 薄包装+删 sim:
- EngineEmitterSpec additive 扩 10 可选字段(count/coneAngle/speed/gravityScale/sizeStart-End/colorStart-End/additive);
- spawnEmitter 改走 getEngine().particles 映射引擎 ParticleEmitter,删自研逐粒子积分 sim;null-engine→no-op 句柄;
- juice 全段(hitStop/屏震/闪白)补层保留;测试转向映射纯单测+真渲出现(逐像素确定性随 sim 退役,§1.4 记账)。
A3 audio 包装+删 vendored+补 ×0.3:
- 删 vendor/zzfx.js+zzfxm.js;audio.synth 包装引擎 zzfxG/zzfxM;
- 播放层补 ×0.3 主音量(引擎样本不烤 0.3,gain 层施)+ SAMPLE_RATE=44100 本地常量;
- impl.js 走 getEngine().audio.synth,null-engine→合成 no-op(不回退 vendored)。
A4 gamefeel easing 改判=门面包装(对抗门源码证伪纠偏):
- easing 改走 getEngine().math.easing 门面(quadIn→Ease.POWER(2) 等),内置降名 builtinEasing 作 null-engine fallback(纯数学族特例);
- backInOut/elasticInOut 引擎 IN_OUT 拼接差 6.6%/17%(另一条曲线)→门面刻意不暴,这二者恒走内置=真缺件补层;
- collision/physics-lite/palette-post 加边界裁定注释(真缺件补层,源码精校:引擎仅 isOverlapping/isIntersecting 返 boolean,无 Manifold/任意几何/RayHit)。
对抗门两逮(均前置于实现·零返工):
- A4 easing 误判 fatal:前轮按小写 quadIn grep 假阴性误判「引擎无 easing」;引擎实有 Ease(esm.js:15118)→创始人拍本波包装;
- collision 注释 under-claim:已精校为源码准措辞。新红线 §1.5:引擎能力普查禁按预期名 grep,须读 export 块/.d.ts。
本机门全绿(亲验):
- node --test 168/168 绿;all-plugins+host-bundle 双 esbuild rc=0(35594B/209516B);
- 真接线门①引擎真入产物 littlejsengine 163641B;门③负扫=0 插件直 import+vendor 已删+0 自研 sim+0 假注释;Q4 引擎 import 仅 entry.js+host.js。
A6(mini-desktop real 像素门 + 真接线门② runtime probe call-ID)待主会话驱动。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88 lines
6.0 KiB
JavaScript
88 lines
6.0 KiB
JavaScript
/**
|
||
* host-dev/engine-math.js — 引擎数学库门面构造(纯函数,零引擎 import)
|
||
* owner:T1b-β A1 | 消费方:host-dev/host.js(real 通道工厂 makeEngineCaps 调它)+ A1 node 单测
|
||
*
|
||
* ════════════════════════════════════════════════════════════════════════════
|
||
* 【为什么单列本模块(不埋进 host.js)】
|
||
* A1 须在 real 通道把 EngineCapabilities.math(lerp/smoothStep + easing 门面)真活包装引擎。
|
||
* 把门面构造抽为「吃 ljs 参数」的纯函数,且放在**零引擎 import 的独立模块**,是 testability 刚需:
|
||
* · F7(node 实跑亲验):`import * as LJS from 'littlejsengine'` 在 node 顶层抛 `window is not defined`
|
||
* → host.js 因顶层 import 引擎,node 根本 import 不了 host.js(实测同样抛 window)。
|
||
* · 故门面纯函数必须住在**不 import 引擎**的模块里,node 测才能 import 它、注入「内联引擎 Ease 曲线的
|
||
* mock ljs」(逐字节复制 esm.js:15118)验门面映射正确性——全程不碰真引擎。
|
||
* blast radius:仅 host.js 内部组织(host.js 改为从本模块 import clampUnit/buildEngineMath);
|
||
* 不改契约形状(api.d.ts)、不改 plugin.js、不改任何插件 impl。
|
||
*
|
||
* 【引擎↔插件 边界(创始人 2026-06-13 拍)】
|
||
* 纯通用数学族特例(lerp/smoothStep/easing 曲线):引擎与门面公式逐字节/ULP 等价、无状态、无语义冲突
|
||
* → math façade=getEngine().math。本模块只构造门面(包装传入的 ljs),不持引擎引用、不做降级 sim。
|
||
* ════════════════════════════════════════════════════════════════════════════
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
/**
|
||
* 把 t 钳到 [0,1](与 gamefeel clamp01 同口径)。
|
||
* 为何必须钳:gamefeel 内置每条 easing 都以 clamp01(t) 打头(gamefeel/impl.js:59 等),且其测断越界钳制
|
||
* (gamefeel.test.mjs 断 fn(-0.5)===fn(0) / fn(1.5)===fn(1));门面是「替换 gamefeel easing 的等价件」,
|
||
* 须与被替换者越界行为一致。而引擎 Ease.POWER/LINEAR 等**不自钳**(POWER:(n)=>(x)=>x**n,越界 x 直接幂运算)
|
||
* → 门面不钳则对越界 t 行为 ≠ 内置 fallback → A4 改门面后行为回归。
|
||
* @param {number} t
|
||
* @returns {number} 钳制到 [0,1] 的值
|
||
*/
|
||
export function clampUnit(t) {
|
||
return t < 0 ? 0 : t > 1 ? 1 : t;
|
||
}
|
||
|
||
/**
|
||
* 【A1】构造引擎数学库门面(EngineMath:lerp/smoothStep + easing 11 条等价曲线)。
|
||
* 纯函数:仅依赖传入的 ljs(引擎名字空间,含 lerp/smoothStep/Ease)。host real 通道传真 LJS;node 测传 mock。
|
||
*
|
||
* ── math.lerp / math.smoothStep ──逐字节包装引擎(口径已对齐,引擎 lerp 自带 percent 钳制 esm.js:1444)。
|
||
* ── math.easing ──门面包装引擎 LJS.Ease 曲线族(esm.js:15118),**仅暴已 node 逐点亲验等价的 11 条曲线**:
|
||
* quadIn=POWER(2)(逐字节恒等)/ quadOut=OUT(POWER(2))(逐字节恒等)/ quadInOut=IN_OUT(POWER(2))(ULP)/
|
||
* cubicIn=POWER(3) / cubicOut=OUT(POWER(3)) / cubicInOut=IN_OUT(POWER(3))(ULP)/
|
||
* backIn=BACK / backOut=OUT(BACK) / elasticIn=ELASTIC / elasticOut=OUT(ELASTIC)(均 ULP <1e-15)。
|
||
* 每条调引擎曲线前先 clampUnit(t)(引擎 Ease 不自钳,须钳齐保端点恒等+越界一致)。
|
||
* **刻意不暴 backInOut/elasticInOut**:引擎 IN_OUT(BACK/ELASTIC) 经 PIECEWISE 拼接是**另一条曲线**
|
||
* (back 差 6.6%、elastic 差 17%,非 ULP)→ gamefeel 对这二者恒走内置(引擎无此精确曲线=补层,非 fallback)。
|
||
*
|
||
* ── 性能:POWER(2)/OUT(POWER(2))/IN_OUT(...) 每次调用会新建闭包;easing 门面在 juice 热路径每帧调,
|
||
* 故在此**预构造一次**这些曲线函数(_p2/_p3/_qOut/... 缓存),各 easing 方法只做 clampUnit + 调缓存曲线。
|
||
*
|
||
* @param {any} ljs 引擎名字空间(须含 lerp/smoothStep/Ease;real=LJS,测=内联引擎曲线的 mock)。
|
||
* @returns {import('../src/core/api.d.ts').EngineMath} 引擎数学门面(lerp/smoothStep/easing)。
|
||
*/
|
||
export function buildEngineMath(ljs) {
|
||
const Ease = ljs.Ease;
|
||
// 预构造曲线一次(避免每次 easing 调用重建 POWER/OUT/IN_OUT 闭包;功能等价,纯性能优化)。
|
||
const _p2 = Ease.POWER(2); // 二次基曲线 x**2
|
||
const _p3 = Ease.POWER(3); // 三次基曲线 x**3
|
||
const _qOut = Ease.OUT(_p2); // 二次缓出 1-(1-x)**2
|
||
const _qInOut = Ease.IN_OUT(_p2); // 二次缓入缓出(PIECEWISE 拼接)
|
||
const _cOut = Ease.OUT(_p3); // 三次缓出
|
||
const _cInOut = Ease.IN_OUT(_p3); // 三次缓入缓出
|
||
const _backOut = Ease.OUT(Ease.BACK); // 回弹缓出
|
||
const _elasticOut = Ease.OUT(Ease.ELASTIC); // 弹性缓出
|
||
return {
|
||
// ── lerp/smoothStep:逐字节包装引擎(F4/F5)──
|
||
lerp: (a, b, p) => ljs.lerp(a, b, p),
|
||
smoothStep: (p) => ljs.smoothStep(p),
|
||
// ── easing 门面:11 条等价曲线,每条钳 t∈[0,1] 后调预构造的引擎曲线 ──
|
||
easing: {
|
||
linear: (t) => Ease.LINEAR(clampUnit(t)),
|
||
quadIn: (t) => _p2(clampUnit(t)),
|
||
quadOut: (t) => _qOut(clampUnit(t)),
|
||
quadInOut: (t) => _qInOut(clampUnit(t)),
|
||
cubicIn: (t) => _p3(clampUnit(t)),
|
||
cubicOut: (t) => _cOut(clampUnit(t)),
|
||
cubicInOut: (t) => _cInOut(clampUnit(t)),
|
||
backIn: (t) => Ease.BACK(clampUnit(t)),
|
||
backOut: (t) => _backOut(clampUnit(t)),
|
||
elasticIn: (t) => Ease.ELASTIC(clampUnit(t)),
|
||
elasticOut: (t) => _elasticOut(clampUnit(t)),
|
||
// backInOut/elasticInOut 刻意不暴(引擎 IN_OUT 拼接≠gamefeel 闭式,真分歧 6.6%/17%);gamefeel 对其恒走内置。
|
||
},
|
||
};
|
||
}
|