UNPKG

awv3

Version:
299 lines (273 loc) 12.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, PositionValueHandle } 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 = [PositionHandle, ValueHandle], ...args } = {}, ) { super(session, { type: 'Dimensions', icon: 'dimension', createDimensions, handlePrototypes, recalc, resources, ...args, }); this.dimensions = new Map(); //note: set manually by sketcher plugin this.selector = undefined; 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: '', hint: 'filter' }); 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, ); if (this.selector) { //update visual representation of GUI element when selection changes this.selector.element.observe( s => s.children, x => { const selectedIds = this.selector.getSelectedIds(); for (let [id, { element }] of this.dimensions.entries()) { const highlight = selectedIds.indexOf(id) !== -1; //TODO: set some other property which affects visual style element.visible = highlight ? true : false; //console.log("Changed selection: " + id + " := " + highlight); } }, 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 isDriven = Number((this.tree[id].members.isDriven || { value: 0 }).value); const readonly = isDriven !== 0; const nameDescriptor = this.getMemberDescriptor(id, 'paramName'); const nameElement = this.makeInputElement(nameDescriptor, 'value', { readonly, hint: 'name' }); const descriptor = this.getDimensionDescriptor(id); const expressionElement = this.makeInputElement(descriptor, 'expression', { readonly, hint: 'expression' }); const fixation = new Button(this, { icon: 'fixation', hint: 'fixation', format: Button.Format.Toggle, color: 'blue', flex: 0, value: isDriven === 0, readonly: isDriven === -1, }); fixation.observe( state => state.value, value => { this.connection.execute( `VAR o; o = CADH_RealToId(${id}); o.SetIsDriven(${Number(!value)});`, ); nameElement.readonly = !value; expressionElement.readonly = !value; }, ); 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, props = {}) { const element = new Input(this, props); element.observe( state => state.lastEvent, event => { if (event.key === 'Enter') { descriptor[descriptorMember] = element.value; this.afterSetCallback(); } }, ); descriptor.observeExpression(value => (element.value = value), { fireOnStart: true, unsubscribeOnUndefined: true, manager: element.addSubscription.bind(element), }); return element; } getDimensionDescriptor(id) { 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'; let expression = master.members[masterLinkName].expression; // supported expression forms: "a_r( ExpressionSet.taper_angle )", "ExpressionSet.limit2", "everything + else" const matchA_R = expression.match(/^ *a_r\((.*)\) *$/); if (matchA_R) expression = matchA_R[1]; const matchES = expression.match(/^ *ExpressionSet\.(\w+) *$/); if (matchES) return this.getMemberDescriptor(this.expressionSet, matchES[1], { hasA_R: Boolean(matchA_R) }); else return this.getMemberDescriptor(dimension.members.master.value, masterLinkName); } getMemberDescriptor(id, memberName, options = {}) { 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)};`, ); }, 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); }, observeExpression(onChange, options) { return scope.connection.observe(state => state.tree[id] && this.expression, onChange, options); }, ...options, }; } 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); }; }