awv3
Version:
⚡ AWV3 embedded CAD
141 lines (130 loc) • 6.04 kB
JavaScript
import * as THREE from 'three';
import Object3 from '../../../three/object3';
import View from '../../../core/view';
export default class BaseGraphics extends THREE.Object3D {
constructor(plugin, nLines = 0, nArcs = 0, nArrows = 0) {
super();
const colors = plugin.session.globals.colors;
this.lineGeometry = new THREE.CylinderBufferGeometry(1, 1, 1);
this.lineMaterial = new THREE.MeshBasicMaterial({ color: colors.primary });
this.arcSegmentGeometry = new THREE.CylinderBufferGeometry(1, 1, 1, 4, 1);
this.arcSegmentMaterial = new THREE.MeshBasicMaterial({ color: colors.primary });
this.arrowGeometry = new THREE.ConeBufferGeometry(1, 1);
this.arrowGeometry.translate(0, -0.5, 0);
this.arrowMaterial = new THREE.MeshBasicMaterial({ color: colors.primary });
this.arcSegments = 64;
this.plugin = plugin;
this.handles = plugin.handlePrototypes.map(handlePrototype => new handlePrototype(plugin));
this.add(...this.handles);
//this callback is async: otherwise CSS text and 3D geometry update at different times (causing visual lag)
this.createInteraction({ priority: 1000 }).on(Object3.Events.Lifecycle.Rendered, this.onRender.bind(this), {sync: false});
if (nLines) {
this.lines = [];
for (let i = 0; i < nLines; ++i)
this.lines.push(new THREE.Mesh(this.lineGeometry, this.lineMaterial));
this.add(...this.lines);
}
if (nArcs) {
this.arcs = [];
for (let i = 0; i < nArcs; ++i) {
this.arcs.push([]);
for (let j = 0; j < this.arcSegments; ++j) {
this.arcs[i].push(new THREE.Mesh(this.arcSegmentGeometry, this.arcSegmentMaterial));
this.add(...this.arcs[i]);
}
}
this.add(...this.arcs);
}
if (nArrows) {
this.arrows = [];
for (let i = 0; i < nArrows; ++i)
this.arrows.push(new THREE.Mesh(this.arrowGeometry, this.arrowMaterial));
this.add(...this.arrows);
}
this.dimPos2 = new THREE.Vector2();
this.text = '';
this.textNode = document.createElement('div');
this.textNode.setAttribute(
'style',
`position: absolute;
display: inline-block;
top: 0; left: 0; padding: 1px 2px;
color: ${colors.text};
font-size: 9px;
pointer-events: none;`,
);
this.userData.meta = {};
for (let hdl of this.handles)
hdl.type = 'DimensionHandle';
this.handles[0].material.meta = {};
this.destroyed = false;
this.viewFound().then(view => {
if (this.destroyed) return;
view.dom.appendChild(this.textNode);
});
}
destroy() {
this.destroyed = true;
if (this.textNode.parentElement) this.textNode.parentElement.removeChild(this.textNode);
super.destroy();
}
onRender() {
const position = this.handles[0].getWorldPosition();
const scale = this.view.calculateScaleFactor(position, 1.0);
for (let line of this.lines || [])
line.scale.x = (line.scale.z = scale);
for (let arc of this.arcs || [])
for (let arcSegment of arc)
arcSegment.scale.x = (arcSegment.scale.z = scale);
for (let arrow of this.arrows || [])
arrow.scale.set(7 * scale, 15 * scale, 7 * scale);
const pos2 = this.view.getPoint2(position.clone());
if (!this.dimPos2.equals(pos2)) {
this.dimPos2.copy(pos2);
this.textNode.style.transform = `translate3d(${pos2.x + 15}px, calc(-50% + ${pos2.y - 15}px), 0)`;
}
if (this.textNode.innerHTML !== this.handles[0].text) this.textNode.innerHTML = this.handles[0].text;
}
updateLine(i, start, end) {
const line = Number.isInteger(i) ? this.lines[i] : i;
// line.geometry describes a cylinder with axis from [0, -0.5, 0] to [0, 0.5, 0] and raidus 1
line.position.lerpVectors(start, end, 0.5);
line.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), end.clone().sub(start).normalize());
line.scale.y = start.distanceTo(end);
}
updateArc(i, center, radius, startAngle, endAngle) {
const arc = this.arcs[i];
for (let j = 0; j < this.arcSegments; ++j) {
const arcSegment = arc[j];
const [sStartAngle, sEndAngle] = [j, j + 1].map(k =>
THREE.Math.lerp(startAngle, endAngle, k / this.arcSegments));
const [start, end] = [sStartAngle, sEndAngle].map(angle =>
new THREE.Vector3(Math.cos(angle), Math.sin(angle), 0).multiplyScalar(radius).add(center));
this.updateLine(arcSegment, start, end);
}
}
updateArrow(i, end, dir) {
const arrow = this.arrows[i];
// arrow.geometry describes a cone with axis from [0, -1, 0] (r=1) to [0, 0, 0] (r=0)
arrow.position.copy(end);
arrow.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir.clone().normalize());
}
updateCoordinateSystem(csys) {
csys = csys.map(row => new THREE.Vector3().fromArray(row));
this.matrix.makeBasis(csys[1], csys[2], csys[3]);
this.matrix.setPosition(csys[0]);
this.matrix.decompose(this.position, this.quaternion, this.scale);
this.matrixWorldNeedsUpdate = true;
this.updateMatrixWorld();
}
updateFromState(state) {
this.lastState = state;
this.userData.meta.id = state.id;
this.updateCoordinateSystem(state.coordinateSystem);
for (let handle of this.handles)
handle.updateFromState(state);
this.handles[0].material.meta.id = state.id;
this.handles[0].material.meta.box = this.handles[0].updateBounds().bounds.box;
this.view.invalidate();
}
}