BIMLayoutTool.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /*
  2. * BIMLayoutTool.js
  3. *
  4. * @author realor
  5. */
  6. import { Tool } from './Tool.js'
  7. import { Tree } from '../ui/Tree.js'
  8. import { ObjectUtils } from '../utils/ObjectUtils.js'
  9. import { Controls } from '../ui/Controls.js'
  10. import * as THREE from '../lib/three.module.js'
  11. class BIMLayoutTool extends Tool {
  12. constructor(application, options) {
  13. super(application)
  14. this.name = 'bim_layout'
  15. this.label = 'bim|tool.bim_layout.label'
  16. this.help = 'bim|tool.bim_layout.help'
  17. this.className = 'bim_layout'
  18. this.setOptions(options)
  19. this._onPointerUp = this.onPointerUp.bind(this)
  20. this._onPointerDown = this.onPointerDown.bind(this)
  21. this._onPointerMove = this.onPointerMove.bind(this)
  22. this._animate = this.animate.bind(this)
  23. this.rotateStart = new THREE.Vector2()
  24. this.rotateEnd = new THREE.Vector2()
  25. this.rotateVector = new THREE.Vector2()
  26. this.phi = 0
  27. this.theta = 0
  28. this.selectedObject = null
  29. this.createPanel()
  30. }
  31. createPanel() {
  32. this.panel = this.application.createPanel(this.label, 'left')
  33. this.layoutPanelElem = document.createElement('div')
  34. this.layoutPanelElem.className = 'bim_layout_panel'
  35. this.panel.bodyElem.appendChild(this.layoutPanelElem)
  36. this.panel.bodyElem.classList.add('padding')
  37. this.showAllButton = Controls.addButton(this.layoutPanelElem, 'show_all', 'bim|button.show_all', () => this.showAll())
  38. this.layoutTree = new Tree(this.layoutPanelElem)
  39. }
  40. activate() {
  41. this.panel.visible = true
  42. if (this.selectedObject !== this.application.selection.object) {
  43. this.selectedObject = null
  44. }
  45. const application = this.application
  46. const container = application.container
  47. container.addEventListener('pointerdown', this._onPointerDown, false)
  48. application.addEventListener('animation', this._animate)
  49. this.sites = []
  50. let sites = this.sites
  51. let site = null
  52. let building = null
  53. let storey = null
  54. let space = null
  55. let getBIMClass = object => {
  56. let bimClass = null
  57. if (object.name.indexOf('.') !== 0) {
  58. let ifcData = object.userData['IFC']
  59. if (ifcData) {
  60. bimClass = ifcData['ifcClassName']
  61. }
  62. }
  63. return bimClass
  64. }
  65. let exploreObject = object => {
  66. let bimClass = getBIMClass(object)
  67. if (bimClass === 'IfcSite') {
  68. site = { object: object, buildings: [], rotation: 0 }
  69. site.rotation = object.rotation.z
  70. sites.push(site)
  71. } else if (bimClass === 'IfcBuilding') {
  72. building = { object: object, storeys: [] }
  73. site.buildings.push(building)
  74. } else if (bimClass === 'IfcBuildingStorey') {
  75. storey = { object: object, spaces: [] }
  76. building.storeys.push(storey)
  77. } else if (bimClass === 'IfcSpace') {
  78. storey.spaces.push({ object: object })
  79. }
  80. let children = object.children
  81. for (let i = 0; i < children.length; i++) {
  82. let child = children[i]
  83. exploreObject(child)
  84. }
  85. }
  86. exploreObject(this.application.baseObject)
  87. this.layoutTree.clear()
  88. const layoutTree = this.layoutTree
  89. for (let s = 0; s < sites.length; s++) {
  90. site = sites[s]
  91. const siteTreeNode = layoutTree.addNode(site.object.name, event => this.focusOnObject(event, site.object, 'IfcSite', 'front'), 'IfcSite')
  92. for (let b = 0; b < site.buildings.length; b++) {
  93. let building = site.buildings[b]
  94. const buildingTreeNode = siteTreeNode.addNode(building.object.name, event => this.focusOnObject(event, building.object, 'IfcBuilding', 'front'), 'IfcBuilding')
  95. for (let st = 0; st < building.storeys.length; st++) {
  96. let storey = building.storeys[st]
  97. const storeyTreeNode = buildingTreeNode.addNode(storey.object.name, event => this.focusOnObject(event, storey.object, 'IfcBuildingStorey', 'top'), 'IfcBuildingStorey')
  98. for (let sp = 0; sp < storey.spaces.length; sp++) {
  99. let space = storey.spaces[sp]
  100. const spaceTreeNode = storeyTreeNode.addNode(space.object.name, event => this.focusOnObject(event, space.object, 'IfcSpace', 'top'), 'IfcSpace')
  101. }
  102. }
  103. }
  104. siteTreeNode.expand(2)
  105. }
  106. }
  107. deactivate() {
  108. this.panel.visible = false
  109. const application = this.application
  110. const container = application.container
  111. container.removeEventListener('pointerdown', this._onPointerDown, false)
  112. application.removeEventListener('animation', this._animate)
  113. }
  114. animate(event) {
  115. if (this.updateCamera && this.selectedObject) {
  116. const application = this.application
  117. const container = application.container
  118. const aspect = container.clientWidth / container.clientHeight
  119. const camera = application.camera
  120. camera.rotation.x = 0
  121. camera.rotation.y = 0
  122. camera.rotation.z = 0
  123. camera.rotateZ(this.theta)
  124. camera.rotateX(this.phi)
  125. camera.updateMatrix()
  126. application.scene.updateMatrixWorld(true)
  127. ObjectUtils.zoomAll(camera, this.selectedObject, aspect, true)
  128. application.notifyObjectsChanged(camera, this)
  129. this.updateCamera = false
  130. }
  131. }
  132. onPointerDown(event) {
  133. if (!this.isCanvasEvent(event)) return
  134. event.preventDefault()
  135. const container = this.application.container
  136. container.addEventListener('pointermove', this._onPointerMove, false)
  137. container.addEventListener('pointerup', this._onPointerUp, false)
  138. this.rotateStart = this.getEventPosition(event)
  139. }
  140. onPointerMove(event) {
  141. if (!this.isCanvasEvent(event)) return
  142. event.preventDefault()
  143. let pointerPosition = this.getEventPosition(event)
  144. this.rotateEnd.copy(pointerPosition)
  145. this.rotateVector.subVectors(this.rotateEnd, this.rotateStart)
  146. this.rotateStart.copy(this.rotateEnd)
  147. this.theta -= 0.005 * this.rotateVector.x
  148. this.phi -= 0.005 * this.rotateVector.y
  149. if (this.phi < 0) this.phi = 0
  150. else if (this.phi > Math.PI) this.phi = Math.PI
  151. this.updateCamera = true
  152. }
  153. onPointerUp(event) {
  154. const container = this.application.container
  155. container.removeEventListener('pointermove', this._onPointerMove, false)
  156. container.removeEventListener('pointerup', this._onPointerUp, false)
  157. }
  158. showAll() {
  159. const application = this.application
  160. application.baseObject.traverse(function(obj) {
  161. let ifcData = obj.userData.IFC
  162. if (ifcData) {
  163. let ifcClassName = ifcData.ifcClassName
  164. if (ifcClassName === 'IfcSite' || ifcClassName === 'IfcBuilding' || ifcClassName === 'IfcBuildingStorey' || ifcClassName === 'IfcSpace') {
  165. if (!obj.visible) {
  166. obj.visible = true
  167. application.notifyObjectsChanged(obj, this)
  168. }
  169. }
  170. }
  171. })
  172. this.pointCamera(90, 0)
  173. this.selectedObject = application.baseObject
  174. application.selection.set(application.baseObject)
  175. }
  176. focusOnObject(event, object, ifcClassName, view) {
  177. event.preventDefault()
  178. const application = this.application
  179. // search site object
  180. let siteObject = object
  181. while (siteObject) {
  182. if (siteObject.userData.IFC && siteObject.userData.IFC.ifcClassName === 'IfcSite') break
  183. else siteObject = siteObject.parent
  184. }
  185. // search object of ifcClassName class
  186. let parentObject = object
  187. while (parentObject) {
  188. if (parentObject.userData.IFC && parentObject.userData.IFC.ifcClassName === ifcClassName) break
  189. else parentObject = parentObject.parent
  190. }
  191. application.baseObject.traverse(function(obj) {
  192. let ifcData = obj.userData.IFC
  193. if (ifcData) {
  194. let ifcClassName = ifcData.ifcClassName
  195. if (ifcClassName === 'IfcSite' || ifcClassName === 'IfcBuilding' || ifcClassName === 'IfcBuildingStorey' || ifcClassName === 'IfcSpace') {
  196. let oldVisibility = obj.visible
  197. obj.visible = obj === object || ObjectUtils.isObjectDescendantOf(obj, parentObject) || ObjectUtils.isObjectDescendantOf(object, obj)
  198. if (oldVisibility !== obj.visible) {
  199. application.notifyObjectsChanged(obj, this)
  200. }
  201. }
  202. }
  203. })
  204. let angle = 0
  205. if (siteObject) {
  206. const euler = new THREE.Euler()
  207. euler.setFromRotationMatrix(siteObject.matrixWorld)
  208. angle = THREE.MathUtils.radToDeg(euler.z)
  209. }
  210. if (view === 'top') {
  211. this.pointCamera(0, angle)
  212. } else if (view === 'front') {
  213. this.pointCamera(90, angle)
  214. }
  215. this.selectedObject = object
  216. application.selection.set(object)
  217. }
  218. pointCamera(phiDeg, tethaDeg) {
  219. this.phi = THREE.MathUtils.degToRad(phiDeg)
  220. this.theta = THREE.MathUtils.degToRad(tethaDeg)
  221. this.updateCamera = true
  222. }
  223. }
  224. export { BIMLayoutTool }