awv3
Version:
⚡ AWV3 embedded CAD
219 lines (193 loc) • 9.22 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, types: ['DimensionHandle'] });
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), { sync: true });
this.draggedState = undefined;
this.text = undefined;
}
[Object3.Events.Interaction.Hovered](hitObject) {
this.view.setCursor('grab');
if (this.parent.plugin.selector) this.parent.plugin.selector.hover(hitObject);
else this.animate({ material: { color: new THREE.Color(this.colors.accent), opacity: 0.6 } }).start(0);
}
[Object3.Events.Interaction.Unhovered](hitObject) {
if (this.parent.plugin.selector) this.parent.plugin.selector.unhover(hitObject);
else this.animate({ material: { color: new THREE.Color(this.colors.primary), opacity: 0.3 } }).start(1000);
}
[Object3.Events.Interaction.Picked](hitObject) {
this.view.controls.enabled = false;
}
[Object3.Events.Interaction.Dragged](hitObject) {
this.view.setCursor('grabbing');
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;
this.draggedState = cloneDeep(this.parent.plugin.connection.tree[id]);
this.onDrag(this.draggedState, position);
this.parent.updateFromState(this.draggedState);
}
[Object3.Events.Interaction.Dropped](hitObject) {
this.view.setCursor('grab');
this.view.controls.enabled = true;
if (!this.draggedState) return;
this.onDrop(this.draggedState);
this.draggedState = undefined;
this.parent.plugin.afterSetCallback();
}
[Object3.Events.Interaction.Clicked](hitObject) {
if (this.parent.plugin.selector) this.parent.plugin.selector.clicked(hitObject);
}
[Object3.Events.Interaction.Missed](hitObject) {
if (this.parent.plugin.selector) this.parent.plugin.selector.missed(hitObject);
}
[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);
const num = state.previewValue !== undefined
? state.previewValue
: state.id ? this.parent.plugin.getDimensionDescriptor(state.id).value : undefined;
if (num !== undefined) 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;
}
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;
}
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),
);
const nearend = (state.members.valHandlePos || {}).value ? start : end;
switch (state.class) {
case 'CC_AngularDimension':
case 'CC_AngularFeatureDimension':
this.position.copy(nearend).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(nearend).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;
}
this.visible = (state.members.isDriven || { value: 0 }).value === 0;
}
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),
);
const [nearend, farend] = (state.members.valHandlePos || {}).value ? [start, end] : [end, start];
switch (state.class) {
case 'CC_AngularDimension':
case 'CC_AngularFeatureDimension':
const toPolar = v => v.sub(corner).set(Math.atan2(v.y, v.x), v.length(), 0);
const fromPolar = v => v.set(v.y * Math.cos(v.x), v.y * Math.sin(v.x), 0).add(corner);
toPolar(nearend);
toPolar(farend);
const diffAng = toPolar(position).x - nearend.x;
nearend.x += diffAng;
if (state.members.valHandleBehaviour.value) farend.x -= diffAng;
let angle = (nearend.x - farend.x) % (2 * Math.PI);
fromPolar(nearend);
fromPolar(farend);
start.toArray(state.members.startPt.value);
end.toArray(state.members.endPt.value);
if (!Boolean(state.members.ccw.value) ^ Boolean(state.members.valHandlePos.value)) angle = -angle;
if (angle < 0) angle += 2 * Math.PI;
if (this.parent.plugin.getDimensionDescriptor(state.id).hasA_R) angle = THREE.Math.radToDeg(angle);
state.previewValue = angle;
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);
const diff = position.clone().sub(nearend).projectOnVector(d);
nearend.add(diff);
if (state.members.valHandleBehaviour.value) farend.sub(diff);
start.toArray(state.members.startPt.value);
end.toArray(state.members.endPt.value);
state.previewValue = Math.abs(end.clone().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);
}
}
export class PositionValueHandle extends ValueHandle {
onDrag(state, position) {
position.toArray(state.members.dimPt.value);
super.onDrag(state, position);
}
onDrop(state) {
const value = `{${state.members.dimPt.value.join(', ')}}`;
this.parent.plugin.getMemberDescriptor(state.id, 'dimPt').expression = value;
super.onDrop(state);
}
}