diff --git a/src/app/components/SMCanvas/TransformControls.js b/src/app/components/SMCanvas/TransformControls.js index a3f939da92..99dc9985a8 100644 --- a/src/app/components/SMCanvas/TransformControls.js +++ b/src/app/components/SMCanvas/TransformControls.js @@ -2,8 +2,6 @@ import { DoubleSide, Float32BufferAttribute, Vector3, - Euler, - Box3, Quaternion, Matrix4, Group, @@ -22,6 +20,8 @@ import { } from 'three'; // import * as THREE from 'three'; +import ThreeUtils from '../three-extensions/ThreeUtils'; + import { ArcBufferGeometry } from './ArcGeometry'; const EVENTS = { @@ -438,49 +438,12 @@ class TransformControls extends Object3D { this.camera.updateMatrixWorld(); this.camera.matrixWorld.decompose(cameraPosition, cameraQuaternion, cameraScale); const multiObjectPosition = this.object.position; - const maxObjectBoundingBox = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); - const minObjectBoundingBox = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); - const multiObjectWidth = new Vector3(); const objectPosition = new Vector3(); const objectScale = new Vector3(); const objectQuaternion = new Quaternion(); - this.object.children.forEach((child, index) => { - const objectChildrenPosition = new Vector3(); - const objectChildrenScale = new Vector3(); - const objectChildrenQuaternion = new Quaternion(); - // object - - child.matrixWorld.decompose(objectChildrenPosition, objectChildrenQuaternion, objectChildrenScale); - - if (this.object.shouldUpdateBoundingbox) { - if (!this.object.boundingBox[index]) { - this.updateBoundingBox(); - } - // Calculate selectedGroup's BBox in Canvas - const boundingBox = this.object.boundingBox[index]; - if (boundingBox && boundingBox.min && boundingBox.max) { - if (this.object.children.length === 1) { - minObjectBoundingBox.x = Math.min(boundingBox.min.x, minObjectBoundingBox.x); - minObjectBoundingBox.y = Math.min(boundingBox.min.y, minObjectBoundingBox.y); - minObjectBoundingBox.z = Math.min(boundingBox.min.z, minObjectBoundingBox.z); - - maxObjectBoundingBox.x = Math.max(boundingBox.max.x, maxObjectBoundingBox.x); - maxObjectBoundingBox.y = Math.max(boundingBox.max.y, maxObjectBoundingBox.y); - maxObjectBoundingBox.z = Math.max(boundingBox.max.z, maxObjectBoundingBox.z); - } else { - minObjectBoundingBox.x = Math.min(boundingBox.min.x + objectChildrenPosition.x - objectPosition.x, minObjectBoundingBox.x); - minObjectBoundingBox.y = Math.min(boundingBox.min.y + objectChildrenPosition.y - objectPosition.y, minObjectBoundingBox.y); - minObjectBoundingBox.z = Math.min(0, minObjectBoundingBox.z); - - maxObjectBoundingBox.x = Math.max(boundingBox.max.x + objectChildrenPosition.x - objectPosition.x, maxObjectBoundingBox.x); - maxObjectBoundingBox.y = Math.max(boundingBox.max.y + objectChildrenPosition.y - objectPosition.y, maxObjectBoundingBox.y); - maxObjectBoundingBox.z = Math.max(boundingBox.max.z - boundingBox.min.z, maxObjectBoundingBox.z); - } - } - } - + this.object.children.forEach((child) => { // Update peripherals this.translatePeripheral.visible = (this.mode === 'translate' && child.visible); this.rotatePeripheral.visible = (this.mode === 'rotate' && child.visible); @@ -502,6 +465,10 @@ class TransformControls extends Object3D { const unitZ = new Vector3(0, 0, 1); if (this.object.shouldUpdateBoundingbox) { + const boundingBox = ThreeUtils.computeBoundingBox(this.object); + const maxObjectBoundingBox = boundingBox.max; + const minObjectBoundingBox = boundingBox.min; + const multiObjectWidth = new Vector3(); multiObjectWidth.x = (maxObjectBoundingBox.x - minObjectBoundingBox.x); multiObjectWidth.y = (maxObjectBoundingBox.y - minObjectBoundingBox.y); multiObjectWidth.z = (maxObjectBoundingBox.z - minObjectBoundingBox.z); @@ -863,48 +830,6 @@ class TransformControls extends Object3D { // Calculate the bbox of each model in the selectedGroup updateBoundingBox() { - this.object.updateMatrixWorld(); - this.object.children.forEach((child, index) => { - // cannot get 'child.geometry.boundingBox', have to use 'child.geometry' - if (child.geometry) { - const clone = child.geometry.clone(); - if (this.object.children.length === 1) { - clone.applyMatrix(child.matrixWorld); - clone.computeBoundingBox(); - this.object.boundingBox[index] = clone.boundingBox; - } else { - const parentClone = this.object.clone(); - const newRotation = new Euler( - child.rotation.x + this.object.rotation.x, - child.rotation.y + this.object.rotation.y, - child.rotation.z + this.object.rotation.z, - ); - const newScale = new Vector3( - child.scale.x * this.object.scale.x, - child.scale.y * this.object.scale.y, - child.scale.z * this.object.scale.z, - ); - - parentClone.rotation.copy(newRotation); - parentClone.scale.copy(newScale); - parentClone.updateMatrix(); - clone.applyMatrix(parentClone.matrix); - clone.computeBoundingBox(); - const minBoundingBox = clone.boundingBox.min; - const maxBoundingBox = clone.boundingBox.max; - const centerPosition = new Vector3( - (minBoundingBox.x + maxBoundingBox.x) / 2, - (minBoundingBox.y + maxBoundingBox.y) / 2, - (minBoundingBox.z + maxBoundingBox.z) / 2, - ); - const newBoundingBox = new Box3( - new Vector3(minBoundingBox.x - centerPosition.x, minBoundingBox.y - centerPosition.y, minBoundingBox.z - centerPosition.z), - new Vector3(maxBoundingBox.x - centerPosition.x, maxBoundingBox.y - centerPosition.y, maxBoundingBox.z - centerPosition.z) - ); - this.object.boundingBox[index] = newBoundingBox; - } - } - }); this.object.shouldUpdateBoundingbox = true; } } diff --git a/src/app/components/three-extensions/ThreeUtils.js b/src/app/components/three-extensions/ThreeUtils.js index a95e8d1dee..bf23176528 100644 --- a/src/app/components/three-extensions/ThreeUtils.js +++ b/src/app/components/three-extensions/ThreeUtils.js @@ -22,18 +22,18 @@ const ThreeUtils = { }, // get matrix for rotating v2 to v1. Applying matrix to v2 can make v2 to parallels v1. - getRotateMatrixBetweenVector3: function (v1, v2){ + getRotateMatrixBetweenVector3: function (v1, v2) { const quaternion = ThreeUtils.getQuaternionBetweenVector3(v1, v2); const matrix4 = new THREE.Matrix4().makeRotationFromQuaternion(quaternion); return matrix4; }, - getMouseXY: function(event, domElement) { - var rect = domElement.getBoundingClientRect(); + getMouseXY: function (event, domElement) { + const rect = domElement.getBoundingClientRect(); return new THREE.Vector2( ((event.clientX - rect.left) / rect.width) * 2 - 1, -((event.clientY - rect.top) / rect.height) * 2 + 1 - ) + ); }, // get world info @@ -45,7 +45,7 @@ const ThreeUtils = { getObjectWorldQuaternion: function (object) { const result = new THREE.Quaternion(); - object.getWorldQuaternion (result); + object.getWorldQuaternion(result); return result; }, @@ -56,26 +56,26 @@ const ThreeUtils = { }, getEventWorldPosition: function (event, domElement, camera) { - var rect = domElement.getBoundingClientRect(); - var tempVector3 = new THREE.Vector3(); + const rect = domElement.getBoundingClientRect(); + const tempVector3 = new THREE.Vector3(); // the x&y in standard thereejs coordinate // standardX, standardY: [-1, 1] - const standardX = ((event.clientX - rect.left) / rect.width ) * 2 - 1; - const standardY = -((event.clientY - rect.top) / rect.height ) * 2 + 1; + const standardX = ((event.clientX - rect.left) / rect.width) * 2 - 1; + const standardY = -((event.clientY - rect.top) / rect.height) * 2 + 1; tempVector3.set(standardX, standardY, 0.5); - tempVector3.unproject(camera ); - tempVector3.sub(camera.position ).normalize(); - var distance = -camera.position.z / tempVector3.z; + tempVector3.unproject(camera); + tempVector3.sub(camera.position).normalize(); + const distance = -camera.position.z / tempVector3.z; - var result = new THREE.Vector3().copy(camera.position).add(tempVector3.multiplyScalar(distance)); + const result = new THREE.Vector3().copy(camera.position).add(tempVector3.multiplyScalar(distance)); return result; }, // set world transformation - setObjectWorldPosition: function(object, position) { + setObjectWorldPosition: function (object, position) { const parent = object.parent; parent.updateMatrixWorld(); const matrix = new THREE.Matrix4().getInverse(parent.matrixWorld); @@ -84,7 +84,7 @@ const ThreeUtils = { }, setObjectWorldScale: function (object, scale) { - var localScale = object.parent.worldToLocal(scale); + const localScale = object.parent.worldToLocal(scale); object.scale.copy(localScale); }, @@ -105,7 +105,7 @@ const ThreeUtils = { const scaleY = targetSize.y / originSize2D.y; const worldScale = new THREE.Vector3(scaleX, scaleY, 1); - ThreeUtils.setObjectWorldScale(object, worldScale ); + ThreeUtils.setObjectWorldScale(object, worldScale); const deltaX = (scaleX - originScale.x) * originSize2D.x; const deltaY = (scaleY - originScale.y) * originSize2D.y; @@ -144,13 +144,13 @@ const ThreeUtils = { box.max.y - box.min.y, box.max.z - box.min.z ); - if (is2D){ + if (is2D) { return new THREE.Vector2(size.x, size.y); } else { return size; } }, - + generateSupportBoxGeometry(width, height, topZ, bottomZ = 0) { const depth = topZ - bottomZ; const box = new THREE.BoxBufferGeometry(width, height, depth).toNonIndexed(); @@ -160,7 +160,7 @@ const ThreeUtils = { removeObjectParent(obj) { const parent = obj.parent; - if(!parent) return () =>{}; + if (!parent) return () => {}; parent.updateMatrixWorld(); parent.remove(obj); @@ -168,31 +168,72 @@ const ThreeUtils = { return () => this.setObjectParent(obj, parent); }, - setObjectParent(obj, parent){ - if(!parent) return; - - this.removeObjectParent(obj) + setObjectParent(obj, parent) { + if (!parent) return; + + this.removeObjectParent(obj); parent.updateMatrixWorld(); - obj.applyMatrix(new THREE.Matrix4().getInverse(parent.matrixWorld)) - parent.add(obj) + obj.applyMatrix(new THREE.Matrix4().getInverse(parent.matrixWorld)); + parent.add(obj); }, applyObjectMatrix(obj, matrix) { const inverse = new THREE.Matrix4().getInverse(matrix); - obj.children.forEach(child=>{ + obj.children.forEach(child => { child.applyMatrix(inverse); - }) + }); obj.applyMatrix(matrix); }, liftObjectOnlyChildMatrix(obj) { - if(obj.children.length !== 1) return; - + if (obj.children.length !== 1) return; + const child = obj.children[0]; const m = child.matrix; obj.applyMatrix(m); child.applyMatrix(new THREE.Matrix4().getInverse(m)); - } + }, + computeBoundingBox: (function () { + const caches = {}; + const initialBox = new THREE.Box3( + new THREE.Vector3(Infinity, Infinity, Infinity), + new THREE.Vector3(-Infinity, -Infinity, -Infinity) + ); + const tmpMatrix = new THREE.Matrix4(); + return function computeBoundingBox(obj) { + let cache = caches[obj.uuid]; + if (!cache) { + cache = { + geometry: obj.isMesh && obj.geometry.clone(), + lastMatrix: new THREE.Matrix4(), + lastBbox: new THREE.Box3().copy(initialBox) + }; + caches[obj.uuid] = cache; + } + const { geometry, lastMatrix, lastBbox } = cache; + + if (obj.isMesh) { + obj.updateMatrixWorld(); + if (lastBbox.equals(initialBox) || !lastMatrix.equals(obj.matrixWorld)) { + geometry.applyMatrix(obj.matrixWorld); + if(!geometry.boundingBox) { + geometry.computeBoundingBox(); + } + lastBbox.copy(geometry.boundingBox); + lastMatrix.copy(obj.matrixWorld); + geometry.applyMatrix(tmpMatrix.getInverse(obj.matrixWorld)); + } + } else { + lastBbox.copy(initialBox); + } + + for (const child of obj.children) { + const cBBox = ThreeUtils.computeBoundingBox(child); + lastBbox.expandByPoint(cBBox.min).expandByPoint(cBBox.max); + } + + return lastBbox; + }; + }()) }; export default ThreeUtils; - diff --git a/src/app/models/Model.js b/src/app/models/Model.js index cd65f64fc0..b6a022f407 100644 --- a/src/app/models/Model.js +++ b/src/app/models/Model.js @@ -462,23 +462,7 @@ class Model { computeBoundingBox() { if (this.sourceType === '3d') { - // after operated(move/scale/rotate), model.geometry is not changed - // so need to call: geometry.applyMatrix(matrixLocal); - // then call: geometry.computeBoundingBox(); to get operated modelMesh BoundingBox - // clone this.convexGeometry then clone.computeBoundingBox() is faster. - if (this.convexGeometry) { - const clone = this.convexGeometry.clone(); - this.meshObject.updateMatrixWorld(); - clone.applyMatrix(this.meshObject.matrixWorld); - clone.computeBoundingBox(); - this.boundingBox = clone.boundingBox; - } else { - const clone = this.meshObject.geometry.clone(); - this.meshObject.updateMatrixWorld(); - clone.applyMatrix(this.meshObject.matrixWorld); - clone.computeBoundingBox(); - this.boundingBox = clone.boundingBox; - } + this.boundingBox = ThreeUtils.computeBoundingBox(this.meshObject); } else { const { width, height, rotationZ, scaleX, scaleY } = this.transformation; const bboxWidth = (Math.abs(width * Math.cos(rotationZ)) + Math.abs(height * Math.sin(rotationZ))) * scaleX; @@ -878,7 +862,7 @@ class Model { removeVertexColors() { const bufferGeometry = this.meshObject.geometry; bufferGeometry.removeAttribute('color'); - this.setSelected(true); + this.setSelected(); this.modelGroup && this.modelGroup.modelChanged(); } diff --git a/src/app/models/ModelGroup.js b/src/app/models/ModelGroup.js index 1079aeae60..9e7bdc6e26 100644 --- a/src/app/models/ModelGroup.js +++ b/src/app/models/ModelGroup.js @@ -1,4 +1,4 @@ -import { Vector3, Group, Euler, Box3, Quaternion, Matrix4, BufferGeometry, MeshPhongMaterial, Mesh, DoubleSide } from 'three'; +import { Vector3, Group, Euler, Matrix4, BufferGeometry, MeshPhongMaterial, Mesh, DoubleSide } from 'three'; import EventEmitter from 'events'; // import { EPSILON } from '../../constants'; import uuid from 'uuid'; @@ -149,39 +149,9 @@ class ModelGroup extends EventEmitter { // Calculate selectedGroup's BBox in modelGroup getSelectedModelBBoxDes() { const selectedGroup = this.selectedGroup; - const boundingBoxArray = this.selectedGroup.boundingBox; - if (selectedGroup.children.length > 0 && boundingBoxArray.length > 0 && selectedGroup.shouldUpdateBoundingbox) { - const maxObjectBoundingBox = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); - const minObjectBoundingBox = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); - selectedGroup.children.forEach((child, index) => { - const boundingBox = boundingBoxArray[index]; - const objectChildrenPosition = new Vector3(); - const objectChildrenScale = new Vector3(); - const objectChildrenQuaternion = new Quaternion(); - // object - child.updateMatrixWorld(); - child.matrixWorld.decompose(objectChildrenPosition, objectChildrenQuaternion, objectChildrenScale); - - if (selectedGroup.children.length === 1) { - minObjectBoundingBox.x = Math.min(boundingBox.min.x, minObjectBoundingBox.x); - minObjectBoundingBox.y = Math.min(boundingBox.min.y, minObjectBoundingBox.y); - minObjectBoundingBox.z = Math.min(boundingBox.min.z, minObjectBoundingBox.z); - - maxObjectBoundingBox.x = Math.max(boundingBox.max.x, maxObjectBoundingBox.x); - maxObjectBoundingBox.y = Math.max(boundingBox.max.y, maxObjectBoundingBox.y); - maxObjectBoundingBox.z = Math.max(boundingBox.max.z, maxObjectBoundingBox.z); - } else { - minObjectBoundingBox.x = Math.min(boundingBox.min.x + objectChildrenPosition.x - selectedGroup.position.x, minObjectBoundingBox.x); - minObjectBoundingBox.y = Math.min(boundingBox.min.y + objectChildrenPosition.y - selectedGroup.position.y, minObjectBoundingBox.y); - minObjectBoundingBox.z = Math.min(0, minObjectBoundingBox.z); - - maxObjectBoundingBox.x = Math.max(boundingBox.max.x + objectChildrenPosition.x - selectedGroup.position.x, maxObjectBoundingBox.x); - maxObjectBoundingBox.y = Math.max(boundingBox.max.y + objectChildrenPosition.y - selectedGroup.position.y, maxObjectBoundingBox.y); - maxObjectBoundingBox.z = Math.max(boundingBox.max.z - boundingBox.min.z, maxObjectBoundingBox.z); - } - }); + if (selectedGroup.children.length > 0 && selectedGroup.shouldUpdateBoundingbox) { const whd = new Vector3(0, 0, 0); - new Box3(minObjectBoundingBox, maxObjectBoundingBox).getSize(whd); + ThreeUtils.computeBoundingBox(this.selectedGroup).getSize(whd); // width-depth-height return `${whd.x.toFixed(1)} x ${whd.y.toFixed(1)} x ${whd.z.toFixed(1)} mm`; } else { diff --git a/src/app/widgets/PrintingVisualizer/VisualizerModelTransformation.jsx b/src/app/widgets/PrintingVisualizer/VisualizerModelTransformation.jsx index 3b80b7b029..d1d066c75d 100644 --- a/src/app/widgets/PrintingVisualizer/VisualizerModelTransformation.jsx +++ b/src/app/widgets/PrintingVisualizer/VisualizerModelTransformation.jsx @@ -35,6 +35,7 @@ class VisualizerModelTransformation extends PureComponent { y: PropTypes.number }).isRequired, isSupporting: PropTypes.bool.isRequired, + isSupportSelected: PropTypes.bool.isRequired, modelSize: PropTypes.object.isRequired, // transformation: PropTypes.object, // getControls: PropTypes.func.isRequired, @@ -147,7 +148,7 @@ class VisualizerModelTransformation extends PureComponent { render() { const actions = this.actions; - const { size, selectedModelArray, transformMode, transformation, defaultSupportSize, isSupporting, modelSize, supportActions } = this.props; + const { size, selectedModelArray, transformMode, transformation, defaultSupportSize, isSupporting, isSupportSelected, modelSize, supportActions } = this.props; let moveX = 0; let moveY = 0; let scaleXPercent = 100; @@ -164,10 +165,6 @@ class VisualizerModelTransformation extends PureComponent { return model.visible === true && !model.supportTag; })); - const isSupportSelected = selectedModelArray.length === 1 && selectedModelArray.every((model) => { - return model.supportTag; - }); - if (selectedModelArray.length >= 1) { moveX = Number(toFixed(transformation.positionX, 1)); @@ -666,9 +663,11 @@ const mapStateToProps = (state) => { transformMode } = printing; let modelSize = {}; - if (modelGroup.selectedModelArray.length === 1) { + const isSupportSelected = modelGroup.selectedModelArray.length === 1 && modelGroup.selectedModelArray.every((model) => { + return model.supportTag; + }); + if (isSupportSelected) { const model = modelGroup.selectedModelArray[0]; - model.computeBoundingBox(); const { min, max } = model.boundingBox; modelSize = { x: Number((max.x - min.x).toFixed(1)), @@ -682,6 +681,7 @@ const mapStateToProps = (state) => { transformation: modelGroup.getSelectedModelTransformationForPrinting(), // defaultSupportSize: modelGroup.defaultSupportSize, hasModel, + isSupportSelected, modelSize, transformMode }; diff --git a/src/server/lib/ToolPathGenerator/LaserToolPathGenerator.js b/src/server/lib/ToolPathGenerator/LaserToolPathGenerator.js index 447ab3ff57..659c378f6e 100644 --- a/src/server/lib/ToolPathGenerator/LaserToolPathGenerator.js +++ b/src/server/lib/ToolPathGenerator/LaserToolPathGenerator.js @@ -381,9 +381,9 @@ class LaserToolPathGenerator extends EventEmitter { // rotation: degree and counter-clockwise const rotationZ = transformation.rotationZ; - const flipFlag = transformation.flip; + // const flipFlag = transformation.flip; - flip(svg, flipFlag); + // flip(svg, flipFlag); scale(svg, { x: (transformation.scaleX < 0 ? -1 : 1) * targetWidth / originWidth, y: (transformation.scaleY < 0 ? -1 : 1) * targetHeight / originHeight