Выделение нескольких объектов, клонирование
Реализовано выделение нескольких объектов. При этом центр выделения устанавливается в геометрическом центре масс, так называемом барицентре, который определяется следующим образом
Барицентр определяется сложением векторов положения точек, входящих с группу и делится на количество точек.
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.