UNPKG

awv3

Version:
194 lines (169 loc) 7.8 kB
import cloneDeep from 'lodash/cloneDeep'; import * as THREE from 'three'; import Object3 from '../../three/object3'; export class Handle extends THREE.Mesh { constructor(geometry, material) { super(geometry, material); this.createInteraction({ priority: 1000 }); for (let category of Object.values(Object3.Events)) for (let event of Object.values(category)) if (this[event]) this.on(event, this[event].bind(this)); this.text = undefined; } [Object3.Events.Interaction.Picked](hitObject) { this.view.controls.enabled = false; } dragInternal(hitObject) { const ray = hitObject.ray.clone().applyMatrix4(new THREE.Matrix4().getInverse(this.parent.matrixWorld)); const position = ray.intersectPlane(new THREE.Plane(new THREE.Vector3(0, 0, 1))); const id = this.parent.userData.meta.id; if (!id) return; let state = cloneDeep(this.parent.plugin.connection.tree[id]); this.onDrag(state, position); this.parent.updateFromState(state); return state; } [Object3.Events.Interaction.Dragged](hitObject) { this.view.setCursor('grabbing'); this.dragInternal(hitObject); } [Object3.Events.Interaction.Dropped](hitObject) { this.view.setCursor('grab'); this.view.controls.enabled = true; const state = this.dragInternal(hitObject); if (!state) return; this.onDrop(state); } [Object3.Events.Lifecycle.Rendered]() { const scale = this.view.calculateScaleFactor(this.getWorldPosition(), 1); this.onScale(scale); } // called when the dimension state changes, should update meshes updateFromState(state) { let textParts = []; if (!state.members.paramName.value.startsWith('_')) textParts.push(state.members.paramName.value); if (state.members.master) { let num = state.previewValue !== undefined ? state.previewValue : this.parent.plugin.getDimensionDescriptor(state.id).value; textParts.push(Number.isInteger(num) ? String(num) : num.toFixed(2)); } this.text = textParts.join(' '); } // called from Object3.Events.Interaction.Dragged/Dropped, should update state from position onDrag(state, position) {} // called from Object3.Events.Interaction.Dropped, should send state update to the server onDrop(state) {} // called from Object3.Events.Lifecycle.Rendered, should scale meshes onScale(scale) {} } export class PositionHandle extends Handle { constructor(plugin) { const colors = plugin.session.globals.colors; super( new THREE.SphereBufferGeometry(1, 16, 16), new THREE.MeshBasicMaterial({ color: colors.primary, transparent: true, opacity: 0.3 }), ); this.colors = colors; } [Object3.Events.Interaction.Hovered](hitObject) { this.view.setCursor('grab'); this.animate({ material: { color: new THREE.Color(this.colors.accent), opacity: 0.6 } }).start(0); } [Object3.Events.Interaction.Unhovered](hitObject) { this.animate({ material: { color: new THREE.Color(this.colors.primary), opacity: 0.3 } }).start(1000); } updateFromState(state) { super.updateFromState(state); this.position.fromArray(state.members.dimPt.value); } onDrag(state, position) { position.toArray(state.members.dimPt.value); } onDrop(state) { const value = `{${state.members.dimPt.value.join(', ')}}`; this.parent.plugin.getMemberDescriptor(state.id, 'dimPt').expression = value; } onScale(scale) { this.scale.setScalar(7 * scale); } } export class ValueHandle extends Handle { constructor(plugin) { const colors = plugin.session.globals.colors; super( new THREE.SphereBufferGeometry(1.5, 16, 16), new THREE.MeshBasicMaterial({ color: colors.primary, transparent: true, opacity: 0.3 }), ); this.colors = colors; } [Object3.Events.Interaction.Hovered](hitObject) { this.view.setCursor('grab'); this.animate({ material: { color: new THREE.Color(this.colors.accent), opacity: 0.6 } }).start(0); } [Object3.Events.Interaction.Unhovered](hitObject) { this.animate({ material: { color: new THREE.Color(this.colors.primary), opacity: 0.3 } }).start(1000); } updateFromState(state) { super.updateFromState(state); const [start, end, dim, corner, center] = ['startPt', 'endPt', 'dimPt', 'cornerPt', 'center'].map(x => new THREE.Vector3().fromArray((state.members[x] || { value: [0, 0, 0] }).value)); switch (state.class) { case 'CC_AngularDimension': case 'CC_AngularFeatureDimension': this.position.copy(end).sub(corner).setLength(corner.distanceTo(dim)).add(corner); break; case 'CC_LinearDimension': case 'CC_LinearFeatureDimension': let d = new THREE.Vector3(Math.cos(state.members.angle.value), Math.sin(state.members.angle.value), 0); this.position.copy(end).sub(dim).projectOnVector(d).add(dim); break; case 'CC_RadialDimension': case 'CC_RadialFeatureDimension': case 'CC_DiameterDimension': case 'CC_DiameterFeatureDimension': this.position.copy(dim).sub(center).setLength(state.members.radius.value).add(center); break; } } onDrag(state, position) { const [start, end, dim, corner, center] = ['startPt', 'endPt', 'dimPt', 'cornerPt', 'center'].map(x => new THREE.Vector3().fromArray((state.members[x] || { value: [0, 0, 0] }).value)); let aux = {}; const descriptor = this.parent.plugin.getDimensionDescriptor(state.id, aux); switch (state.class) { case 'CC_AngularDimension': case 'CC_AngularFeatureDimension': const r = corner.distanceTo(end); end.copy(position.sub(corner).setLength(r).add(corner)).toArray(state.members.endPt.value); let startAng = Math.atan2(start.y - corner.y, start.x - corner.x); let endAng = Math.atan2(end.y - corner.y, end.x - corner.x); if (!state.members.ccw.value) [startAng, endAng] = [endAng, startAng]; if (endAng < startAng) endAng += 2 * Math.PI; state.previewValue = (endAng - startAng) * (aux.a_r ? THREE.Math.RAD2DEG : 1); break; case 'CC_LinearDimension': case 'CC_LinearFeatureDimension': let d = new THREE.Vector3(Math.cos(state.members.angle.value), Math.sin(state.members.angle.value), 0); end.copy(position.sub(end).projectOnVector(d).add(end)).toArray(state.members.endPt.value); state.previewValue = Math.abs(end.sub(start).dot(d)); break; case 'CC_RadialDimension': case 'CC_RadialFeatureDimension': state.members.radius.value = center.distanceTo(position); state.previewValue = state.members.radius.value; break; case 'CC_DiameterDimension': case 'CC_DiameterFeatureDimension': state.members.radius.value = center.distanceTo(position); state.previewValue = 2 * state.members.radius.value; break; } } onDrop(state) { this.parent.plugin.getDimensionDescriptor(state.id).expression = state.previewValue.toFixed(2); } onScale(scale) { this.scale.setScalar(7 * scale); } }