UNPKG

awv3

Version:
257 lines (229 loc) 8.97 kB
import * as THREE from 'three'; import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; import Element from './element'; import { reducer } from './store/index.js'; import { actions as globalActions } from './store/globals'; import { actions as elementActions } from './store/elements'; import { actions as connectionActions, base as connectionBase } from './store/connections'; import { base as pluginBase } from './store/plugins'; import { createObserver, Pool, ObjectPrototype } from './helpers'; import { Selector } from './selection/selector'; import Connection from './connection'; import Defaults from '../core/defaults'; export default class Session { constructor(options = {}) { this.options = { throttle: 200, updateMaterials: false, centerGeometry: false, pool: Pool, objectPrototype: ObjectPrototype, ...Defaults.all, ...options, }; const composeEnhancers = this.options.debug && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose; this.store = options.store || createStore(reducer, composeEnhancers(applyMiddleware(thunk /*, logger*/))); // Support hot reloading reducers if (process.env.NODE_ENV !== 'production' && !options.store && module.hot) { module.hot.accept( ['./store/globals', './store/plugins', './store/elements', './store/connections', './store/index'], () => this.store.replaceReducer(require('./store/index.js').reducer) ); } this.pool = new Pool({ session: this, name: 'session.pool' }); this.defaultFeaturePlugin; this.featurePlugins = {}; this.materials = {}; this.layerNames = []; this.observe = createObserver(this.store, this.getState.bind(this)); this.selector = new Selector(this, options); // Global observers this.observe(state => state.globals.activeConnection, observeActiveConnection.bind(this)); options.resources && this.addResources(options.resources); options.featurePlugins && this.registerFeaturePlugins(options.featurePlugins); options.defaultFeaturePlugin && this.registerDefaultFeaturePlugins(options.defaultFeaturePlugin); // Add a global keylistener that will forward events to a plugins console let keydownEventHandler = keydownEvent.bind(this); document.addEventListener('keydown', keydownEventHandler); // Link view if (this.options.view) { this.view = this.options.view; this.view.scene.add(this.pool); } else { this.pool.viewFound().then(view => this.view = view); } // Add default connection for convenience if (this.options.defaultConnection) { let connection = this.addConnection('default'); if (this.options.url && this.options.protocol) connection.on('connected', connection => this.options.defaultConnection(connection)); else this.options.defaultConnection(connection); } this.observe(state => state.globals.camera, state => this.view.camera.orthographic = state === 'orthographic'); } showLayer(args = []) { this.layerNames = Array.isArray(args) ? args : [args]; this.pool.traverse(obj => { if (obj.material) { let isMultiMaterial = Array.isArray(obj.material); if (isMultiMaterial) { obj.material.forEach( mat => mat.visible = mat.meta ? this.layerNames.length == 0 || this.layerNames.indexOf(mat.meta.layer) > -1 : mat.visible ); } else if (obj.material.meta) { obj.material.visible = this.layerNames.length == 0 || this.layerNames.indexOf(obj.material.meta.layer) > -1; } } }); this.pool.view.invalidate(); } isVisibleLayer(layerName) { if (this.layerNames.length === 0 || this.layerNames.indexOf(layerName) > -1) return true; return false; } getState() { return this.store.getState() || {}; } dispatch(action) { return this.store.dispatch(action); } registerDefaultFeaturePlugin(prototype) { this.defaultFeaturePlugin = prototype; } registerFeaturePlugins(map) { this.featurePlugins = { ...this.featurePlugins, ...map }; } async resolveFeaturePlugin(promise, ...args) { let { default: plugin } = await promise; this.registerFeaturePlugins(args.reduce((prev, key) => ({ ...prev, [key]: plugin }), {})); } async resolveDefaultFeaturePlugin(promise) { let { default: Plugin } = await promise; this.registerDefaultFeaturePlugin(Plugin); } async resolveGlobalPlugins(...promises) { let plugins = (await Promise.all(promises)).map(({ default: Plugin }) => new Plugin(session).id); session.dispatch(globalActions.linkPlugins(plugins)); } findFeaturePlugin(id) { return this.featurePlugins.find(item => item.id === id); } addResources(map) { this.dispatch(globalActions.addResources(map)); } addConnection(name) { let connection = new Connection(this, { name }); this.store.dispatch(globalActions.setActiveConnection(connection.id)); return connection; } resolveResource(name, plugin) { let resource; let scope = this.plugins[plugin]; if (scope) resource = scope.resources[name]; if (!resource) resource = this.globals.resources[name]; return resource; } resolveTree(args) { let id = this.globals.activeConnection; let connection = connectionBase.references[id]; return connection.resolveTree(args); } printSceneGraph() { let count = 1; const traverse = (obj, level = 0) => { if (obj) { obj.children.forEach(item => { console.log( `${count++}:\t${Array.from(new Array(level)) .fill(' ') .join('')}${item.type}${item.name ? '_' + item.name : ''}` ); traverse(item, level + 2); }); } }; traverse(this.scene); } get scene() { return this.pool.scene; } get state() { return this.getState(); } get globals() { return this.state.globals; } get plugins() { return this.state.plugins; } get elements() { return this.state.elements; } get connections() { return this.state.connections; } get collections() { return this.state.collections; } // Computed props get activeConnection() { return this.state.connections[this.state.globals.activeConnection]; } get activePlugin() { return this.plugins[ this.activeConnection.plugins.find( item => this.plugins[item].feature === this.activeConnection.activeFeature ) ]; } get tree() { return this.activeConnection.tree; } get activeConnectionClass() { return connectionBase.references[this.globals.activeConnection]; } get activePluginClass() { return pluginBase.references[this.activePlugin.id]; } } function checkElements(session, elements) { for (let key of elements) { let element = session.elements[key]; if (element.type === Element.Type.Console) { session.store.dispatch(elementActions.update(element.id, { focus: true })); session.store.dispatch(elementActions.event(element.id, event)); return true; } else if (element.type === Element.Type.Group && checkElements(session, element.children)) { return true; } } return false; } function keydownEvent(event) { let target = event.target; if (!(target instanceof HTMLInputElement)) { for (let key in this.plugins) { let plugin = this.plugins[key]; if (plugin.enabled && !plugin.collapsed && checkElements(this, plugin.elements)) { return; } } } } async function observeActiveConnection(id, previous) { if (previous) this.pool.removeAsync(connectionBase.references[previous].pool); if (id) { let activeConnection = connectionBase.references[id]; await this.pool.addAsync(activeConnection.pool); await this.pool.viewFound(); this.pool.view.updateBounds(); } }