import { StringEncoder } from '@uino/base-thing';
import { Utils } from '../common/Utils'
import { MathUtils } from '../math/MathUtils';
import { Object3D } from './Object3D';
import { RenderType, PivotMode, SideType } from '../const';
const __ = {
private: Symbol('private'),
}
const _spriteTypeName = StringEncoder.toText("<@secret Sprite>");
const _defaultPlaneOptions = {
renderLayer: 1,
envMap: false,
lights: false,
};
// #endregion
/**
* @class Marker
* The marker object.
* @memberof THING
* @extends THING.Object3D
* @public
*/
class Marker extends Object3D {
/**
* The marker object that show image in scene.
* @param {Object} param The initial parameters.
*/
constructor(param = {}) {
super(Utils.cloneObject(param));
this._autoFitBodyScale = Utils.parseValue(param['autoFitBodyScale'], false);
this._scaleFactor = Utils.parseValue(param['scaleFactor'], 0.01);
this._renderType = Utils.parseValue(param['renderType'], RenderType.Sprite);
this._pivotMode = Utils.parseValue(param['pivotMode'] || param['pivotModeType'], PivotMode.Auto);
this._spriteRotation = Utils.parseValue(param['spriteRotation'], 0);
this._onBeforeFitBodyScale = null;
this._onAfterFitBodyScale = null;
let pivot = param['pivot'];
if (pivot) {
this._updatePivot(pivot);
}
}
// #region Private Functions
_useSpriteCenterAsPivot() {
if (this.renderType != RenderType.Sprite) {
return false;
}
if (this.pivotMode != PivotMode.Auto) {
return false;
}
return true;
}
_fitBodyScale() {
let image = this.style.image;
if (!image) {
return;
}
image.waitForComplete().then(() => {
// Get the image size
let width = image.width;
let height = image.height;
if (!width || !height) {
return;
}
// Notify we are going to set body scale
if (this._onBeforeFitBodyScale) {
this._onBeforeFitBodyScale({ object: this, image });
}
// Adjust body scale with scale factor
const scaleFactor = this._scaleFactor;
width *= scaleFactor;
height *= scaleFactor;
// Ignore parent scale
let parent = this.parent;
if (parent) {
let parentScale = parent.scale;
width /= parentScale[0];
height /= parentScale[1];
}
// Update body scale to fit ratio
this.body.localScale = [width, height, 1];
// Notify we finished to set body scale
if (this._onAfterFitBodyScale) {
this._onAfterFitBodyScale({ object: this, image });
}
// Refresh keep size
let keepSizeInfo = this.transform.keepSizeInfo;
if (keepSizeInfo) {
keepSizeInfo.refresh();
}
});
}
_updatePivot(pivot) {
this.clearPivot();
// Get body node
let bodyNode = this.bodyNode;
// It's sprite render type
if (this._useSpriteCenterAsPivot()) {
if (pivot) {
bodyNode.setAttribute('Center', [pivot[0], pivot[1]]);
}
bodyNode.setAttribute('Rotation', MathUtils.degToRad(this._spriteRotation));
bodyNode.setWorldPosition(this.position);
}
else {
if (pivot) {
this.pivot = pivot;
}
}
if (this._autoFitBodyScale) {
this._fitBodyScale();
}
}
_refreshRenderableNode(options) {
let renderType = this.renderType;
// Create render node
if (renderType == RenderType.Sprite) {
// If user provide renderable node then skip to create it
let renderableNode = options['renderableNode'];
if (!renderableNode) {
if (this.bodyNode.getType() != _spriteTypeName) {
let node = Utils.createObject(_spriteTypeName, this.external);
this.body.setNode(node);
}
}
var spriteRotation = options['spriteRotation'];
if (spriteRotation) {
this.bodyNode.setAttribute('Rotation', MathUtils.degToRad(spriteRotation));
}
}
else if (renderType == RenderType.Plane) {
// If user provide renderable node then skip to create it
let renderableNode = options['renderableNode'];
if (!renderableNode) {
if (this.bodyNode.getType() != 'Node') {
let node = this.app.resourceManager.parseModel('Plane', _defaultPlaneOptions);
this.body.setNode(node);
}
}
}
// Get the pivot and prepare to set it (in delay mode)
let pivot = options['pivot'];
if (pivot) {
this._updatePivot(pivot);
}
}
// #endregion
// #region Overrides
onBeforeSetup(param) {
// If user provide renderable node then skip to create it
let renderableNode = param['renderableNode'];
if (renderableNode) {
return;
}
// Create render node
let renderType = Utils.parseValue(param['renderType'], RenderType.Sprite);
switch (renderType) {
case RenderType.Sprite:
renderableNode = Utils.createObject(_spriteTypeName, param['extras'] || param['external']);
break;
case RenderType.Plane:
renderableNode = this.app.resourceManager.parseModel('Plane', _defaultPlaneOptions);
break;
default:
break;
}
param['renderableNode'] = renderableNode;
}
onSetupStyle(param) {
let style = param['style'];
if (style) {
// Enable transparent mode as default
if (style.transparent === undefined) {
style.transparent = true;
}
// Enable double side mode as default
if (style.sideType === undefined) {
style.sideType = SideType.Double;
}
// Disable ligths mode as default
if (style.lights === undefined) {
style.lights = false;
}
}
else {
param['style'] = {
transparent: true,
sideType: SideType.Double
}
}
super.onSetupStyle(param);
}
onCopy(object) {
super.onCopy(object);
this._autoFitBodyScale = object.autoFitBodyScale;
this._scaleFactor = object.scaleFactor;
this._renderType = object.renderType;
this._pivotMode = object.pivotMode;
this._spriteRotation = object.spriteRotation;
this.onRefresh();
}
onRefresh() {
if (this._autoFitBodyScale) {
this._fitBodyScale();
}
super.onRefresh();
}
/**
* When load reosurce.
* @param {Object} options The options to load.
* @param {Function} resolve The promise resolve callback function.
* @param {Function} reject The promise reject callback function.
* @private
*/
onLoadResource(options, resolve, reject) {
let dynamic = Utils.parseValue(options['dynamic'], false);
if (!dynamic) {
Utils.setTimeout(() => {
if (this.destroyed) {
return;
}
this._refreshRenderableNode(options);
resolve();
});
}
else {
Utils.setTimeout(() => {
resolve();
});
}
}
onCreateBodyNode(type) {
let node;
switch (this.renderType) {
case RenderType.Sprite:
node = Utils.createObject(_spriteTypeName, this.external);
break;
case RenderType.Plane:
node = this.app.resourceManager.parseModel('Plane', _defaultPlaneOptions);
break;
default:
node = super.onCreateBodyNode(type);
break;
}
return node;
}
onGetPivot() {
if (this._useSpriteCenterAsPivot()) {
let pivot = this.bodyNode.getAttribute('Center');
return [pivot[0], pivot[1], 0.5];
}
else {
return super.onGetPivot();
}
}
onSetPivot(value) {
if (this._useSpriteCenterAsPivot()) {
this.bodyNode.setAttribute('Center', [value[0], value[1]]);
}
else {
super.onSetPivot(value);
}
}
onChangeImage() {
this.waitForComplete().then(() => {
if (this.destroyed) {
return;
}
if (this._autoFitBodyScale) {
this._fitBodyScale();
}
});
}
onSetupComplete(param) {
super.onSetupComplete(param);
this.level.config.ignoreStyle = true; // skip level outline
}
// #endregion
// #region Accessor
/**
* Enable/Disable auto fit body scale, true indicates it will use the ratio of style's image size to fit body scale.
* Default value is: false.
* @type {Boolean}
* @example
* marker.autoFitBodyScale = true;
* @public
*/
get autoFitBodyScale() {
return this._autoFitBodyScale;
}
set autoFitBodyScale(value) {
this._autoFitBodyScale = value;
if (this._autoFitBodyScale) {
this._fitBodyScale();
}
}
/**
* Get/Set scale factor of autoFitBodyScale attribute(only works when [autoFitBodyScale=true]).
* Default value is: 0.01.
* @type {Number}
* @example
* marker.scaleFactor = 0.1;
* @public
*/
get scaleFactor() {
return this._scaleFactor;
}
set scaleFactor(value) {
this._scaleFactor = value;
if (this._autoFitBodyScale) {
this._fitBodyScale();
}
}
/**
* Get/Set the render type.
* @type {RenderType}
* @public
*/
get renderType() {
if (this._renderType !== undefined) {
return this._renderType;
}
let type = this.bodyNode.getType();
switch (type) {
case 'Sprite': return RenderType.Sprite;
case 'Plane': return RenderType.Plane;
default:
return RenderType.Plane;
}
}
set renderType(value) {
let pivot = this.pivot;
this._renderType = value;
this.reloadResource(false, { pivot });
}
/**
* Get/Set the pivot mode.
* @type {PivotMode}
* @private
*/
get pivotMode() {
return this._pivotMode;
}
set pivotMode(value) {
if (this._pivotMode != value) {
this._pivotMode = value;
this.onSetPivot(this.pivot);
}
}
/**
* Get/Set the pivot mode type.
* @type {PivotModeType}
* @deprecated 2.7
* @private
*/
get pivotModeType() {
return this.pivotMode;
}
set pivotModeType(value) {
this.pivotMode = value;
}
/**
* Get/Set the sprite rotation in degree, only works on THING.RenderType.Sprite mode.
* @type {Number}
* @public
*/
get spriteRotation() {
return this._spriteRotation;
}
set spriteRotation(value) {
this._spriteRotation = value;
if (this.loaded) {
if (this.renderType == RenderType.Sprite) {
this.bodyNode.setAttribute('Rotation', MathUtils.degToRad(value));
}
}
}
/**
* @typedef {Object} FitBodyScaleInfo
* @property {THING.Marker} object The object.
* @property {Object} image The image.
*/
/**
* The function to call when fit body scale.
* @callback OnFitBodyScaleCallback
* @param {FitBodyScaleInfo} info The info.
*/
/**
* Get/Set before fit body scale callback function.
* @type {OnFitBodyScaleCallback}
* @public
*/
get onBeforeFitBodyScale() {
return this._onBeforeFitBodyScale;
}
set onBeforeFitBodyScale(value) {
this._onBeforeFitBodyScale = value;
}
/**
* Get/Set after fit body scale callback function.
* @type {OnFitBodyScaleCallback}
* @public
*/
get onAfterFitBodyScale() {
return this._onAfterFitBodyScale;
}
set onAfterFitBodyScale(value) {
this._onAfterFitBodyScale = value;
}
// #endregion
get isMarker() {
return true;
}
}
export { Marker }