12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520 |
- /**
- * Application.js
- *
- * @author realor
- */
- import { Panel, PanelManager } from '../ui/Panel.js'
- import { ProgressBar } from '../ui/ProgressBar.js'
- import { MenuBar } from '../ui/Menu.js'
- import { Tool } from '../tools/Tool.js'
- import { ToolBar } from '../ui/ToolBar.js'
- import { MessageDialog } from '../ui/MessageDialog.js'
- import { Cord } from '../core/Cord.js'
- import { Profile } from '../core/Profile.js'
- import { Solid } from '../core/Solid.js'
- import { Cloner } from '../builders/Cloner.js'
- import { ObjectBuilder } from '../builders/ObjectBuilder.js'
- import { SolidGeometry } from '../core/SolidGeometry.js'
- import { ServiceManager } from '../io/ServiceManager.js'
- import { IOManager } from '../io/IOManager.js'
- import { Selection } from '../utils/Selection.js'
- import { ObjectUtils } from '../utils/ObjectUtils.js'
- import { WebUtils } from '../utils/WebUtils.js'
- import { ModuleLoader } from '../utils/ModuleLoader.js'
- import { WEBGL } from '../utils/WebGL.js'
- import { PointSelector } from '../utils/PointSelector.js'
- import { LineDashedShaderMaterial } from '../materials/LineDashedShaderMaterial.js'
- import { Inspector } from '../ui/Inspector.js'
- import { FakeRenderer } from '../renderers/FakeRenderer.js'
- import { Formula } from '../formula/Formula.js'
- import { LoginDialog } from './LoginDialog.js'
- import { ScriptDialog } from './ScriptDialog.js'
- import { I18N } from '../i18n/I18N.js'
- import * as THREE from '../lib/three.module.js'
- import { transitions, easing, lerp } from '../utils/transitions.js'
- class Application {
- static NAME = 'BIMROCKET'
- static VERSION = '$VERSION$'
- static EDGES_SELECTION = 'edges'
- static FACES_SELECTION = 'faces'
- static UNITS = [
- ['km', 'units.km'],
- ['m', 'units.m'],
- ['cm', 'units.cm'],
- ['mm', 'units.mm'],
- ['in', 'units.in'],
- ]
- static SET_SELECTION_MODE = 'set'
- static ADD_SELECTION_MODE = 'add'
- static REMOVE_SELECTION_MODE = 'remove'
- constructor(element = document.body) {
- this.element = element
- this.i18n = new I18N()
- this.scene = null
- this.camera = null
- this.perspectiveCamera = null
- this.orthographicCamera = null
- this.baseObject = null
- this.clippingPlane = null
- this.clippingGroup = null
- this.overlays = null
- this.tools = {}
- this.tool = null
- this._units = null
- this._decimals = null
- this._backgroundColor1 = null
- this._backgroundColor2 = null
- this._panelOpacity = null
- this._frameRateDivisor = null
- this._shadowsEnabled = null
- /* services */
- this.services = {} // Service instances
- /* selection */
- this.selection = new Selection(this, true)
- this.selectionMode = Application.SET_SELECTION_MODE
- this.selectionPaintMode = Application.EDGES_SELECTION
- this._showDeepSelection = null
- this._showLocalAxes = null
- this._selectionLines = null
- this._axisLines = null
- this.clock = new THREE.Clock()
- this.autoRepaint = false
- this.needsRepaint = true
- /* internal properties */
- this._cutObjects = []
- this._eventListeners = {
- scene: [],
- selection: [],
- animation: [],
- tool: [],
- }
- this.loadingManager = new THREE.LoadingManager()
- const loadingManager = this.loadingManager
- loadingManager.onStart = (url, itemsLoaded, itemsTotal) => {
- if (!url.startsWith('data:')) {
- console.info(url, itemsLoaded, itemsTotal)
- }
- }
- loadingManager.onLoad = () => {
- console.info('Load completed.')
- setTimeout(() => {
- document.querySelector('button.tool_button.orbit').click()
- }, 2000)
- this.repaint()
- }
- THREE.Object3D.DefaultMatrixAutoUpdate = false
- THREE.Object3D.DefaultUp = new THREE.Vector3(0, 0, 1)
- THREE.Object3D.HIDDEN_PREFIX = '.'
- /* create sub elements */
- const logoPanelElem = document.createElement('div')
- logoPanelElem.className = 'logo_panel'
- logoPanelElem.addEventListener('click', () => this.hideLogo())
- this.logoPanel = logoPanelElem
- element.appendChild(logoPanelElem)
- const bigLogoImage = document.createElement('img')
- bigLogoImage.src = 'css/images/grey-logo.png'
- bigLogoImage.title = Application.NAME
- bigLogoImage.alt = Application.NAME
- logoPanelElem.appendChild(bigLogoImage)
- const headerElem = document.createElement('header')
- element.appendChild(headerElem)
- // const logoLink = document.createElement('a')
- // logoLink.className = 'logo_link'
- // logoLink.addEventListener('click', event => this.showLogo())
- // headerElem.appendChild(logoLink)
- // const logoImage = document.createElement('img')
- // logoImage.src = 'css/images/bimrocket.svg'
- // logoImage.title = Application.NAME
- // logoImage.alt = Application.NAME
- // logoLink.appendChild(logoImage)
- const toolBarElem = document.createElement('div')
- toolBarElem.className = 'toolbar'
- element.appendChild(toolBarElem)
- const container = document.createElement('div')
- container.className = 'container'
- container.style.touchAction = 'none'
- this.container = container
- element.appendChild(container)
- const progressBarElem = document.createElement('div')
- progressBarElem.className = 'progress_bar'
- element.appendChild(progressBarElem)
- this.restoreBackground()
- this.updateBackground()
- // renderer
- let renderer
- if (WEBGL.isWebGLAvailable()) {
- // WebGL renderer
- renderer = new THREE.WebGLRenderer({
- antialias: true,
- alpha: true,
- preserveDrawingBuffer: true,
- })
- renderer.shadowMap.enabled = this.shadowsEnabled
- renderer.shadowMap.type = THREE.PCFSoftShadowMap
- } else {
- // fake renderer
- renderer = new FakeRenderer()
- }
- this.renderer = renderer
- renderer.alpha = true
- renderer.setClearColor(0x000000, 0)
- renderer.sortObjects = true
- renderer.setPixelRatio(window.devicePixelRatio)
- renderer.setSize(container.clientWidth, container.clientHeight)
- container.appendChild(renderer.domElement)
- // panelManager
- this.panelManager = new PanelManager(container)
- // general tabbed panel
- this.progressBar = new ProgressBar(progressBarElem)
- // menuBar
- this.menuBar = new MenuBar(this, headerElem)
- // toolBar
- this.toolBar = new ToolBar(this, toolBarElem)
- /* selection materials */
- const selectionColor = new THREE.Color(0, 0, 1)
- this.selectionMaterial = new THREE.LineBasicMaterial({ color: selectionColor, linewidth: 1.5, depthTest: true, depthWrite: true, transparent: true })
- this.deepSelectionMaterial = new THREE.LineBasicMaterial({ color: selectionColor, linewidth: 1.5, depthTest: false, depthWrite: false, transparent: true })
- this.boxSelectionMaterial = new THREE.LineBasicMaterial({ color: selectionColor, linewidth: 1.5, depthTest: true, depthWrite: true })
- this.invisibleSelectionMaterial = new LineDashedShaderMaterial({ color: selectionColor, dashSize: 4, gapSize: 4, depthTest: true, depthWrite: true })
- this.deepInvisibleSelectionMaterial = new LineDashedShaderMaterial({ color: selectionColor, dashSize: 4, gapSize: 4, depthTest: false, depthWrite: false, transparent: true })
- this.pointSelector = new PointSelector(this)
- // set language
- this.i18n.userLanguages = window.localStorage.getItem('bimrocket.language') || navigator.language
- // init scene
- this.initScene()
- // listeners
- this.addEventListener('scene', event => {
- if (event.type === 'cameraActivated') {
- this.repaint()
- } else if (event.type !== 'cut') {
- if (event.type === 'nodeChanged') {
- let updateSelection = false
- for (let object of event.objects) {
- if (event.source !== ObjectBuilder) {
- object.needsRebuild = true
- }
- if (object instanceof THREE.Camera && event.source instanceof Inspector) {
- // inspector has changed a camera
- const camera = object
- camera.updateProjectionMatrix()
- } else if (this.selection.contains(object)) {
- updateSelection = true
- }
- }
- if (updateSelection) this.updateSelection()
- } else if (event.type === 'added' || event.type === 'removed') {
- event.parent.needsRebuild = true
- }
- this.repaint()
- }
- })
- this.addEventListener('selection', event => {
- if (event.type === 'changed') {
- this.updateSelection()
- }
- })
- window.addEventListener('resize', this.onResize.bind(this), false)
- // window.addEventListener('beforeunload', event => {
- // if (this.baseObject.children.length > 0) {
- // event.preventDefault()
- // event.returnValue = ''
- // }
- // })
- // animation
- let __animationEvent = { delta: 0 }
- let __animationCounter = 0
- let animate = () => {
- requestAnimationFrame(animate)
- __animationEvent.delta = this.clock.getDelta()
- if (this._eventListeners.animation.length > 0) {
- this.notifyEventListeners('animation', __animationEvent)
- }
- transitions.update(__animationEvent.delta) //add
- __animationCounter++
- if (__animationCounter >= this.frameRateDivisor) {
- __animationCounter = 0
- if (this.autoRepaint || this.needsRepaint) {
- this.render()
- }
- }
- }
- animate()
- }
- initScene() {
- const container = this.container
- if (this.scene) {
- ObjectUtils.dispose(this.scene)
- this.stopControllers()
- }
- this.scene = new THREE.Scene()
- const scene = this.scene
- scene.userData.selection = { type: 'none' }
- scene.name = 'Scene'
- // Add lights
- const hemisphereLight = new THREE.HemisphereLight(0xf0f0f0, 0x808080, 1)
- hemisphereLight.name = 'HemisphereLight'
- hemisphereLight.updateMatrix()
- scene.add(hemisphereLight)
- const sunLight = new THREE.DirectionalLight(0xffffff, 1)
- sunLight.position.x = 20
- sunLight.position.y = 20
- sunLight.position.z = 80
- sunLight.name = 'SunLight'
- sunLight.castShadow = true
- sunLight.shadow.mapSize.width = 4096
- sunLight.shadow.mapSize.height = 4096
- sunLight.shadow.camera.left = -40
- sunLight.shadow.camera.right = 40
- sunLight.shadow.camera.top = 40
- sunLight.shadow.camera.bottom = -40
- sunLight.shadow.camera.far = 3000
- sunLight.shadow.camera.near = 0.01
- sunLight.shadow.camera.matrixAutoUpdate = true
- sunLight.shadow.bias = -0.0001
- sunLight.target = scene
- scene.add(sunLight)
- sunLight.updateMatrix()
- // initial camera
- let camera = new THREE.OrthographicCamera(-10, 10, 10, -10, -2000, 2000)
- camera.position.set(0, -30, 2)
- camera.name = 'Orthographic'
- camera.updateProjectionMatrix()
- camera.updateMatrix()
- camera.lookAt(new THREE.Vector3(0, 0, 0))
- camera.updateMatrix()
- scene.add(camera)
- this.orthographicCamera = camera
- camera = new THREE.PerspectiveCamera(60, container.clientWidth / container.clientHeight, 0.1, 4000)
- camera.position.set(0, -10, 0.2)
- camera.name = 'Perspective'
- camera.aspect = container.clientWidth / container.clientHeight
- camera.updateProjectionMatrix()
- camera.updateMatrix()
- camera.lookAt(new THREE.Vector3(0, 0, 0))
- camera.updateMatrix()
- scene.add(camera)
- this.perspectiveCamera = camera
- this.camera = camera
- // Add base group
- this.baseObject = new THREE.Group()
- const baseObject = this.baseObject
- baseObject.name = 'Base'
- baseObject.userData.selection = { type: 'none' }
- baseObject.updateMatrix()
- scene.add(baseObject)
- // Add clipping group
- this.clippingGroup = new THREE.Group()
- this.clippingGroup.name = THREE.Object3D.HIDDEN_PREFIX + 'clipping'
- this.scene.add(this.clippingGroup)
- // Add overlays group
- this.overlays = new THREE.Group()
- this.overlays.name = THREE.Object3D.HIDDEN_PREFIX + 'overlays'
- this.scene.add(this.overlays)
- let sceneEvent = { type: 'structureChanged', objects: [this.scene], source: this }
- this.notifyEventListeners('scene', sceneEvent)
- this.selection.set(baseObject)
- }
- render() {
- this.renderer.render(this.scene, this.camera)
- this.needsRepaint = false
- }
- repaint() {
- this.needsRepaint = true
- }
- /* gets the model root depending on selection */
- getModelRoot(lowestRoot = false) {
- let root
- const roots = this.selection.roots
- const baseObject = this.baseObject
- if (roots.length === 0) {
- // nothing selected
- if (baseObject.children.length === 1) {
- root = baseObject.children[0]
- } else {
- root = baseObject
- }
- } else if (roots.length === 1) {
- root = roots[0]
- if (!lowestRoot) {
- // find top root (under baseObject)
- while (root.parent !== baseObject && root !== this.scene && root !== baseObject) {
- root = root.parent
- }
- }
- } // multiple roots
- else {
- root = baseObject
- }
- return root
- }
- get units() {
- if (this._units === null) {
- this._units = window.localStorage.getItem('bimrocket.units') || 'm'
- }
- return this._units
- }
- set units(units) {
- this._units = units
- window.localStorage.setItem('bimrocket.units', units)
- }
- get decimals() {
- if (this._decimals === null) {
- this._decimals = parseInt(window.localStorage.getItem('bimrocket.decimals') || '2')
- }
- return this._decimals
- }
- set decimals(decimals) {
- this._decimals = decimals
- window.localStorage.setItem('bimrocket.decimals', String(decimals))
- }
- get backgroundColor() {
- return this._backgroundColor1
- }
- set backgroundColor(color) {
- this._backgroundColor1 = color
- this._backgroundColor2 = color
- this.updateBackground()
- this.saveBackground()
- }
- get backgroundColor1() {
- return this._backgroundColor1
- }
- set backgroundColor1(color) {
- this._backgroundColor1 = color
- this.updateBackground()
- this.saveBackground()
- }
- get backgroundColor2() {
- return this._backgroundColor2
- }
- set backgroundColor2(color) {
- this._backgroundColor2 = color
- this.updateBackground()
- this.saveBackground()
- }
- get panelOpacity() {
- if (this._panelOpacity === null) {
- let opacityValue = window.localStorage.getItem('bimrocket.panelOpacity')
- this._panelOpacity = opacityValue ? parseFloat(opacityValue) : 0.8
- }
- return this._panelOpacity
- }
- set panelOpacity(opacity) {
- this._panelOpacity = opacity
- window.localStorage.setItem('bimrocket.panelOpacity', String(opacity))
- let panels = this.panelManager.getPanels()
- for (let panel of panels) {
- panel.opacity = opacity
- }
- }
- get frameRateDivisor() {
- if (this._frameRateDivisor === null) {
- let value = window.localStorage.getItem('bimrocket.frameRateDivisor')
- this._frameRateDivisor = value ? parseInt(value) : 1
- }
- return this._frameRateDivisor
- }
- set frameRateDivisor(frd) {
- this._frameRateDivisor = frd
- window.localStorage.setItem('bimrocket.frameRateDivisor', String(frd))
- }
- get showDeepSelection() {
- if (this._showDeepSelection === null) {
- this._showDeepSelection = window.localStorage.getItem('bimrocket.showDeepSelection') !== 'false'
- }
- return this._showDeepSelection
- }
- set showDeepSelection(enabled) {
- this._showDeepSelection = enabled
- window.localStorage.setItem('bimrocket.showDeepSelection', enabled)
- this.updateSelection()
- }
- get showLocalAxes() {
- if (this._showLocalAxes === null) {
- this._showLocalAxes = window.localStorage.getItem('bimrocket.showLocalAxes') !== 'false'
- }
- return this._showLocalAxes
- }
- set showLocalAxes(enabled) {
- this._showLocalAxes = enabled
- window.localStorage.setItem('bimrocket.showLocalAxes', enabled)
- this.updateSelection()
- }
- get shadowsEnabled() {
- if (this._shadowsEnabled === null) {
- this._shadowsEnabled = window.localStorage.getItem('bimrocket.shadowsEnabled') === 'true'
- }
- return this._shadowsEnabled
- }
- set shadowsEnabled(enabled) {
- this._shadowsEnabled = enabled
- window.localStorage.setItem('bimrocket.shadowsEnabled', enabled)
- if (this.renderer.shadowMap) {
- if (this.renderer.shadowMap.enabled !== enabled) {
- this.renderer.shadowMap.enabled = enabled
- this.scene.traverse(object => {
- if (object.material) object.material.needsUpdate = true
- })
- this.repaint()
- }
- }
- }
- updateBackground() {
- if (this._backgroundColor1 === this._backgroundColor2) {
- this.container.style.background = this._backgroundColor1
- } else {
- this.container.style.background = 'linear-gradient(' + this._backgroundColor1 + ',' + this._backgroundColor2 + ')'
- }
- }
- restoreBackground() {
- this._backgroundColor1 = window.localStorage.getItem('bimrocket.backgroundColor1')
- if (this._backgroundColor1 === null) this._backgroundColor1 = '#E0E0FF'
- this._backgroundColor2 = window.localStorage.getItem('bimrocket.backgroundColor2')
- if (this._backgroundColor2 === null) this._backgroundColor2 = '#E0F0E0'
- }
- saveBackground() {
- window.localStorage.setItem('bimrocket.backgroundColor1', this._backgroundColor1)
- window.localStorage.setItem('bimrocket.backgroundColor2', this._backgroundColor2)
- }
- updateSelection() {
- this.hideSelectionLines()
- this.showSelectionLines()
- this.hideAxisLines()
- if (this.showLocalAxes) {
- this.showAxisLines()
- }
- }
- hideSelectionLines() {
- if (this._selectionLines !== null) {
- this.overlays.remove(this._selectionLines)
- ObjectUtils.dispose(this._selectionLines)
- this._selectionLines = null
- this.repaint()
- }
- }
- showSelectionLines() {
- if (this._selectionLines === null && !this.selection.isEmpty()) {
- const linesGroup = new THREE.Group()
- linesGroup.renderOrder = 1
- linesGroup.name = 'SelectionLines'
- const iterator = this.selection.iterator
- let item = iterator.next()
- while (!item.done) {
- let object = item.value
- this.collectLines(object, linesGroup)
- item = iterator.next()
- }
- this._selectionLines = linesGroup
- this.overlays.add(this._selectionLines)
- this.repaint()
- }
- }
- transformSelectionLines(matrix) {
- if (this._selectionLines !== null) {
- const lines = this._selectionLines
- matrix.decompose(lines.position, lines.quaternion, lines.scale)
- lines.updateMatrix()
- this.repaint()
- }
- }
- collectLines(object, linesGroup) {
- let material = this.getSelectionMaterial(object)
- if (object instanceof Solid) {
- let solid = object
- if (this.selectionPaintMode === Application.EDGES_SELECTION) {
- let edgesGeometry = solid.edgesGeometry
- if (edgesGeometry) {
- let lines = new THREE.LineSegments(edgesGeometry, material)
- lines.name = 'SelectionLines'
- lines.raycast = function () {}
- solid.updateMatrixWorld()
- solid.matrixWorld.decompose(lines.position, lines.rotation, lines.scale)
- lines.updateMatrix()
- linesGroup.add(lines)
- }
- } // show faces (triangles)
- else {
- let geometry = solid.geometry
- if (geometry) {
- let edgesGeometry = geometry.getTrianglesGeometry()
- let lines = new THREE.LineSegments(edgesGeometry, material)
- lines.name = 'SelectionLines'
- lines.raycast = function () {}
- solid.updateMatrixWorld()
- solid.matrixWorld.decompose(lines.position, lines.rotation, lines.scale)
- lines.updateMatrix()
- linesGroup.add(lines)
- }
- }
- } else if (object instanceof THREE.Camera) {
- let camera = object
- if (camera !== this.camera) {
- camera.updateMatrixWorld()
- let lines = new THREE.CameraHelper(camera)
- lines.updateMatrix()
- lines.name = 'SelectionLines'
- lines.raycast = function () {}
- linesGroup.add(lines)
- }
- } else if (object instanceof THREE.DirectionalLight) {
- let light = object
- let lines = new THREE.DirectionalLightHelper(light, 1, 0x0)
- lines.name = 'SelectionLines'
- lines.raycast = function () {}
- lines.lightPlane.updateMatrix()
- lines.targetLine.updateMatrix()
- linesGroup.add(lines)
- } else if (object instanceof THREE.Mesh) {
- object.updateMatrixWorld()
- let edgesGeometry = new THREE.EdgesGeometry(object.geometry)
- let lines = new THREE.LineSegments(edgesGeometry, material)
- lines.raycast = function () {}
- lines.name = 'OuterLines'
- object.matrixWorld.decompose(lines.position, lines.rotation, lines.scale)
- lines.updateMatrix()
- linesGroup.add(lines)
- } else if (object instanceof Cord) {
- object.updateMatrixWorld()
- let lines = new THREE.LineSegments(object.geometry, material)
- lines.raycast = function () {}
- lines.name = 'Lines'
- object.matrixWorld.decompose(lines.position, lines.rotation, lines.scale)
- lines.updateMatrix()
- linesGroup.add(lines)
- } else if (object instanceof Profile) {
- object.updateMatrixWorld()
- let lines = new THREE.LineSegments(object.geometry, material)
- lines.raycast = function () {}
- lines.name = 'Lines'
- object.matrixWorld.decompose(lines.position, lines.rotation, lines.scale)
- lines.updateMatrix()
- linesGroup.add(lines)
- } else if (object instanceof THREE.Group || object instanceof THREE.Object3D) {
- object.updateMatrixWorld()
- let selectionProperties = object.userData.selection
- let selectionType = selectionProperties && selectionProperties.type ? selectionProperties.type : 'edges'
- if (selectionType === 'edges') {
- let children = object.children
- for (let i = 0; i < children.length; i++) {
- var child = children[i]
- this.collectLines(child, linesGroup)
- }
- } else if (selectionType === 'box') {
- let box = ObjectUtils.getLocalBoundingBox(object, true)
- if (!box.isEmpty()) {
- let geometry = ObjectUtils.getBoxGeometry(box)
- let lines = new THREE.LineSegments(geometry, object.visible ? this.boxSelectionMaterial : material)
- lines.raycast = function () {}
- object.updateMatrixWorld()
- object.matrixWorld.decompose(lines.position, lines.rotation, lines.scale)
- lines.updateMatrix()
- linesGroup.add(lines)
- }
- }
- }
- }
- getSelectionMaterial(object) {
- if (object.visible) {
- return this.showDeepSelection ? this.deepSelectionMaterial : this.selectionMaterial
- } else {
- return this.showDeepSelection ? this.deepInvisibleSelectionMaterial : this.invisibleSelectionMaterial
- }
- }
- hideAxisLines() {
- if (this._axisLines !== null) {
- this.overlays.remove(this._axisLines)
- ObjectUtils.dispose(this._axisLines)
- this._axisLines = null
- this.repaint()
- }
- }
- showAxisLines() {
- if (this._axisLines === null) {
- var object = this.selection.object
- if (object) {
- this._axisLines = new THREE.AxesHelper(1)
- var lines = this._axisLines
- lines.name = 'AxisLines'
- object.updateMatrixWorld(true)
- object.matrixWorld.decompose(lines.position, lines.rotation, lines.scale)
- lines.updateMatrix()
- lines.raycast = function () {}
- lines.material.depthTest = false
- lines.material.depthWrite = false
- this.overlays.add(lines)
- this.repaint()
- }
- }
- }
- addEventListener(type, eventListener) {
- var eventListeners = this._eventListeners[type]
- if (eventListeners) {
- eventListeners.push(eventListener)
- }
- }
- removeEventListener(type, eventListener) {
- var eventListeners = this._eventListeners[type]
- if (eventListeners) {
- var index = eventListeners.indexOf(eventListener)
- if (index !== -1) {
- eventListeners.splice(index, 1)
- }
- }
- }
- notifyEventListeners(type, event) {
- const eventListeners = this._eventListeners[type]
- for (let listener of eventListeners) {
- listener(event)
- }
- }
- addService(service, group, save = true) {
- if (group === undefined) {
- console.warn('group is mandatory.')
- return
- }
- if (this.services[group] === undefined) {
- this.services[group] = {}
- }
- this.services[group][service.name] = service
- if (save) this.saveServices(group)
- }
- removeService(service, group, save = true) {
- if (this.services[group]) {
- delete this.services[group][service.name]
- if (save) this.saveServices(group)
- }
- }
- saveServices(group) {
- let data = []
- let serviceGroup = this.services[group]
- for (let name in serviceGroup) {
- let service = serviceGroup[name]
- data.push({
- className: service.constructor.name,
- parameters: service.getParameters(),
- })
- }
- let json = JSON.stringify(data)
- window.localStorage.setItem('bimrocket.services.' + group, json)
- console.info('save services.' + group + ': ', data)
- }
- restoreServices(group) {
- let json = window.localStorage.getItem('bimrocket.services.' + group)
- if (json) {
- let array = JSON.parse(json)
- for (let i = 0; i < array.length; i++) {
- let entry = array[i]
- let className = entry.className
- let parameters = entry.parameters
- let serviceClass = ServiceManager.classes[className]
- if (serviceClass) {
- let service = new serviceClass()
- service.setParameters(parameters)
- this.addService(service, group, false)
- } else console.warn('Service ' + className + ' not restored.')
- }
- }
- }
- addTool(tool) {
- if (!this.tools[tool.name]) {
- this.tools[tool.name] = tool
- var toolEvent = { type: 'added', tool: tool }
- this.notifyEventListeners('tool', toolEvent)
- }
- }
- removeTool(tool) {
- if (this.tool === tool) return
- if (this.tools[tool.name]) {
- delete this.tools[tool.name]
- var toolEvent = { type: 'removed', tool: tool }
- this.notifyEventListeners('tool', toolEvent)
- }
- }
- useTool(tool) {
- if (typeof tool === 'string') {
- tool = this.tools[tool]
- }
- if (tool === undefined) tool = null
- let toolEvent
- if (tool && tool.immediate) {
- tool.execute()
- toolEvent = { type: 'executed', tool: tool }
- this.notifyEventListeners('tool', toolEvent)
- } else {
- if (this.tool === tool) return // already active
- if (this.tool !== null) {
- this.tool.deactivate()
- toolEvent = { type: 'deactivated', tool: this.tool }
- this.notifyEventListeners('tool', toolEvent)
- }
- this.tool = tool
- if (tool) {
- tool.activate()
- toolEvent = { type: 'activated', tool: tool }
- this.notifyEventListeners('tool', toolEvent)
- }
- }
- }
- addObject(object, parent = null, attach = false, select = false, source = this) {
- if (!(object instanceof THREE.Object3D)) return
- const scene = this.scene
- if (parent === null) {
- parent = this.selection.object || this.baseObject
- while (parent !== scene) {
- if (parent.type === 'Object3D' || parent.type === 'Group') {
- break
- } else {
- parent = parent.parent
- }
- }
- }
- if (parent === scene) {
- parent = this.baseObject
- }
- if (attach) {
- parent.attach(object)
- } else {
- parent.add(object)
- }
- object.updateMatrix()
- let addEvent = {
- type: 'added',
- object: object,
- parent: parent,
- source: source,
- }
- this.notifyEventListeners('scene', addEvent)
- if (select) {
- this.selection.set(object)
- }
- return object
- }
- removeObject(object, source = this) {
- if (!(object instanceof THREE.Object3D)) {
- object = this.selection.object
- }
- if (object && object !== this.baseObject && object !== this.scene) {
- this.stopControllers(object)
- let parent = object.parent
- if (parent) {
- parent.remove(object)
- ObjectUtils.dispose(object)
- }
- let removeEvent = {
- type: 'removed',
- object: object,
- parent: parent,
- source: source,
- }
- this.notifyEventListeners('scene', removeEvent)
- this.selection.remove(object) // TODO: unselect child objects
- }
- }
- cloneObject(object, dynamic = false) {
- if (object === undefined) {
- object = this.selection.object
- }
- if (object && object !== this.baseObject) {
- let clone
- if (dynamic) {
- clone = new THREE.Object3D()
- clone.name = object.name + '_cloner'
- clone.builder = new Cloner(object)
- clone.userData.selection = { group: true }
- ObjectBuilder.build(clone)
- } else {
- clone = object.clone(true)
- clone.name = object.name + '_clone'
- }
- this.addObject(clone, object.parent)
- }
- }
- cutObjects() {
- let cutObjects = this.selection.roots
- cutObjects = cutObjects.filter(root => root !== this.scene && root.parent !== this.scene)
- this._cutObjects = cutObjects
- if (cutObjects.length > 0) {
- let cutEvent = {
- type: 'cut',
- objects: cutObjects,
- source: this,
- }
- this.notifyEventListeners('scene', cutEvent)
- }
- }
- pasteObjects(parent) {
- let cutObjects = this._cutObjects
- if (cutObjects.length > 0) {
- if (parent === undefined) {
- parent = this.selection.object
- }
- if (parent instanceof THREE.Object3D) {
- let object = parent
- while (object && object !== this.baseObject && cutObjects.indexOf(object) === -1) {
- object = object.parent
- }
- if (object === this.baseObject) {
- // paste only under baseObject
- for (let cutObject of cutObjects) {
- let removeEvent = {
- type: 'removed',
- object: cutObject,
- parent: cutObject.parent,
- source: this,
- }
- let addEvent = {
- type: 'added',
- object: cutObject,
- parent: parent,
- source: this,
- }
- parent.attach(cutObject)
- cutObject.updateMatrix()
- cutObject.updateMatrixWorld()
- this.notifyEventListeners('scene', removeEvent)
- this.notifyEventListeners('scene', addEvent)
- }
- let pasteEvent = {
- type: 'pasted',
- objects: cutObjects,
- source: this,
- }
- this.notifyEventListeners('scene', pasteEvent)
- this._cutObjects = []
- }
- }
- this.selection.set(parent)
- }
- }
- notifyObjectsChanged(
- objects,
- source = this,
- type = 'nodeChanged',
- properties = null // properties: array of property names or null
- ) {
- if (objects instanceof THREE.Object3D) {
- objects = [objects]
- }
- let sceneEvent = {
- type: type,
- source: source,
- objects: objects,
- properties: properties,
- }
- this.notifyEventListeners('scene', sceneEvent)
- }
- selectParentObject() {
- if (!this.selection.isEmpty()) {
- let parent = this.selection.object.parent
- if (parent) {
- this.selection.set(parent)
- }
- }
- }
- findObjects(objectExpression, baseObject = this.baseObject, nested = false) {
- let objects
- if (objectExpression === null) {
- objects = this.selection.roots
- } else if (objectExpression instanceof THREE.Object3D) {
- objects = [objectExpression]
- } else if (objectExpression instanceof Array) {
- // assume objectExpression is an Object3D array
- objects = objectExpression
- } else if (typeof objectExpression === 'string' || typeof objectExpression === 'function') {
- const condition = ObjectUtils.createEvalFunction(objectExpression)
- objects = ObjectUtils.findObjects(condition, baseObject, nested)
- } else {
- objects = []
- }
- return objects
- }
- selectObjects(objectExpression, selectionMode) {
- const objects = this.findObjects(objectExpression)
- const selection = this.selection
- if (selectionMode === Application.ADD_SELECTION_MODE) {
- selection.add(...objects)
- } else if (selectionMode === Application.REMOVE_SELECTION_MODE) {
- selection.remove(...objects)
- } else {
- selection.set(...objects)
- }
- }
- updateVisibility(objectExpression, visible) {
- let objects = this.findObjects(objectExpression)
- const changed = ObjectUtils.updateVisibility(objects, visible)
- this.notifyObjectsChanged(Array.from(changed))
- return changed
- }
- updateStyle(objectExpression, edgesVisible, facesVisible) {
- let objects = this.findObjects(objectExpression)
- const changed = ObjectUtils.updateStyle(objects, edgesVisible, facesVisible)
- this.notifyObjectsChanged(Array.from(changed))
- return changed
- }
- updateAppearance(objectExpression, appearance) {
- let objects = this.findObjects(objectExpression)
- const changed = ObjectUtils.updateAppearance(objects, appearance)
- this.notifyObjectsChanged(Array.from(changed))
- return changed
- }
- updateObjects(objectExpression, updateFunction, recursive = false) {
- let objects = this.findObjects(objectExpression)
- const changed = ObjectUtils.updateObjects(objects, updateFunction, recursive)
- this.notifyObjectsChanged(Array.from(changed))
- return changed
- }
- userSelectObjects(objects, event) {
- event.preventDefault()
- const selection = this.selection
- if (event.shiftKey) {
- selection.add(...objects)
- } else if (event.ctrlKey) {
- selection.remove(...objects)
- } else {
- if (this.selectionMode === Application.ADD_SELECTION_MODE) {
- selection.add(...objects)
- } else if (this.selectionMode === Application.REMOVE_SELECTION_MODE) {
- selection.remove(...objects)
- } else {
- selection.set(...objects)
- }
- }
- }
- initControllers(object) {
- this.handleControllers(controller => controller.init(this), object)
- }
- startControllers(object) {
- const set = new Set()
- this.handleControllers(controller => {
- controller.start()
- set.add(controller.object)
- }, object)
- const objects = Array.from(set)
- this.notifyObjectsChanged(objects, this)
- }
- stopControllers(object) {
- const set = new Set()
- this.handleControllers(controller => {
- controller.stop()
- set.add(controller.object)
- }, object)
- const objects = Array.from(set)
- this.notifyObjectsChanged(objects, this)
- }
- handleControllers(handler, object) {
- if (object === undefined) object = this.scene
- object.traverse(obj => {
- let objectControllers = obj.controllers
- if (objectControllers) {
- for (let name in objectControllers) {
- let controller = objectControllers[name]
- handler(controller)
- }
- }
- })
- }
- createController(controllerClass = null, object = null, name = null) {
- if (controllerClass === null) return
- if (object === null) {
- object = this.baseObject
- }
- if (name === null || name.trim().length === 0) {
- let count = object.controllers ? Object.keys(object.controllers).length : 0
- name = 'ctr_' + count
- }
- let controller = new controllerClass(object, name)
- if (object.controllers === undefined) {
- object.controllers = {}
- }
- object.controllers[name] = controller
- controller.init(this)
- this.notifyObjectsChanged(object)
- return controller
- }
- evaluateFormulas() {
- const updatedObjects = Formula.updateTree(this.scene)
- console.info('Objects updated by formulas', updatedObjects)
- if (updatedObjects.length > 0) {
- this.notifyObjectsChanged(updatedObjects)
- }
- }
- rebuild() {
- const baseObject = this.baseObject
- const built = []
- ObjectBuilder.markAndBuild(baseObject, built)
- console.info('Objects rebuilt', built)
- if (built.length > 0) {
- let sceneEvent = { type: 'structureChanged', objects: built, source: ObjectBuilder }
- this.notifyEventListeners('scene', sceneEvent)
- this.updateSelection()
- }
- }
- updateCameraAspectRatio() {
- var camera = this.camera
- var container = this.container
- var aspect = container.clientWidth / container.clientHeight
- ObjectUtils.updateCameraAspectRatio(camera, aspect)
- }
- activateCamera(camera) {
- this.camera = camera
- this.updateCameraAspectRatio()
- const changeEvent = { type: 'cameraActivated', object: camera, source: this }
- this.notifyEventListeners('scene', changeEvent)
- }
- onResize() {
- this.updateCameraAspectRatio()
- const container = this.container
- const renderer = this.renderer
- renderer.setSize(container.clientWidth, container.clientHeight)
- this.repaint()
- }
- createPanel(title, position, className) {
- const panel = new Panel(this)
- if (title) panel.title = title
- if (position) panel.position = position
- if (className) panel.setClassName(className)
- this.panelManager.addPanel(panel)
- return panel
- }
- loadModules(...modulePaths) {
- const pendent = []
- const loadNextModule = () => {
- if (pendent.length > 0) {
- let modulePath = pendent.pop()
- ModuleLoader.load(modulePath)
- .then(
- module => {
- console.info('module ' + modulePath + ' completed.')
- module.load(this)
- },
- error => {
- console.info(`module " + modulePath + " failed: ${error}`)
- }
- )
- .finally(() => loadNextModule())
- } else {
- setTimeout(() => {
- this.hideLogo()
- this.loadModelFromUrl()
- }, 1000)
- }
- }
- for (let i = modulePaths.length - 1; i >= 0; i--) {
- pendent.push(modulePaths[i])
- }
- loadNextModule()
- }
- showLogo() {
- this.logoPanel.classList.add('visible')
- this.logoPanel.classList.remove('hidden')
- }
- hideLogo() {
- this.logoPanel.classList.add('hidden')
- this.logoPanel.classList.remove('visible')
- }
- fullscreen() {
- if (document.body.requestFullscreen) {
- // chrome & firefox
- document.body.requestFullscreen()
- } else if (document.body.webkitRequestFullscreen) {
- // safari
- document.body.webkitRequestFullscreen()
- }
- }
- loadModelFromUrl() {
- const params = WebUtils.getQueryParams()
- const url = params['url']
- if (url === undefined) return
- const application = this
- const intent = {
- url: url,
- onProgress: data => {
- application.progressBar.progress = data.progress
- application.progressBar.message = data.message
- },
- onCompleted: object => {
- //模型加载完毕
- application.addObject(object)
- application.progressBar.visible = false
- application.initTasks(params)
- console.log('loaded', object)
- let boundingBox = new THREE.Box3()
- object.traverse(child => {
- if (child instanceof THREE.Mesh) {
- child.geometry.computeBoundingBox()
- boundingBox.union(child.geometry.boundingBox.clone().applyMatrix4(child.matrixWorld)) //但感觉如果最外层object大小不为1,要还原下scale再乘
- //获取在scale为1时,表现出的大小
- }
- })
- object.boundingBox = boundingBox //未乘上matrixWorld的本地boundingBox
- object.matrixAutoUpdate = true //原先为什么是false
- this.getBoundCenter(object)
- this.moveBoundCenterTo(object, new THREE.Vector3(0, 0, 0))
- this.tools.zoom_all.execute()
- //this.tools.orbit.deactivate()
- //this.tools.panorama.activate()
- },
- onError: error => {
- application.progressBar.visible = false
- MessageDialog.create('ERROR', error).setClassName('error').setI18N(application.i18n).show()
- },
- options: { units: application.units },
- }
- application.progressBar.message = 'Loading file...'
- application.progressBar.progress = undefined
- application.progressBar.visible = true
- const auth = params['auth']
- if (auth === 'Basic') {
- let dialog = new LoginDialog(application)
- dialog.login = (username, password) => {
- intent.basicAuthCredentials = { username: username, password: password }
- IOManager.load(intent)
- }
- dialog.onCancel = () => {
- dialog.hide()
- application.progressBar.visible = false
- }
- dialog.show()
- } else {
- IOManager.load(intent) // async load
- }
- }
- initTasks(params) {
- const toolName = params['tool']
- if (toolName) {
- let tool = this.tools[toolName]
- if (tool) {
- this.useTool(tool)
- }
- } else {
- const scriptPath = params['script']
- if (scriptPath) {
- const request = new XMLHttpRequest()
- request.open('GET', scriptPath, true)
- request.onload = () => {
- console.info(request.status)
- if (request.status === 200) {
- const scriptCode = request.responseText
- const dialog = new ScriptDialog(this)
- dialog.scriptName = 'init_script'
- dialog.scriptCode = scriptCode
- let error = dialog.run()
- if (error) {
- dialog.show()
- }
- } else {
- let message = WebUtils.getHttpStatusMessage(request.status)
- MessageDialog.create('ERROR', "Can't open script: " + message + ' (HTTP ' + request.status + ')')
- .setClassName('error')
- .setI18N(this.i18n)
- .show()
- }
- }
- request.send()
- }
- }
- }
- //=============================
- moveBoundCenterTo(model, pos) {
- //使boundCenter在所要的位置
- let diff = new THREE.Vector3().subVectors(pos, model.boundCenter)
- model.position.add(diff)
- }
- getBoundCenter(model) {
- if (!model.boundCenter) model.boundCenter = new THREE.Vector3()
- model.boundingBox.getCenter(model.boundCenter).applyMatrix4(model.matrixWorld)
- }
- moveCamera() {}
- setView(info = {}) {
- let camera = this.camera // this.perspectiveCamera
- //this.cancelFlying()
- let done = () => {
- /* if(endTarget){
- this.lookAt(endTarget); //compute radius for orbitcontrol
- }else if(endQuaternion){
- this.rotation = new THREE.Euler().setFromQuaternion(endQuaternion)
- }*/
- let f = () => {
- info.callback && info.callback()
- //this.dispatchEvent('flyingDone')
- }
- if (info.duration) {
- setTimeout(f, 1) //延迟是为了使isFlying先为false
- } else {
- f() //有的需要迅速执行回调
- }
- }
- let endPosition = new THREE.Vector3().copy(info.position)
- let startPosition = camera.position.clone()
- let startQuaternion,
- endQuaternion,
- endTarget = null
- if (info.target) {
- endTarget = new THREE.Vector3().copy(info.target)
- endQuaternion = math.getQuaFromPosAim(endPosition, endTarget)
- } else if (info.quaternion) {
- endQuaternion = info.quaternion.clone()
- }
- if (endQuaternion) {
- startQuaternion = new THREE.Quaternion().copy(camera.quaternion)
- }
- if (!info.duration) {
- this.position.copy(endPosition)
- this.restrictPos()
- info.onUpdate && info.onUpdate(1)
- done()
- } else {
- transitions.start(
- lerp.vector(camera.position, endPosition, (pos, progress) => {
- let t = progress
- if (endQuaternion) {
- let quaternion = new THREE.Quaternion().copy(startQuaternion)
- lerp.quaternion(quaternion, endQuaternion)(progress)
- camera.quaternion.copy(quaternion)
- }
- camera.updateMatrix()
- this.notifyObjectsChanged(camera) //才会生效
- info.onUpdate && info.onUpdate(t)
- }),
- info.duration,
- done,
- 0,
- info.Easing ? easing[info.Easing] : easing.easeInOutSine /*easeInOutQuad */,
- null,
- this.LookTransition,
- info.cancelFun
- )
- }
- } //app.setView({duration:1000, position:{x:10,y:0,z:0}})
- //=============================
- }
- export { Application }
|