463 lines
14 KiB
JavaScript
463 lines
14 KiB
JavaScript
import * as THREE from 'three'
|
|
import SelectionHelper from './legacy/SelectionHelper'
|
|
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js'
|
|
import { Box3 } from 'three'
|
|
import { ViewerEvent } from '../IViewer'
|
|
|
|
export default class SectionBox {
|
|
constructor(viewer) {
|
|
this.viewer = viewer
|
|
|
|
this.viewer.speckleRenderer.renderer.localClippingEnabled = true
|
|
|
|
this.dragging = false
|
|
this.display = new THREE.Group()
|
|
this.display.name = 'SectionBox'
|
|
this.viewer.speckleRenderer.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
|
|
const sphere = new THREE.SphereGeometry(0.01, 10, 10)
|
|
this.sphere = new THREE.Mesh(
|
|
sphere,
|
|
new THREE.MeshStandardMaterial({ color: 0x00ffff })
|
|
)
|
|
this.sphere.visible = false
|
|
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: false,
|
|
checkForSectionBoxInclusion: false
|
|
})
|
|
this.selectionHelper.on(ViewerEvent.ObjectClicked, this._clickHandler.bind(this))
|
|
this.selectionHelper.on('hovered', () => {
|
|
// TODO: cannot get this to work reliably
|
|
// 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.viewer.needsRender = true
|
|
// }
|
|
})
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && this.viewer.mouseOverRenderer) {
|
|
this._attachControlsToBox()
|
|
}
|
|
})
|
|
|
|
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.speckleRenderer.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) => {
|
|
if (!this.display.visible) return
|
|
const val = !!event.value
|
|
if (val) {
|
|
this.dragging = val
|
|
//@Dim: Not sure what this needs to do in the new viewer
|
|
//@Alex: this prevents(?) involuntary selection happening on mobile
|
|
// this.viewer.interactions.preventSelection = val
|
|
this.viewer.cameraHandler.enabled = !val
|
|
} else {
|
|
setTimeout(() => {
|
|
this.dragging = val
|
|
//@Dim: Not sure what this needs to do in the new viewer
|
|
//@Alex: this prevents(?) involuntary selection happening on mobile
|
|
// this.viewer.interactions.preventSelection = val
|
|
this.viewer.cameraHandler.enabled = !val
|
|
}, 100)
|
|
}
|
|
})
|
|
this.viewer.needsRender = true
|
|
}
|
|
|
|
_draggingChangeHandler() {
|
|
if (!this.display.visible) return
|
|
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()
|
|
const boxArr = this.boxGeometry.attributes.position.array
|
|
for (let i = 0; i < this.currentRange.length; i++) {
|
|
const 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
|
|
const 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
|
|
|
|
const boxArr = this.boxGeometry.attributes.position
|
|
let index = 0
|
|
const planeArr = this.plane.attributes.position.array
|
|
const centre = new THREE.Vector3()
|
|
|
|
const 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
|
|
]
|
|
|
|
const positions = []
|
|
for (const vert of vertices) {
|
|
positions.push(...vert)
|
|
}
|
|
|
|
const 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
|
|
const 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) {
|
|
const a = new THREE.Vector3(
|
|
boxArr.getX(indexes[i]),
|
|
boxArr.getY(indexes[i]),
|
|
boxArr.getZ(indexes[i])
|
|
)
|
|
const b = new THREE.Vector3(
|
|
boxArr.getX(indexes[i + 1]),
|
|
boxArr.getY(indexes[i + 1]),
|
|
boxArr.getZ(indexes[i + 1])
|
|
)
|
|
const c = new THREE.Vector3(
|
|
boxArr.getX(indexes[i + 2]),
|
|
boxArr.getY(indexes[i + 2]),
|
|
boxArr.getZ(indexes[i + 2])
|
|
)
|
|
const plane = this.planes[index]
|
|
plane.setFromCoplanarPoints(a, b, c)
|
|
index++
|
|
}
|
|
}
|
|
|
|
_attachControlsToBox() {
|
|
this.controls.detach()
|
|
|
|
const centre = new THREE.Vector3()
|
|
const 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
|
|
}
|
|
|
|
// setBoxFromObjects(objectIds: string[], offset = 0.05) {
|
|
// WorldTree.getInstance().walk() => Solved
|
|
// this.setBox(...)
|
|
// }
|
|
|
|
setBox(targetBox, offset = 0.05) {
|
|
let box
|
|
|
|
if (targetBox) box = targetBox // targetbox = { min: {x, y, z}, max: {x, y, z} }
|
|
else {
|
|
// // @Alex: part of the old behaviour: if a selected object is present, we set the box to it
|
|
// // if no selection is present, we set the box to whole scene. TBD re API, etc.
|
|
// /* //@Dim: Not sure what this needs to do in the new viewer
|
|
// if (this.viewer.interactions.selectedObjects.children.length !== 0) {
|
|
// box = new THREE.Box3().setFromObject(this.viewer.interactions.selectedObjects)
|
|
// } else*/ if (this.viewer.speckleRenderer.allObjects.children.length !== 0) {
|
|
// box = new THREE.Box3().setFromObject(this.viewer.speckleRenderer.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
|
|
]
|
|
|
|
const 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 will not get set internally anymore
|
|
// this.setBox()
|
|
this.display.visible = !this.display.visible
|
|
this.viewer.speckleRenderer.renderer.localClippingEnabled = this.display.visible
|
|
this.viewer.needsRender = true
|
|
}
|
|
|
|
off() {
|
|
this.display.visible = false
|
|
this.viewer.speckleRenderer.renderer.localClippingEnabled = false
|
|
this.viewer.needsRender = true
|
|
}
|
|
|
|
on() {
|
|
this.display.visible = true
|
|
this.viewer.speckleRenderer.renderer.localClippingEnabled = true
|
|
this.viewer.needsRender = true
|
|
}
|
|
|
|
displayOff() {
|
|
this.display.visible = false
|
|
}
|
|
|
|
displayOn() {
|
|
this.display.visible = true
|
|
}
|
|
|
|
getCurrentBox() {
|
|
if (!this.display.visible) return null
|
|
const box = new THREE.Box3().setFromBufferAttribute(
|
|
this.boxGeometry.attributes.position
|
|
)
|
|
return box
|
|
}
|
|
}
|