From 2562094d58d86d01482cdc6c8ffef5bbf4654f3f Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Sun, 14 Nov 2021 11:13:23 +0000 Subject: [PATCH] feat(viewer): various fixes, fiddles --- packages/viewer/src/modules/Filtering.js | 218 ++- .../viewer/src/modules/InteractionHandler.js | 50 +- .../viewer/src/modules/SceneObjectManager.js | 6 +- packages/viewer/src/modules/SceneObjects.js | 8 +- packages/viewer/src/modules/SectionBox.js | 547 ++++-- packages/viewer/src/modules/SectionBoxNew.js | 353 ---- packages/viewer/src/modules/Viewer.js | 9 +- .../context/{cameras.js => CameraHanlder.js} | 78 +- .../src/modules/external/TransformControls.js | 1709 ----------------- 9 files changed, 500 insertions(+), 2478 deletions(-) delete mode 100644 packages/viewer/src/modules/SectionBoxNew.js rename packages/viewer/src/modules/context/{cameras.js => CameraHanlder.js} (66%) delete mode 100644 packages/viewer/src/modules/external/TransformControls.js diff --git a/packages/viewer/src/modules/Filtering.js b/packages/viewer/src/modules/Filtering.js index 28e705aaf..cadce8633 100644 --- a/packages/viewer/src/modules/Filtering.js +++ b/packages/viewer/src/modules/Filtering.js @@ -1,26 +1,31 @@ import * as THREE from 'three' import Rainbow from 'rainbowvis.js' -import { cloneUniforms } from 'three' -const WireframeMaterial = new THREE.MeshStandardMaterial( { - color: 0x7080A0, - side: THREE.DoubleSide, - transparent: true, - opacity: 0.04, - wireframe: true -} ) +export default class FilteringManager { + + constructor( viewer ) { + this.viewer = viewer + this.WireframeMaterial = new THREE.MeshStandardMaterial( { + color: 0x7080A0, + side: THREE.DoubleSide, + transparent: true, + opacity: 0.04, + wireframe: true, + clippingPlanes: this.viewer.sectionBox.planes + } ) + + this.ColoredMaterial = new THREE.MeshStandardMaterial( { + color: 0x7080A0, + side: THREE.DoubleSide, + transparent: false, + clippingPlanes: this.viewer.sectionBox.planes + } ) + } -const ColoredMaterial = new THREE.MeshStandardMaterial( { - color: 0x7080A0, - side: THREE.DoubleSide, - transparent: false -} ) - - -export function filterAndColorObject( obj, filter ) { + filterAndColorObject( obj, filter ) { if ( !filter ) return obj.clone() - if ( !passesFilter( obj.userData, filter.filterBy ) ) + if ( !this.passesFilter( obj.userData, filter.filterBy ) ) { if ( filter.ghostOthers && obj.type === 'Mesh' ) { let clone = obj.clone() @@ -37,104 +42,105 @@ export function filterAndColorObject( obj, filter ) { let clone = obj.clone() if ( filter.colorBy ) { if ( filter.colorBy.type === 'category' ) { - clone.material = colorWithCategory( obj.userData, filter.colorBy ) + clone.material = this.colorWithCategory( obj.userData, filter.colorBy ) } else if ( filter.colorBy.type === 'gradient' ) { - clone.material = colorWithGradient( obj, filter.colorBy ) + clone.material = this.colorWithGradient( obj, filter.colorBy ) } } - return clone -} - -function getObjectProperty( obj, property ) { - if ( !property ) return - let keyParts = property.split( '.' ) - let crtObj = obj - for ( let i = 0; i < keyParts.length - 1; i++ ) { - if ( !( keyParts[i] in crtObj ) ) return - crtObj = crtObj[ keyParts[i] ] - if ( crtObj.constructor !== Object ) return - } - let attributeName = keyParts[ keyParts.length - 1 ] - return crtObj[ attributeName ] -} - -function colorWithCategory( obj, colors ) { - let defaultValue = colors.default - let color = defaultValue - let objValue = getObjectProperty( obj, colors.property ) - let customPallete = colors.values || {} - if ( objValue in customPallete ) { - color = customPallete[ objValue ] } - if ( !color ) { - // compute value hash - let objValueAsString = '' + objValue - let hash = 0 - for( let i = 0; i < objValueAsString.length; i++ ) { - let chr = objValueAsString.charCodeAt( i ) - hash = ( ( hash << 5 ) - hash ) + chr - hash |= 0 // Convert to 32bit integer - } - hash = Math.abs( hash ) - let colorHue = hash % 360 - color = `hsl(${colorHue}, 50%, 30%)` - } - - let material = ColoredMaterial.clone() - material.color = new THREE.Color( color ) - return material -} - -function colorWithGradient( threejsObj, colors ) { - let obj = threejsObj.userData - let rainbow = new Rainbow( ) - if ( 'minValue' in colors && 'maxValue' in colors ) - rainbow.setNumberRange( colors.minValue, colors.maxValue ) - if ( 'gradientColors' in colors ) - rainbow.setSpectrum( ...colors.gradientColors ) - - let objValue = getObjectProperty( obj, colors.property ) - objValue = Number( objValue ) - if ( Number.isNaN( objValue ) ) { - return WireframeMaterial + getObjectProperty( obj, property ) { + if ( !property ) return + let keyParts = property.split( '.' ) + let crtObj = obj + for ( let i = 0; i < keyParts.length - 1; i++ ) { + if ( !( keyParts[i] in crtObj ) ) return + crtObj = crtObj[ keyParts[i] ] + if ( crtObj.constructor !== Object ) return + } + let attributeName = keyParts[ keyParts.length - 1 ] + return crtObj[ attributeName ] } - let material = ColoredMaterial.clone() - material.color = new THREE.Color( `#${rainbow.colourAt( objValue )}` ) - return material -} - -function passesFilter( obj, filterBy ) { - if ( !filterBy ) return true - for ( let filterKey in filterBy ) { - let objValue = getObjectProperty( obj, filterKey ) - - let passesFilter = filterValue( objValue, filterBy[ filterKey ] ) - if ( !passesFilter ) return false - } - return true -} - -function filterValue( objValue, valueFilter ) { - // Array value filter means it can be any value from the array - if ( Array.isArray( valueFilter ) ) - return valueFilter.includes( objValue ) - - // Dictionary value filter can specify ranges with `lte` and `gte` fields (LowerThanOrEqual, GreaterThanOrEqual) - if ( valueFilter.constructor === Object ) { - if ( 'not' in valueFilter && Array.isArray( valueFilter.not ) ) { - if ( valueFilter.not.includes( objValue ) ) - return false + colorWithCategory( obj, colors ) { + let defaultValue = colors.default + let color = defaultValue + let objValue = this.getObjectProperty( obj, colors.property ) + let customPallete = colors.values || {} + if ( objValue in customPallete ) { + color = customPallete[ objValue ] + } + + if ( !color ) { + // compute value hash + let objValueAsString = '' + objValue + let hash = 0 + for( let i = 0; i < objValueAsString.length; i++ ) { + let chr = objValueAsString.charCodeAt( i ) + hash = ( ( hash << 5 ) - hash ) + chr + hash |= 0 // Convert to 32bit integer + } + hash = Math.abs( hash ) + let colorHue = hash % 360 + color = `hsl(${colorHue}, 50%, 30%)` + } + + let material = this.ColoredMaterial.clone() + material.color = new THREE.Color( color ) + return material + } + + colorWithGradient( threejsObj, colors ) { + let obj = threejsObj.userData + let rainbow = new Rainbow( ) + if ( 'minValue' in colors && 'maxValue' in colors ) + rainbow.setNumberRange( colors.minValue, colors.maxValue ) + if ( 'gradientColors' in colors ) + rainbow.setSpectrum( ...colors.gradientColors ) + + let objValue = this.getObjectProperty( obj, colors.property ) + objValue = Number( objValue ) + if ( Number.isNaN( objValue ) ) { + return this.WireframeMaterial + } + + let material = this.ColoredMaterial.clone() + material.color = new THREE.Color( `#${rainbow.colourAt( objValue )}` ) + return material + } + + passesFilter( obj, filterBy ) { + if ( !filterBy ) return true + for ( let filterKey in filterBy ) { + let objValue = this.getObjectProperty( obj, filterKey ) + + let passesFilter = this.filterValue( objValue, filterBy[ filterKey ] ) + if ( !passesFilter ) return false } - if ( 'lte' in valueFilter && objValue > valueFilter.lte ) - return false - if ( 'gte' in valueFilter && objValue < valueFilter.gte ) - return false return true } - - // Can also filter by specific value - return objValue === valueFilter + + filterValue( objValue, valueFilter ) { + // Array value filter means it can be any value from the array + if ( Array.isArray( valueFilter ) ) + return valueFilter.includes( objValue ) + + // Dictionary value filter can specify ranges with `lte` and `gte` fields (LowerThanOrEqual, GreaterThanOrEqual) + if ( valueFilter.constructor === Object ) { + if ( 'not' in valueFilter && Array.isArray( valueFilter.not ) ) { + if ( valueFilter.not.includes( objValue ) ) + return false + } + if ( 'lte' in valueFilter && objValue > valueFilter.lte ) + return false + if ( 'gte' in valueFilter && objValue < valueFilter.gte ) + return false + return true + } + + // Can also filter by specific value + return objValue === valueFilter + } } + diff --git a/packages/viewer/src/modules/InteractionHandler.js b/packages/viewer/src/modules/InteractionHandler.js index 88b904b1d..58678db1c 100644 --- a/packages/viewer/src/modules/InteractionHandler.js +++ b/packages/viewer/src/modules/InteractionHandler.js @@ -1,5 +1,4 @@ import * as THREE from 'three' -import SectionBox from './SectionBox' import SelectionHelper from './SelectionHelper' export default class InteractionHandler { @@ -26,11 +25,11 @@ export default class InteractionHandler { this.selectionHelper.on( 'object-doubleclicked', this._handleDoubleClick.bind( this ) ) this.selectionHelper.on( 'object-clicked', this._handleSelect.bind( this ) ) - document.addEventListener('keydown', (e) => { - if(e.key === 'Escape' && this.viewer.mouseOverRenderer){ + document.addEventListener( 'keydown', ( e ) => { + if( e.key === 'Escape' && this.viewer.mouseOverRenderer ) { this.deselectObjects() } - }) + } ) } _handleDoubleClick( objs ) { @@ -47,8 +46,8 @@ export default class InteractionHandler { } _handleSelect( objs ) { - console.log(this.viewer.cameraHandler.orbiting ) - if(this.viewer.cameraHandler.orbiting ) return + console.log( this.viewer.cameraHandler.orbiting ) + if( this.viewer.cameraHandler.orbiting ) return if ( this.preventSelection ) return if ( objs.length === 0 ) { @@ -100,34 +99,16 @@ export default class InteractionHandler { this.viewer.emit( 'select', this.selectedObjectsUserData ) } - toggleSectionBox() { - this.sectionBox.toggle() - if ( this.sectionBox.display.visible ) { - if ( this.selectedObjects.children.length === 0 ) { - this.sectionBox.setBox( this.viewer.sceneManager.getSceneBoundingBox() ) - this.zoomExtents() - } - else { - let box = new THREE.Box3().setFromObject( this.selectedObjects ) - this.sectionBox.setBox( box ) - this.zoomToBox( box ) - } - } else { - this.preventSelection = false - } - this.viewer.needsRender = true - } - zoomToObject( target, fit = 1.2, transition = true ) { const box = new THREE.Box3().setFromObject( target ) this.zoomToBox( box, fit, transition ) } zoomExtents( fit = 1.2, transition = true ) { - // if ( this.sectionBox.display.visible ) { - // this.zoomToObject( this.sectionBox.boxMesh ) - // return - // } + if ( this.viewer.sectionBox.display.visible ) { + this.zoomToObject( this.viewer.sectionBox.cube ) + return + } if ( this.viewer.sceneManager.sceneObjects.allObjects.length === 0 ) { let box = new THREE.Box3( new THREE.Vector3( -1,-1,-1 ), new THREE.Vector3( 1,1,1 ) ) this.zoomToBox( box, fit, transition ) @@ -140,8 +121,8 @@ export default class InteractionHandler { } zoomToBox( box, fit = 1.2, transition = true ) { - if(box.max.x === Infinity || box.max.x === -Infinity) { - box = new THREE.Box3( new THREE.Vector3( -10,-10,-10 ), new THREE.Vector3( 10,10,10 ) ) + if( box.max.x === Infinity || box.max.x === -Infinity ) { + box = new THREE.Box3( new THREE.Vector3( -5,-5,-5 ), new THREE.Vector3( 5,5,5 ) ) } const fitOffset = fit @@ -167,15 +148,6 @@ export default class InteractionHandler { this.viewer.cameraHandler.orthoCamera.updateProjectionMatrix() } - /** - * Allows camera to go "underneath" or not. By default, this function will set - * the max polar angle to Pi, allowing the camera to look from down upwards. - * @param {[type]} angle [description] - */ - setMaxPolarAngle( angle = Math.PI ) { - this.viewer.controls.maxPolarAngle = angle - } - rotateCamera( azimuthAngle = 0.261799, polarAngle = 0, transition = true ) { this.viewer.controls.rotate( azimuthAngle, polarAngle, transition ) } diff --git a/packages/viewer/src/modules/SceneObjectManager.js b/packages/viewer/src/modules/SceneObjectManager.js index 4bccbbbdf..0a494379d 100644 --- a/packages/viewer/src/modules/SceneObjectManager.js +++ b/packages/viewer/src/modules/SceneObjectManager.js @@ -109,7 +109,7 @@ export default class SceneObjectManager { } - addSolid( wrapper, addToScene = true ) { + addSolid( wrapper, _addToScene = true ) { // Do we have a defined material? if ( wrapper.meta.renderMaterial ) { let renderMat = wrapper.meta.renderMaterial @@ -242,10 +242,6 @@ export default class SceneObjectManager { return group } - removeObject( id ) { - // TODO - } - async removeImportedObject( importedUrl ) { for ( let objGroup of this.sceneObjects.allObjects.children ) { let toRemove = objGroup.children.filter( obj => obj.userData?.__importedUrl === importedUrl ) diff --git a/packages/viewer/src/modules/SceneObjects.js b/packages/viewer/src/modules/SceneObjects.js index 4301a8b82..2660b0b9d 100644 --- a/packages/viewer/src/modules/SceneObjects.js +++ b/packages/viewer/src/modules/SceneObjects.js @@ -2,7 +2,7 @@ import * as THREE from 'three' import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils' // import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils' import debounce from 'lodash.debounce' -import { filterAndColorObject } from './Filtering' +import FilteringManager from './Filtering' /** * Container for the scene objects, to allow loading/unloading/filtering/coloring/grouping @@ -38,6 +38,7 @@ export default class SceneObjects { this.groupedSolidObjects.name = 'groupedSolidObjects' this.allObjects.add( this.groupedSolidObjects ) + this.filteringManager = new FilteringManager( this.viewer ) this.filteredObjects = null this.appliedFilter = null @@ -125,7 +126,7 @@ export default class SceneObjects { for ( let obj of threejsGroup.children ) { await this.asyncPause() - let filteredObj = filterAndColorObject( obj, filter ) + let filteredObj = this.filteringManager.filterAndColorObject( obj, filter ) if ( filteredObj ) ret.add( filteredObj ) } @@ -248,9 +249,6 @@ export default class SceneObjects { let groupGeometry = BufferGeometryUtils.mergeBufferGeometries( materialIdToBufferGeometry[ materialId ] ) await this.asyncPause() let groupMaterial = materialIdToMaterial[ materialId ] - // // console.log( this.viewer.sectionBox.planes ) - // // groupMaterial.clippingPlanes = [ this.viewer.sectionBox.planes[0], this.viewer.sectionBox.planes[1] ] - // groupMaterial.clippingPlanes = [ ...this.viewer.sectionBox.planes ] let groupMesh = new THREE.Mesh( groupGeometry, groupMaterial ) groupMesh.userData = null groupedObjects.add( groupMesh ) diff --git a/packages/viewer/src/modules/SectionBox.js b/packages/viewer/src/modules/SectionBox.js index dd18afb4d..d6af28e8d 100644 --- a/packages/viewer/src/modules/SectionBox.js +++ b/packages/viewer/src/modules/SectionBox.js @@ -1,246 +1,371 @@ import * as THREE from 'three' import SelectionHelper from './SelectionHelper' -import { TransformControls } from './external/TransformControls.js' +import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js' +import { Box3 } from 'three' export default class SectionBox { - constructor( viewer, bbox ) { + constructor( viewer ) { this.viewer = viewer - this.orbiting = false + this.viewer.renderer.localClippingEnabled = true + this.dragging = false this.display = new THREE.Group() - this.viewer.controls.addEventListener( 'wake', () => { this.orbiting = true } ) - this.viewer.controls.addEventListener( 'controlend', () => { this.orbiting = false } ) - - this.box = bbox || this.viewer.sceneManager.getSceneBoundingBox() - const dimensions = new THREE.Vector3().subVectors( this.box.max, this.box.min ) - this.boxGeo = new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z ) - - const matrix = new THREE.Matrix4().setPosition( dimensions.addVectors( this.box.min, this.box.max ).multiplyScalar( 0.5 ) ) - this.boxGeo.applyMatrix4( matrix ) - this.boxMesh = new THREE.Mesh( this.boxGeo, new THREE.MeshBasicMaterial() ) - - this.boxHelper = new THREE.BoxHelper( this.boxMesh, 0x0A66FF ) - - const plane = new THREE.PlaneGeometry( 1, 1 ) - this.hoverPlane = new THREE.Mesh( plane, new THREE.MeshStandardMaterial( { - transparent: true, - side: THREE.DoubleSide, - opacity: 0.02, - color: 0x0A66FF, - metalness: 0.1, - roughness: 0.75 - } ) ) - - this.display.add( this.boxHelper ) - this.display.add( this.hoverPlane ) - this.viewer.scene.add( this.display ) - this.boxMesh.userData.planes = [] - this.boxMesh.userData.indices = [] - this.planes = [] + // box + this.boxGeometry = this._generateSimpleCube( 5, 5, 5 ) + this.material = new THREE.MeshStandardMaterial( { color: 0x00ffff, opacity:0, wireframe: false, side: THREE.DoubleSide } ) + this.cube = new THREE.Mesh( this.boxGeometry, this.material ) + this.cube.visible = false - // Gen box and planes - this._generatePlanes() + this.display.add( this.cube ) - // Box face selection controls - this.selectionHelper = new SelectionHelper( this.viewer, { subset: this.boxMesh, hover: true } ) - let targetFaceIndex = -1 + this.boxHelper = new THREE.BoxHelper( this.cube, 0x0A66FF ) + this.boxHelper.material.opacity = 0.4 + this.display.add( this.boxHelper ) - this.selectionHelper.on( 'hovered', ( obj ) => { - if ( obj.length === 0 && !this.dragging ) { - this.hoverPlane.visible = false + // we're attaching the gizmo mover to this sphere in the box centre + let sphere = new THREE.SphereGeometry( 0.01, 10, 10 ) + this.sphere = new THREE.Mesh( sphere, new THREE.MeshStandardMaterial( { color:0x00ffff } ) ) + this.display.add( this.sphere ) + + // plane + this.plane = new THREE.PlaneGeometry( 1, 1 ) + this.hoverPlane = new THREE.Mesh( this.plane, new THREE.MeshStandardMaterial( { transparent: true, side: THREE.DoubleSide, opacity: 0.1, wireframe: false, color: 0x0A66FF, metalness: 0.1, roughness: 0.75 } ) ) + this.hoverPlane.visible = false + this.display.add( this.hoverPlane ) + + this.dragging = false + this._setupControls() + + this.sidesSimple = { + '256': { verts: [ 1, 2, 5, 6 ], axis:'x' }, + '152': { verts: [ 1, 2, 5, 6 ], axis:'x' }, + '407': { verts: [ 0, 3, 4, 7 ], axis:'x' }, + '703': { verts: [ 0, 3, 4, 7 ], axis:'x' }, + '327': { verts: [ 2, 3, 6, 7 ], axis:'y' }, + '726': { verts: [ 2, 3, 6, 7 ], axis:'y' }, + '450': { verts: [ 0, 1, 4, 5 ], axis:'y' }, + '051': { verts: [ 0, 1, 4, 5 ], axis:'y' }, + '312': { verts: [ 0, 1, 3, 2 ], axis:'z' }, + '013': { verts: [ 0, 1, 3, 2 ], axis:'z' }, + '546': { verts: [ 4, 5, 7, 6 ], axis:'z' }, + '647': { verts: [ 4, 5, 7, 6 ], axis:'z' } + } + + this._generateOrUpdatePlanes() + + this.currentRange = null + this.prevPosition = null + this.attachedToBox = true + + this.selectionHelper = new SelectionHelper( this.viewer, { subset: this.cube, hover: true } ) + this.selectionHelper.on( 'object-clicked', this._clickHandler.bind( this ) ) + this.selectionHelper.on( 'hovered', ( objs ) =>{ + if( !this.attachedToBox ) return + if( objs.length === 0 ) { + this.controls.visible = false + this.viewer.needsRender = true + } + else if( objs.length !== 0 ) { this.controls.visible = true - this.planeControls.detach() - this.viewer.controls.enabled = true - this.viewer.interactions.preventSelection = false this.viewer.needsRender = true - targetFaceIndex = -1 - return - } - if ( this.orbiting || this.dragging ) return - - this.controls.visible = false - this.hoverPlane.visible = true - - let centre = new THREE.Vector3() - for ( let i = 0; i < 4; i++ ) { - centre.add( this.boxGeo.vertices[ obj[0].object.userData.indices[ obj[0].faceIndex ][i] ].clone().applyMatrix4( this.boxMesh.matrixWorld ) ) - } - centre.multiplyScalar( 0.25 ) - this.hoverPlane.position.copy( centre ) - - for ( let i = 0; i < 4; i++ ) { - let vertex = this.boxGeo.vertices[ obj[0].object.userData.indices[ obj[0].faceIndex ][i] ].clone().applyMatrix4( this.boxMesh.matrixWorld ) - this.hoverPlane.geometry.vertices[i].set( vertex.x - centre.x, vertex.y - centre.y , vertex.z - centre.z ) - } - - this.hoverPlane.geometry.verticesNeedUpdate = true - - let normal = obj[0].face.normal - this.planeControls.showX = normal.x !== 0 - this.planeControls.showY = normal.y !== 0 - this.planeControls.showZ = normal.z !== 0 - - this.planeControls.attach( this.hoverPlane ) - - if ( obj[0].faceIndex !== targetFaceIndex ) { - this.viewer.needsRender = true - targetFaceIndex = obj[0].faceIndex } } ) - // Whole box controls - this._globalControlsTarget = new THREE.Mesh( new THREE.SphereGeometry( 0.0001 ), new THREE.MeshBasicMaterial( ) ) - this._globalControlsTarget.position.copy( this.boxGeo.vertices[ 5 ].clone().multiplyScalar( 1.1 ) ) - this.display.add( this._globalControlsTarget ) + document.addEventListener( 'keydown', ( e ) => { + if( e.key === 'Escape' && this.viewer.mouseOverRenderer ) { + this._attachControlsToBox() + } + } ) - this.controls = new TransformControls( this.viewer.camera, this.viewer.renderer.domElement ) - this.controls.setSize( 0.5 ) - this.controls.attach( this._globalControlsTarget ) + this._attachControlsToBox() + + this.viewer.on( 'projection-change', function() { this._setupControls(); this._attachControlsToBox() }.bind( this ) ) + } + + _setupControls() { + this.controls?.dispose() + this.controls?.detach() + this.controls = new TransformControls( this.viewer.cameraHandler.activeCam.camera, this.viewer.renderer.domElement ) + this.controls.setSize( 0.75 ) this.display.add( this.controls ) - - // Section plane controls - this.planeControls = new TransformControls( this.viewer.camera, this.viewer.renderer.domElement, true ) - this.display.add( this.planeControls ) - - this.prevGizmoPos = this._globalControlsTarget.position.clone() - this.controls.addEventListener( 'change', ( ) => { - this.prevGizmoPos.sub( this._globalControlsTarget.position ) - this.boxMesh.translateX( -this.prevGizmoPos.x ) - this.boxMesh.translateY( -this.prevGizmoPos.y ) - this.boxMesh.translateZ( -this.prevGizmoPos.z ) - - this.prevGizmoPos = this._globalControlsTarget.position.clone() - this.setPlanesFromBox( new THREE.Box3().setFromObject( this.boxMesh ) ) - this.boxHelper.update() - this.viewer.needsRender = true - } ) - + this.controls.addEventListener( 'change', this._draggingChangeHandler.bind( this ) ) this.controls.addEventListener( 'dragging-changed', ( event ) => { - this.viewer.controls.enabled = !event.value - this.viewer.interactions.preventSelection = !event.value - if ( !event.value ) - this.viewer.interactions.zoomToObject( this.boxMesh ) - } ) - - let prevPlaneGizmoPos = null - this.planeControls.addEventListener( 'change', ( ) => { - if ( !this.dragging ) return - if ( targetFaceIndex === -1 ) return - if ( prevPlaneGizmoPos === null ) prevPlaneGizmoPos = this.hoverPlane.position.clone() - prevPlaneGizmoPos.sub( this.hoverPlane.position ) - let plane = this.boxMesh.userData.planes[ targetFaceIndex ] - - prevPlaneGizmoPos.negate() - plane.translate( prevPlaneGizmoPos ) - let indices = this.boxMesh.userData.indices[ targetFaceIndex ] - for ( let i = 0; i < 4; i++ ) { - let index = indices[i] - this.boxGeo.vertices[index].add( prevPlaneGizmoPos ) + let val = !!event.value + if( val ) { + this.dragging = val + this.viewer.interactions.preventSelection = val + this.viewer.cameraHandler.enabled = !val + } else { + setTimeout( ()=> { + this.dragging = val + this.viewer.interactions.preventSelection = val + this.viewer.cameraHandler.enabled = !val + }, 100 ) } - this.boxGeo.verticesNeedUpdate = true - this.boxMesh.geometry.computeBoundingBox() - this.boxMesh.geometry.computeBoundingSphere() - - let gizmoPos = this.boxGeo.vertices[ 5 ].clone() - gizmoPos.multiplyScalar( 1.1 ) - gizmoPos.applyMatrix4( this.boxMesh.matrixWorld ) - this._globalControlsTarget.position.copy( gizmoPos ) - this.prevGizmoPos = gizmoPos - - prevPlaneGizmoPos = this.hoverPlane.position.clone() - this.boxHelper.update() - this.viewer.needsRender = true - } ) - - this.planeControls.addEventListener( 'dragging-changed', ( event ) => { - this.viewer.controls.enabled = !event.value - this.viewer.interactions.preventSelection = !event.value - this.dragging = !!event.value - if ( !this.dragging ) { - prevPlaneGizmoPos = null - this.viewer.interactions.zoomToObject( this.boxMesh ) - targetFaceIndex = -1 - - } - this.viewer.needsRender = true } ) + this.viewer.needsRender = true } - _generatePlanes() { - for ( let i = 0; i < this.boxGeo.faces.length; i += 2 ) { - let face = this.boxGeo.faces[i] - let pairFace = this.boxGeo.faces[i+1] - let plane = new THREE.Plane() - // inverting points so plane - plane.setFromCoplanarPoints( this.boxGeo.vertices[face.c], this.boxGeo.vertices[face.b], this.boxGeo.vertices[face.a] ) - // adding it twice for ease of use - this.boxMesh.userData.planes.push( plane ) - this.boxMesh.userData.planes.push( plane ) - - this.boxMesh.userData.indices.push( [ face.a, face.b, face.c, pairFace.b ] ) - this.boxMesh.userData.indices.push( [ face.a, face.b, face.c, pairFace.b ] ) - - this.planes.push( plane ) - } - } - - setPlanesFromBox( box ) { - const dimensions = new THREE.Vector3().subVectors( box.max, box.min ) - let boxGeo = new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z ) - - const matrix = new THREE.Matrix4().setPosition( dimensions.addVectors( box.min, box.max ).multiplyScalar( 0.5 ) ) - boxGeo.applyMatrix4( matrix ) - - for ( let i = 0; i < this.boxGeo.faces.length; i += 2 ) { - let face = boxGeo.faces[i] - let plane = this.boxMesh.userData.planes[i] - plane.setFromCoplanarPoints( boxGeo.vertices[face.c], boxGeo.vertices[face.b], boxGeo.vertices[face.a] ) // invert pts - } - } - - setBox( box ) { - box = box.clone().expandByScalar( 0.5 ) - const dimensions = new THREE.Vector3().subVectors( box.max, box.min ) - let boxGeo = new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z ) - - const matrix = new THREE.Matrix4().setPosition( dimensions.addVectors( box.min, box.max ).multiplyScalar( 0.5 ) ) - boxGeo.applyMatrix4( matrix ) - - for ( let i = 0; i < this.boxGeo.vertices.length; i++ ) { - this.boxGeo.vertices[i].copy( boxGeo.vertices[i] ) - } - - this._globalControlsTarget.position.copy( this.boxGeo.vertices[ 5 ].clone().multiplyScalar( 1.1 ) ) - this.prevGizmoPos = this._globalControlsTarget.position.clone() - this.boxMesh.position.copy( new THREE.Vector3() ) - this.boxMesh.geometry.verticesNeedUpdate = true - this.boxMesh.geometry.computeBoundingBox() - this.boxMesh.geometry.computeBoundingSphere() + _draggingChangeHandler( ) { + this.boxHelper.update() + this._generateOrUpdatePlanes() + + // Dragging a side / plane + if( this.dragging && this.currentRange ) { + if( this.prevPosition === null ) this.prevPosition = this.hoverPlane.position.clone() + this.prevPosition.sub( this.hoverPlane.position ) + this.prevPosition.negate() + let boxArr = this.boxGeometry.attributes.position.array + for( let i = 0; i < this.currentRange.length; i++ ) { + let index = this.currentRange[i] + boxArr[3 * index] += this.prevPosition.x + boxArr[3 * index + 1] += this.prevPosition.y + boxArr[3 * index + 2] += this.prevPosition.z + } + + this.prevPosition = this.hoverPlane.position.clone() + this.boxGeometry.attributes.position.needsUpdate = true + this.boxGeometry.computeVertexNormals() + this.boxGeometry.computeBoundingBox() + this.boxGeometry.computeBoundingSphere() + } + + // Dragging the whole section box + if( this.dragging && !this.currentRange ) { + if( this.prevPosition === null ) this.prevPosition = this.sphere.position.clone() + this.prevPosition.sub( this.sphere.position ) + this.prevPosition.negate() + + for( let i = 0; i < this.boxGeometry.attributes.position.array.length; i += 3 ) { + this.boxGeometry.attributes.position.array[i] += this.prevPosition.x + this.boxGeometry.attributes.position.array[i + 1] += this.prevPosition.y + this.boxGeometry.attributes.position.array[i + 2] += this.prevPosition.z + } + this.boxGeometry.attributes.position.needsUpdate = true + this.boxGeometry.computeVertexNormals() + this.boxGeometry.computeBoundingBox() + this.boxGeometry.computeBoundingSphere() + + this.prevPosition = this.sphere.position.clone() + } + this.viewer.needsRender = true + } + + _clickHandler( args ) { + if( this.viewer.cameraHandler.orbiting || this.dragging ) return + if( args.length === 0 && !this.dragging ) { + this._attachControlsToBox() + this.boxHelper.material.opacity = 0.5 + this.attachedToBox = true + return + } + this.attachedToBox = false + this.boxHelper.material.opacity = 0.3 + this.hoverPlane.visible = true + let side = this.sidesSimple[`${args[0].face.a}${args[0].face.b}${args[0].face.c}`] + this.controls.showX = side.axis === 'x' + this.controls.showY = side.axis === 'y' + this.controls.showZ = side.axis === 'z' + + this.currentRange = side.verts + + let boxArr = this.boxGeometry.attributes.position + let index = 0 + let planeArr = this.plane.attributes.position.array + let centre = new THREE.Vector3() + + let tempArr = [] + for( let i = 0; i < planeArr.length; i++ ) { + if( i % 3 === 0 ) { + tempArr.push( boxArr.getX( this.currentRange[index] ) ) + } + else if( i % 3 === 1 ) { + tempArr.push( boxArr.getY( this.currentRange[index] ) ) + } + else if( i % 3 === 2 ) { + tempArr.push( boxArr.getZ( this.currentRange[index] ) ) + centre.add( new THREE.Vector3( tempArr[i - 2], tempArr[i - 1], tempArr[i] ) ) + index++ + } + } + + centre.multiplyScalar( 0.25 ) + this.hoverPlane.position.copy( centre.applyMatrix4( this.cube.matrixWorld ) ) + this.prevPosition = this.hoverPlane.position.clone() + index = 0 + for( let i = 0; i < planeArr.length; i++ ) { + if( i % 3 === 0 ) { + planeArr[i] = boxArr.getX( this.currentRange[index] ) - centre.x + } + else if( i % 3 === 1 ) { + planeArr[i] = boxArr.getY( this.currentRange[index] ) - centre.y + } + else if( i % 3 === 2 ) { + planeArr[i] = boxArr.getZ( this.currentRange[index] ) - centre.z + index++ + } + } + + this.plane.applyMatrix4( this.cube.matrixWorld ) + this.plane.attributes.position.needsUpdate = true + this.plane.computeBoundingSphere() + this.plane.computeBoundingBox() + this.controls.detach() + this.controls.attach( this.hoverPlane ) + this.controls.updateMatrixWorld() + } + + _generateSimpleCube( width = 0.5, depth = 0.5, height = 0.5 ) { + const vertices = [ + [ -1 * width, -1 * depth, -1 * height ], + [ 1 * width, -1 * depth, -1 * height ], + [ 1 * width, 1 * depth, -1 * height ], + [ -1 * width, 1 * depth, -1 * height ], + [ -1 * width, -1 * depth, 1 * height ], + [ 1 * width, -1 * depth, 1 * height ], + [ 1 * width, 1 * depth, 1 * height ], + [ -1 * width, 1 * depth, 1 * height ] + ] + + const indexes = [ + 0, 1, 3, 3, 1, 2, + 1, 5, 2, 2, 5, 6, + 5, 4, 6, 6, 4, 7, + 4, 0, 7, 7, 0, 3, + 3, 2, 7, 7, 2, 6, + 4, 5, 0, 0, 5, 1 + ] + + let positions = [] + for( let vert of vertices ) { + positions.push( ...vert ) + } + + let g = new THREE.BufferGeometry() + g.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) ) + g.setIndex( indexes ) + g.computeVertexNormals() + return g + } + + _generateOrUpdatePlanes() { + this.planes = this.planes || [ new THREE.Plane(), new THREE.Plane(), new THREE.Plane(), new THREE.Plane(), new THREE.Plane(), new THREE.Plane() ] + + let index = 0 + let boxArr = this.boxGeometry.attributes.position + const indexes = [ + 0, 1, 3, 3, 1, 2, + 1, 5, 2, 2, 5, 6, + 5, 4, 6, 6, 4, 7, + 4, 0, 7, 7, 0, 3, + 3, 2, 7, 7, 2, 6, + 4, 5, 0, 0, 5, 1 + ] + + for( let i = 0; i < indexes.length; i += 6 ) { + let a = new THREE.Vector3( boxArr.getX( indexes[i] ), boxArr.getY( indexes[i] ), boxArr.getZ( indexes[i] ) ) + let b = new THREE.Vector3( boxArr.getX( indexes[i + 1] ), boxArr.getY( indexes[i + 1] ), boxArr.getZ( indexes[i + 1] ) ) + let c = new THREE.Vector3( boxArr.getX( indexes[i + 2] ), boxArr.getY( indexes[i + 2] ), boxArr.getZ( indexes[i + 2] ) ) + let plane = this.planes[index] + plane.setFromCoplanarPoints( a, b, c ) + index++ + } + } + + _attachControlsToBox() { + this.controls.detach() + + let centre = new THREE.Vector3() + let boxArr = this.boxGeometry.attributes.position.array + for( let i = 0; i < boxArr.length; i += 3 ) { + centre.add( new THREE.Vector3( boxArr[i], boxArr[i + 1], boxArr[i + 2] ) ) + } + centre.multiplyScalar( 1 / 8 ) + this.sphere.position.copy( centre ) + + this.cube.geometry.computeBoundingSphere() + this.cube.geometry.computeBoundingBox() + this.controls.attach( this.sphere ) + this.currentRange = null + this.prevPosition = null + this.hoverPlane.visible = false + this.controls.showX = true + this.controls.showY = true + this.controls.showZ = true + } + + setBox( targetBox, offset = 0.05 ) { + let box + + if( targetBox ) box = targetBox + else { + if( this.viewer.interactions.selectedObjects.children.length !== 0 ) { + box = new THREE.Box3( ).setFromObject( this.viewer.interactions.selectedObjects ) + } else if( this.viewer.sceneManager.sceneObjects.allObjects.children.length !== 0 ) { + box = new THREE.Box3( ).setFromObject( this.viewer.sceneManager.sceneObjects.allObjects ) + } else { + box = new Box3( new THREE.Vector3( -1, -1, -1 ), new THREE.Vector3( 1, 1, 1 ) ) + } + } + + if( box.min.x === Infinity ) { + box = new Box3( new THREE.Vector3( -1, -1, -1 ), new THREE.Vector3( 1, 1, 1 ) ) + } + + const x1 = box.min.x - ( box.max.x - box.min.x ) * offset + const y1 = box.min.y - ( box.max.y - box.min.y ) * offset + const z1 = box.min.z - ( box.max.z - box.min.z ) * offset + const x2 = box.max.x + ( box.max.x - box.min.x ) * offset + const y2 = box.max.y + ( box.max.y - box.min.y ) * offset + const z2 = box.max.z + ( box.max.z - box.min.z ) * offset + + const newVertices = [ + x1, y1, z1, + x2, y1, z1, + x2, y2, z1, + x1, y2, z1, + x1, y1, z2, + x2, y1, z2, + x2, y2, z2, + x1, y2, z2 + ] + + let boxVerts = this.boxGeometry.attributes.position.array + for( let i = 0; i < newVertices.length; i++ ) { + boxVerts[i] = newVertices[i] + } + + this.boxGeometry.attributes.position.needsUpdate = true + this.boxGeometry.computeVertexNormals() + this.boxGeometry.computeBoundingBox() + this.boxGeometry.computeBoundingSphere() + this._generateOrUpdatePlanes() + this._attachControlsToBox() this.boxHelper.update() - this.setPlanesFromBox( box ) this.viewer.needsRender = true } toggle() { - if ( this.display.visible ) { - this.viewer.renderer.localClippingEnabled = false - this.display.visible = false - this.viewer.emit( 'section-box', false ) - } else { - this.viewer.renderer.localClippingEnabled = true - this.display.visible = true - this.viewer.emit( 'section-box', true ) - } + this.setBox() + this.display.visible = !this.display.visible + this.viewer.renderer.localClippingEnabled = this.display.visible + this.viewer.needsRender = true } - dispose() { - this.selectionHelper.dispose() - this.controls.dispose() - this.planeControls.dispose() - this.display.clear() + off() { + this.display.visible = false + this.viewer.renderer.localClippingEnabled = false + this.viewer.needsRender = true } + + on() { + this.display.visible = true + this.viewer.renderer.localClippingEnabled = true + this.viewer.needsRender = true + } + } diff --git a/packages/viewer/src/modules/SectionBoxNew.js b/packages/viewer/src/modules/SectionBoxNew.js deleted file mode 100644 index caad8aac6..000000000 --- a/packages/viewer/src/modules/SectionBoxNew.js +++ /dev/null @@ -1,353 +0,0 @@ -import * as THREE from 'three' -import SelectionHelper from './SelectionHelper' -import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js' -import { Box3 } from 'three' - -export default class SectionBox { - - constructor( viewer, bbox ) { - this.viewer = viewer - - this.viewer.renderer.localClippingEnabled = true - - this.dragging = false - this.display = new THREE.Group() - this.viewer.scene.add( this.display ) - - // box - this.boxGeometry = this._generateSimpleCube( 5, 5, 5 ) - this.material = new THREE.MeshStandardMaterial( { color: 0x00ffff, opacity:0, wireframe: false, side: THREE.DoubleSide } ) - this.cube = new THREE.Mesh( this.boxGeometry, this.material ) - this.cube.visible = false - - this.display.add( this.cube ) - - this.boxHelper = new THREE.BoxHelper( this.cube, 0x0A66FF ) - this.boxHelper.material.opacity = 0.4 - this.display.add( this.boxHelper ) - - // we're attaching the gizmo mover to this sphere in the box centre - let sphere = new THREE.SphereGeometry( 0.01, 10, 10 ) - this.sphere = new THREE.Mesh( sphere, new THREE.MeshStandardMaterial( { color:0x00ffff } ) ) - this.display.add( this.sphere ) - - // plane - this.plane = new THREE.PlaneGeometry( 1, 1 ) - this.hoverPlane = new THREE.Mesh( this.plane, new THREE.MeshStandardMaterial( { transparent: true, side: THREE.DoubleSide, opacity: 0.1, wireframe: false, color: 0x0A66FF, metalness: 0.1, roughness: 0.75 } ) ) - this.hoverPlane.visible = false - this.display.add( this.hoverPlane ) - - this.dragging = false - this._setupControls() - - this.sidesSimple = { - '256': { verts: [ 1, 2, 5, 6 ], axis:'x' }, - '152': { verts: [ 1, 2, 5, 6 ], axis:'x' }, - '407': { verts: [ 0, 3, 4, 7 ], axis:'x' }, - '703': { verts: [ 0, 3, 4, 7 ], axis:'x' }, - '327': { verts: [ 2, 3, 6, 7 ], axis:'y' }, - '726': { verts: [ 2, 3, 6, 7 ], axis:'y' }, - '450': { verts: [ 0, 1, 4, 5 ], axis:'y' }, - '051': { verts: [ 0, 1, 4, 5 ], axis:'y' }, - '312': { verts: [ 0, 1, 3, 2 ], axis:'z' }, - '013': { verts: [ 0, 1, 3, 2 ], axis:'z' }, - '546': { verts: [ 4, 5, 7, 6 ], axis:'z' }, - '647': { verts: [ 4, 5, 7, 6 ], axis:'z' } - } - - this._generateOrUpdatePlanes() - - this.currentRange = null - this.prevPosition = null - - this.selectionHelper = new SelectionHelper( this.viewer, { subset: this.cube, hover: true } ) - this.selectionHelper.on( 'object-clicked', this._clickHandler.bind( this ) ) - this._attachControlsToBox() - - this.viewer.on('projection-change', function() { this._setupControls(); this._attachControlsToBox(); }.bind(this) ) - } - - _setupControls() { - this.controls?.dispose() - this.controls?.detach() - this.controls = new TransformControls( this.viewer.cameraHandler.activeCam.camera, this.viewer.renderer.domElement ) - this.controls.setSize( 0.75 ) - this.display.add( this.controls ) - this.controls.addEventListener( 'change', this._draggingChangeHandler.bind( this ) ) - this.controls.addEventListener( 'dragging-changed', ( event ) => { - let val = !!event.value - if( val ) { - this.dragging = val - this.viewer.interactions.preventSelection = val - this.viewer.cameraHandler.enabled = !val - } else { - setTimeout( ()=> { - this.dragging = val - this.viewer.interactions.preventSelection = val - this.viewer.cameraHandler.enabled = !val - }, 100 ) - - } - } ) - this.viewer.needsRender = true - } - - _draggingChangeHandler( ) { - this.boxHelper.update() - this._generateOrUpdatePlanes() - - // Dragging a side / plane - if( this.dragging && this.currentRange ) { - if( this.prevPosition === null ) this.prevPosition = this.hoverPlane.position.clone() - this.prevPosition.sub( this.hoverPlane.position ) - this.prevPosition.negate() - let boxArr = this.boxGeometry.attributes.position.array - for( let i = 0; i < this.currentRange.length; i++ ) { - let index = this.currentRange[i] - boxArr[3 * index] += this.prevPosition.x - boxArr[3 * index + 1] += this.prevPosition.y - boxArr[3 * index + 2] += this.prevPosition.z - } - - this.prevPosition = this.hoverPlane.position.clone() - this.boxGeometry.attributes.position.needsUpdate = true - this.boxGeometry.computeVertexNormals() - this.boxGeometry.computeBoundingBox() - this.boxGeometry.computeBoundingSphere() - } - - // Dragging the whole section box - if( this.dragging && !this.currentRange ) { - if( this.prevPosition === null ) this.prevPosition = this.sphere.position.clone() - this.prevPosition.sub( this.sphere.position ) - this.prevPosition.negate() - - for( let i = 0; i < this.boxGeometry.attributes.position.array.length; i += 3 ) { - this.boxGeometry.attributes.position.array[i] += this.prevPosition.x - this.boxGeometry.attributes.position.array[i + 1] += this.prevPosition.y - this.boxGeometry.attributes.position.array[i + 2] += this.prevPosition.z - } - this.boxGeometry.attributes.position.needsUpdate = true - this.boxGeometry.computeVertexNormals() - this.boxGeometry.computeBoundingBox() - this.boxGeometry.computeBoundingSphere() - - this.prevPosition = this.sphere.position.clone() - } - this.viewer.needsRender = true - } - - _clickHandler( args ) { - if( this.viewer.cameraHandler.orbiting || this.dragging ) return - if( args.length === 0 && !this.dragging ) { - this._attachControlsToBox() - this.boxHelper.material.opacity = 0.5 - return - } - - this.boxHelper.material.opacity = 0.3 - this.hoverPlane.visible = true - let side = this.sidesSimple[`${args[0].face.a}${args[0].face.b}${args[0].face.c}`] - this.controls.showX = side.axis === 'x' - this.controls.showY = side.axis === 'y' - this.controls.showZ = side.axis === 'z' - - this.currentRange = side.verts - - let boxArr = this.boxGeometry.attributes.position - let index = 0 - let planeArr = this.plane.attributes.position.array - let centre = new THREE.Vector3() - - let tempArr = [] - for( let i = 0; i < planeArr.length; i++ ) { - if( i % 3 === 0 ) { - tempArr.push( boxArr.getX( this.currentRange[index] ) ) - } - else if( i % 3 === 1 ) { - tempArr.push( boxArr.getY( this.currentRange[index] ) ) - } - else if( i % 3 === 2 ) { - tempArr.push( boxArr.getZ( this.currentRange[index] ) ) - centre.add( new THREE.Vector3( tempArr[i - 2], tempArr[i - 1], tempArr[i] ) ) - index++ - } - } - - centre.multiplyScalar( 0.25 ) - this.hoverPlane.position.copy( centre.applyMatrix4( this.cube.matrixWorld ) ) - this.prevPosition = this.hoverPlane.position.clone() - index = 0 - for( let i = 0; i < planeArr.length; i++ ) { - if( i % 3 === 0 ) { - planeArr[i] = boxArr.getX( this.currentRange[index] ) - centre.x - } - else if( i % 3 === 1 ) { - planeArr[i] = boxArr.getY( this.currentRange[index] ) - centre.y - } - else if( i % 3 === 2 ) { - planeArr[i] = boxArr.getZ( this.currentRange[index] ) - centre.z - index++ - } - } - - this.plane.applyMatrix4( this.cube.matrixWorld ) - this.plane.attributes.position.needsUpdate = true - this.plane.computeBoundingSphere() - this.plane.computeBoundingBox() - this.controls.detach() - this.controls.attach( this.hoverPlane ) - this.controls.updateMatrixWorld() - } - - _generateSimpleCube( width = 0.5, depth = 0.5, height = 0.5 ) { - const vertices = [ - [ -1 * width, -1 * depth, -1 * height ], - [ 1 * width, -1 * depth, -1 * height ], - [ 1 * width, 1 * depth, -1 * height ], - [ -1 * width, 1 * depth, -1 * height ], - [ -1 * width, -1 * depth, 1 * height ], - [ 1 * width, -1 * depth, 1 * height ], - [ 1 * width, 1 * depth, 1 * height ], - [ -1 * width, 1 * depth, 1 * height ] - ] - - const indexes = [ - 0, 1, 3, 3, 1, 2, - 1, 5, 2, 2, 5, 6, - 5, 4, 6, 6, 4, 7, - 4, 0, 7, 7, 0, 3, - 3, 2, 7, 7, 2, 6, - 4, 5, 0, 0, 5, 1 - ] - - let positions = [] - for( let vert of vertices ) { - positions.push( ...vert ) - } - - let g = new THREE.BufferGeometry() - g.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) ) - g.setIndex( indexes ) - g.computeVertexNormals() - return g - } - - _generateOrUpdatePlanes() { - this.planes = this.planes || [ new THREE.Plane(), new THREE.Plane(), new THREE.Plane(), new THREE.Plane(), new THREE.Plane(), new THREE.Plane() ] - - let index = 0 - let boxArr = this.boxGeometry.attributes.position - const indexes = [ - 0, 1, 3, 3, 1, 2, - 1, 5, 2, 2, 5, 6, - 5, 4, 6, 6, 4, 7, - 4, 0, 7, 7, 0, 3, - 3, 2, 7, 7, 2, 6, - 4, 5, 0, 0, 5, 1 - ] - - for( let i = 0; i < indexes.length; i += 6 ) { - let a = new THREE.Vector3( boxArr.getX( indexes[i] ), boxArr.getY( indexes[i] ), boxArr.getZ( indexes[i] ) ) - let b = new THREE.Vector3( boxArr.getX( indexes[i + 1] ), boxArr.getY( indexes[i + 1] ), boxArr.getZ( indexes[i + 1] ) ) - let c = new THREE.Vector3( boxArr.getX( indexes[i + 2] ), boxArr.getY( indexes[i + 2] ), boxArr.getZ( indexes[i + 2] ) ) - let plane = this.planes[index] - plane.setFromCoplanarPoints( a, b, c ) - index++ - } - } - - _attachControlsToBox() { - this.controls.detach() - - let centre = new THREE.Vector3() - let boxArr = this.boxGeometry.attributes.position.array - for( let i = 0; i < boxArr.length; i += 3 ) { - centre.add( new THREE.Vector3( boxArr[i], boxArr[i + 1], boxArr[i + 2] ) ) - } - centre.multiplyScalar( 1 / 8 ) - this.sphere.position.copy( centre ) - - this.cube.geometry.computeBoundingSphere() - this.cube.geometry.computeBoundingBox() - this.controls.attach( this.sphere ) - this.currentRange = null - this.prevPosition = null - this.hoverPlane.visible = false - this.controls.showX = true - this.controls.showY = true - this.controls.showZ = true - } - - setBox( ) { - let box = null - console.log(this.viewer.interactions.selectedObjects.children.length ) - console.log(this.viewer.sceneManager.sceneObjects.allObjects.children.length ) - if( this.viewer.interactions.selectedObjects.children.length !== 0 ) { - box = new THREE.Box3( ).setFromObject(this.viewer.interactions.selectedObjects) - } else if( this.viewer.sceneManager.sceneObjects.allObjects.children.length !== 0 ){ - box = new THREE.Box3( ).setFromObject(this.viewer.sceneManager.sceneObjects.allObjects) - } else { - box = new Box3( new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1)) - } - if(box.min.x === Infinity) { - box = new Box3( new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1)) - } - const dist = box.min.distanceTo(box.max) - - const x1 = box.min.x - (box.max.x-box.min.x) * 0.05 // - dist * 0.05 - const y1 = box.min.y - (box.max.y-box.min.y) * 0.05 // - dist * 0.05 - const z1 = box.min.z - (box.max.z-box.min.z) * 0.05 // - dist * 0.05 - const x2 = box.max.x + (box.max.x-box.min.x) * 0.05 // + dist * 0.05 - const y2 = box.max.y + (box.max.y-box.min.y) * 0.05 // + dist * 0.05 - const z2 = box.max.z + (box.max.z-box.min.z) * 0.05 // + dist * 0.05 - - const newVertices = [ - x1, y1, z1, - x2, y1, z1, - x2, y2, z1, - x1, y2, z1, - x1, y1, z2, - x2, y1, z2, - x2, y2, z2, - x1, y2, z2, - ] - - let boxVerts = this.boxGeometry.attributes.position.array - for(let i = 0; i < newVertices.length; i++) { - boxVerts[i] = newVertices[i] - } - - this.boxGeometry.attributes.position.needsUpdate = true - this.boxGeometry.computeVertexNormals() - this.boxGeometry.computeBoundingBox() - this.boxGeometry.computeBoundingSphere() - this._generateOrUpdatePlanes() - this._attachControlsToBox() - this.boxHelper.update() - this.viewer.needsRender = true - } - - toggle() { - this.setBox() - this.display.visible = !this.display.visible - this.viewer.renderer.localClippingEnabled = this.display.visible - this.viewer.needsRender = true - } - - off() { - this.display.visible = false - this.viewer.renderer.localClippingEnabled = false - this.viewer.needsRender = true - } - - on() { - this.display.visible = true - this.viewer.renderer.localClippingEnabled = true - this.viewer.needsRender = true - } - - dispose() { - - } -} diff --git a/packages/viewer/src/modules/Viewer.js b/packages/viewer/src/modules/Viewer.js index e1cef933b..795c5527b 100644 --- a/packages/viewer/src/modules/Viewer.js +++ b/packages/viewer/src/modules/Viewer.js @@ -1,18 +1,14 @@ import * as THREE from 'three' -import CameraControls from 'camera-controls' -import { HOLD_EVENT_TYPE, ElementHold, KeyboardKeyHold } from 'hold-event' -import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' -import { SSAOPass } from 'three/examples/jsm/postprocessing/SSAOPass.js' import Stats from 'three/examples/jsm/libs/stats.module.js' import ObjectManager from './SceneObjectManager' import ViewerObjectLoader from './ViewerObjectLoader' import EventEmitter from './EventEmitter' import InteractionHandler from './InteractionHandler' -import CameraHandler from './context/cameras' +import CameraHandler from './context/CameraHanlder' -import SectionBox from './SectionBoxNew' +import SectionBox from './SectionBox' export default class Viewer extends EventEmitter { @@ -64,6 +60,7 @@ export default class Viewer extends EventEmitter { this.sceneLights() this.animate() this.onWindowResize() + this.interactions.zoomExtents() this.needsRender = true } diff --git a/packages/viewer/src/modules/context/cameras.js b/packages/viewer/src/modules/context/CameraHanlder.js similarity index 66% rename from packages/viewer/src/modules/context/cameras.js rename to packages/viewer/src/modules/context/CameraHanlder.js index 06bcc1a65..18790422e 100644 --- a/packages/viewer/src/modules/context/cameras.js +++ b/packages/viewer/src/modules/context/CameraHanlder.js @@ -1,9 +1,9 @@ import * as THREE from 'three' import CameraControls from 'camera-controls' -import { HOLD_EVENT_TYPE, ElementHold, KeyboardKeyHold } from 'hold-event' +import { KeyboardKeyHold } from 'hold-event' export default class CameraHandler { - constructor(viewer) { + constructor( viewer ) { this.viewer = viewer this.camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight ) @@ -13,7 +13,7 @@ export default class CameraHandler { let aspect = this.viewer.container.offsetWidth / this.viewer.container.offsetHeight let fustrumSize = 50 - this.orthoCamera = new THREE.OrthographicCamera( (-fustrumSize * aspect ) / 2, (fustrumSize * aspect ) / 2, fustrumSize / 2, -fustrumSize/ 2, 0.001, 1000) + this.orthoCamera = new THREE.OrthographicCamera( ( -fustrumSize * aspect ) / 2, ( fustrumSize * aspect ) / 2, fustrumSize / 2, -fustrumSize / 2, 0.001, 10000 ) this.orthoCamera.up.set( 0, 0, 1 ) this.orthoCamera.position.set( 100, 100, 100 ) this.orthoCamera.updateProjectionMatrix() @@ -39,20 +39,10 @@ export default class CameraHandler { ] this.orbiting = false - this.controls.addEventListener('wake', () => { - // console.log( 'orbiting') - this.orbiting = true - } ) - - this.controls.addEventListener('controlend', () => { - // console.log( 'controlend') - // this.orbiting = false - } ) - - this.controls.addEventListener('rest', () => { - // console.log( 'rest') - setTimeout( () => { this.orbiting = false }, 400 ) - } ) + this.controls.addEventListener( 'wake', () => { this.orbiting = true } ) + // note: moved to new controls event called "rest" + this.controls.addEventListener( 'controlend', () => { } ) + this.controls.addEventListener( 'rest', () => { setTimeout( () => { this.orbiting = false }, 400 ) } ) window.addEventListener( 'resize', this.onWindowResize.bind( this ), false ) this.onWindowResize() @@ -64,17 +54,17 @@ export default class CameraHandler { set activeCam( val ) { if( val === 'perspective' ) - return this.setPerspectiveCameraOn() + this.setPerspectiveCameraOn() if( val === 'ortho' ) - return this.setOrthoCameraOn() + this.setOrthoCameraOn() } set enabled( val ) { this.controls.enabled = val } - setPerspectiveCameraOn(){ - if(this.cameras[0].active) return + setPerspectiveCameraOn() { + if( this.cameras[0].active ) return this.cameras[0].active = true this.cameras[1].active = false @@ -82,8 +72,8 @@ export default class CameraHandler { this.viewer.needsRender = true } - setOrthoCameraOn(){ - if(this.cameras[1].active) return + setOrthoCameraOn() { + if( this.cameras[1].active ) return this.cameras[0].active = false this.cameras[1].active = true @@ -91,8 +81,8 @@ export default class CameraHandler { this.viewer.needsRender = true } - toggleCameras(){ - if(this.cameras[0].active) this.setOrthoCameraOn() + toggleCameras() { + if( this.cameras[0].active ) this.setOrthoCameraOn() else this.setPerspectiveCameraOn() } @@ -101,15 +91,15 @@ export default class CameraHandler { this.controls.mouseButtons.wheel = CameraControls.ACTION.ZOOM const lineOfSight = new THREE.Vector3() - this.camera.getWorldDirection(lineOfSight) + this.camera.getWorldDirection( lineOfSight ) const target = new THREE.Vector3() - this.controls.getTarget(target) - const distance = target.clone().sub(this.camera.position) - const depth = distance.dot(lineOfSight) + this.controls.getTarget( target ) + const distance = target.clone().sub( this.camera.position ) + const depth = distance.dot( lineOfSight ) const dims = { x: this.viewer.container.offsetWidth, y: this.viewer.container.offsetHeight } const aspect = dims.x / dims.y const fov = this.camera.fov - const height = depth * 2 * Math.atan((fov * (Math.PI / 180)) / 2) + const height = depth * 2 * Math.atan( ( fov * ( Math.PI / 180 ) ) / 2 ) const width = height * aspect this.orthoCamera.zoom = 1 @@ -119,21 +109,21 @@ export default class CameraHandler { this.orthoCamera.bottom = height / -2 this.orthoCamera.far = this.camera.far this.orthoCamera.updateProjectionMatrix() - this.orthoCamera.position.copy(this.camera.position) - this.orthoCamera.quaternion.copy(this.camera.quaternion) + this.orthoCamera.position.copy( this.camera.position ) + this.orthoCamera.quaternion.copy( this.camera.quaternion ) this.controls.camera = this.orthoCamera - this.viewer.emit('projection-change', 'ortho' ) + this.viewer.emit( 'projection-change', 'ortho' ) } setupPerspectiveCamera() { this.controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY - this.camera.position.copy(this.orthoCamera.position) - this.camera.quaternion.copy(this.orthoCamera.quaternion) + this.camera.position.copy( this.orthoCamera.position ) + this.camera.quaternion.copy( this.orthoCamera.quaternion ) this.camera.updateProjectionMatrix() this.controls.distance = this.previousDistance this.controls.camera = this.camera - this.controls.zoomTo(1) - this.viewer.emit('projection-change', 'perspective' ) + this.controls.zoomTo( 1 ) + this.viewer.emit( 'projection-change', 'perspective' ) } setupWASDControls() { @@ -143,10 +133,10 @@ export default class CameraHandler { const aKey = new KeyboardKeyHold( KEYCODE.A, 16.666 ) const sKey = new KeyboardKeyHold( KEYCODE.S, 16.666 ) const dKey = new KeyboardKeyHold( KEYCODE.D, 16.666 ) - aKey.addEventListener( 'holding', function( event ) { if( this.viewer.mouseOverRenderer === false ) return; this.controls.truck( -0.01 * event.deltaTime, 0, false ); return; }.bind( this ) ) - dKey.addEventListener( 'holding', function( event ) { if( this.viewer.mouseOverRenderer === false ) return; this.controls.truck( 0.01 * event.deltaTime, 0, false ); return;}.bind( this ) ) - wKey.addEventListener( 'holding', function( event ) { if( this.viewer.mouseOverRenderer === false ) return; this.controls.forward( 0.01 * event.deltaTime, false ); return; }.bind( this ) ) - sKey.addEventListener( 'holding', function( event ) { if( this.viewer.mouseOverRenderer === false ) return; this.controls.forward( -0.01 * event.deltaTime, false ); return; }.bind( this ) ) + aKey.addEventListener( 'holding', function( event ) { if( this.viewer.mouseOverRenderer === false ) return; this.controls.truck( -0.01 * event.deltaTime, 0, false ); return }.bind( this ) ) + dKey.addEventListener( 'holding', function( event ) { if( this.viewer.mouseOverRenderer === false ) return; this.controls.truck( 0.01 * event.deltaTime, 0, false ); return}.bind( this ) ) + wKey.addEventListener( 'holding', function( event ) { if( this.viewer.mouseOverRenderer === false ) return; this.controls.forward( 0.01 * event.deltaTime, false ); return }.bind( this ) ) + sKey.addEventListener( 'holding', function( event ) { if( this.viewer.mouseOverRenderer === false ) return; this.controls.forward( -0.01 * event.deltaTime, false ); return }.bind( this ) ) } onWindowResize() { @@ -155,10 +145,10 @@ export default class CameraHandler { let aspect = this.viewer.container.offsetWidth / this.viewer.container.offsetHeight let fustrumSize = 50 - this.orthoCamera.left = (-fustrumSize * aspect ) / 2 - this.orthoCamera.right = (fustrumSize * aspect ) / 2 + this.orthoCamera.left = ( -fustrumSize * aspect ) / 2 + this.orthoCamera.right = ( fustrumSize * aspect ) / 2 this.orthoCamera.top = fustrumSize / 2 - this.orthoCamera.bottom = -fustrumSize/2 + this.orthoCamera.bottom = -fustrumSize / 2 this.orthoCamera.updateProjectionMatrix() } } \ No newline at end of file diff --git a/packages/viewer/src/modules/external/TransformControls.js b/packages/viewer/src/modules/external/TransformControls.js deleted file mode 100644 index 2955e291f..000000000 --- a/packages/viewer/src/modules/external/TransformControls.js +++ /dev/null @@ -1,1709 +0,0 @@ -/* eslint-disable */ -// src: https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/TransformControls.js -// Note: customisations: -// - cosmetic blues for materials -// - added "hover" event -// - added "translationDotsOnly" prop for box face manipulation - -import { - BoxGeometry, - BufferGeometry, - Color, - CylinderGeometry, - DoubleSide, - Euler, - Float32BufferAttribute, - Line, - LineBasicMaterial, - Matrix4, - Mesh, - MeshBasicMaterial, - Object3D, - OctahedronGeometry, - PlaneGeometry, - Quaternion, - Raycaster, - SphereGeometry, - TorusGeometry, - Vector3 -} from 'three'; - -var TransformControls = function ( camera, domElement, translationDotsOnly ) { - - if ( domElement === undefined ) { - - console.warn( 'THREE.TransformControls: The second parameter "domElement" is now mandatory.' ); - domElement = document; - - } - - Object3D.call( this ); - - this.visible = false; - this.domElement = domElement; - - var _gizmo = new TransformControlsGizmo( translationDotsOnly ); - this.add( _gizmo ); - - var _plane = new TransformControlsPlane(); - this.add( _plane ); - - var scope = this; - - // Define properties with getters/setter - // Setting the defined property will automatically trigger change event - // Defined properties are passed down to gizmo and plane - - defineProperty( 'camera', camera ); - defineProperty( 'object', undefined ); - defineProperty( 'enabled', true ); - defineProperty( 'axis', null ); - defineProperty( 'mode', 'translate' ); - defineProperty( 'translationSnap', null ); - defineProperty( 'rotationSnap', null ); - defineProperty( 'scaleSnap', null ); - defineProperty( 'space', 'world' ); - defineProperty( 'size', 1 ); - defineProperty( 'dragging', false ); - defineProperty( 'showX', true ); - defineProperty( 'showY', true ); - defineProperty( 'showZ', true ); - - var changeEvent = { type: 'change' }; - var mouseDownEvent = { type: 'mouseDown' }; - var mouseUpEvent = { type: 'mouseUp', mode: scope.mode }; - var objectChangeEvent = { type: 'objectChange' }; - - // Reusable utility variables - - var raycaster = new Raycaster(); - - function intersectObjectWithRay( object, raycaster, includeInvisible ) { - - var allIntersections = raycaster.intersectObject( object, true ); - - for ( var i = 0; i < allIntersections.length; i ++ ) { - - if ( allIntersections[ i ].object.visible || includeInvisible ) { - - return allIntersections[ i ]; - - } - - } - - return false; - - } - - var _tempVector = new Vector3(); - var _tempVector2 = new Vector3(); - var _tempQuaternion = new Quaternion(); - var _unit = { - X: new Vector3( 1, 0, 0 ), - Y: new Vector3( 0, 1, 0 ), - Z: new Vector3( 0, 0, 1 ) - }; - - var pointStart = new Vector3(); - var pointEnd = new Vector3(); - var offset = new Vector3(); - var rotationAxis = new Vector3(); - var startNorm = new Vector3(); - var endNorm = new Vector3(); - var rotationAngle = 0; - - var cameraPosition = new Vector3(); - var cameraQuaternion = new Quaternion(); - var cameraScale = new Vector3(); - - var parentPosition = new Vector3(); - var parentQuaternion = new Quaternion(); - var parentQuaternionInv = new Quaternion(); - var parentScale = new Vector3(); - - var worldPositionStart = new Vector3(); - var worldQuaternionStart = new Quaternion(); - var worldScaleStart = new Vector3(); - - var worldPosition = new Vector3(); - var worldQuaternion = new Quaternion(); - var worldQuaternionInv = new Quaternion(); - var worldScale = new Vector3(); - - var eye = new Vector3(); - - var positionStart = new Vector3(); - var quaternionStart = new Quaternion(); - var scaleStart = new Vector3(); - - // TODO: remove properties unused in plane and gizmo - - defineProperty( 'worldPosition', worldPosition ); - defineProperty( 'worldPositionStart', worldPositionStart ); - defineProperty( 'worldQuaternion', worldQuaternion ); - defineProperty( 'worldQuaternionStart', worldQuaternionStart ); - defineProperty( 'cameraPosition', cameraPosition ); - defineProperty( 'cameraQuaternion', cameraQuaternion ); - defineProperty( 'pointStart', pointStart ); - defineProperty( 'pointEnd', pointEnd ); - defineProperty( 'rotationAxis', rotationAxis ); - defineProperty( 'rotationAngle', rotationAngle ); - defineProperty( 'eye', eye ); - - { - - domElement.addEventListener( 'pointerdown', onPointerDown ); - domElement.addEventListener( 'pointermove', onPointerHover ); - scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp ); - - } - - this.dispose = function () { - - domElement.removeEventListener( 'pointerdown', onPointerDown ); - domElement.removeEventListener( 'pointermove', onPointerHover ); - scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove ); - scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp ); - - this.traverse( function ( child ) { - - if ( child.geometry ) child.geometry.dispose(); - if ( child.material ) child.material.dispose(); - - } ); - - }; - - // Set current object - this.attach = function ( object ) { - - this.object = object; - this.visible = true; - - return this; - - }; - - // Detach from object - this.detach = function () { - - this.object = undefined; - this.visible = false; - this.axis = null; - - return this; - - }; - - // Defined getter, setter and store for a property - function defineProperty( propName, defaultValue ) { - - var propValue = defaultValue; - - Object.defineProperty( scope, propName, { - - get: function () { - - return propValue !== undefined ? propValue : defaultValue; - - }, - - set: function ( value ) { - - if ( propValue !== value ) { - - propValue = value; - _plane[ propName ] = value; - _gizmo[ propName ] = value; - - scope.dispatchEvent( { type: propName + '-changed', value: value } ); - scope.dispatchEvent( changeEvent ); - - } - - } - - } ); - - scope[ propName ] = defaultValue; - _plane[ propName ] = defaultValue; - _gizmo[ propName ] = defaultValue; - - } - - // updateMatrixWorld updates key transformation variables - this.updateMatrixWorld = function () { - - if ( this.object !== undefined ) { - - this.object.updateMatrixWorld(); - - if ( this.object.parent === null ) { - - console.error( 'TransformControls: The attached 3D object must be a part of the scene graph.' ); - - } else { - - this.object.parent.matrixWorld.decompose( parentPosition, parentQuaternion, parentScale ); - - } - - this.object.matrixWorld.decompose( worldPosition, worldQuaternion, worldScale ); - - parentQuaternionInv.copy( parentQuaternion ).invert(); - worldQuaternionInv.copy( worldQuaternion ).invert(); - - } - - this.camera.updateMatrixWorld(); - this.camera.matrixWorld.decompose( cameraPosition, cameraQuaternion, cameraScale ); - - eye.copy( cameraPosition ).sub( worldPosition ).normalize(); - - Object3D.prototype.updateMatrixWorld.call( this ); - - }; - - this.pointerHover = function ( pointer ) { - - if ( this.object === undefined || this.dragging === true ) return; - - raycaster.setFromCamera( pointer, this.camera ); - - var intersect = intersectObjectWithRay( _gizmo.picker[ this.mode ], raycaster ); - - if ( intersect ) { - - this.axis = intersect.object.name; - - this.dispatchEvent( { type: 'hover', value: true } ); - - } else { - - this.axis = null; - - this.dispatchEvent( { type: 'hover', value: false } ); - } - - }; - - this.pointerDown = function ( pointer ) { - - if ( this.object === undefined || this.dragging === true || pointer.button !== 0 ) return; - - if ( this.axis !== null ) { - - raycaster.setFromCamera( pointer, this.camera ); - - var planeIntersect = intersectObjectWithRay( _plane, raycaster, true ); - - if ( planeIntersect ) { - - var space = this.space; - - if ( this.mode === 'scale' ) { - - space = 'local'; - - } else if ( this.axis === 'E' || this.axis === 'XYZE' || this.axis === 'XYZ' ) { - - space = 'world'; - - } - - if ( space === 'local' && this.mode === 'rotate' ) { - - var snap = this.rotationSnap; - - if ( this.axis === 'X' && snap ) this.object.rotation.x = Math.round( this.object.rotation.x / snap ) * snap; - if ( this.axis === 'Y' && snap ) this.object.rotation.y = Math.round( this.object.rotation.y / snap ) * snap; - if ( this.axis === 'Z' && snap ) this.object.rotation.z = Math.round( this.object.rotation.z / snap ) * snap; - - } - - this.object.updateMatrixWorld(); - this.object.parent.updateMatrixWorld(); - - positionStart.copy( this.object.position ); - quaternionStart.copy( this.object.quaternion ); - scaleStart.copy( this.object.scale ); - - this.object.matrixWorld.decompose( worldPositionStart, worldQuaternionStart, worldScaleStart ); - - pointStart.copy( planeIntersect.point ).sub( worldPositionStart ); - - } - - this.dragging = true; - mouseDownEvent.mode = this.mode; - this.dispatchEvent( mouseDownEvent ); - - } - - }; - - this.pointerMove = function ( pointer ) { - - var axis = this.axis; - var mode = this.mode; - var object = this.object; - var space = this.space; - - if ( mode === 'scale' ) { - - space = 'local'; - - } else if ( axis === 'E' || axis === 'XYZE' || axis === 'XYZ' ) { - - space = 'world'; - - } - - if ( object === undefined || axis === null || this.dragging === false || pointer.button !== - 1 ) return; - - raycaster.setFromCamera( pointer, this.camera ); - - var planeIntersect = intersectObjectWithRay( _plane, raycaster, true ); - - if ( ! planeIntersect ) return; - - pointEnd.copy( planeIntersect.point ).sub( worldPositionStart ); - - if ( mode === 'translate' ) { - - // Apply translate - - offset.copy( pointEnd ).sub( pointStart ); - - if ( space === 'local' && axis !== 'XYZ' ) { - - offset.applyQuaternion( worldQuaternionInv ); - - } - - if ( axis.indexOf( 'X' ) === - 1 ) offset.x = 0; - if ( axis.indexOf( 'Y' ) === - 1 ) offset.y = 0; - if ( axis.indexOf( 'Z' ) === - 1 ) offset.z = 0; - - if ( space === 'local' && axis !== 'XYZ' ) { - - offset.applyQuaternion( quaternionStart ).divide( parentScale ); - - } else { - - offset.applyQuaternion( parentQuaternionInv ).divide( parentScale ); - - } - - object.position.copy( offset ).add( positionStart ); - - // Apply translation snap - - if ( this.translationSnap ) { - - if ( space === 'local' ) { - - object.position.applyQuaternion( _tempQuaternion.copy( quaternionStart ).invert() ); - - if ( axis.search( 'X' ) !== - 1 ) { - - object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap; - - } - - if ( axis.search( 'Y' ) !== - 1 ) { - - object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap; - - } - - if ( axis.search( 'Z' ) !== - 1 ) { - - object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap; - - } - - object.position.applyQuaternion( quaternionStart ); - - } - - if ( space === 'world' ) { - - if ( object.parent ) { - - object.position.add( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) ); - - } - - if ( axis.search( 'X' ) !== - 1 ) { - - object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap; - - } - - if ( axis.search( 'Y' ) !== - 1 ) { - - object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap; - - } - - if ( axis.search( 'Z' ) !== - 1 ) { - - object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap; - - } - - if ( object.parent ) { - - object.position.sub( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) ); - - } - - } - - } - - } else if ( mode === 'scale' ) { - - if ( axis.search( 'XYZ' ) !== - 1 ) { - - var d = pointEnd.length() / pointStart.length(); - - if ( pointEnd.dot( pointStart ) < 0 ) d *= - 1; - - _tempVector2.set( d, d, d ); - - } else { - - _tempVector.copy( pointStart ); - _tempVector2.copy( pointEnd ); - - _tempVector.applyQuaternion( worldQuaternionInv ); - _tempVector2.applyQuaternion( worldQuaternionInv ); - - _tempVector2.divide( _tempVector ); - - if ( axis.search( 'X' ) === - 1 ) { - - _tempVector2.x = 1; - - } - - if ( axis.search( 'Y' ) === - 1 ) { - - _tempVector2.y = 1; - - } - - if ( axis.search( 'Z' ) === - 1 ) { - - _tempVector2.z = 1; - - } - - } - - // Apply scale - - object.scale.copy( scaleStart ).multiply( _tempVector2 ); - - if ( this.scaleSnap ) { - - if ( axis.search( 'X' ) !== - 1 ) { - - object.scale.x = Math.round( object.scale.x / this.scaleSnap ) * this.scaleSnap || this.scaleSnap; - - } - - if ( axis.search( 'Y' ) !== - 1 ) { - - object.scale.y = Math.round( object.scale.y / this.scaleSnap ) * this.scaleSnap || this.scaleSnap; - - } - - if ( axis.search( 'Z' ) !== - 1 ) { - - object.scale.z = Math.round( object.scale.z / this.scaleSnap ) * this.scaleSnap || this.scaleSnap; - - } - - } - - } else if ( mode === 'rotate' ) { - - offset.copy( pointEnd ).sub( pointStart ); - - var ROTATION_SPEED = 20 / worldPosition.distanceTo( _tempVector.setFromMatrixPosition( this.camera.matrixWorld ) ); - - if ( axis === 'E' ) { - - rotationAxis.copy( eye ); - rotationAngle = pointEnd.angleTo( pointStart ); - - startNorm.copy( pointStart ).normalize(); - endNorm.copy( pointEnd ).normalize(); - - rotationAngle *= ( endNorm.cross( startNorm ).dot( eye ) < 0 ? 1 : - 1 ); - - } else if ( axis === 'XYZE' ) { - - rotationAxis.copy( offset ).cross( eye ).normalize(); - rotationAngle = offset.dot( _tempVector.copy( rotationAxis ).cross( this.eye ) ) * ROTATION_SPEED; - - } else if ( axis === 'X' || axis === 'Y' || axis === 'Z' ) { - - rotationAxis.copy( _unit[ axis ] ); - - _tempVector.copy( _unit[ axis ] ); - - if ( space === 'local' ) { - - _tempVector.applyQuaternion( worldQuaternion ); - - } - - rotationAngle = offset.dot( _tempVector.cross( eye ).normalize() ) * ROTATION_SPEED; - - } - - // Apply rotation snap - - if ( this.rotationSnap ) rotationAngle = Math.round( rotationAngle / this.rotationSnap ) * this.rotationSnap; - - this.rotationAngle = rotationAngle; - - // Apply rotate - if ( space === 'local' && axis !== 'E' && axis !== 'XYZE' ) { - - object.quaternion.copy( quaternionStart ); - object.quaternion.multiply( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) ).normalize(); - - } else { - - rotationAxis.applyQuaternion( parentQuaternionInv ); - object.quaternion.copy( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) ); - object.quaternion.multiply( quaternionStart ).normalize(); - - } - - } - - this.dispatchEvent( changeEvent ); - this.dispatchEvent( objectChangeEvent ); - - }; - - this.pointerUp = function ( pointer ) { - - if ( pointer.button !== 0 ) return; - - if ( this.dragging && ( this.axis !== null ) ) { - - mouseUpEvent.mode = this.mode; - this.dispatchEvent( mouseUpEvent ); - - } - - this.dragging = false; - this.axis = null; - - }; - - // normalize mouse / touch pointer and remap {x,y} to view space. - - function getPointer( event ) { - - if ( scope.domElement.ownerDocument.pointerLockElement ) { - - return { - x: 0, - y: 0, - button: event.button - }; - - } else { - - var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event; - - var rect = domElement.getBoundingClientRect(); - - return { - x: ( pointer.clientX - rect.left ) / rect.width * 2 - 1, - y: - ( pointer.clientY - rect.top ) / rect.height * 2 + 1, - button: event.button - }; - - } - - } - - // mouse / touch event handlers - - function onPointerHover( event ) { - - if ( ! scope.enabled ) return; - - switch ( event.pointerType ) { - - case 'mouse': - case 'pen': - scope.pointerHover( getPointer( event ) ); - break; - - } - - } - - function onPointerDown( event ) { - - if ( ! scope.enabled ) return; - - scope.domElement.style.touchAction = 'none'; // disable touch scroll - scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove ); - - scope.pointerHover( getPointer( event ) ); - scope.pointerDown( getPointer( event ) ); - - } - - function onPointerMove( event ) { - - if ( ! scope.enabled ) return; - - scope.pointerMove( getPointer( event ) ); - - } - - function onPointerUp( event ) { - - if ( ! scope.enabled ) return; - - scope.domElement.style.touchAction = ''; - scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove ); - - scope.pointerUp( getPointer( event ) ); - - } - - // TODO: deprecate - - this.getMode = function () { - - return scope.mode; - - }; - - this.setMode = function ( mode ) { - - scope.mode = mode; - - }; - - this.setTranslationSnap = function ( translationSnap ) { - - scope.translationSnap = translationSnap; - - }; - - this.setRotationSnap = function ( rotationSnap ) { - - scope.rotationSnap = rotationSnap; - - }; - - this.setScaleSnap = function ( scaleSnap ) { - - scope.scaleSnap = scaleSnap; - - }; - - this.setSize = function ( size ) { - - scope.size = size; - - }; - - this.setSpace = function ( space ) { - - scope.space = space; - - }; - - this.update = function () { - - console.warn( 'THREE.TransformControls: update function has no more functionality and therefore has been deprecated.' ); - - }; - -}; - -TransformControls.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: TransformControls, - - isTransformControls: true - -} ); - - -var TransformControlsGizmo = function ( translationDotsOnly ) { - - 'use strict'; - - Object3D.call( this ); - - this.type = 'TransformControlsGizmo'; - - // shared materials - - var gizmoMaterial = new MeshBasicMaterial( { - depthTest: false, - depthWrite: false, - transparent: true, - side: DoubleSide, - fog: false, - toneMapped: false - } ); - - var gizmoLineMaterial = new LineBasicMaterial( { - depthTest: false, - depthWrite: false, - transparent: true, - linewidth: 1, - fog: false, - toneMapped: false - } ); - - // Make unique material for each axis/color - - var matInvisible = gizmoMaterial.clone(); - matInvisible.opacity = 0.15; - - var matHelper = gizmoMaterial.clone(); - matHelper.opacity = 0.33; - - var matRed = gizmoMaterial.clone(); - matRed.color.set( 0x0A66FF ); - - var matGreen = gizmoMaterial.clone(); - matGreen.color.set( 0x0A66FF ); - - var matBlue = gizmoMaterial.clone(); - matBlue.color.set( 0x0A66FF ); - - var matWhiteTransparent = gizmoMaterial.clone(); - matWhiteTransparent.opacity = 0.25; - - var matYellowTransparent = matWhiteTransparent.clone(); - matYellowTransparent.color.set( 0x0A66FF ); - - var matCyanTransparent = matWhiteTransparent.clone(); - matCyanTransparent.color.set( 0x0A66FF ); - - var matMagentaTransparent = matWhiteTransparent.clone(); - matMagentaTransparent.color.set( 0x0A66FF ); - - var matYellow = gizmoMaterial.clone(); - matYellow.color.set( 0x0A66FF ); - - var matLineRed = gizmoLineMaterial.clone(); - matLineRed.color.set( 0x0A66FF ); - - var matLineGreen = gizmoLineMaterial.clone(); - matLineGreen.color.set( 0x0A66FF ); - - var matLineBlue = gizmoLineMaterial.clone(); - matLineBlue.color.set( 0x0A66FF ); - - var matLineCyan = gizmoLineMaterial.clone(); - matLineCyan.color.set( 0x0A66FF ); - - var matLineMagenta = gizmoLineMaterial.clone(); - matLineMagenta.color.set( 0x0A66FF ); - - var matLineYellow = gizmoLineMaterial.clone(); - matLineYellow.color.set( 0x0A66FF ); - - var matLineGray = gizmoLineMaterial.clone(); - matLineGray.color.set( 0x787878 ); - - var matLineYellowTransparent = matLineYellow.clone(); - matLineYellowTransparent.opacity = 0.25; - - // reusable geometry - - var arrowGeometry = new CylinderGeometry( 0, 0.1, 0.2, 12, 1, false ); - var sphereGeometry = new SphereGeometry( 0.075, 10, 10 ); - - var scaleHandleGeometry = new BoxGeometry( 0.125, 0.125, 0.125 ); - - var lineGeometry = new BufferGeometry(); - lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) ); - - var CircleGeometry = function ( radius, arc ) { - - var geometry = new BufferGeometry( ); - var vertices = []; - - for ( var i = 0; i <= 64 * arc; ++ i ) { - - vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius ); - - } - - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - return geometry; - - }; - - // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position - - var TranslateHelperGeometry = function () { - - var geometry = new BufferGeometry(); - - geometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) ); - - return geometry; - - }; - - // Gizmo definitions - custom hierarchy definitions for setupGizmo() function - - var gizmoTranslate = { - X: [ - [ new Mesh( translationDotsOnly ? sphereGeometry : arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, - Math.PI / 2 ], null, 'fwd' ], - [ new Mesh( translationDotsOnly ? sphereGeometry : arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, Math.PI / 2 ], null, 'bwd' ], - [ translationDotsOnly ? null : new Line( lineGeometry, matLineRed ) ] - ], - Y: [ - [ new Mesh( translationDotsOnly ? sphereGeometry : arrowGeometry, matGreen ), [ 0, 1, 0 ], null, null, 'fwd' ], - [ new Mesh( translationDotsOnly ? sphereGeometry : arrowGeometry, matGreen ), [ 0, 1, 0 ], [ Math.PI, 0, 0 ], null, 'bwd' ], - [ translationDotsOnly ? null : new Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ]] - ], - Z: [ - [ new Mesh( translationDotsOnly ? sphereGeometry : arrowGeometry, matBlue ), [ 0, 0, 1 ], [ Math.PI / 2, 0, 0 ], null, 'fwd' ], - [ new Mesh( translationDotsOnly ? sphereGeometry : arrowGeometry, matBlue ), [ 0, 0, 1 ], [ - Math.PI / 2, 0, 0 ], null, 'bwd' ], - [ translationDotsOnly ? null : new Line( lineGeometry, matLineBlue ), null, [ 0, - Math.PI / 2, 0 ]] - ], - XYZ: [ - [ new Mesh( new OctahedronGeometry( 0.1, 0 ), matWhiteTransparent.clone() ), [ 0, 0, 0 ], [ 0, 0, 0 ]] - ], - XY: [ - [ new Mesh( new PlaneGeometry( 0.295, 0.295 ), matYellowTransparent.clone() ), [ 0.15, 0.15, 0 ]], - [ new Line( lineGeometry, matLineYellow ), [ 0.18, 0.3, 0 ], null, [ 0.125, 1, 1 ]], - [ new Line( lineGeometry, matLineYellow ), [ 0.3, 0.18, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]] - ], - YZ: [ - [ new Mesh( new PlaneGeometry( 0.295, 0.295 ), matCyanTransparent.clone() ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]], - [ new Line( lineGeometry, matLineCyan ), [ 0, 0.18, 0.3 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]], - [ new Line( lineGeometry, matLineCyan ), [ 0, 0.3, 0.18 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]] - ], - XZ: [ - [ new Mesh( new PlaneGeometry( 0.295, 0.295 ), matMagentaTransparent.clone() ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]], - [ new Line( lineGeometry, matLineMagenta ), [ 0.18, 0, 0.3 ], null, [ 0.125, 1, 1 ]], - [ new Line( lineGeometry, matLineMagenta ), [ 0.3, 0, 0.18 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]] - ] - }; - - var pickerTranslate = { - X: [ - [ new Mesh( new CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ]] - ], - Y: [ - [ new Mesh( new CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0.6, 0 ]] - ], - Z: [ - [ new Mesh( new CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ]] - ], - XYZ: [ - [ new Mesh( new OctahedronGeometry( 0.2, 0 ), matInvisible ) ] - ], - XY: [ - [ new Mesh( new PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0.2, 0 ]] - ], - YZ: [ - [ new Mesh( new PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ]] - ], - XZ: [ - [ new Mesh( new PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0, 0.2 ], [ - Math.PI / 2, 0, 0 ]] - ] - }; - - var helperTranslate = { - START: [ - [ new Mesh( new OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ] - ], - END: [ - [ new Mesh( new OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ] - ], - DELTA: [ - [ new Line( TranslateHelperGeometry(), matHelper ), null, null, null, 'helper' ] - ], - X: [ - [ translationDotsOnly ? null : new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ] - ], - Y: [ - [ translationDotsOnly ? null : new Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ] - ], - Z: [ - [ translationDotsOnly ? null : new Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ] - ] - }; - - var gizmoRotate = { - X: [ - [ new Line( CircleGeometry( 1, 0.5 ), matLineRed ) ], - [ new Mesh( new OctahedronGeometry( 0.04, 0 ), matRed ), [ 0, 0, 0.99 ], null, [ 1, 3, 1 ]], - ], - Y: [ - [ new Line( CircleGeometry( 1, 0.5 ), matLineGreen ), null, [ 0, 0, - Math.PI / 2 ]], - [ new Mesh( new OctahedronGeometry( 0.04, 0 ), matGreen ), [ 0, 0, 0.99 ], null, [ 3, 1, 1 ]], - ], - Z: [ - [ new Line( CircleGeometry( 1, 0.5 ), matLineBlue ), null, [ 0, Math.PI / 2, 0 ]], - [ new Mesh( new OctahedronGeometry( 0.04, 0 ), matBlue ), [ 0.99, 0, 0 ], null, [ 1, 3, 1 ]], - ], - E: [ - [ new Line( CircleGeometry( 1.25, 1 ), matLineYellowTransparent ), null, [ 0, Math.PI / 2, 0 ]], - [ new Mesh( new CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 1.17, 0, 0 ], [ 0, 0, - Math.PI / 2 ], [ 1, 1, 0.001 ]], - [ new Mesh( new CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ - 1.17, 0, 0 ], [ 0, 0, Math.PI / 2 ], [ 1, 1, 0.001 ]], - [ new Mesh( new CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, - 1.17, 0 ], [ Math.PI, 0, 0 ], [ 1, 1, 0.001 ]], - [ new Mesh( new CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, 1.17, 0 ], [ 0, 0, 0 ], [ 1, 1, 0.001 ]], - ], - XYZE: [ - [ new Line( CircleGeometry( 1, 1 ), matLineGray ), null, [ 0, Math.PI / 2, 0 ]] - ] - }; - - var helperRotate = { - AXIS: [ - [ new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ] - ] - }; - - var pickerRotate = { - X: [ - [ new Mesh( new TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ]], - ], - Y: [ - [ new Mesh( new TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ]], - ], - Z: [ - [ new Mesh( new TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], - ], - E: [ - [ new Mesh( new TorusGeometry( 1.25, 0.1, 2, 24 ), matInvisible ) ] - ], - XYZE: [ - [ new Mesh( new SphereGeometry( 0.7, 10, 8 ), matInvisible ) ] - ] - }; - - var gizmoScale = { - X: [ - [ new Mesh( scaleHandleGeometry, matRed ), [ 0.8, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], - [ new Line( lineGeometry, matLineRed ), null, null, [ 0.8, 1, 1 ]] - ], - Y: [ - [ new Mesh( scaleHandleGeometry, matGreen ), [ 0, 0.8, 0 ]], - [ new Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ], [ 0.8, 1, 1 ]] - ], - Z: [ - [ new Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, 0.8 ], [ Math.PI / 2, 0, 0 ]], - [ new Line( lineGeometry, matLineBlue ), null, [ 0, - Math.PI / 2, 0 ], [ 0.8, 1, 1 ]] - ], - XY: [ - [ new Mesh( scaleHandleGeometry, matYellowTransparent ), [ 0.85, 0.85, 0 ], null, [ 2, 2, 0.2 ]], - [ new Line( lineGeometry, matLineYellow ), [ 0.855, 0.98, 0 ], null, [ 0.125, 1, 1 ]], - [ new Line( lineGeometry, matLineYellow ), [ 0.98, 0.855, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]] - ], - YZ: [ - [ new Mesh( scaleHandleGeometry, matCyanTransparent ), [ 0, 0.85, 0.85 ], null, [ 0.2, 2, 2 ]], - [ new Line( lineGeometry, matLineCyan ), [ 0, 0.855, 0.98 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]], - [ new Line( lineGeometry, matLineCyan ), [ 0, 0.98, 0.855 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]] - ], - XZ: [ - [ new Mesh( scaleHandleGeometry, matMagentaTransparent ), [ 0.85, 0, 0.85 ], null, [ 2, 0.2, 2 ]], - [ new Line( lineGeometry, matLineMagenta ), [ 0.855, 0, 0.98 ], null, [ 0.125, 1, 1 ]], - [ new Line( lineGeometry, matLineMagenta ), [ 0.98, 0, 0.855 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]] - ], - XYZX: [ - [ new Mesh( new BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 1.1, 0, 0 ]], - ], - XYZY: [ - [ new Mesh( new BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 0, 1.1, 0 ]], - ], - XYZZ: [ - [ new Mesh( new BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 0, 0, 1.1 ]], - ] - }; - - var pickerScale = { - X: [ - [ new Mesh( new CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ]] - ], - Y: [ - [ new Mesh( new CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0.5, 0 ]] - ], - Z: [ - [ new Mesh( new CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ]] - ], - XY: [ - [ new Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0.85, 0 ], null, [ 3, 3, 0.2 ]], - ], - YZ: [ - [ new Mesh( scaleHandleGeometry, matInvisible ), [ 0, 0.85, 0.85 ], null, [ 0.2, 3, 3 ]], - ], - XZ: [ - [ new Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0, 0.85 ], null, [ 3, 0.2, 3 ]], - ], - XYZX: [ - [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 1.1, 0, 0 ]], - ], - XYZY: [ - [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 1.1, 0 ]], - ], - XYZZ: [ - [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 0, 1.1 ]], - ] - }; - - var helperScale = { - X: [ - [ new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ] - ], - Y: [ - [ new Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ] - ], - Z: [ - [ new Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ] - ] - }; - - // Creates an Object3D with gizmos described in custom hierarchy definition. - - var setupGizmo = function ( gizmoMap ) { - - var gizmo = new Object3D(); - - for ( var name in gizmoMap ) { - - for ( var i = gizmoMap[ name ].length; i --; ) { - - var object = gizmoMap[ name ][ i ][ 0 ] - if( object ) object = object.clone(); - else continue - - var position = gizmoMap[ name ][ i ][ 1 ]; - var rotation = gizmoMap[ name ][ i ][ 2 ]; - var scale = gizmoMap[ name ][ i ][ 3 ]; - var tag = gizmoMap[ name ][ i ][ 4 ]; - - // name and tag properties are essential for picking and updating logic. - object.name = name; - object.tag = tag; - - if ( position ) { - - object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] ); - - } - - if ( rotation ) { - - object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] ); - - } - - if ( scale ) { - - object.scale.set( scale[ 0 ], scale[ 1 ], scale[ 2 ] ); - - } - - object.updateMatrix(); - - if( !translationDotsOnly ) { - - var tempGeometry = object.geometry.clone(); - - tempGeometry.applyMatrix4( object.matrix ); - - object.geometry = tempGeometry; - - } - - object.renderOrder = Infinity; - - object.position.set( 0, 0, 0 ); - object.rotation.set( 0, 0, 0 ); - object.scale.set( 1, 1, 1 ); - - gizmo.add( object ); - - } - - } - - return gizmo; - - }; - - // Reusable utility variables - - var tempVector = new Vector3( 0, 0, 0 ); - var tempEuler = new Euler(); - var alignVector = new Vector3( 0, 1, 0 ); - var zeroVector = new Vector3( 0, 0, 0 ); - var lookAtMatrix = new Matrix4(); - var tempQuaternion = new Quaternion(); - var tempQuaternion2 = new Quaternion(); - var identityQuaternion = new Quaternion(); - - var unitX = new Vector3( 1, 0, 0 ); - var unitY = new Vector3( 0, 1, 0 ); - var unitZ = new Vector3( 0, 0, 1 ); - - // Gizmo creation - - this.gizmo = {}; - this.picker = {}; - this.helper = {}; - - this.add( this.gizmo[ 'translate' ] = setupGizmo( gizmoTranslate ) ); - this.add( this.gizmo[ 'rotate' ] = setupGizmo( gizmoRotate ) ); - this.add( this.gizmo[ 'scale' ] = setupGizmo( gizmoScale ) ); - this.add( this.picker[ 'translate' ] = setupGizmo( pickerTranslate ) ); - this.add( this.picker[ 'rotate' ] = setupGizmo( pickerRotate ) ); - this.add( this.picker[ 'scale' ] = setupGizmo( pickerScale ) ); - this.add( this.helper[ 'translate' ] = setupGizmo( helperTranslate ) ); - this.add( this.helper[ 'rotate' ] = setupGizmo( helperRotate ) ); - this.add( this.helper[ 'scale' ] = setupGizmo( helperScale ) ); - - // Pickers should be hidden always - - this.picker[ 'translate' ].visible = false; - this.picker[ 'rotate' ].visible = false; - this.picker[ 'scale' ].visible = false; - - // updateMatrixWorld will update transformations and appearance of individual handles - - this.updateMatrixWorld = function () { - - var space = this.space; - - if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation - - var quaternion = space === 'local' ? this.worldQuaternion : identityQuaternion; - - // Show only gizmos for current transform mode - - this.gizmo[ 'translate' ].visible = this.mode === 'translate'; - this.gizmo[ 'rotate' ].visible = this.mode === 'rotate'; - this.gizmo[ 'scale' ].visible = this.mode === 'scale'; - - this.helper[ 'translate' ].visible = this.mode === 'translate'; - this.helper[ 'rotate' ].visible = this.mode === 'rotate'; - this.helper[ 'scale' ].visible = this.mode === 'scale'; - - - var handles = []; - handles = handles.concat( this.picker[ this.mode ].children ); - handles = handles.concat( this.gizmo[ this.mode ].children ); - handles = handles.concat( this.helper[ this.mode ].children ); - - for ( var i = 0; i < handles.length; i ++ ) { - - var handle = handles[ i ]; - - // hide aligned to camera - - handle.visible = true; - handle.rotation.set( 0, 0, 0 ); - handle.position.copy( this.worldPosition ); - - var factor; - - if ( this.camera.isOrthographicCamera ) { - - factor = ( this.camera.top - this.camera.bottom ) / this.camera.zoom; - - } else { - - factor = this.worldPosition.distanceTo( this.cameraPosition ) * Math.min( 1.9 * Math.tan( Math.PI * this.camera.fov / 360 ) / this.camera.zoom, 7 ); - - } - - handle.scale.set( 1, 1, 1 ).multiplyScalar( factor * this.size / 7 ); - - // TODO: simplify helpers and consider decoupling from gizmo - - if ( handle.tag === 'helper' ) { - - handle.visible = false; - - if ( handle.name === 'AXIS' ) { - - handle.position.copy( this.worldPositionStart ); - handle.visible = !! this.axis; - - if ( this.axis === 'X' ) { - - tempQuaternion.setFromEuler( tempEuler.set( 0, 0, 0 ) ); - handle.quaternion.copy( quaternion ).multiply( tempQuaternion ); - - if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) { - - handle.visible = false; - - } - - } - - if ( this.axis === 'Y' ) { - - tempQuaternion.setFromEuler( tempEuler.set( 0, 0, Math.PI / 2 ) ); - handle.quaternion.copy( quaternion ).multiply( tempQuaternion ); - - if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) { - - handle.visible = false; - - } - - } - - if ( this.axis === 'Z' ) { - - tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) ); - handle.quaternion.copy( quaternion ).multiply( tempQuaternion ); - - if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) { - - handle.visible = false; - - } - - } - - if ( this.axis === 'XYZE' ) { - - tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) ); - alignVector.copy( this.rotationAxis ); - handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( zeroVector, alignVector, unitY ) ); - handle.quaternion.multiply( tempQuaternion ); - handle.visible = this.dragging; - - } - - if ( this.axis === 'E' ) { - - handle.visible = false; - - } - - - } else if ( handle.name === 'START' ) { - - handle.position.copy( this.worldPositionStart ); - handle.visible = this.dragging; - - } else if ( handle.name === 'END' ) { - - handle.position.copy( this.worldPosition ); - handle.visible = this.dragging; - - } else if ( handle.name === 'DELTA' ) { - - handle.position.copy( this.worldPositionStart ); - handle.quaternion.copy( this.worldQuaternionStart ); - tempVector.set( 1e-10, 1e-10, 1e-10 ).add( this.worldPositionStart ).sub( this.worldPosition ).multiplyScalar( - 1 ); - tempVector.applyQuaternion( this.worldQuaternionStart.clone().invert() ); - handle.scale.copy( tempVector ); - handle.visible = this.dragging; - - } else { - - handle.quaternion.copy( quaternion ); - - if ( this.dragging ) { - - handle.position.copy( this.worldPositionStart ); - - } else { - - handle.position.copy( this.worldPosition ); - - } - - if ( this.axis ) { - - handle.visible = this.axis.search( handle.name ) !== - 1; - - } - - } - - // If updating helper, skip rest of the loop - continue; - - } - - // Align handles to current local or world rotation - - handle.quaternion.copy( quaternion ); - - if ( this.mode === 'translate' || this.mode === 'scale' ) { - - // Hide translate and scale axis facing the camera - - var AXIS_HIDE_TRESHOLD = 0.99; - var PLANE_HIDE_TRESHOLD = 0.2; - var AXIS_FLIP_TRESHOLD = 0.0; - - - if ( handle.name === 'X' || handle.name === 'XYZX' ) { - - if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) { - - handle.scale.set( 1e-10, 1e-10, 1e-10 ); - handle.visible = false; - - } - - } - - if ( handle.name === 'Y' || handle.name === 'XYZY' ) { - - if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) { - - handle.scale.set( 1e-10, 1e-10, 1e-10 ); - handle.visible = false; - - } - - } - - if ( handle.name === 'Z' || handle.name === 'XYZZ' ) { - - if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) { - - handle.scale.set( 1e-10, 1e-10, 1e-10 ); - handle.visible = false; - - } - - } - - if ( handle.name === 'XY' ) { - - if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) { - - handle.scale.set( 1e-10, 1e-10, 1e-10 ); - handle.visible = false; - - } - - } - - if ( handle.name === 'YZ' ) { - - if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) { - - handle.scale.set( 1e-10, 1e-10, 1e-10 ); - handle.visible = false; - - } - - } - - if ( handle.name === 'XZ' ) { - - if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) { - - handle.scale.set( 1e-10, 1e-10, 1e-10 ); - handle.visible = false; - - } - - } - - // Flip translate and scale axis ocluded behind another axis - - if ( handle.name.search( 'X' ) !== - 1 ) { - - if ( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) { - - if ( handle.tag === 'fwd' ) { - - handle.visible = false; - - } else { - - handle.scale.x *= - 1; - - } - - } else if ( handle.tag === 'bwd' ) { - - handle.visible = false; - - } - - } - - if ( handle.name.search( 'Y' ) !== - 1 ) { - - if ( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) { - - if ( handle.tag === 'fwd' ) { - - handle.visible = false; - - } else { - - handle.scale.y *= - 1; - - } - - } else if ( handle.tag === 'bwd' ) { - - handle.visible = false; - - } - - } - - if ( handle.name.search( 'Z' ) !== - 1 ) { - - if ( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) { - - if ( handle.tag === 'fwd' ) { - - handle.visible = false; - - } else { - - handle.scale.z *= - 1; - - } - - } else if ( handle.tag === 'bwd' ) { - - handle.visible = false; - - } - - } - - } else if ( this.mode === 'rotate' ) { - - // Align handles to current local or world rotation - - tempQuaternion2.copy( quaternion ); - alignVector.copy( this.eye ).applyQuaternion( tempQuaternion.copy( quaternion ).invert() ); - - if ( handle.name.search( 'E' ) !== - 1 ) { - - handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( this.eye, zeroVector, unitY ) ); - - } - - if ( handle.name === 'X' ) { - - tempQuaternion.setFromAxisAngle( unitX, Math.atan2( - alignVector.y, alignVector.z ) ); - tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion ); - handle.quaternion.copy( tempQuaternion ); - - } - - if ( handle.name === 'Y' ) { - - tempQuaternion.setFromAxisAngle( unitY, Math.atan2( alignVector.x, alignVector.z ) ); - tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion ); - handle.quaternion.copy( tempQuaternion ); - - } - - if ( handle.name === 'Z' ) { - - tempQuaternion.setFromAxisAngle( unitZ, Math.atan2( alignVector.y, alignVector.x ) ); - tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion ); - handle.quaternion.copy( tempQuaternion ); - - } - - } - - // Hide disabled axes - handle.visible = handle.visible && ( handle.name.indexOf( 'X' ) === - 1 || this.showX ); - handle.visible = handle.visible && ( handle.name.indexOf( 'Y' ) === - 1 || this.showY ); - handle.visible = handle.visible && ( handle.name.indexOf( 'Z' ) === - 1 || this.showZ ); - handle.visible = handle.visible && ( handle.name.indexOf( 'E' ) === - 1 || ( this.showX && this.showY && this.showZ ) ); - - // highlight selected axis - - handle.material._opacity = handle.material._opacity || handle.material.opacity; - handle.material._color = handle.material._color || handle.material.color.clone(); - - handle.material.color.copy( handle.material._color ); - handle.material.opacity = handle.material._opacity; - - if ( ! this.enabled ) { - - handle.material.opacity *= 0.5; - handle.material.color.lerp( new Color( 1, 1, 1 ), 0.5 ); - - } else if ( this.axis ) { - - if ( handle.name === this.axis ) { - - handle.material.opacity = 1.0; - handle.material.color.lerp( new Color( 1, 1, 1 ), 0.5 ); - - } else if ( this.axis.split( '' ).some( function ( a ) { - - return handle.name === a; - - } ) ) { - - handle.material.opacity = 1.0; - handle.material.color.lerp( new Color( 1, 1, 1 ), 0.5 ); - - } else { - - handle.material.opacity *= 0.25; - handle.material.color.lerp( new Color( 1, 1, 1 ), 0.5 ); - - } - - } - - } - - Object3D.prototype.updateMatrixWorld.call( this ); - - }; - -}; - -TransformControlsGizmo.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: TransformControlsGizmo, - - isTransformControlsGizmo: true - -} ); - - -var TransformControlsPlane = function () { - - 'use strict'; - - Mesh.call( this, - new PlaneGeometry( 100000, 100000, 2, 2 ), - new MeshBasicMaterial( { visible: false, wireframe: true, side: DoubleSide, transparent: true, opacity: 0.1, toneMapped: false } ) - ); - - this.type = 'TransformControlsPlane'; - - var unitX = new Vector3( 1, 0, 0 ); - var unitY = new Vector3( 0, 1, 0 ); - var unitZ = new Vector3( 0, 0, 1 ); - - var tempVector = new Vector3(); - var dirVector = new Vector3(); - var alignVector = new Vector3(); - var tempMatrix = new Matrix4(); - var identityQuaternion = new Quaternion(); - - this.updateMatrixWorld = function () { - - var space = this.space; - - this.position.copy( this.worldPosition ); - - if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation - - unitX.set( 1, 0, 0 ).applyQuaternion( space === 'local' ? this.worldQuaternion : identityQuaternion ); - unitY.set( 0, 1, 0 ).applyQuaternion( space === 'local' ? this.worldQuaternion : identityQuaternion ); - unitZ.set( 0, 0, 1 ).applyQuaternion( space === 'local' ? this.worldQuaternion : identityQuaternion ); - - // Align the plane for current transform mode, axis and space. - - alignVector.copy( unitY ); - - switch ( this.mode ) { - - case 'translate': - case 'scale': - switch ( this.axis ) { - - case 'X': - alignVector.copy( this.eye ).cross( unitX ); - dirVector.copy( unitX ).cross( alignVector ); - break; - case 'Y': - alignVector.copy( this.eye ).cross( unitY ); - dirVector.copy( unitY ).cross( alignVector ); - break; - case 'Z': - alignVector.copy( this.eye ).cross( unitZ ); - dirVector.copy( unitZ ).cross( alignVector ); - break; - case 'XY': - dirVector.copy( unitZ ); - break; - case 'YZ': - dirVector.copy( unitX ); - break; - case 'XZ': - alignVector.copy( unitZ ); - dirVector.copy( unitY ); - break; - case 'XYZ': - case 'E': - dirVector.set( 0, 0, 0 ); - break; - - } - - break; - case 'rotate': - default: - // special case for rotate - dirVector.set( 0, 0, 0 ); - - } - - if ( dirVector.length() === 0 ) { - - // If in rotate mode, make the plane parallel to camera - this.quaternion.copy( this.cameraQuaternion ); - - } else { - - tempMatrix.lookAt( tempVector.set( 0, 0, 0 ), dirVector, alignVector ); - - this.quaternion.setFromRotationMatrix( tempMatrix ); - - } - - Object3D.prototype.updateMatrixWorld.call( this ); - - }; - -}; - -TransformControlsPlane.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: TransformControlsPlane, - - isTransformControlsPlane: true - -} ); - -export { TransformControls, TransformControlsGizmo, TransformControlsPlane };