import { Object3D, BaseComponent, MathUtils, Utils } from '@uino/thing';
/**
* @class DragControlComponent
* @summary 对象拖拽控制组件
* @memberof THING
* @extends THING.BaseComponent
* @public
*/
class DragControlComponent extends BaseComponent {
/**
* 拖拽控制组件
* @param {object} param
* @param {Array.<Number>} param.position - 工作平面的位置坐标
* @param {Array.<Number>} param.quaternion - 工作平面的四元数
* @param {number} param.precision - 工作平面的缩放值
*/
constructor(param) {
super(param);
this._workPlane = { position: [0, 0, 0], quaternion: [0, 0, 0, 1], precision: 0 };
this._orientedBox = {};
if (param) {
if (param.workPlane) {
this._workPlane = param.workPlane;
}
}
this._mouseOffsetV3 = null;
this._mousePos = null;
this._objQuaterStart = null;
this._anchorPosV3 = null;
this._oldForward = null;
this._oldScale = null;
this._oldPosition = null;
this._edgeXPosV3 = null;
this._edgeZPosV3 = null;
this._angleBtnDir = null;
this._mode = null;
this._btnOffset = [];
this._downCenter = null;
this._cancelShiftScale = false;
this._oldSize = null;
this._objMap = {};
this._emptyObjects = [];
this._snapRotationStep = 45;
}
onRemove() {
this._orientedBox = null;
super.onRemove();
}
onUpdate() {
let result = {};
if (this._emptyObjects.length) {
this._calculation_orientedBox();
result = this._calc();
}
this.object.trigger('TRANSFORM_CONTROL_UPDATE', { data: result });
}
_initOrientedBoxData(object) {
const orientedBox = object.orientedBox;
this._orientedBox = {
'center': orientedBox.center,
'size': orientedBox.size
};
this.__oldSize = MathUtils.divideVector(this._orientedBox.size, object.scale);
this.__oldOffsetPos = MathUtils.divideVector(
MathUtils.vec3ApplyQuat(
MathUtils.subVector(this._orientedBox.center, object.position), [
-1 * object.quaternion[0],
-1 * object.quaternion[1],
-1 * object.quaternion[2],
object.quaternion[3],
]), object.scale);
const pY = MathUtils.round(object.position[1] * 1000) / 1000;
const cY = MathUtils.round(this._orientedBox.center[1] * 1000) / 1000;
this._centerPos = pY === cY ? 1 : 0;
}
_calc() {
let result = [];
const camera = this.object.app.camera;
const obbSizeX = MathUtils.abs(this.__oldSize[0] / 2);
const obbSizeY = MathUtils.abs(this.__oldSize[1] / 2);
const obbSizeZ = MathUtils.abs(this.__oldSize[2] / 2);
let lu = this._selfToWorld([-obbSizeX, -obbSizeY, -obbSizeZ]);
let ru = this._selfToWorld([obbSizeX, -obbSizeY, -obbSizeZ]);
let rd = this._selfToWorld([obbSizeX, -obbSizeY, obbSizeZ]);
let ld = this._selfToWorld([-obbSizeX, -obbSizeY, obbSizeZ]);
let front = this._selfToWorld([0, -obbSizeY, obbSizeZ]);
let after = this._selfToWorld([0, -obbSizeY, -obbSizeZ]);
let left = this._selfToWorld([-obbSizeX, -obbSizeY, 0]);
let right = this._selfToWorld([obbSizeX, -obbSizeY, 0]);
const top = this._selfToWorld([0, obbSizeY, 0]);
const objPosition = this.object.position;
const objCenterPos = camera.worldToScreen([objPosition[0], this._orientedBox.center[1], objPosition[2]]);
let rotateYPos;
if (!this._angleBtnDir) {
rotateYPos = [objCenterPos[0], objCenterPos[1]];
}
else {
const _objPosV2 = [objCenterPos[0] - this._btnOffset[0], objCenterPos[1] - this._btnOffset[1], 0];
const temp = [
this._angleBtnDir[0] * 30,
this._angleBtnDir[1] * 30,
this._angleBtnDir[2] * 30
];
rotateYPos = MathUtils.addVector(_objPosV2, temp);
}
result.push({ mode: 'scale', axis: 'z', anchor: [0, -1], pos: camera.worldToScreen(front), visible: true });
result.push({ mode: 'scale', axis: 'z', anchor: [0, 1], pos: camera.worldToScreen(after), visible: true });
result.push({ mode: 'scale', axis: 'x', anchor: [1, 0], pos: camera.worldToScreen(left), visible: true });
result.push({ mode: 'scale', axis: 'x', anchor: [-1, 0], pos: camera.worldToScreen(right), visible: true });
result.push({ mode: 'scale', axis: 'xz', anchor: [1, 1], pos: camera.worldToScreen(lu), visible: true });
result.push({ mode: 'scale', axis: 'xz', anchor: [-1, 1], pos: camera.worldToScreen(ru), visible: true });
result.push({ mode: 'scale', axis: 'xz', anchor: [-1, -1], pos: camera.worldToScreen(rd), visible: true });
result.push({ mode: 'scale', axis: 'xz', anchor: [1, -1], pos: camera.worldToScreen(ld), visible: true });
result.push({ mode: 'scale', axis: 'y', anchor: [0, 1], pos: camera.worldToScreen(top), visible: true });
result.push({ mode: 'translate', axis: 'xz', pos: objCenterPos, visible: true });
result.push({ mode: 'translate', axis: 'y', pos: objCenterPos, visible: true });
result.push({ mode: 'angle', axis: 'y', anchor: [0, 0], pos: rotateYPos, visible: true });
return result;
}
_calculation_orientedBox() {
const curScale = this.object.scale;
const sizeX = MathUtils.abs(this.__oldSize[0] * curScale[0]);
const sizeY = MathUtils.abs(this.__oldSize[1] * curScale[1]);
const sizeZ = MathUtils.abs(this.__oldSize[2] * curScale[2]);
const afterAngles = MathUtils.vec3ApplyQuat([
this.__oldOffsetPos[0] * curScale[0],
this.__oldOffsetPos[1] * curScale[1],
this.__oldOffsetPos[2] * curScale[2]
], this.object.quaternion);
this._orientedBox.size = [sizeX, sizeY, sizeZ];
this._orientedBox.center = [
this.object.position[0] + afterAngles[0],
this.object.position[1] + afterAngles[1],
this.object.position[2] + afterAngles[2]
];
}
_moveY(x, y) {
const normal = this.object.app.camera.transform.getForward();
const constant = -MathUtils.dotVector(normal, this._orientedBox.center);
const position = this.object.app.camera.intersectPlane(x, y, normal, constant);
if (!this._mouseOffsetV3) {
this._oldPosition = this.object.position;
this._mouseOffsetV3 = MathUtils.subVector(position, this._oldPosition);
}
else {
this.object.position = [
this._oldPosition[0],
position[1] - this._mouseOffsetV3[1],
this._oldPosition[2]
];
}
}
_moveXZ(x, y) {
const normal = MathUtils.vec3ApplyQuat([0, 1, 0], this._workPlane.quaternion);
const constant = -MathUtils.dotVector(normal, this._orientedBox.center);
const position = this.object.app.camera.intersectPlane(x, y, normal, constant);
if (!position) { return; }
if (!this._mouseOffsetV3) {
this._oldPosition = this.object.position;
this._mouseOffsetV3 = [position[0] - this._oldPosition[0], position[1] - this._oldPosition[1], position[2] - this._oldPosition[2]];
}
else {
this.object.position = [
position[0] - this._mouseOffsetV3[0],
this._oldPosition[1],
position[2] - this._mouseOffsetV3[2]
];
}
}
_angleY(x, y, step) {
const objCenterPos = this.object.app.camera.worldToScreen(this._orientedBox.center);
const _to = MathUtils.normalizeVector(MathUtils.subVector([x, y, 0], [objCenterPos[0], objCenterPos[1], 0]));
if (!this._mousePos) {
this._mousePos = _to.slice();
this._objQuaterStart = this.object.quaternion;
this._oldAxis = MathUtils.vec3ApplyQuat([0, 1, 0], this.object.quaternion);
}
const angleY = this._angleTwoVectorsY(this._mousePos, _to);
if (step) {
const _objCurrentQuaterEng = MathUtils.getQuatFromAxisAngle(this._oldAxis, angleY - (angleY % step));
this.object.quaternion = MathUtils.multiplyQuat(_objCurrentQuaterEng, this._objQuaterStart);
}
else {
this.object.quaternion = MathUtils.getQuatFromAxisAngle(this._oldAxis, angleY - (angleY % this._snapRotationStep));
}
this._angleBtnDir = _to;
}
_scaleY(x, y) {
const normal = this.object.app.camera.transform.getForward();
const constant = -MathUtils.dotVector(normal, this._selfToWorld([0, -this.__oldSize[1] / 2, 0]));
const position = this.object.app.camera.intersectPlane(x, y, normal, constant);
if (!this._mouseOffsetV3) {
this._mouseOffsetV3 = position.slice();
if (!this._oldScale) { this._oldScale = this.object.scale; }
if (!this._oldSize) { this._oldSize = this._orientedBox.size; }
}
else {
let scaleOffset = MathUtils.divideVector(MathUtils.subVector(position, this._mouseOffsetV3), this.__oldSize);
let axisX = MathUtils.vec3ApplyQuat([1, 0, 0], this.object.quaternion);
let newX = MathUtils.dotVector(scaleOffset, axisX);
let axisY = MathUtils.vec3ApplyQuat([0, 1, 0], this.object.quaternion);
let newY = MathUtils.dotVector(scaleOffset, axisY);
let axisZ = MathUtils.vec3ApplyQuat([0, 0, 1], this.object.quaternion);
let newZ = MathUtils.dotVector(scaleOffset, axisZ);
this.object.scale = [
this._oldScale[0],
this._oldScale[1] + axisX[1] * newX + axisY[1] * newY + axisZ[1] * newZ,
this._oldScale[2]
];
}
}
_scaleY_equalProportion(x, y) {
if (this._cancelShiftScale) { return; }
const normal = this.object.app.camera.transform.getForward();
const constant = -MathUtils.dotVector(normal, this._selfToWorld([0, -this.__oldSize[1] / 2, 0]));
const position = this.object.app.camera.intersectPlane(x, y, normal, constant);
if (!this._mouseOffsetV3) {
this._mouseOffsetV3 = position.slice();
if (!this._oldScale) { this._oldScale = this.object.scale; }
if (!this._oldSize) { this._oldSize = this._orientedBox.size; }
}
else {
const oldScaleY = this._oldScale[1] > 0 ? 1 : -1;
const dir = MathUtils.normalizeVector(MathUtils.subVector(
[0, position[1], 0],
[0, this._mouseOffsetV3[1], 0]
));
const oriDir = MathUtils.normalizeVector(MathUtils.subVector(this._mouseOffsetV3, this.object.position));
const result = MathUtils.dotVector(dir, oriDir) > 0 ? 1 : -1;
const distance = MathUtils.getDistance([0, position[1], 0], [0, this._mouseOffsetV3[1], 0]);
const scaleY = distance / this._oldSize[1] * oldScaleY * result + 1;
this.object.scale = [
this._oldScale[0] * scaleY,
this._oldScale[1] * scaleY,
this._oldScale[2] * scaleY
];
}
}
_scaleXZ(anchor, x, y) {
const beforeProportion = this._beforeScaleXZ_equalProportion(anchor, x, y);
if (beforeProportion) {
const [newpos, scalex, scalez] = beforeProportion.slice();
if (anchor[0] !== 0 && anchor[1] === 0) {
this.object.scale = [scalex, this._oldScale[1], this._oldScale[2]];
const afterAngle = MathUtils.vec3ApplyQuat([
this.__oldOffsetPos[0] * this.object.scale[0],
this.__oldOffsetPos[1] * this.object.scale[1],
this.__oldOffsetPos[2] * this.object.scale[2]
], this.object.quaternion);
let nowVec3x = MathUtils.vec3ApplyQuat([1, 0, 0], this.object.quaternion);
let newX = MathUtils.dotVector(newpos, nowVec3x);
this.object.position = [
this._downCenter[0] - afterAngle[0] + newX * nowVec3x[0],
this._downCenter[1] - afterAngle[1] + newX * nowVec3x[1],
this._downCenter[2] - afterAngle[2] + newX * nowVec3x[2]
];
}
else if (anchor[0] === 0 && anchor[1] !== 0) {
this.object.scale = [this._oldScale[0], this._oldScale[1], scalez];
const afterAngle = MathUtils.vec3ApplyQuat([
this.__oldOffsetPos[0] * this.object.scale[0],
this.__oldOffsetPos[1] * this.object.scale[1],
this.__oldOffsetPos[2] * this.object.scale[2]
], this.object.quaternion);
let nowVec3z = MathUtils.vec3ApplyQuat([0, 0, 1], this.object.quaternion);
let newZ = MathUtils.dotVector(newpos, nowVec3z);
this.object.position = [
this._downCenter[0] - afterAngle[0] + newZ * nowVec3z[0],
this._downCenter[1] - afterAngle[1] + newZ * nowVec3z[1],
this._downCenter[2] - afterAngle[2] + newZ * nowVec3z[2]
];
}
else {
this.object.scale = [scalex, this._oldScale[1], scalez];
const afterAngle = MathUtils.vec3ApplyQuat([
this.__oldOffsetPos[0] * this.object.scale[0],
this.__oldOffsetPos[1] * this.object.scale[1],
this.__oldOffsetPos[2] * this.object.scale[2]
], this.object.quaternion);
let nowVec3x = MathUtils.vec3ApplyQuat([1, 0, 0], this.object.quaternion);
let newX = MathUtils.dotVector(newpos, nowVec3x);
let nowVec3z = MathUtils.vec3ApplyQuat([0, 0, 1], this.object.quaternion);
let newZ = MathUtils.dotVector(newpos, nowVec3z);
this.object.position = [
this._downCenter[0] - afterAngle[0] + newX * nowVec3x[0] + newZ * nowVec3z[0],
this._downCenter[1] - afterAngle[1] + newX * nowVec3x[1] + newZ * nowVec3z[1],
this._downCenter[2] - afterAngle[2] + newX * nowVec3x[2] + newZ * nowVec3z[2]
];
}
}
}
_scaleXZ_equalProportion(anchor, x, y) {
const beforeProportion = this._beforeScaleXZ_equalProportion(anchor, x, y);
if (beforeProportion) {
const [newpos, scalex, scalez] = beforeProportion.slice();
if (anchor[0] !== 0 && anchor[1] === 0) {
const addScaleX = scalex * this.__oldSize[0] / this._oldSize[0];
this.object.scale = [
scalex,
this._oldScale[1] * addScaleX,
this._oldScale[2] * addScaleX
];
const afterAngle = MathUtils.vec3ApplyQuat([
this.__oldOffsetPos[0] * this.object.scale[0],
this.__oldOffsetPos[1] * this._oldScale[1],
this.__oldOffsetPos[2] * this.object.scale[2]
], this.object.quaternion);
let nowVec3x = MathUtils.vec3ApplyQuat([1, 0, 0], this.object.quaternion);
let newX = MathUtils.dotVector(newpos, nowVec3x);
this.object.position = [
this._downCenter[0] - afterAngle[0] + newX * nowVec3x[0],
this._downCenter[1] - afterAngle[1] + newX * nowVec3x[1],
this._downCenter[2] - afterAngle[2] + newX * nowVec3x[2]
];
}
else if (anchor[0] === 0 && anchor[1] !== 0) {
const addScaleZ = scalez * this.__oldSize[2] / this._oldSize[2];
this.object.scale = [
this._oldScale[0] * addScaleZ,
this._oldScale[1] * addScaleZ,
scalez
];
const afterAngle = MathUtils.vec3ApplyQuat([
this.__oldOffsetPos[0] * this.object.scale[0],
this.__oldOffsetPos[1] * this._oldScale[1],
this.__oldOffsetPos[2] * this.object.scale[2]
], this.object.quaternion);
let nowVec3z = MathUtils.vec3ApplyQuat([0, 0, 1], this.object.quaternion);
let newZ = MathUtils.dotVector(newpos, nowVec3z);
this.object.position = [
this._downCenter[0] - afterAngle[0] + newZ * nowVec3z[0],
this._downCenter[1] - afterAngle[1] + newZ * nowVec3z[1],
this._downCenter[2] - afterAngle[2] + newZ * nowVec3z[2]
];
}
}
}
_beforeScaleXZ_equalProportion(anchor, x, y) {
const obbSizeX = this.__oldSize[0] / 2;
const obbSizeY = this.__oldSize[1] / 2;
const obbSizeZ = this.__oldSize[2] / 2;
if (!this._anchorPosV3) {
this._anchorPosV3 = this._selfToWorld([obbSizeX * anchor[0], -obbSizeY, obbSizeZ * anchor[1]]);
this._edgeXPosV3 = this._selfToWorld([obbSizeX * -anchor[0], -obbSizeY, obbSizeZ * anchor[1]]);
this._edgeZPosV3 = this._selfToWorld([obbSizeX * anchor[0], -obbSizeY, obbSizeZ * -anchor[1]]);
this._oldForward = this.object.forward;
if (!this._oldScale) { this._oldScale = this.object.scale; }
if (!this._oldPosition) { this._oldPosition = this.object.positio; }
if (!this._downCenter) { this._downCenter = this._orientedBox.center; }
if (!this._oldSize) { this._oldSize = this._orientedBox.size; }
}
else {
const normal = MathUtils.vec3ApplyQuat(MathUtils.vec3ApplyQuat([0, 1, 0], this._workPlane.quaternion), this.object.quaternion);
const constant = -MathUtils.dotVector(normal, this._selfToWorld([0, -this.__oldSize[1] / 2, 0]));
const position = this.object.app.camera.intersectPlane(x, y, normal, constant);
const footpointX = this._footPoint(this._anchorPosV3, this._edgeXPosV3, position);
const footpointZ = this._footPoint(this._anchorPosV3, this._edgeZPosV3, position);
const v1 = this._anchorPosV3.slice();
const v2_x = footpointX.slice();
const xdistance = MathUtils.sqrt(MathUtils.getDistanceToSquared(v1, v2_x));
const v2_z = footpointZ.slice();
const zdistance = MathUtils.sqrt(MathUtils.getDistanceToSquared(v1, v2_z));
const oldScaleDir = this._oldScale[2] > 0 ? 1 : -1;
const _v2_xSub_v1 = [v2_x[0] - v1[0], v2_x[1] - v1[1], v2_x[2] - v1[2]];
const xresult = MathUtils.crossVector(this._oldForward.slice(), MathUtils.normalizeVector(_v2_xSub_v1));
const xscaleDir = xresult[1] < 0 ? -1 : 1;
const scalex = xdistance / (obbSizeX * 2) * xscaleDir * oldScaleDir * -anchor[0];
const _v2_zSub_v1 = [v2_z[0] - v1[0], v2_z[1] - v1[1], v2_z[2] - v1[2]];
const zresult = MathUtils.dotVector(MathUtils.normalizeVector(_v2_zSub_v1), this._oldForward.slice());
const zscaleDir = zresult > 0 ? 1 : -1;
const scalez = zdistance / (obbSizeZ * 2) * zscaleDir * oldScaleDir * -anchor[1];
const v2 = position.slice();
const distance = MathUtils.sqrt(MathUtils.getDistanceToSquared(v2, v1));
const v2_Sub_v1Nor = MathUtils.normalizeVector([v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2]]);
const newpos = [
v2_Sub_v1Nor[0] * (distance / 2) + v1[0] - this._downCenter[0],
v2_Sub_v1Nor[1] * (distance / 2) + v1[1] - this._downCenter[1],
v2_Sub_v1Nor[2] * (distance / 2) + v1[2] - this._downCenter[2]
];
return [newpos, scalex, scalez];
}
}
_selfToWorld(val) {
if (this._centerPos) {
return this.object.selfToWorld(MathUtils.addVector(val, this.__oldOffsetPos));
}
const yPos = val[1] > 0 ? val[1] * 2 : 0;
const offsetXYZ = [
this.__oldOffsetPos[0],
this.__oldOffsetPos[1] - this.__oldSize[1] / 2,
this.__oldOffsetPos[2]
];
return this.object.selfToWorld([val[0] + offsetXYZ[0], yPos + offsetXYZ[1], val[2] + offsetXYZ[2]]);
}
_footPoint(a, b, c) {
let result = [];
let dx = a[0] - b[0];
let dy = a[1] - b[1];
let dz = a[2] - b[2];
if (MathUtils.abs(dx) < 0.000001 && MathUtils.abs(dy) < 0.000001 && MathUtils.abs(dz) < 0.000001) {
result = [a[0], a[1], a[2]];
}
else {
let u = (c[0] - a[0]) * (a[0] - b[0]) + (c[1] - a[1]) * (a[1] - b[1]) + (c[2] - a[2]) * (a[2] - b[2]);
u = u / ((dx * dx) + (dy * dy) + (dz * dz));
result[0] = a[0] + u * dx;
result[1] = a[1] + u * dy;
result[2] = a[2] + u * dz;
}
return result;
}
_angleTwoVectorsY(from, to) {
const angle = MathUtils.getAngleBetweenVectors(from, to);
const v3 = MathUtils.crossVector(from, to);
return v3[2] < 0 ? angle : 360 - angle;
}
/**
* 更新对象的变换操作
* @param {object} param
* @param {string} param.mode - 当前对象的变换方式
* @param {string} param.axis - 当前对象的操作方向
* @param {Array.<Number>} param.anchor - 局部坐标系中x和z的负方向
* @param {boolean} param.shift - 是否按下shift键
* @param {number} param.step - 对象旋转的精度值
* @param {Array.<Number>} param.btnOffset - 旋转按钮相对于对象中心的屏幕坐标偏移
* @param {Array.<Number>} param.offset - 用户屏幕坐标的偏移量
* @param {number} param.x - 屏幕x坐标
* @param {number} param.y - 屏幕y坐标
* @public
*/
update(param) {
this._mode = param.mode;
const axis = param.axis;
const anchor = param.anchor;
const shift = param.shift;
const step = param.step < 0 ? 0.1 : param.step;
this._btnOffset = param.btnOffset || [0, 0];
const offset = param.offset || [0, 0];
const x = param.x - offset[0];
const y = param.y - offset[1];
if (this._mode == 'translate') {
if (axis == 'y') {
this._moveY(x, y);
}
else if (axis == 'xz') {
this._moveXZ(x, y);
}
}
else if (this._mode == 'angle') {
if (shift) {
this._angleY(x, y);
}
else {
this._angleY(x, y, step);
}
}
else if (this._mode == 'scale') {
if (axis == 'y') {
if (shift) {
this._scaleY_equalProportion(x, y);
}
else {
this._scaleY(x, y);
}
}
else {
if (shift) {
this._scaleXZ_equalProportion(anchor, x, y);
}
else {
this._scaleXZ(anchor, x, y);
}
}
}
this._updateRealObjectsTransform();
}
/**
* 停止拖拽
* @public
*/
stop() {
this._mouseOffsetV3 = null;
this._mousePos = null;
this._objQuaterStart = null;
this._anchorPosV3 = null;
this._oldForward = null;
this._oldScale = null;
this._oldPosition = null;
this._edgeXPosV3 = null;
this._edgeZPosV3 = null;
this._angleBtnDir = null;
this._mode = null;
this._downCenter = null;
this._oldSize = null;
}
/**
* 取消等比例缩放,默认为false
* @type {boolean}
* @public
*/
get cancelShiftScale() {
return this._cancelShiftScale;
}
set cancelShiftScale(val) {
if (typeof val === 'boolean') {
this._cancelShiftScale = val;
}
}
get snapRotationStep() {
return this._snapRotationStep;
}
set snapRotationStep(val) {
if (this._snapRotationStep > 0) {
this._snapRotationStep = val;
}
else {
this._snapRotationStep = 45;
}
}
/**
* 设置变换对象
* @param {Array.<Object>} objs - 变换对象
* @param {boolean} ignoreCenter - 是否忽略中心位置
* @public
*/
setObjects(objs = [], ignoreCenter = false) {
let objects = Utils.uniqueObjects(objs);
objects = objects.filter(object => {
return !object.destroyed;
});
this._destroyBaseObject3D();
if (!objects.length) {
return;
}
this._createObjMap(objects, ignoreCenter);
}
getObjects() {
return Object.values(this._objMap);
}
_destroyBaseObject3D() {
if (this._emptyObjects.length) {
for (let i = 0; i < this._emptyObjects.length; i++) {
const emptyObject = this._emptyObjects[i];
emptyObject.destroy();
}
this._emptyObjects = [];
}
this._objMap = {};
}
async _createObjMap(objs, ignoreCenter) {
// get obb
let obbs = [];
for (let i = 0; i < objs.length; i++) {
await objs[i].waitForComplete();
obbs.push(objs[i].getOBB(false));
}
let max = obbs[0].max, min = obbs[0].min;
for (let i = 1; i < objs.length; i++) {
max[0] = MathUtils.max(obbs[i].max[0], max[0]);
max[1] = MathUtils.max(obbs[i].max[1], max[1]);
max[2] = MathUtils.max(obbs[i].max[2], max[2]);
min[0] = MathUtils.min(obbs[i].min[0], min[0]);
min[1] = MathUtils.min(obbs[i].min[1], min[1]);
min[2] = MathUtils.min(obbs[i].min[2], min[2]);
}
this.object.position = ignoreCenter ? [0, 0, 0] : [min[0] + (max[0] - min[0]) / 2, min[1], min[2] + (max[2] - min[2]) / 2];
this.object.angles = objs.length > 1 ? [0, 0, 0] : objs[0].angles;
for (let i = 0; i < objs.length; i++) {
const obj = objs[i];
this._objMap[obj.uuid] = obj;
const emptyObject = new Object3D({
tags: ['helper']
});
emptyObject.parent = this.object;
emptyObject.name = obj.uuid;
emptyObject.setQueryable(false, true);
const halfSize = MathUtils.divideVector(MathUtils.divideVector(obbs[i].size, obj.scale), 2);
const center = MathUtils.divideVector(
MathUtils.vec3ApplyQuat(
MathUtils.subVector(obbs[i].center, obj.position), MathUtils.quat.invert([], obj.quaternion)
), obj.scale);
emptyObject.initialLocalBoundingBox = { halfSize, center };
emptyObject.matrixWorld = obj.matrixWorld;
this._emptyObjects.push(emptyObject);
}
this._initOrientedBoxData(this.object);
}
_updateRealObjectsTransform() {
if (!this._emptyObjects.length) { return; }
for (let i = 0; i < this._emptyObjects.length; i++) {
const emptyObject = this._emptyObjects[i];
const realObject = this._objMap[emptyObject.name];
realObject.matrixWorld = emptyObject.matrixWorld;
}
}
}
export { DragControlComponent };