Выделение нескольких объектов, клонирование
Реализовано выделение нескольких объектов. При этом центр выделения устанавливается в геометрическом центре масс, так называемом барицентре, который определяется следующим образом
Барицентр определяется сложением векторов положения точек, входящих с группу и делится на количество точек.
function findBaryCenter( points) {
if ( !Array.isArray( points) ) return null;
let pointsCount = points.length;
let resultVector = points.reduce( (sum, current) => sum.add(current) );
let baryCenter = resultVector.divideScalar( pointsCount );
return baryCenter;
}
Для реализации множественного выделения создана группа в объекте currentselection и добавлена в сцену момент инициализации сцены
this.currentSelection = {
object: null,
objectOwnColor: null,
multiselection: new Group()
};
scene.add( this.currentSelection.multiselection );
Для инициализации множественного выделения нужно удерживать нажатой клавишу Ctrl.
Сначала проверяем, выделен ли какой-то объект. Если выделен, то добавляем его в группу multiselection.
if ( event.ctrlKey ){
if ( scope.currentSelection.object ) {
taEntities.removeWireframeAndBoundingBox( scope.currentSelection.object );
scope.currentSelection.multiselection.attach( scope.currentSelection.object );
scope.currentSelection.object = null;
}
Создаем массив из объектов, входящих в группу multiselection и проверяем, есть выделяемый объект в этой группе. Если нет — то добавляем его в группу, если да — то удаляем его из выделения:
let arrayObjectsInSelection = [];
arrayObjectsInSelection = arrayObjectsInSelection.concat( scope.currentSelection.multiselection.children );
if ( arrayObjectsInSelection.includes( objectToSelect )) {
taEntities.removeWireframeAndBoundingBox( objectToSelect );
arrayObjectsInSelection.splice( arrayObjectsInSelection.indexOf( objectToSelect), 1 );
}
else {
arrayObjectsInSelection.push( objectToSelect );
}
Возвращаем все объекты из группы в сцену ,чтобы установить новое положение группы, переместив его в геометрический центр масс. Для этого определяем положение всех объектов выделения
scope.returnObjectsToScene();
let centerPoints = [];
arrayObjectsInSelection.forEach( element => {
centerPoints.push( element.position.clone() )
})
let baryCenter = findBaryCenter( centerPoints ).clone();
scope.currentSelection.multiselection.position.set( baryCenter.x, baryCenter.y, baryCenter.z);
Возвращаем все объекты из сцены в группу
for (let i = arrayObjectsInSelection.length - 1; i >= 0; i--) {
scope.currentSelection.multiselection.attach( arrayObjectsInSelection[i] );
arrayObjectsInSelection[i].add( taEntities.createBoundingBox( arrayObjectsInSelection[i] ) );
}
При этом используем метод .attach, который, в отличие от .add, добавляет объекты в группу с учетом мировых координат. То есть, если у объекта координаты ( 0, 0, 0 ), а у группы ( 10, 10 , 10 ), то .add добавит в точку размещения группы, то есть в ( 10, 10 , 10 ), а .attach оставит объект в точке ( 0, 0, 0 ), но для группы в локальных координатах группы он окажется в с координатами ( -10, -10, -10 ).
Функции возврата объектов в сцену и сброса Multyselection выглядит следующим образом
this.returnObjectsToScene = function(){
if (scope.currentSelection.multiselection.children.length > 0 ){
let lengthArray = scope.currentSelection.multiselection.children.length;
for (let i = lengthArray - 1; i >= 0; i--) {
taEntities.removeWireframeAndBoundingBox( scope.currentSelection.multiselection.children[i] );
scene.attach( scope.currentSelection.multiselection.children[i] )
}
}
scope.transformControls.detach( scope.currentSelection.multiselection )
}
this.resetMultyselection = function() {
scope.currentSelection.multiselection.children = [];
scope.currentSelection.multiselection.position.set( 0, 0, 0);
scope.currentSelection.multiselection.scale.set( 1, 1, 1);
scope.currentSelection.multiselection.rotation.set( 0, 0, 0);
}
Клонирование нескольких выделенных объектов. Можно клонировать клавишей «с» или пунктом меню Edit
if ( ta_scene.currentSelection.multiselection.children.length > 0 ){
let lengthArray = ta_scene.currentSelection.multiselection.children.length;
let multuSelectionArray = ta_scene.currentSelection.multiselection.children;
for (let i = lengthArray - 1; i >= 0; i--) {
this.removeWireframeAndBoundingBox( multuSelectionArray[i] );
let id = multuSelectionArray[i].id;
let copiedObject = ta_scene.scene.getObjectById( id );
ta_scene.scene.attach( multuSelectionArray[i]);
let newObject = copiedObject.clone();
ta_scene.scene.add( newObject );
ta_scene.selectableObjects.push( newObject );
ta_scene.currentSelection.multiselection.attach( copiedObject );
copiedObject.add( this.createBoundingBox( copiedObject ) );
}
}
Выявилась проблема, описанная мной на форуме three.js.
Изменил кнопки трансформации на radio, чтобы было видно, какой режим включен в данный момент. Кнопку Drag сделал checkbox, теперь режим перетаскивания может работать параллельно с другими режимами трансформации. При этом выявлена проблема, описанная мной в issue в GitHub репозитории three.js
Переключение режимов трансформации можно клавишами «m» — move, «r» — rotate, «s» — scale, «d» — drag.
Функции переключения режимов перенес в отдельный модуль Actions.js.
Три дня потратил на изучение Webpack, настроил проект, теперь он собирается WebPack.
