awv3
Version:
⚡ AWV3 embedded CAD
184 lines (167 loc) • 7.17 kB
JavaScript
import * as THREE from 'three'
import Lifecycle from './lifecycle.js'
import { halt } from '../core/error'
import { actions } from './store/plugins'
import { actions as elementActions, base as elementsBase } from './store/elements'
import { actions as globalActions } from './store/globals'
import { actions as connectionActions, base as connectionBase } from './store/connections'
import { base as pluginBase } from './store/plugins'
import { createObserver } from './helpers'
import Element from './element'
import { Group, Button, Spacer, Divider } from './elements'
import { prepare, render, destroy } from './renderer'
export default class Plugin extends Lifecycle() {
static persistent = false
static measurable = false
constructor(session = halt('plugin must be initialized with a session'), { feature, connection, pool, ...props }) {
super(session, actions, state => state.plugins[this.id], {
type: '',
name: '',
title: '',
resources: {},
icon: undefined,
enabled: false,
active: false,
collapsed: true,
closeable: true,
parent: undefined,
elements: [],
feature: feature ? feature.id : false,
connection: connection ? connection.id : undefined,
managed: true,
...props,
})
// If a parent exists (which mean this is a linked plugin, invoked by a parent plugin)
if (this.parent) {
let parentPlugin = pluginBase.references[this.parent]
// And if the parent is a feature, it gets to store its own pool under the parent, while
// it is allowed to pose as a feature plugin itself
if (parentPlugin.feature) {
feature = parentPlugin
connection = parentPlugin.connection
this.pool = new THREE.Group()
this.pool.updateParentMaterials = false
this.pool.measurable = false
this.pool.name = `plugin.linked_pool.${this.type}${this.name ? '+' + this.name : ''}`
feature.pool.add(this.pool)
}
}
if (feature) {
Object.defineProperty(this, 'feature', { get: () => feature.id })
Object.defineProperty(this, 'connection', { get: () => connection })
if (!this.parent) {
this.pool = pool
this.reset = new Button(this, { name: 'Reset', flex: 0 })
this.apply = new Button(this, { name: 'Apply', flex: 0 })
this.addElement(
new Group(this, {
index: 10000000,
children: [
new Divider(this),
new Spacer(this),
new Group(this, {
format: Group.Format.Rows,
flexDirection: 'row-reverse',
children: [this.apply, new Spacer(this), this.reset],
}),
],
}),
)
}
} else {
Object.defineProperty(this, 'connection', {
get: () => connectionBase.references[this.session.globals.activeConnection],
})
this.pool = new THREE.Group()
this.pool.updateParentMaterials = false
this.pool.measurable = false
this.pool.name = `plugin.pool.${this.type}${this.name ? '+' + this.name : ''}`
this.session.pool.temporary.add(this.pool)
}
// this.tree refers to the connections tree
Object.defineProperty(this, 'tree', { get: () => this.connection.tree })
Object.defineProperty(this, 'root', { get: () => this.tree[this.tree.root] })
Object.defineProperty(this, 'view', { get: () => this.pool.view })
// Internal observer for enabled state, gets cleared after destroy
// The enabled flag has to be kept locally as the destroyed event can be dispatched
// without calling destroy() directly, leaving the linked state empty
this.__enabled = this.enabled
this.__enabledUnsubscribe = this.observe(
// selector
state => state.enabled,
// onChanged
(enabled, prev) => {
this.__enabled = enabled
if (enabled !== prev) {
if (enabled) {
this.onEnabled()
if (this.render) render(this, this.render.bind(this))
} else {
this.onDisabled()
if (this.render) destroy(this.id)
}
}
!enabled && this.removeSubscriptions()
},
// options
{ manager: undefined },
)
}
findElement(condition, elements = this.elements) {
for (let key of elements) {
const el = this.session.elements[key]
const test = el.plugin === this.id && condition(el)
if (test) {
return el.id
} else if (el.children.length) {
return this.findElement(condition, el.children)
}
}
}
findElementClass(condition) {
return elementsBase.references[this.findElement(condition)]
}
addElement(...elements) {
elements.forEach(element =>
this.store.dispatch(actions.addElement(this.id, element instanceof Element ? element.id : element)),
)
}
removeElement(element) {
this.store.dispatch(actions.removeElement(this.id, element instanceof Element ? element.id : element))
}
destroyElements() {
this.store.dispatch(
elementActions.unregister(
Object.keys(this.session.elements).filter(id => this.session.elements[id].plugin === this.id),
),
)
}
resetElements() {
this.store.dispatch(
elementActions.merge(this.dependencies.reduce((prev, obj) => ({ ...prev, [obj.id]: obj.props }), {})),
)
}
createToolbar(keys, cb, columns = 7) {
let children = []
for (let count = 0; count < keys.length / columns; count++) {
children.push(
new Group(this, {
format: Group.Format.Buttons,
margin: count == keys.length / columns - 1,
children: cb(keys.slice(count * columns, Math.min(count * columns + columns, keys.length))),
}),
)
}
return new Group(this, { children })
}
onEnabled() {}
onDisabled() {}
__onDestroyed() {
this.store.dispatch(globalActions.unlinkPlugins(this.id))
if (this.connection) this.store.dispatch(connectionActions.unlinkPlugins(this.connection.id, this.id))
this.__enabledUnsubscribe()
this.__enabled && this.onDisabled()
this.render && destroy(this.id)
this.pool && this.pool.destroy()
}
}