MoveTool.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * MoveTool.js
  3. *
  4. * @author realor
  5. */
  6. import { TransformationTool } from './TransformationTool.js'
  7. import { Controls } from '../ui/Controls.js'
  8. import { I18N } from '../i18n/I18N.js'
  9. import { PointSelector } from '../utils/PointSelector.js'
  10. import * as THREE from '../lib/three.module.js'
  11. class MoveTool extends TransformationTool {
  12. static CHANGED_PROPERTIES = ['position']
  13. constructor(application, options) {
  14. super(application)
  15. this.name = 'move'
  16. this.label = 'tool.move.label'
  17. this.className = 'move'
  18. this.snapDistance = 0.1
  19. this.snapPercent = 0.3
  20. this.setOptions(options)
  21. // internals
  22. this.anchorPointWorld = new THREE.Vector3()
  23. this.targetPointWorld = new THREE.Vector3()
  24. this.offset = 0
  25. this.moveVectorWorld = new THREE.Vector3()
  26. this.createPanel()
  27. }
  28. createPanel() {
  29. this.panel = this.application.createPanel(this.label, 'left', 'panel_move')
  30. this.panel.preferredHeight = 140
  31. this.helpElem = document.createElement('div')
  32. this.panel.bodyElem.appendChild(this.helpElem)
  33. this.offsetInputElem = Controls.addNumberField(this.panel.bodyElem, 'move_offset', 'label.offset', 0)
  34. this.offsetInputElem.step = this.snapDistance
  35. this.offsetInputElem.addEventListener(
  36. 'change',
  37. event => {
  38. this.offset = this.offsetInputElem.value
  39. this.moveObjects()
  40. },
  41. false
  42. )
  43. this.buttonsPanel = document.createElement('div')
  44. this.panel.bodyElem.appendChild(this.buttonsPanel)
  45. this.acceptButton = Controls.addButton(this.buttonsPanel, 'cancel_section', 'button.accept', event => {
  46. this.offset = this.offsetInputElem.value
  47. this.moveObjects()
  48. this.setStage(0)
  49. })
  50. this.cancelButton = Controls.addButton(this.buttonsPanel, 'cancel_section', 'button.cancel', event => {
  51. this.offset = 0
  52. this.moveObjects()
  53. this.setStage(0)
  54. })
  55. }
  56. onPointerMove(event) {
  57. if (this.stage === 1) {
  58. const application = this.application
  59. const pointSelector = application.pointSelector
  60. if (!pointSelector.isPointSelectionEvent(event)) return
  61. event.preventDefault()
  62. const snap = pointSelector.snap
  63. if (snap) {
  64. this.targetPointWorld.copy(snap.positionWorld)
  65. const moveVectorWorld = this.moveVectorWorld
  66. moveVectorWorld.subVectors(this.targetPointWorld, this.anchorPointWorld)
  67. this.offset = moveVectorWorld.length()
  68. if (snap.type === PointSelector.GUIDE_SNAP) {
  69. let divisions = this.offset / this.snapDistance
  70. let intDivisions = Math.round(divisions)
  71. let decimals = divisions - intDivisions
  72. if (decimals >= -this.snapPercent && decimals <= this.snapPercent) {
  73. const k = 1000000
  74. this.offset = Math.round(k * intDivisions * this.snapDistance) / k
  75. }
  76. }
  77. this.moveObjects()
  78. this.offsetInputElem.value = this.offset
  79. } else {
  80. this.offsetInputElem.value = null
  81. }
  82. }
  83. }
  84. onPointerUp(event) {
  85. const application = this.application
  86. const pointSelector = application.pointSelector
  87. if (!pointSelector.isPointSelectionEvent(event)) return
  88. const snap = pointSelector.snap
  89. if (snap) {
  90. if (this.stage === 0 || this.stage === 2) {
  91. // anchor point
  92. this.objectMatrices.clear()
  93. this.anchorPointWorld.copy(snap.positionWorld)
  94. if (snap.object) {
  95. let axisMatrixWorld = this.axisMatrixWorld
  96. if (snap.object) {
  97. axisMatrixWorld.copy(snap.object.matrixWorld)
  98. } else {
  99. axisMatrixWorld.identity()
  100. }
  101. axisMatrixWorld.setPosition(this.anchorPointWorld)
  102. if (application.selection.isEmpty()) {
  103. application.selection.set(snap.object)
  104. }
  105. }
  106. this.setStage(1)
  107. } else if (this.stage === 1) {
  108. // destination point
  109. this.setStage(2)
  110. }
  111. } else {
  112. this.setStage(0)
  113. }
  114. }
  115. setStage(stage) {
  116. this.stage = stage
  117. const application = this.application
  118. switch (stage) {
  119. case 0: // set anchor point
  120. this.offset = 0
  121. application.pointSelector.clearAxisGuides()
  122. application.pointSelector.excludeSelection = false
  123. application.pointSelector.auxiliaryPoints = []
  124. application.pointSelector.activate()
  125. this.offsetInputElem.parentElement.style.display = 'none'
  126. this.buttonsPanel.style.display = 'none'
  127. I18N.set(this.helpElem, 'innerHTML', 'tool.move.select_anchor_point')
  128. application.i18n.update(this.helpElem)
  129. break
  130. case 1: // set destination point
  131. application.pointSelector.setAxisGuides(this.axisMatrixWorld, true)
  132. application.pointSelector.excludeSelection = true
  133. application.pointSelector.auxiliaryPoints = []
  134. application.pointSelector.activate()
  135. this.offsetInputElem.parentElement.style.display = ''
  136. this.offsetInputElem.disabled = true
  137. this.buttonsPanel.style.display = 'none'
  138. I18N.set(this.helpElem, 'innerHTML', 'tool.move.select_destination_point')
  139. application.i18n.update(this.helpElem)
  140. break
  141. case 2: // set distance
  142. application.pointSelector.clearAxisGuides()
  143. application.pointSelector.excludeSelection = false
  144. application.pointSelector.auxiliaryPoints = []
  145. application.pointSelector.activate()
  146. this.offsetInputElem.parentElement.style.display = ''
  147. this.offsetInputElem.disabled = false
  148. this.buttonsPanel.style.display = ''
  149. I18N.set(this.helpElem, 'innerHTML', 'tool.move.edit_offset')
  150. application.i18n.update(this.helpElem)
  151. break
  152. }
  153. }
  154. moveObjects() {
  155. const application = this.application
  156. const moveVectorWorld = this.moveVectorWorld
  157. moveVectorWorld.subVectors(this.targetPointWorld, this.anchorPointWorld)
  158. moveVectorWorld.normalize()
  159. moveVectorWorld.multiplyScalar(this.offset)
  160. const moveMatrixWorld = this._matrix1
  161. moveMatrixWorld.identity().setPosition(moveVectorWorld)
  162. this.transformObjects(moveMatrixWorld, MoveTool.CHANGED_PROPERTIES)
  163. }
  164. resetTool() {
  165. super.resetTool()
  166. this.offset = 0
  167. }
  168. }
  169. export { MoveTool }