bricks/bricks/inherit.js
yumoqing 406ed6f17a feat(scene3d): Scene3D 接入脚本引擎 + 按需注入 three.js + 自写环绕相机
- engine.js/inherit.js 以 UMD 双分支引入(node 测试照跑 + 浏览器拼进 bricks.js)
- Scene3D 新增本地逻辑引擎(独立世界):描述符 properties/events 喂 SceneRuntime,
  点击→tap 注入、每帧 step 推进、transform/visible 回填 three.js
- three.min.js 按需注入(非 3D 页面不背 608KB)
- 自写环绕相机:单指旋转/双指缩放/双指平移(three.js r148+ 移除 UMD OrbitControls)
- 新增 API:set_entity_transform / set_paused / reset_runtime
- 已验证:门铃示例(定时+条件+点击+moveTo+属性变化)、相机旋转全链路
2026-08-27 15:41:51 +08:00

135 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* inherit.js — 《实体继承设计规范》§3「有效定义求解」的实现
*
* 原型式单继承:沿 parent 链自根向叶合并属性定义与事件集。
* 求解产出扁平化定义,脚本引擎不感知继承(规范 P1
* 所有非法结构抛 InheritError错误码见规范 §6.1),对应保存期校验。
*/
(function (root, factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory();
} else {
root.bricks = root.bricks || {};
root.bricks.ScriptInherit = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
const E_INH_CYCLE = 'E-INH-CYCLE';
const E_INH_DEPTH = 'E-INH-DEPTH';
const E_INH_NOSUCHPARENT = 'E-INH-NOSUCHPARENT';
const E_INH_TYPE = 'E-INH-TYPE';
const E_INH_DUPID = 'E-INH-DUPID';
const E_INH_NOSUCHEVENT = 'E-INH-NOSUCHEVENT';
const E_INH_CONFLICT = 'E-INH-CONFLICT';
class InheritError extends Error {
constructor(code, message) {
super(`[${code}] ${message}`);
this.code = code;
}
}
// 属性定义的默认值:规范用 {type, default},兼容旧格式 {type, value}
function defDefault(def) {
return def.default !== undefined ? def.default : def.value;
}
// §3.4 继承链校验:环 / 深度(≤32含自身) / 父存在性
function validateChain(e, all) {
const seen = new Set();
let cur = e, depth = 0;
for (;;) {
if (seen.has(cur.id)) {
throw new InheritError(E_INH_CYCLE, `继承环: ${[...seen, cur.id].join(' → ')}`);
}
seen.add(cur.id);
depth++;
if (depth > 32) throw new InheritError(E_INH_DEPTH, `继承链深度超过 32实体 ${e.id}`);
if (!cur.parent) break;
const p = all[cur.parent];
if (!p) throw new InheritError(E_INH_NOSUCHPARENT, `父实体不存在: ${cur.parent}(引用自 ${cur.id}`);
cur = p;
}
}
// §3.2 属性求解(自根向叶;覆盖只改默认值,类型必须一致)
function resolveProperties(e, all) {
if (!e.parent) {
const out = {};
for (const [k, v] of Object.entries(e.properties || {})) out[k] = JSON.parse(JSON.stringify(v));
return out;
}
const base = resolveProperties(all[e.parent], all);
for (const [name, def] of Object.entries(e.properties || {})) {
if (name in base) {
const bt = base[name].type || 'string';
const dt = def.type || 'string';
if (bt !== dt) {
throw new InheritError(E_INH_TYPE, `实体 ${e.id} 覆盖属性 "${name}" 类型不匹配:继承类型 ${bt},覆盖类型 ${dt}`);
}
base[name] = { type: bt, default: defDefault(def) }; // 类型不变,只覆盖默认值
} else {
base[name] = JSON.parse(JSON.stringify(def)); // 新增
}
}
return base;
}
// §3.3 事件求解(自根向叶;同 id 原位覆盖;禁用移除)
// 依赖 JS Map 语义set 已存在的 key 保持原插入位置 → 精确实现 §5.3「覆盖原位替换,不挪到末尾」
function resolveEvents(e, all) {
const own = e.events || [];
const ownIds = new Set();
for (const ev of own) {
if (ownIds.has(ev.id)) throw new InheritError(E_INH_DUPID, `实体 ${e.id} 自身事件 id 重复: ${ev.id}`);
ownIds.add(ev.id);
}
let map;
if (!e.parent) {
map = new Map(own.map(ev => [ev.id, ev]));
} else {
map = resolveEvents(all[e.parent], all);
for (const ev of own) map.set(ev.id, ev); // 覆盖(原位)或新增
for (const id of (e.disabled_events || [])) {
if (ownIds.has(id)) throw new InheritError(E_INH_CONFLICT, `实体 ${e.id}: 事件 ${id} 既覆盖又禁用`);
if (!map.has(id)) throw new InheritError(E_INH_NOSUCHEVENT, `实体 ${e.id} 禁用了不存在的继承事件: ${id}`);
map.delete(id);
}
}
return map;
}
// §3.6 外观求解:沿链取第一个非 null 的 asset_ref
function resolveAsset(e, all) {
let cur = e;
while (cur) {
if (cur.asset_ref) return cur.asset_ref;
cur = cur.parent ? all[cur.parent] : null;
}
return null;
}
// §3.1 求解入口:对全部实体先校验后求解,返回 id → 扁平化定义
function resolveAll(list) {
const all = {};
for (const e of list) all[e.id] = e;
const out = {};
for (const e of list) {
validateChain(e, all);
out[e.id] = {
...e,
asset_ref: resolveAsset(e, all),
properties: resolveProperties(e, all),
events: [...resolveEvents(e, all).values()],
};
}
return out;
}
return {
resolveAll, validateChain, resolveProperties, resolveEvents, resolveAsset,
InheritError,
E_INH_CYCLE, E_INH_DEPTH, E_INH_NOSUCHPARENT, E_INH_TYPE, E_INH_DUPID, E_INH_NOSUCHEVENT, E_INH_CONFLICT,
};
}));