import { CancelablePromise } from '@uino/base-thing';
import { Utils } from '../common/Utils';
import { BaseComponent } from './BaseComponent';
import { ActionQueueType } from '../const';
const __ = {
private: Symbol('private'),
}
// #region Private Functions
// Wait for async complete.
function _waitAsyncCallback(resolve, asyncCallback) {
if (_DEBUG) {
const _checkAsyncCallbackInterval = 10 * 1000;
let handler = setInterval(() => {
Utils.error(`Did you forget to invoke resolve() in\n`, asyncCallback);
clearInterval(handler);
}, _checkAsyncCallbackInterval);
let callback = () => {
clearInterval(handler);
resolve();
}
return callback;
}
else {
return resolve;
}
}
// #endregion
/**
* 层级事件信息。
* @typedef {object} LevelEventInfo
* @property {Array<THING.BaseObject>} path 层级路径。
* @property {THING.BaseObject} origin 原始层级对象。
* @property {THING.BaseObject} prev 前一个层级对象。
* @property {THING.BaseObject} current 当前层级对象。
* @property {THING.BaseObject} next 下一个层级对象(仅用于离开操作)。
* @property {object} options 层级选项。
*/
/**
* 异步模式下离开自身层级时的回调。
* @callback OnLeaveLevelAsyncCallback
* @param {LevelEventInfo} ev 事件信息。
* @param {Function} resolve Promise resolve 回调函数。
* @param {Function} reject Promise reject 回调函数。
*/
/**
* 离开自身层级时的回调。
* @callback OnLeaveLevelCallback
* @param {LevelEventInfo} ev 事件信息。
*/
/**
* 异步模式下进入自身层级时的回调。
* @callback OnEnterLevelAsyncCallback
* @param {LevelEventInfo} ev 事件信息。
* @param {Function} resolve Promise resolve 回调函数。
* @param {Function} reject Promise reject 回调函数。
*/
/**
* 进入自身层级时的回调。
* @callback OnEnterLevelCallback
* @param {LevelEventInfo} ev 事件信息。
*/
/**
* 进入自身层级完成时的回调。
* @callback OnFinishedEnterLevelCallback
* @param {LevelEventInfo} ev 事件信息。
*/
/**
* @class LevelComponent
* 对象层级控制器
* @memberof THING
* @extends THING.BaseComponent
* @public
*/
class LevelComponent extends BaseComponent {
static mustCopyWithInstance = true;
/**
* 提供物体层级切换能力的功能组件
*/
constructor() {
// #region Overrides
/**
* 离开层级时的回调函数。
* @member {OnLeaveLevelCallback} onLeave
* @memberof THING.LevelComponent
* @instance
* @private
*/
/**
* 异步模式下离开层级时的回调函数。
* @member {OnLeaveLevelAsyncCallback} onLeaveAsync
* @memberof THING.LevelComponent
* @instance
* @private
*/
/**
* 进入层级时的回调函数。
* @member {OnEnterLevelCallback} onEnter
* @memberof THING.LevelComponent
* @instance
* @private
*/
/**
* 异步模式下进入层级时的回调函数。
* @member {OnEnterLevelAsyncCallback} onEnterAsync
* @memberof THING.LevelComponent
* @instance
* @private
*/
/**
* 完成进入层级时的回调函数。
* @member {OnFinishedEnterLevelCallback} onFinish
* @memberof THING.LevelComponent
* @instance
* @private
*/
// #endregion
super();
this[__.private] = {};
let _private = this[__.private];
_private.isLeave = false;
// Settings config in the level
_private.config = {
ignoreVisible: false,
ignoreEvent: false,
ignoreStyle: false
}
_private.generateLevelControlParams = (eventParams) => {
let target = null;
if (eventParams.path) {
target = eventParams.path[eventParams.path.length - 1];
}
return {
origin: eventParams.origin,
prev: eventParams.prev,
current: eventParams.current,
next: eventParams.next,
target: target,
path: eventParams.path,
options: eventParams.options
};
}
_private.viewpoint = null;
_private.viewpointName = '';
_private.viewpointMap = new Map();
}
// #region Private
#importViewpoint(params, _private) {
if (params.viewpointName) {
_private.viewpointName = params.viewpointName;
_private.viewpointMap = new Map(Object.entries(params.viewpointMap));
}
else if (params.viewpoint) {
_private.viewpoint = Utils.cloneObject(params.viewpoint);
}
}
#importConfig(params, _private) {
if (params.config) {
_private.config = {
ignoreVisible: params.config.ignoreVisible || false,
ignoreEvent: params.config.ignoreEvent || false,
ignoreStyle: params.config.ignoreStyle || false
};
}
}
#exportViewpoint(result, _private) {
if (_private.viewpointName) {
result.viewpointName = Utils.cloneObject(_private.viewpointName);
result.viewpointMap = Object.fromEntries(_private.viewpointMap);
}
else if (_private.viewpoint) {
result.viewpoint = Utils.cloneObject(_private.viewpoint);
}
}
#exportConfig(result, _private) {
let config = null;
if (_private.config.ignoreEvent === true) {
config = config ? config : {};
config.ignoreEvent = true;
}
if (_private.config.ignoreStyle === true) {
config = config ? config : {};
config.ignoreStyle = true;
}
if (_private.config.ignoreVisible === true) {
config = config ? config : {};
config.ignoreVisible = true;
}
// Export config
if (config) {
result.config = config;
}
}
/**
* 离开自身层级。
* @param {object} param 参数。
* @private
*/
leave(param) {
return new CancelablePromise((resolve, reject, onCancel) => {
onCancel(() => {
});
if (this.onLeaveAsync) {
this.onLeaveAsync.call(this, param, _waitAsyncCallback(resolve, this.onLeaveAsync), reject);
}
else if (this.onLeave) {
this.onLeave.call(this, param);
resolve();
}
else {
resolve();
}
});
}
/**
* 进入自身层级。
* @param {object} param 参数。
* @private
*/
enter(param) {
let _private = this[__.private];
return new CancelablePromise((resolve, reject, onCancel) => {
onCancel((cancelParam) => {
let levelControls = cancelParam.levelControls;
let controlParams = _private.generateLevelControlParams(cancelParam);
levelControls.forEach(control => {
if (control.isEnabled) {
control.onLeave(controlParams);
}
});
_private.isLeave = true;
});
if (this.onEnterAsync) {
this.onEnterAsync.call(this, param, _waitAsyncCallback(resolve, this.onEnterAsync), reject);
}
else if (this.onEnter) {
this.onEnter.call(this, param);
resolve();
}
else {
resolve();
}
});
}
/**
* 完成进入自身层级。
* @param {object} param 参数。
* @private
*/
finish(param) {
if (this.onFinish) {
this.onFinish(param);
}
}
/**
* 根据名称获取层级动作。
* @param {string} name 动作名称。
* @returns {ActionProcessor}
* @private
*/
getActionByName(name) {
let actionQueue = this.actionQueue;
if (!actionQueue) {
return null;
}
return actionQueue.getByName(name);
}
/**
* 根据类型获取层级动作。
* @param {*} type 动作类型。
* @returns {ActionProcessor}
* @private
*/
getActionByType(type) {
let actionQueue = this.actionQueue;
if (!actionQueue) {
return null;
}
return actionQueue.getByType(type);
}
/**
* 启用/禁用动作。
* @param {string} name 动作名称或类型。
* @param {boolean} value 动作启用状态。
* @private
*/
enableAction(name, value) {
let actionQueue = this.actionQueue;
if (!actionQueue) {
return;
}
actionQueue.enable(name, value);
}
/**
* 检查是否启用了动作。
* @param {string} name 动作名称或类型。
* @returns {boolean}
* @private
*/
isActionEnabled(name) {
let actionQueue = this.actionQueue;
if (!actionQueue) {
return false;
}
return actionQueue.isEnabled(name);
}
// #endregion
// #region Property
get isCurrentLevel() {
let app = this.object.app;
return this.object == app.levelManager.current;
}
/**
* 获取层级动作队列。
* @type {THING.ActionQueue}
* @private
*/
get actionQueue() {
return this.object.actionGroup.get(ActionQueueType.EnterLevel);
}
/**
* 获取层级动作。
* @type {Array<ActionProcessor>}
* @private
*/
get actions() {
let actionQueue = this.actionQueue;
if (!actionQueue) {
return [];
}
return actionQueue.actions;
}
/**
* 层级配置参数信息
* @typedef {object} LevelConfig
* @property {boolean} ignoreVisible - 是否忽略层级设置中对象的可见性设置。
* @property {boolean} ignoreEvent - 是否忽略层级设置中对象的鼠标事件。
* @property {boolean} ignoreStyle - 是否忽略层级设置中对象的样式。
* @public
*/
/**
* 获取/设置层级配置。需要在层级改变之前,进行层级配置。
* @type {LevelConfig}
*
* @example
* // 获取当前层级配置
* let levelComponent = THING.App.current.root.level;
* levelComponent.config = { ignoreVisible: true, ignoreStyle: false };
* // @expect { levelComponent.config.ignoreVisible === true }
* // @expect { levelComponent.config.ignoreStyle === false }
* @public
*/
get config() {
let _private = this[__.private];
return _private.config;
}
set config(value) {
let _private = this[__.private];
_private.config = value;
}
/**
* 视点信息
* @typedef {object} Viewpoint
* @property {Array<number>} position 摄像机位置。
* @property {Array<number>} target 目标位置。
* @public
*/
/**
* 获取/设置摄像机的局部位置和目标位置。此信息是从场景文件里读取出来的
* @type {Viewpoint}
*
* @example
* let levelComponent = THING.App.current.root.level;
* // Get the local viewpoint
* let localViewpoint = levelComponent.localViewpoint;
* let ret1 = typeof localViewpoint === 'object';
* let ret2 = localViewpoint === null;
* // @expect { ret1 == true || ret2 == true }
*
* // Set the local viewpoint
* levelComponent.localViewpoint = {
* position: [0, 0, 0],
* target: [1, 1, 1]
* };
* // @expect {levelComponent.localViewpoint.position[0] === 0 && levelComponent.localViewpoint.target[2] === 1}
* @public
*/
get localViewpoint() {
const _private = this[__.private];
if (_private.viewpointName) {
return _private.viewpointMap.get(_private.viewpointName);
}
return _private.viewpoint;
}
set localViewpoint(value) {
const _private = this[__.private];
if (_private.viewpointName) {
value ? _private.viewpointMap.set(_private.viewpointName, Utils.cloneObject(value)) : _private.viewpointMap.set(_private.viewpointName, null);
}
else {
_private.viewpoint = value ? Utils.cloneObject(value) : null;
}
}
/**
* 获取/设置摄像机的位置和目标位置。此信息是从场景文件里读取出来的
* @type {Viewpoint}
*
* @example
* let levelComponent = THING.App.current.root.level;
* // Get the world viewpoint
* let viewpoint = levelComponent.viewpoint;
* let ret1 = typeof viewpoint === 'object';
* let ret2 = viewpoint === null;
* // @expect { ret1 == true || ret2 == true }
*
* // Set the world viewpoint
* levelComponent.viewpoint = {
* position: [0, 0, 0],
* target: [1, 1, 1]
* };
* // @expect {levelComponent.viewpoint.position[0] === 0 && levelComponent.viewpoint.target[2] === 1}
* @public
*/
get viewpoint() {
const _private = this[__.private];
const viewpoint = _private.viewpoint || (_private.viewpointName && _private.viewpointMap.get(_private.viewpointName));
if (!viewpoint) {
return null;
}
return {
target: this.object.selfToWorld(viewpoint.target),
position: this.object.selfToWorld(viewpoint.position)
};
}
set viewpoint(value) {
const _private = this[__.private];
if (!value) {
_private.viewpointName ? _private.viewpointMap.set(_private.viewpointName, null) : _private.viewpoint = null;
return;
}
const viewpoint = {
target: this.object.worldToSelf(value.target),
position: this.object.worldToSelf(value.position)
};
_private.viewpointName ? _private.viewpointMap.set(_private.viewpointName, viewpoint) : _private.viewpoint = viewpoint;
}
get viewpointName() {
const _private = this[__.private];
return _private.viewpointName;
}
set viewpointName(value) {
const _private = this[__.private];
_private.viewpointName = value;
}
get viewpointMap() {
const _private = this[__.private];
return _private.viewpointMap;
}
set viewpointMap(value) {
const _private = this[__.private];
if (value instanceof Map) {
_private.viewpointMap = new Map(value);
}
else {
_private.viewpointMap = new Map(Object.entries(value));
}
}
// #endregion
/**
* @type {Function}
* @private
*/
async onLeaveAsync(param, resolve, reject) {
let _private = this[__.private];
if (!_private.isLeave) {
let controlParams = _private.generateLevelControlParams(param);
let levelControls = param.levelControls;
for (let i = 0; i < levelControls.length; i++) {
const control = levelControls[i];
await control.onLeave(controlParams);
}
_private.isLeave = true;
}
resolve();
}
/**
* @type {Function}
* @private
*/
async onEnterAsync(param, resolve, reject) {
let _private = this[__.private];
_private.isLeave = false;
let controlParams = _private.generateLevelControlParams(param);
let levelControls = param.levelControls;
for (let i = 0; i < levelControls.length; i++) {
const control = levelControls[i];
if (control) {
if (control.isEnabled) {
await control.onEnter(controlParams);
}
}
}
resolve();
}
/**
* 从外部资源导入数据。
* @param {object} external 配置信息 参加下面示例
*
* @example
* let levelComponent = THING.App.current.root.level;
* // Import data from an external source
* // Example 1: Importing data
* levelComponent.onImport({ viewpoint: { position: [1, 2, 3], target: [4, 5, 6] }, config: { ignoreStyle:true, ignoreVisible:false } });
* // @expect { levelComponent.localViewpoint.position[0] === 1 }
* // @expect { levelComponent.localViewpoint.target[2] === 6 }
* // @expect { levelComponent.config.ignoreStyle === true }
* // @expect { levelComponent.config.ignoreVisible === false }
* @public
*/
onImport(external) {
if (!external) {
return;
}
const _private = this[__.private];
// Import viewpoint
this.#importViewpoint(external, _private);
// Import config
this.#importConfig(external, _private);
}
/**
* 导出配置信息
* @returns {object|null} 配置信息
*
* @example
* let levelComponent = THING.App.current.root.level;
* levelComponent.onImport({ viewpoint: { position: [1, 2, 3], target: [4, 5, 6] }, config: { ignoreStyle:true, ignoreVisible:false } });
* // Export data to an external source
* let exportedData = levelComponent.onExport();
* // @expect { exportedData.viewpoint.position[1] === 2 }
* // @expect { exportedData.viewpoint.target[0] === 4 }
* // @expect { exportedData.config.ignoreStyle === true }
* let ret = exportedData.config.ignoreVisible == undefined;
* // @expect { ret === true }
* @public
*/
onExport() {
let _private = this[__.private];
let result = {};
// Export viewpoint
this.#exportViewpoint(result, _private);
// False is the default value and is not export
this.#exportConfig(result, _private);
return Object.keys(result).length ? result : null;
}
}
export { LevelComponent }