TransformationTool.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import {Utils} from "../utils.js";
  3. //add-------------------------------------
  4. const OpaWhenNotSelect = 0.75
  5. const ScaleRatio = 4
  6. const OutlineColor = 0x666666
  7. //----------------------------------------
  8. const hideFocusHandles = true//add
  9. export class TransformationTool {
  10. constructor(viewer) {
  11. this.viewer = viewer;
  12. this.scene = new THREE.Scene();
  13. this.selection = [];
  14. this.pivot = new THREE.Vector3();
  15. this.dragging = false;
  16. this.showPickVolumes = false;
  17. this.viewer.inputHandler.registerInteractiveScene(this.scene);
  18. this.viewer.inputHandler.addEventListener('selection_changed', (e) => {
  19. for(let selected of this.selection){
  20. this.viewer.inputHandler.blacklist.delete(selected);
  21. }
  22. this.selection = e.selection;
  23. for(let selected of this.selection){
  24. this.viewer.inputHandler.blacklist.add(selected);
  25. }
  26. });
  27. let red = Potree.config.axis.x.color
  28. let green = Potree.config.axis.y.color
  29. let blue = Potree.config.axis.z.color
  30. this.activeHandle = null;
  31. this.scaleHandles = {
  32. "scale.x+": {name: "scale.x+", node: new THREE.Object3D(), color: red, alignment: [+1, +0, +0]},
  33. "scale.x-": {name: "scale.x-", node: new THREE.Object3D(), color: red, alignment: [-1, +0, +0]},
  34. "scale.y+": {name: "scale.y+", node: new THREE.Object3D(), color: green, alignment: [+0, +1, +0]},
  35. "scale.y-": {name: "scale.y-", node: new THREE.Object3D(), color: green, alignment: [+0, -1, +0]},
  36. "scale.z+": {name: "scale.z+", node: new THREE.Object3D(), color: blue, alignment: [+0, +0, +1]},
  37. "scale.z-": {name: "scale.z-", node: new THREE.Object3D(), color: blue, alignment: [+0, +0, -1]},
  38. };
  39. this.focusHandles = {
  40. "focus.x+": {name: "focus.x+", node: new THREE.Object3D(), color: red, alignment: [+1, +0, +0]},
  41. "focus.x-": {name: "focus.x-", node: new THREE.Object3D(), color: red, alignment: [-1, +0, +0]},
  42. "focus.y+": {name: "focus.y+", node: new THREE.Object3D(), color: green, alignment: [+0, +1, +0]},
  43. "focus.y-": {name: "focus.y-", node: new THREE.Object3D(), color: green, alignment: [+0, -1, +0]},
  44. "focus.z+": {name: "focus.z+", node: new THREE.Object3D(), color: blue, alignment: [+0, +0, +1]},
  45. "focus.z-": {name: "focus.z-", node: new THREE.Object3D(), color: blue, alignment: [+0, +0, -1]},
  46. };
  47. this.translationHandles = {
  48. "translation.x": {name: "translation.x", node: new THREE.Object3D(), color: red, alignment: [1, 0, 0]},
  49. "translation.y": {name: "translation.y", node: new THREE.Object3D(), color: green, alignment: [0, 1, 0]},
  50. "translation.z": {name: "translation.z", node: new THREE.Object3D(), color: blue, alignment: [0, 0, 1]},
  51. };
  52. this.rotationHandles = {
  53. "rotation.x": {name: "rotation.x", node: new THREE.Object3D(), color: red, alignment: [1, 0, 0]},
  54. "rotation.y": {name: "rotation.y", node: new THREE.Object3D(), color: green, alignment: [0, 1, 0]},
  55. "rotation.z": {name: "rotation.z", node: new THREE.Object3D(), color: blue, alignment: [0, 0, 1]},
  56. };
  57. this.handles = Object.assign({}, this.scaleHandles, hideFocusHandles?{}:this.focusHandles, this.translationHandles, this.rotationHandles);
  58. this.pickVolumes = [];
  59. this.initializeScaleHandles();
  60. this.initializeFocusHandles();
  61. this.initializeTranslationHandles();
  62. this.initializeRotationHandles();
  63. let boxFrameGeometry = new THREE.Geometry();
  64. {
  65. // bottom
  66. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5));
  67. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5));
  68. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5));
  69. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5));
  70. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5));
  71. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5));
  72. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5));
  73. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5));
  74. // top
  75. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5));
  76. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5));
  77. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5));
  78. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5));
  79. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5));
  80. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5));
  81. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5));
  82. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5));
  83. // sides
  84. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5));
  85. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5));
  86. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5));
  87. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5));
  88. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5));
  89. boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5));
  90. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5));
  91. boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5));
  92. }
  93. this.frame = new THREE.LineSegments(boxFrameGeometry, new THREE.LineBasicMaterial({color: 0xffff00}));
  94. this.scene.add(this.frame);
  95. viewer.setObjectLayers(this.scene, 'transformationTool' )
  96. }
  97. initializeScaleHandles(){
  98. let sgSphere = new THREE.SphereGeometry(1, 32, 32);
  99. let sgLowPolySphere = new THREE.SphereGeometry(1, 16, 16);
  100. for(let handleName of Object.keys(this.scaleHandles)){
  101. let handle = this.scaleHandles[handleName];
  102. let node = handle.node;
  103. this.scene.add(node);
  104. node.position.set(...handle.alignment).multiplyScalar(0.5);
  105. let material = new THREE.MeshBasicMaterial({
  106. color: handle.color,
  107. opacity: OpaWhenNotSelect,
  108. transparent: true
  109. });
  110. let outlineMaterial = new THREE.MeshBasicMaterial({
  111. color: OutlineColor,
  112. side: THREE.BackSide,
  113. opacity: OpaWhenNotSelect,
  114. transparent: true});
  115. let pickMaterial = new THREE.MeshNormalMaterial({
  116. opacity: 0.2,
  117. transparent: true,
  118. visible: this.showPickVolumes});
  119. let sphere = new THREE.Mesh(sgSphere, material);
  120. sphere.scale.set(2, 2, 2 );
  121. sphere.name = `${handleName}.handle`;
  122. node.add(sphere);
  123. let outline = new THREE.Mesh(sgSphere, outlineMaterial);
  124. outline.scale.set(1.1, 1.1, 1.1);
  125. outline.name = `${handleName}.outline`;
  126. sphere.add(outline);
  127. let pickSphere = new THREE.Mesh(sgLowPolySphere, pickMaterial);
  128. pickSphere.name = `${handleName}.pick_volume`;
  129. pickSphere.scale.set(1.5, 1.5, 1.5);
  130. sphere.add(pickSphere);
  131. pickSphere.handle = handleName;
  132. this.pickVolumes.push(pickSphere);
  133. node.setOpacity = (target) => {
  134. let opacity = {x: material.opacity};
  135. let t = new TWEEN.Tween(opacity).to({x: target}, 100);
  136. t.onUpdate(() => {
  137. sphere.visible = opacity.x > 0;
  138. pickSphere.visible = opacity.x > 0;
  139. material.opacity = opacity.x;
  140. outlineMaterial.opacity = opacity.x;
  141. pickSphere.material.opacity = opacity.x * 0.5;
  142. });
  143. t.start();
  144. };
  145. pickSphere.addEventListener("drag", (e) => this.dragScaleHandle(e));
  146. pickSphere.addEventListener("drop", (e) => this.dropScaleHandle(e));
  147. pickSphere.addEventListener("mouseover", e => {
  148. //node.setOpacity(1);
  149. });
  150. pickSphere.addEventListener("click", e => {
  151. e.consume();
  152. });
  153. pickSphere.addEventListener("mouseleave", e => {
  154. //node.setOpacity(OpaWhenNotSelect);
  155. });
  156. }
  157. }
  158. initializeFocusHandles(){
  159. if(hideFocusHandles)return//add
  160. //let sgBox = new THREE.BoxGeometry(1, 1, 1);
  161. let sgPlane = new THREE.PlaneGeometry(4, 4, 1, 1);
  162. let sgLowPolySphere = new THREE.SphereGeometry(1, 16, 16);
  163. let texture = new THREE.TextureLoader().load(`${exports.resourcePath}/icons/eye_2.png`);
  164. for(let handleName of Object.keys(this.focusHandles)){
  165. let handle = this.focusHandles[handleName];
  166. let node = handle.node;
  167. this.scene.add(node);
  168. let align = handle.alignment;
  169. //node.lookAt(new THREE.Vector3().addVectors(node.position, new THREE.Vector3(...align)));
  170. node.lookAt(new THREE.Vector3(...align));
  171. let off = 0.8;
  172. if(align[0] === 1){
  173. node.position.set(1, off, -off).multiplyScalar(0.5);
  174. node.rotation.z = Math.PI / 2;
  175. }else if(align[0] === -1){
  176. node.position.set(-1, -off, -off).multiplyScalar(0.5);
  177. node.rotation.z = Math.PI / 2;
  178. }else if(align[1] === 1){
  179. node.position.set(-off, 1, -off).multiplyScalar(0.5);
  180. node.rotation.set(Math.PI / 2, Math.PI, 0.0);
  181. }else if(align[1] === -1){
  182. node.position.set(off, -1, -off).multiplyScalar(0.5);
  183. node.rotation.set(Math.PI / 2, 0.0, 0.0);
  184. }else if(align[2] === 1){
  185. node.position.set(off, off, 1).multiplyScalar(0.5);
  186. }else if(align[2] === -1){
  187. node.position.set(-off, off, -1).multiplyScalar(0.5);
  188. }
  189. let material = new THREE.MeshBasicMaterial({
  190. color: handle.color,
  191. opacity: 0,
  192. transparent: true,
  193. map: texture
  194. });
  195. //let outlineMaterial = new THREE.MeshBasicMaterial({
  196. // color: 0x000000,
  197. // side: THREE.BackSide,
  198. // opacity: 0,
  199. // transparent: true});
  200. let pickMaterial = new THREE.MeshNormalMaterial({
  201. //opacity: 0,
  202. transparent: true,
  203. visible: this.showPickVolumes});
  204. let box = new THREE.Mesh(sgPlane, material);
  205. box.name = `${handleName}.handle`;
  206. box.scale.set(1.5, 1.5, 1.5);
  207. box.position.set(0, 0, 0);
  208. box.visible = false;
  209. node.add(box);
  210. //handle.focusNode = box;
  211. //let outline = new THREE.Mesh(sgPlane, outlineMaterial);
  212. //outline.scale.set(1.4, 1.4, 1.4);
  213. //outline.name = `${handleName}.outline`;
  214. //box.add(outline);
  215. let pickSphere = new THREE.Mesh(sgLowPolySphere, pickMaterial);
  216. pickSphere.name = `${handleName}.pick_volume`;
  217. pickSphere.scale.set(2, 2, 2);
  218. box.add(pickSphere);
  219. pickSphere.handle = handleName;
  220. this.pickVolumes.push(pickSphere);
  221. node.setOpacity = (target) => {
  222. let opacity = {x: material.opacity};
  223. let t = new TWEEN.Tween(opacity).to({x: target}, 100);
  224. t.onUpdate(() => {
  225. pickSphere.visible = opacity.x > 0;
  226. box.visible = opacity.x > 0;
  227. material.opacity = opacity.x;
  228. //outlineMaterial.opacity = opacity.x;
  229. pickSphere.material.opacity = opacity.x * 0.5;
  230. });
  231. t.start();
  232. };
  233. //pickSphere.addEventListener("drag", e => {});
  234. pickSphere.addEventListener("mouseup", e => {
  235. e.consume();
  236. });
  237. pickSphere.addEventListener("mousedown", e => {
  238. e.consume();
  239. });
  240. pickSphere.addEventListener("click", e => {
  241. e.consume();
  242. let selected = this.selection[0];
  243. let maxScale = Math.max(...selected.scale.toArray());
  244. let minScale = Math.min(...selected.scale.toArray());
  245. let handleLength = Math.abs(selected.scale.dot(new THREE.Vector3(...handle.alignment)));
  246. let alignment = new THREE.Vector3(...handle.alignment).multiplyScalar(2 * maxScale / handleLength);
  247. alignment.applyMatrix4(selected.matrixWorld);
  248. let newCamPos = alignment;
  249. let newCamTarget = selected.getWorldPosition(new THREE.Vector3());
  250. Utils.moveTo(this.viewer.scene, newCamPos, newCamTarget);
  251. });
  252. pickSphere.addEventListener("mouseover", e => {
  253. //box.setOpacity(1);
  254. });
  255. pickSphere.addEventListener("mouseleave", e => {
  256. //box.setOpacity(OpaWhenNotSelect);
  257. });
  258. }
  259. }
  260. initializeTranslationHandles(){
  261. let boxGeometry = new THREE.BoxGeometry(1, 1, 1);
  262. for(let handleName of Object.keys(this.translationHandles)){
  263. let handle = this.handles[handleName];
  264. let node = handle.node;
  265. this.scene.add(node);
  266. let material = new THREE.MeshBasicMaterial({
  267. color: handle.color,
  268. opacity: OpaWhenNotSelect,
  269. transparent: true});
  270. let outlineMaterial = new THREE.MeshBasicMaterial({
  271. color: OutlineColor,
  272. side: THREE.BackSide,
  273. opacity: OpaWhenNotSelect,
  274. transparent: true});
  275. let pickMaterial = new THREE.MeshNormalMaterial({
  276. opacity: 0.2,
  277. transparent: true,
  278. visible: this.showPickVolumes
  279. });
  280. let box = new THREE.Mesh(boxGeometry, material);
  281. box.name = `${handleName}.handle`;
  282. box.scale.set(1, 1, 36);
  283. box.lookAt(new THREE.Vector3(...handle.alignment));
  284. box.renderOrder = 10;
  285. node.add(box);
  286. handle.translateNode = box;
  287. let outline = new THREE.Mesh(boxGeometry, outlineMaterial);
  288. outline.name = `${handleName}.outline`;
  289. outline.scale.set(1.3, 1.3, 1.01);
  290. outline.renderOrder = 0;
  291. box.add(outline);
  292. let pickVolume = new THREE.Mesh(boxGeometry, pickMaterial);
  293. pickVolume.name = `${handleName}.pick_volume`;
  294. pickVolume.scale.set(4, 4, 1.1);
  295. pickVolume.handle = handleName;
  296. box.add(pickVolume);
  297. this.pickVolumes.push(pickVolume);
  298. node.setOpacity = (target) => {
  299. let opacity = {x: material.opacity};
  300. let t = new TWEEN.Tween(opacity).to({x: target}, 100);
  301. t.onUpdate(() => {
  302. box.visible = opacity.x > 0;
  303. pickVolume.visible = opacity.x > 0;
  304. material.opacity = opacity.x;
  305. outlineMaterial.opacity = opacity.x;
  306. pickMaterial.opacity = opacity.x * 0.5;
  307. });
  308. t.start();
  309. };
  310. pickVolume.addEventListener("drag", (e) => {this.dragTranslationHandle(e)});
  311. pickVolume.addEventListener("drop", (e) => {this.dropTranslationHandle(e)});
  312. }
  313. }
  314. initializeRotationHandles(){
  315. let adjust = 1.5;
  316. let torusGeometry = new THREE.TorusGeometry(1, adjust * 0.015, 8, 64, Math.PI / 2);
  317. let outlineGeometry = new THREE.TorusGeometry(1, adjust * 0.018, 8, 64, Math.PI / 2);
  318. let pickGeometry = new THREE.TorusGeometry(1, adjust * 0.07, 6, 4, Math.PI / 2);
  319. for(let handleName of Object.keys(this.rotationHandles)){
  320. let handle = this.handles[handleName];
  321. let node = handle.node;
  322. this.scene.add(node);
  323. let material = new THREE.MeshBasicMaterial({
  324. color: handle.color,
  325. opacity: OpaWhenNotSelect,
  326. transparent: true
  327. });
  328. let outlineMaterial = new THREE.MeshBasicMaterial({
  329. color: OutlineColor,
  330. side: THREE.BackSide,
  331. opacity: OpaWhenNotSelect,
  332. transparent: true
  333. });
  334. let pickMaterial = new THREE.MeshNormalMaterial({
  335. opacity: 0.2,
  336. transparent: true,
  337. visible: this.showPickVolumes
  338. });
  339. let box = new THREE.Mesh(torusGeometry, material);
  340. box.name = `${handleName}.handle`;
  341. box.scale.set(30, 30, 30);
  342. box.lookAt(new THREE.Vector3(...handle.alignment));
  343. node.add(box);
  344. handle.translateNode = box;
  345. let outline = new THREE.Mesh(outlineGeometry, outlineMaterial);
  346. outline.name = `${handleName}.outline`;
  347. outline.scale.set(1, 1, 1);
  348. outline.renderOrder = 0;
  349. box.add(outline);
  350. let pickVolume = new THREE.Mesh(pickGeometry, pickMaterial);
  351. pickVolume.name = `${handleName}.pick_volume`;
  352. pickVolume.scale.set(1, 1, 1);
  353. pickVolume.handle = handleName;
  354. box.add(pickVolume);
  355. this.pickVolumes.push(pickVolume);
  356. node.setOpacity = (target) => {
  357. let opacity = {x: material.opacity};
  358. let t = new TWEEN.Tween(opacity).to({x: target}, 100);
  359. t.onUpdate(() => {
  360. box.visible = opacity.x > 0;
  361. pickVolume.visible = opacity.x > 0;
  362. material.opacity = opacity.x;
  363. outlineMaterial.opacity = opacity.x;
  364. pickMaterial.opacity = opacity.x * 0.5;
  365. });
  366. t.start();
  367. };
  368. //pickVolume.addEventListener("mouseover", (e) => {
  369. // //let a = this.viewer.scene.getActiveCamera().getWorldDirection(new THREE.Vector3()).dot(pickVolume.getWorldDirection(new THREE.Vector3()));
  370. // console.log(pickVolume.getWorldDirection(new THREE.Vector3()));
  371. //});
  372. pickVolume.addEventListener("drag", (e) => {this.dragRotationHandle(e)});
  373. pickVolume.addEventListener("drop", (e) => {this.dropRotationHandle(e)});
  374. }
  375. }
  376. dragRotationHandle(e){
  377. let drag = e.drag;
  378. let handle = this.activeHandle;
  379. let camera = this.viewer.scene.getActiveCamera();
  380. if(!handle){
  381. return
  382. };
  383. let localNormal = new THREE.Vector3(...handle.alignment);
  384. let n = new THREE.Vector3();
  385. n.copy(new THREE.Vector4(...localNormal.toArray(), 0).applyMatrix4(handle.node.matrixWorld));
  386. n.normalize();
  387. if (!drag.intersectionStart){
  388. //this.viewer.scene.scene.remove(this.debug);
  389. //this.debug = new THREE.Object3D();
  390. //this.viewer.scene.scene.add(this.debug);
  391. //Utils.debugSphere(this.debug, drag.location, 3, 0xaaaaaa);
  392. //let debugEnd = drag.location.clone().add(n.clone().multiplyScalar(20));
  393. //Utils.debugLine(this.debug, drag.location, debugEnd, 0xff0000);
  394. drag.intersectionStart = drag.location;
  395. drag.objectStart = drag.object.getWorldPosition(new THREE.Vector3());
  396. drag.handle = handle;
  397. let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(n, drag.intersectionStart);
  398. drag.dragPlane = plane;
  399. drag.pivot = drag.intersectionStart;
  400. }else{
  401. handle = drag.handle;
  402. }
  403. this.dragging = true;
  404. let pointer = this.viewer.inputHandler.pointer
  405. let domElement = this.viewer.renderer.domElement;
  406. let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
  407. let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3());
  408. if (I) {
  409. let center = this.scene.getWorldPosition(new THREE.Vector3());
  410. let from = drag.pivot;
  411. let to = I;
  412. let v1 = from.clone().sub(center).normalize();
  413. let v2 = to.clone().sub(center).normalize();
  414. let angle = Math.acos(v1.dot(v2));
  415. let sign = Math.sign(v1.cross(v2).dot(n));
  416. angle = angle * sign;
  417. if (Number.isNaN(angle)) {
  418. return;
  419. }
  420. let normal = new THREE.Vector3(...handle.alignment);
  421. for (let selection of this.selection) {
  422. selection.rotateOnAxis(normal, angle);
  423. selection.dispatchEvent({
  424. type: "orientation_changed",
  425. object: selection
  426. });
  427. }
  428. drag.pivot = I;
  429. }
  430. }
  431. dropRotationHandle(e){
  432. this.dragging = false;
  433. this.setActiveHandle(null);
  434. }
  435. dragTranslationHandle(e){
  436. let drag = e.drag;
  437. let handle = this.activeHandle;
  438. let camera = this.viewer.scene.getActiveCamera();
  439. if(!drag.intersectionStart && handle){
  440. drag.intersectionStart = drag.location;
  441. drag.objectStart = drag.object.getWorldPosition(new THREE.Vector3());
  442. let start = drag.intersectionStart;
  443. let dir = new THREE.Vector4(...handle.alignment, 0).applyMatrix4(this.scene.matrixWorld);
  444. let end = new THREE.Vector3().addVectors(start, dir);
  445. let line = new THREE.Line3(start.clone(), end.clone());
  446. drag.line = line;
  447. let camOnLine = line.closestPointToPoint(camera.position, false, new THREE.Vector3());
  448. let normal = new THREE.Vector3().subVectors(camera.position, camOnLine);
  449. let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, drag.intersectionStart);
  450. drag.dragPlane = plane;
  451. drag.pivot = drag.intersectionStart;
  452. }else{
  453. handle = drag.handle;
  454. }
  455. this.dragging = true;
  456. {
  457. let pointer = this.viewer.inputHandler.pointer
  458. let domElement = this.viewer.renderer.domElement;
  459. let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
  460. let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3());
  461. if (I) {
  462. let iOnLine = drag.line.closestPointToPoint(I, false, new THREE.Vector3());
  463. let diff = new THREE.Vector3().subVectors(iOnLine, drag.pivot);
  464. for (let selection of this.selection) {
  465. selection.position.add(diff);
  466. selection.dispatchEvent({
  467. type: "position_changed",
  468. object: selection
  469. });
  470. }
  471. drag.pivot = drag.pivot.add(diff);
  472. }
  473. }
  474. }
  475. dropTranslationHandle(e){
  476. this.dragging = false;
  477. this.setActiveHandle(null);
  478. }
  479. dropScaleHandle(e){
  480. this.dragging = false;
  481. this.setActiveHandle(null);
  482. }
  483. dragScaleHandle(e){
  484. let drag = e.drag;
  485. let handle = this.activeHandle;
  486. let camera = this.viewer.scene.getActiveCamera();
  487. if(!drag.intersectionStart){
  488. drag.intersectionStart = drag.location;
  489. drag.objectStart = drag.object.getWorldPosition(new THREE.Vector3());
  490. drag.handle = handle;
  491. let start = drag.intersectionStart;
  492. let dir = new THREE.Vector4(...handle.alignment, 0).applyMatrix4(this.scene.matrixWorld);
  493. let end = new THREE.Vector3().addVectors(start, dir);
  494. let line = new THREE.Line3(start.clone(), end.clone());
  495. drag.line = line;
  496. let camOnLine = line.closestPointToPoint(camera.position, false, new THREE.Vector3());
  497. let normal = new THREE.Vector3().subVectors(camera.position, camOnLine);
  498. let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, drag.intersectionStart);
  499. drag.dragPlane = plane;
  500. drag.pivot = drag.intersectionStart;
  501. //Utils.debugSphere(viewer.scene.scene, drag.pivot, 0.05);
  502. }else{
  503. handle = drag.handle;
  504. }
  505. this.dragging = true;
  506. {
  507. let pointer = this.viewer.inputHandler.pointer
  508. let domElement = this.viewer.renderer.domElement;
  509. let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
  510. let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3());
  511. if (I) {
  512. let iOnLine = drag.line.closestPointToPoint(I, false, new THREE.Vector3());
  513. let direction = handle.alignment.reduce( (a, v) => a + v, 0);
  514. let toObjectSpace = this.selection[0].matrixWorld.clone().invert();
  515. let iOnLineOS = iOnLine.clone().applyMatrix4(toObjectSpace);
  516. let pivotOS = drag.pivot.clone().applyMatrix4(toObjectSpace);
  517. let diffOS = new THREE.Vector3().subVectors(iOnLineOS, pivotOS);
  518. let dragDirectionOS = diffOS.clone().normalize();
  519. if(iOnLine.distanceTo(drag.pivot) === 0){
  520. dragDirectionOS.set(0, 0, 0);
  521. }
  522. let dragDirection = dragDirectionOS.dot(new THREE.Vector3(...handle.alignment));
  523. let diff = new THREE.Vector3().subVectors(iOnLine, drag.pivot);
  524. let diffScale = new THREE.Vector3(...handle.alignment).multiplyScalar(diff.length() * direction * dragDirection);
  525. let diffPosition = diff.clone().multiplyScalar(0.5);
  526. for (let selection of this.selection) {
  527. selection.scale.add(diffScale);
  528. selection.scale.x = Math.max(0.1, selection.scale.x);
  529. selection.scale.y = Math.max(0.1, selection.scale.y);
  530. selection.scale.z = Math.max(0.1, selection.scale.z);
  531. selection.position.add(diffPosition);
  532. selection.dispatchEvent({
  533. type: "position_changed",
  534. object: selection
  535. });
  536. selection.dispatchEvent({
  537. type: "scale_changed",
  538. object: selection
  539. });
  540. }
  541. drag.pivot.copy(iOnLine);
  542. //Utils.debugSphere(viewer.scene.scene, drag.pivot, 0.05);
  543. }
  544. }
  545. }
  546. setActiveHandle(handle){
  547. if(this.dragging){
  548. return;
  549. }
  550. if(this.activeHandle === handle){
  551. return;
  552. }
  553. this.activeHandle = handle;
  554. if(handle === null){
  555. for(let handleName of Object.keys(this.handles)){
  556. let handle = this.handles[handleName];
  557. handle.node.setOpacity(0);
  558. }
  559. }
  560. if(!hideFocusHandles){
  561. for(let handleName of Object.keys(this.focusHandles)){
  562. let handle = this.focusHandles[handleName];
  563. if(this.activeHandle === handle){
  564. handle.node.setOpacity(1.0);
  565. }else{
  566. handle.node.setOpacity(OpaWhenNotSelect)
  567. }
  568. }
  569. }
  570. for(let handleName of Object.keys(this.translationHandles)){
  571. let handle = this.translationHandles[handleName];
  572. if(this.activeHandle === handle){
  573. handle.node.setOpacity(1.0);
  574. }else{
  575. handle.node.setOpacity(OpaWhenNotSelect)
  576. }
  577. }
  578. for(let handleName of Object.keys(this.rotationHandles)){
  579. let handle = this.rotationHandles[handleName];
  580. //if(this.activeHandle === handle){
  581. // handle.node.setOpacity(1.0);
  582. //}else{
  583. // handle.node.setOpacity(OpaWhenNotSelect)
  584. //}
  585. handle.node.setOpacity(OpaWhenNotSelect);
  586. }
  587. for(let handleName of Object.keys(this.scaleHandles)){
  588. let handle = this.scaleHandles[handleName];
  589. if(this.activeHandle === handle){
  590. handle.node.setOpacity(1.0);
  591. if(!hideFocusHandles){
  592. let relatedFocusHandle = this.focusHandles[handle.name.replace("scale", "focus")];
  593. let relatedFocusNode = relatedFocusHandle.node;
  594. relatedFocusNode.setOpacity(OpaWhenNotSelect);
  595. }
  596. for(let translationHandleName of Object.keys(this.translationHandles)){
  597. let translationHandle = this.translationHandles[translationHandleName];
  598. translationHandle.node.setOpacity(OpaWhenNotSelect);
  599. }
  600. //let relatedTranslationHandle = this.translationHandles[
  601. // handle.name.replace("scale", "translation").replace(/[+-]/g, "")];
  602. //let relatedTranslationNode = relatedTranslationHandle.node;
  603. //relatedTranslationNode.setOpacity(OpaWhenNotSelect);
  604. }else{
  605. handle.node.setOpacity(OpaWhenNotSelect)
  606. }
  607. }
  608. if(handle){
  609. handle.node.setOpacity(1.0);
  610. }
  611. }
  612. update () {
  613. if(this.selection.length === 1){
  614. this.scene.visible = true;
  615. this.scene.updateMatrix();
  616. this.scene.updateMatrixWorld();
  617. let selected = this.selection[0];
  618. let world = selected.matrixWorld;
  619. let camera = this.viewer.scene.getActiveCamera();
  620. let domElement = this.viewer.renderer.domElement;
  621. let pointer = this.viewer.inputHandler.pointer;
  622. let center = selected.boundingBox.getCenter(new THREE.Vector3()).clone().applyMatrix4(selected.matrixWorld);
  623. this.scene.scale.copy(selected.boundingBox.getSize(new THREE.Vector3()).multiply(selected.scale));
  624. this.scene.position.copy(center);
  625. this.scene.rotation.copy(selected.rotation);
  626. this.scene.updateMatrixWorld();
  627. {
  628. // adjust scale of components
  629. for(let handleName of Object.keys(this.handles)){
  630. let handle = this.handles[handleName];
  631. let node = handle.node;
  632. let handlePos = node.getWorldPosition(new THREE.Vector3());
  633. let distance = handlePos.distanceTo(camera.position);
  634. let pr = Utils.projectedRadius(1, camera, distance, domElement.clientWidth, domElement.clientHeight);
  635. let ws = node.parent.getWorldScale(new THREE.Vector3());
  636. let s = (ScaleRatio / pr);
  637. let scale = new THREE.Vector3(s, s, s).divide(ws);
  638. let rot = new THREE.Matrix4().makeRotationFromEuler(node.rotation);
  639. let rotInv = rot.clone().invert();
  640. scale.applyMatrix4(rotInv);
  641. scale.x = Math.abs(scale.x);
  642. scale.y = Math.abs(scale.y);
  643. scale.z = Math.abs(scale.z);
  644. node.scale.copy(scale);
  645. }
  646. // adjust rotation handles
  647. if(!this.dragging){
  648. let tWorld = this.scene.matrixWorld;
  649. let tObject = tWorld.clone().invert();
  650. let camObjectPos = camera.getWorldPosition(new THREE.Vector3()).applyMatrix4(tObject);
  651. let x = this.rotationHandles["rotation.x"].node.rotation;
  652. let y = this.rotationHandles["rotation.y"].node.rotation;
  653. let z = this.rotationHandles["rotation.z"].node.rotation;
  654. x.order = "ZYX";
  655. y.order = "ZYX";
  656. let above = camObjectPos.z > 0;
  657. let below = !above;
  658. let PI_HALF = Math.PI / 2;
  659. if(above){
  660. if(camObjectPos.x > 0 && camObjectPos.y > 0){
  661. x.x = 1 * PI_HALF;
  662. y.y = 3 * PI_HALF;
  663. z.z = 0 * PI_HALF;
  664. }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
  665. x.x = 1 * PI_HALF;
  666. y.y = 2 * PI_HALF;
  667. z.z = 1 * PI_HALF;
  668. }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
  669. x.x = 2 * PI_HALF;
  670. y.y = 2 * PI_HALF;
  671. z.z = 2 * PI_HALF;
  672. }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
  673. x.x = 2 * PI_HALF;
  674. y.y = 3 * PI_HALF;
  675. z.z = 3 * PI_HALF;
  676. }
  677. }else if(below){
  678. if(camObjectPos.x > 0 && camObjectPos.y > 0){
  679. x.x = 0 * PI_HALF;
  680. y.y = 0 * PI_HALF;
  681. z.z = 0 * PI_HALF;
  682. }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
  683. x.x = 0 * PI_HALF;
  684. y.y = 1 * PI_HALF;
  685. z.z = 1 * PI_HALF;
  686. }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
  687. x.x = 3 * PI_HALF;
  688. y.y = 1 * PI_HALF;
  689. z.z = 2 * PI_HALF;
  690. }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
  691. x.x = 3 * PI_HALF;
  692. y.y = 0 * PI_HALF;
  693. z.z = 3 * PI_HALF;
  694. }
  695. }
  696. }
  697. {
  698. let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
  699. let raycaster = new THREE.Raycaster(ray.origin, ray.direction);
  700. raycaster.layers.enableAll()//add
  701. let intersects = raycaster.intersectObjects(this.pickVolumes.filter(v => v.visible), true);
  702. if(intersects.length > 0){
  703. let I = intersects[0];
  704. let handleName = I.object.handle;
  705. this.setActiveHandle(this.handles[handleName]);
  706. }else{
  707. this.setActiveHandle(null);
  708. }
  709. }
  710. //
  711. for(let handleName of Object.keys(this.scaleHandles)){
  712. let handle = this.handles[handleName];
  713. let node = handle.node;
  714. let alignment = handle.alignment;
  715. }
  716. }
  717. }else{
  718. this.scene.visible = false;
  719. }
  720. }
  721. };