import { Utils } from '../common/Utils';
import { MathUtils } from '../math/MathUtils';
import { BaseComponent } from './BaseComponent';
import { PickType } from '../const';
const __ = {
private: Symbol('private'),
}
let _position = MathUtils.createVec3();
let _origin = MathUtils.createVec3();
// #region Private Functions
// #endregion
/**
* @class CameraPickerComponent
* The camera picker compnent.
* @memberof THING
* @extends THING.BaseComponent
*/
class CameraPickerComponent extends BaseComponent {
/**
* The picker by camera, user can pick object(s) from it.
*/
constructor() {
super();
this[__.private] = {};
let _private = this[__.private];
_private.pickFuncName = 'pickFromGPU';
_private.onPickObject = null;
}
// #region Private
// Get adjusted position of screen to pick.
_getAdjustedPosition(x, y) {
let style = this.app.delegate.getAttribute('Style');
if (style) {
let transform = style.transform;
if (transform) {
let scale = transform.scale;
if (scale) {
x /= scale[0];
y /= scale[1];
}
}
}
return [x, y];
}
// #endregion
/**
* Check intersection between object and region.
* @param {Array<Number>} region The [left, top, width, height] region in screen.
* @param {THING.Object3D} object The object.
* @returns {Boolean}
*/
intersectObjectInRegion(region, object) {
let x = region[0];
let y = region[1];
let width = region[2];
let height = region[3];
return this.object.node.intersectNodeInRegion([x, y, width, height], object.bodyNode);
}
/**
* @typedef {Object} PickResult
* @property {THING.BaseObject} object The object.
* @property {Array<Number>} position The picked position.
* @property {Number} pickedId The picked Id.
* @property {Object} external The external info.
*/
/**
* Pick node.
* @param {Number} x The x coordinate in screen.
* @param {Number} y The y coordinate in screen.
* @param {THING.BaseObject} root? The root object to pick.
* @returns {PickResult}
*/
pick(x, y, root) {
let _private = this[__.private];
let app = this.app;
let cameraNode = this.object.node;
let pickFuncName = _private.pickFuncName;
// Get screen position in pixel
let position = this._getAdjustedPosition(x, y);
// Get the pick scene
let pickScene = root ? root : app.scene.root;
// Pick object in scene
let result = cameraNode[pickFuncName](position, pickScene);
if (!result) {
return null;
}
// Get the picked object
let object = app.objectManager.getBaseObjectFromNode(result.node);
// Notify outside we had picked object
if (_private.onPickObject) {
object = _private.onPickObject(object);
if (!object) {
return null; // Pick nothing
}
}
// Build picked result
let ev = {
object,
subNodeName: result.subNodeName,
external: {}
};
// Get the picked Id
if (result.pickedId !== undefined) {
ev.pickedId = result.pickedId;
}
// To prevent BIG deal for these actions, we make property here to delay some operations
Object.defineProperties(ev, {
'position': {
get() {
return result.position;
}
},
'normal': {
get() {
return result.normal;
}
}
});
return ev;
}
/**
* Pick from cross planes.
* @param {Number} x The x coordinate in screen.
* @param {Number} y The y coordinate in screen.
* @param {Boolean} isVertical Whether vertical and horizontal planes will always be vertical.
* @returns {PickResult}
*/
pickFromCrossPlanes(x, y, isVertical = true) {
let position = this.object.control.pickFromCrossPlanes(x, y, isVertical);
return {
position,
object: null,
pickedId: null,
external: null
};
}
/**
* Calculate the points where the plane intersects.
* @param {Number} x The x coordinate in screen.
* @param {Number} y The y coordinate in screen.
* @param {Array<Number>} normal The plane normal. (default [0,1,0])
* @param {Number} constant Distance of plane. (default 0)
* @returns {Array<Number>}
*/
intersectPlane(x, y, normal = [0, 1, 0], constant = 0) {
let object = this.object;
let origin, direction;
if (object.projectionType === 'Orthographic') {
origin = object.screenToWorld([x, y, (object.near + object.far) / (object.near - object.far)]);
direction = MathUtils.transformDirection([0, 0, -1], object.matrixWorld);
}
else {
let position = object.screenToWorld([x, y, 0], _position);
origin = object.getWorldPosition(_origin);
direction = MathUtils.normalizeVector(MathUtils.subVector(position, origin));
}
return MathUtils.intersectPlane(origin, direction, normal, constant);
}
/**
* Get/Set pick type.
* @type {PickType}
* @example
* THING.App.current.camera.pickType = THING.PickType.GPU;
* @public
*/
get pickType() {
let _private = this[__.private];
switch (_private.pickFuncName) {
case 'pickFromGPU':
return PickType.GPU;
case 'pickFromGPUFast':
return PickType.GPUFast;
case 'pick':
return PickType.Raycaster;
default:
break;
}
return null;
}
set pickType(value) {
let _private = this[__.private];
switch (value) {
case PickType.GPU:
_private.pickFuncName = 'pickFromGPU';
break;
case PickType.GPUFast:
_private.pickFuncName = 'pickFromGPUFast';
break;
case PickType.Raycaster:
_private.pickFuncName = 'pick';
break;
default:
break;
}
}
/**
* The function to call when pick object.
* @callback onPickObjectCallback
* @param {THING.Object3D} object The object.
* @returns {THING.Object3D} The latest picked object, if it's null indicates pick nothing
*/
/**
* Get/Set the callback function when pick object.
* @type {onPickObjectCallback}
* @private
*/
get onPickObject() {
let _private = this[__.private];
return _private.onPickObject;
}
set onPickObject(value) {
let _private = this[__.private];
_private.onPickObject = value;
}
}
CameraPickerComponent.exportProperties = [
'pickType'
];
export { CameraPickerComponent }