awv3
Version:
⚡ AWV3 embedded CAD
194 lines (169 loc) • 7.8 kB
JavaScript
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);
}
}