UNPKG

awv3

Version:
250 lines (226 loc) 10.3 kB
import * as THREE from 'three'; import { Group, Input, Label, Spacer, Button } from '../../session/elements'; import { arrayDiff } from '../../session/helpers'; import Plugin from '../../session/plugin'; import Object3 from '../../three/object3'; import Hud from '../../core/hud'; import Graphics from './graphics'; import { PositionHandle, ValueHandle } from './handle'; const resources = ['dimension', 'fixation'].reduce( (prev, item) => ({ ...prev, [item]: require('!!url-loader!awv3-icons/32x32/' + item + '.png') }), {}, ); export default class Dimension extends Plugin { constructor( session, { createDimensions = true, recalc = true, handlePrototypes = [ValueHandle, PositionHandle], ...args } = {}, ) { super(session, { type: 'Dimensions', icon: 'dimension', createDimensions, handlePrototypes, recalc, resources, ...args, }); this.root = undefined; this.dimensions = new Map(); this.afterSetCallback = () => {}; this.valueHandleTexturePromise = new Promise((resolve, reject) => new THREE.TextureLoader().load(resources['dimension'], resolve, undefined, reject), ); } CreateDimensions(feature) { this.connection.execute(`VAR obj, set; obj = CADH_RealToId(${feature}); set = @BaseModelingCommonFuncs.GetDimensionSet(obj); set.CreateDimensions(obj);`); } onEnabled() { // Init new heads up display, add it to the view this.hud = new Hud(this.view); this.view.addHud(this.hud); this.filter = new Input(this, { name: 'Filter', value: '' }); this.addElement(this.filter); if (!this.connection) { console.log('dimensions called without active connection'); return; } const observerConfig = { manager: this.addSubscription.bind(this), fireOnStart: true, unsubscribeOnUndefined: true, }; const featureParent = (this.session.plugins[this.parent] || {}).feature; // Watch current root for changes this.rootUnsubscribe = this.session.observe( state => { let tree = state.connections[state.globals.activeConnection].tree; return tree[tree.root].item || tree.root; }, root => { this.removeSubscriptions(); const tree = this.connection.tree; const itemRef = (tree[tree.root].instances || []).find(instance => tree[instance].link === root) || root; // Only react to parts and assemblies if (!tree[itemRef].class) return; const csys = tree[itemRef].coordinateSystem.map(row => new THREE.Vector3().fromArray(row)); this.hud.scene.matrix .makeBasis(csys[1], csys[2], csys[3]) .setPosition(csys[0]) .decompose(this.hud.scene.position, this.hud.scene.quaternion, this.hud.scene.scale); this.hud.scene.matrixWorldNeedsUpdate = true; // Observe features and create dimensions on them if (featureParent) { this.createDimensions && this.CreateDimensions(featureParent); } else { this.createDimensions && this.connection.observe( state => state.tree[root].features, features => features.forEach(feature => this.CreateDimensions(feature)), observerConfig, ); } // Fetch expressionset this.expressionSet = tree[root].expressionSet; this.connection.observe( state => state.tree[root].dimensions, (next, prev) => arrayDiff(next, prev, newDims => newDims.forEach(id => { const tree = this.connection.tree; if (featureParent && featureParent !== tree[tree[id].parent].members.owner.value) return; // skip dimensions of different features this.connection.observe(state => state.tree[id], this.onChange, { manager: this.addSubscription.bind(this), onRemove: this.onRemove, fireOnStart: true, unsubscribeOnUndefined: true, }); }), ), observerConfig, ); //update all graphics on expression set changes //note: this is necessary to update dimension value in non-incremental sketcher mode this.connection.observe( state => state.tree[this.expressionSet], () => { for (let id of this.dimensions.keys()) this.onChange(this.connection.tree[id]); }, observerConfig, ); }, // no manager so that this sub won't be unsubscribed by removeSubscriptions when root changes { ...observerConfig, manager: undefined }, ); } onDisabled() { // Remove and destroy heads up display this.view.removeHud(this.hud); this.hud.destroy(); this.rootUnsubscribe(); this.destroyElements(); } makeDimensionElement(id) { const nameDescriptor = this.getMemberDescriptor(id, 'paramName'); const nameElement = this.makeInputElement(nameDescriptor, 'value'); const descriptor = this.getDimensionDescriptor(id); const expressionElement = this.makeInputElement(descriptor, 'expression'); const fixation = new Button(this, { icon: 'fixation', format: Button.Format.Toggle, color: 'blue', flex: 0 }); const element = new Group(this, { format: Group.Format.Rows, children: [nameElement, expressionElement, fixation], }); this.filter.observe(state => state.value, value => (element.visible = nameElement.value.includes(value)), { fireOnStart: true, manager: element.addSubscription.bind(element), }); return element; } makeInputElement(descriptor, descriptorMember) { const element = new Input(this); element.observe( state => state.lastEvent, event => { if (event.key === 'Enter') descriptor[descriptorMember] = element.value; }, ); descriptor.observeExpression(value => (element.value = value), { fireOnStart: true, unsubscribeOnUndefined: true, manager: element.addSubscription.bind(element), }); return element; } getDimensionDescriptor(id, aux = {}) { const tree = this.connection.tree, dimension = tree[id]; const master = tree[dimension.members.master.value]; // extrusion or constraint const masterLinkName = master.class === 'CC_Extrusion' ? dimension.name : master.members.userValue ? 'userValue' : 'value'; const match = master.members[masterLinkName].expression.match(/ExpressionSet\.(\w+)/); aux.a_r = masterLinkName === 'userValue' && master.members.userValue.expression.includes('a_r'); if (match) return this.getMemberDescriptor(this.expressionSet, match[1], this.afterSetCallback); else return this.getMemberDescriptor(dimension.members.master.value, masterLinkName, this.afterSetCallback); } getMemberDescriptor(id, memberName, afterChangeCallback = function() {}) { const scope = this; return { get value() { return scope.connection.tree[id] && scope.connection.tree[id].members[memberName].value; }, set value(value) { scope.connection.execute(`VAR o; o = CADH_RealToId(${id}); o.${memberName} = ${JSON.stringify(value)};`); afterChangeCallback(); }, get expression() { return ( scope.connection.tree[id] && (scope.connection.tree[id].members[memberName].expression || scope.connection.tree[id].members[memberName].value) ); }, set expression(value) { const expressionSet = scope.connection.tree[id].class === 'CC_ExpressionSet'; const flag = scope.recalc && expressionSet ? 1 : 2; const command = `_C.CADApplication.SetExpressions(${id},["${memberName}"],[${JSON.stringify(value)}],${flag});`; scope.connection.execute(command); afterChangeCallback(); }, observeExpression(onChange, options) { return scope.connection.observe(state => state.tree[id] && this.expression, onChange, options); }, connection: scope.connection, }; } onChange = dimension => { const id = dimension.id; let element, graphics; if (this.dimensions.has(id)) { ({ element, graphics } = this.dimensions.get(id)); } else { element = this.makeDimensionElement(id); this.addElement(element); graphics = Graphics(dimension.class, this); this.hud.scene.add(graphics); this.dimensions.set(id, { element, graphics }); } graphics.updateFromState(dimension); graphics.onRender(); }; onRemove = (dimension, unsubscribe) => { const id = dimension.id; const { element, graphics } = this.dimensions.get(id); this.dimensions.delete(id); element.removeSubscriptions(); this.removeElement(element); graphics.destroy(); this.removeSubscription(unsubscribe); }; }