UNPKG

awv3

Version:
304 lines (277 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 => { console.log(this.tree[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], isRef = dimension.class.endsWith('RefDimension') const master = isRef ? dimension : 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(master.id, 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) } }