awv3
Version:
⚡ AWV3 embedded CAD
202 lines (193 loc) • 8.18 kB
JavaScript
import * as THREE from 'three'
import Plugin from '../../session/plugin'
import { Group, Input, Button, Label, Dropdown, Spacer } from '../../session/elements'
import React, { Component, connect } from '../../session/renderer'
import { actions as globalActions } from '../../session/store/globals'
import Hud from '../../core/hud'
import Graphics from './graphics'
import { PositionHandle, ValueHandle } from './handle'
class Dimension extends Component {
componentWillMount() {
this.info = this.getDimensionInfo(this.props)
this.state = { inputValue: this.info.value, inputName: this.info.name }
this.graphics = Graphics(this.info.dimension.class, this.plugin)
this.plugin.hud.scene.add(this.graphics)
this.graphics.updateFromState(this.info)
}
componentWillUnmount() {
this.graphics && this.graphics.destroy()
}
componentWillReceiveProps(props) {
if (props.dimension) {
this.info = this.getDimensionInfo(props)
if (!this.graphics) this.graphics = Graphics(this.info.dimension.class, this.plugin)
this.graphics.updateFromState(this.info)
} else {
this.graphics.destroy()
this.graphics = undefined
}
// This is kind of a hack. Which value should "win"? The one that the user typed, or the one
// that comes through the wire from ClassCAD? The current solution overrides the input value.
this.state = { ...this.state, inputValue: this.info.value }
}
setExpression(target, name, expression) {
const expressionSet = target.class === 'CC_ExpressionSet'
const flag = this.plugin.recalc && expressionSet ? 1 : 2
this.plugin.connection.execute(
`_C.CADApplication.SetExpressions(${target.id},["${name}"],[${JSON.stringify(expression)}],${flag});`,
)
this.plugin.afterSetCallback()
}
setMember(target, name, value) {
this.plugin.connection.execute(`VAR o; o = CADH_RealToId(${target.id}); o.${name} = ${JSON.stringify(value)};`)
}
getDimensionInfo(props) {
const scope = this
const { dimension, expressions, feature, search } = props
dimension.previewValue = undefined
const master = dimension.members.master ? this.plugin.tree[dimension.members.master.value] : dimension
const driven = (dimension.members.isDriven || {}).value
const linkName = master.class === 'CC_Extrusion'
? dimension.name
: master.members.userValue ? 'userValue' : 'value'
let expression = master.members[linkName].expression
const angle = expression.match(/^ *a_r\((.*)\) *$/)
if (angle) expression = angle[1]
const matches = expression.match(/^ *ExpressionSet\.(\w+) *$/)
const target = matches ? expressions : master
const memberName = matches ? matches[1] : linkName
const owner = (this.plugin.tree[dimension.parent].members.owner || {}).value
const visible =
(!search || dimension.members.paramName.value.toLowerCase().indexOf(search) != -1) &&
(!feature || owner === feature)
const setMember = this.setMember.bind(this)
const setExpression = this.setExpression.bind(this)
return {
dimension,
angle,
driven,
visible,
get name() {
return dimension.members.paramName.value
},
set name(value) {
setMember(dimension, 'paramName', value)
},
get value() {
return target.members[memberName].value
},
set value(value) {
setMember(target, memberName, value)
},
get expression() {
return target.members[memberName].expression || target.members[memberName].value
},
set expression(value) {
setExpression(target, memberName, value)
},
set dimPt(value) {
setExpression(dimension, 'dimPt', value)
},
}
}
render() {
if (!this.info) return null
const { dimension, visible, name, value, driven } = this.info
return (
<Group visible={visible} format={Group.Format.Rows}>
<Input
value={this.state.inputName}
onValue={inputName => this.setState({ inputName })}
onLastEvent={e => e.key === 'Enter' && (this.info.name = this.state.inputName)}
readonly={!!driven}
flex={1}
/>
<Input
format={Input.Format.Number}
value={this.state.inputValue}
onValue={inputValue => this.setState({ inputValue })}
onLastEvent={e => e.key === 'Enter' && (this.info.expression = this.state.inputValue)}
readonly={!!driven}
flex={2}
/>
<Button
name={driven ? '🔒' : '🔓'}
disabled={driven === undefined}
onLastEvent={() => this.setMember(dimension, 'isDriven', !!driven ? 0 : 1)}
flex={0}
/>
</Group>
)
}
}
class Root extends Component {
state = { search: '' }
componentWillReceiveProps(props) {
const hud = this.plugin.hud
const tree = this.plugin.tree
const itemRef =
(tree[tree.root].instances || []).find(instance => tree[instance].link === props.root) || props.root
const csys = tree[itemRef].coordinateSystem.map(row => new THREE.Vector3().fromArray(row))
hud.scene.matrix
.makeBasis(csys[1], csys[2], csys[3])
.setPosition(csys[0])
.decompose(hud.scene.position, hud.scene.quaternion, hud.scene.scale)
hud.scene.matrixWorldNeedsUpdate = true
}
render({ connection, root, feature, expressions, dimensions = [] } = {}, { search }, set) {
const commons = { feature, search, expressions, connection }
return (
<Group>
<Group format={Group.Format.Rows}>
<Input value={search} placeholder="Search..." onValue={search => set({ search })} />
<Button name="✘" flex={0} disabled={!search} onLastEvent={() => set({ search: '' })} />
</Group>
<Spacer />
{dimensions.map(id => <Dimension key={id} {...commons} />)}
</Group>
)
}
}
const resources = ['dimension'].reduce(
(prev, item) => ({ ...prev, [item]: require('!!url-loader!awv3-icons/32x32/' + item + '.png') }),
{},
)
export default class extends Plugin {
constructor(session, args) {
super(session, {
type: 'Dimensions',
icon: 'dimension',
recalc: true,
handlePrototypes: [PositionHandle, ValueHandle],
resources,
...args,
})
this.selector = undefined
this.afterSetCallback = () => {}
}
render() {
return <Root id={this.id} />
}
onEnabled() {
this.hud = new Hud(this.view)
this.view.addHud(this.hud)
}
onDisabled() {
this.view.removeHud(this.hud)
this.hud.destroy()
}
}