awv3
Version:
⚡ AWV3 embedded CAD
429 lines (391 loc) • 14.4 kB
JavaScript
/**
* @module Measure plugin for awv3
*/
import * as THREE from 'three';
import Plugin from '../../session/plugin';
import { Group, Label, Selection } from '../../session/elements';
import Object3 from '../../three/object3';
const resources = ['measure'].reduce((prev, item) => ({ ...prev, [item]: require('!!url-loader!awv3-icons/32x32/' + item + '.png') }), {
});
/**
* Class Measure is based on awv3's plugin architecture. It calculates distances
* and angles between various selected geometry elements.
*/
export default class Measure extends Plugin {
constructor(session, args) {
super(session, { type: 'Measure', icon: 'measure', resources, ...args });
this.selection = new Selection(this, { name: 'Items', types: ['Mesh', 'LineSegments', 'Point'] });
this.addElement(this.selection);
this.measureLabels = new Group(this, { format: Group.Format.Table });
this.addElement(this.measureLabels);
this.scene = null;
// maximum number of labels
this.maxNofLabels = 10;
}
onEnabled() {
// 6. You can call resetElements() here, this will reset() each element to its original state
// You can also call reset() manually on elements.
this.resetElements();
// 7. To react to an element you can observe its properties as they are tied to the state-tree.
// The Element class offers the observe() function targeted to its state, just like session and plugin do
// selection changed
this.selection.observe(state => state.children, value => {
// get selected items, put them in an array
const selectedItems = this.session.selector.getSelectedElements();
const info = this.solve(selectedItems);
this.measureLabels.removeAllChilds();
info.forEach(item =>
this.measureLabels.addChild(new Label(this, { name: item.description, value: item.result })));
});
window.selection = this.selection;
// TODO: This should be done by selector.js, the values depend on the model-size
// Store original interaction values
//this.linePrecision = this.session.pool.view.interaction.raycaster.linePrecision
//this.session.pool.view.interaction.raycaster.linePrecision = 0.1
this.scene = this.session.pool.scene;
}
onDisabled() {
// restore original interaction values
//this.session.pool.view.interaction.raycaster.linePrecision = this.linePrecision
}
onDestroyed() {}
/**
* This function triggers the calculation of distances / angles between
* two geometry objects or gets a description for each selected object
* if the number of objects is not equal to two.
*/
solve(objects) {
// this.storeSelectedIds(objects)
switch (objects.length) {
case 2:
return Measure.calculateMeasures(objects);
default:
return objects.map(item => {
return Measure.getInfo(item);
});
}
}
/**
* Collects information about a single geometry element
* @param {three object} element - the three geometry element
* @return { object } - Calculated information object with result
* {number} and description {string}
*/
static getInfo(element) {
switch (element.meta.type) {
case 'point':
const info = element.meta.position.toArray();
return {
result: `[ ${info.join(', ')} ]`,
description: 'Position'
};
case 'line': {
let absoluteDistance = element.meta.start.distanceTo(element.meta.end);
absoluteDistance = Measure.round(absoluteDistance, 3);
return {
result: absoluteDistance,
description: 'Line length'
};
}
case 'arc': {
const radius = element.meta.radius;
return {
result: radius,
description: 'Arc radius'
};
}
case 'circle': {
const radius = element.meta.radius;
return {
result: radius,
description: 'Circle radius'
};
}
case 'nurbs':
return {
result: 0,
description: 'NURBS'
};
case 'plane': {
const info = element.meta.normal.toArray();
return {
result: `[ ${info.join(', ')} ]`,
description: 'Plane normal'
};
}
case 'cylinder': {
const radius = element.meta.radius;
return {
result: radius,
description: 'Cylinder radius'
};
}
case 'cone': {
return {
result: 0,
description: 'Cone'
};
}
case 'sphere': {
const radius = element.meta.radius;
return {
result: radius,
description: 'Sphere radius'
};
}
case 'nurbsSurface':
return {
result: 0,
description: 'NURBS Surface'
};
default:
return {
result: 0,
description: 'Undefined'
};
}
}
static calculateMeasures(objects) {
const first = objects[0];
const second = objects[1];
let results = [];
switch (first.meta.type) {
case 'point':
switch (second.meta.type) {
case 'point': {
results = Measure.calculatePointPoint(first, second);
}
break;
case 'line': {
results = Measure.calculatePointLine(first, second);
}
break;
case 'plane': {
results = Measure.calculatePointPlane(first, second);
}
}
break;
case 'line': {
switch (second.meta.type) {
case 'point': {
results = Measure.calculatePointLine(second, first);
}
break;
case 'line': {
results = Measure.calculateLineLine(first, second);
}
break;
case 'plane': {
results = Measure.calculateLinePlane(first, second);
}
}
}
break;
case 'plane': {
switch (second.meta.type) {
case 'point': {
results = Measure.calculatePointPlane(second, first);
}
break;
case 'line': {
results = Measure.calculateLinePlane(second, first);
}
break;
case 'arc': {
results = Measure.calculateArcPlane(first, second);
}
break;
case 'plane': {
results = Measure.calculatePlanePlane(first, second);
}
}
}
}
return results;
}
static calculatePointPoint(point1, point2) {
let distance = Measure.distancePointPoint(point1, point2);
distance = Measure.round(distance, 3);
const dx = Measure.round(Math.abs(point1.meta.position.x - point2.meta.position.x), 3);
const dy = Measure.round(Math.abs(point1.meta.position.y - point2.meta.position.y), 3);
const dz = Measure.round(Math.abs(point1.meta.position.z - point2.meta.position.z), 3);
return [
{
result: distance,
description: 'Distance'
},
{
result: dx,
description: 'dx'
},
{
result: dy,
description: 'dy'
},
{
result: dz,
description: 'dz'
}
];
}
static calculatePointLine(point, line) {
let distance = Measure.distancePointLine(point.meta.position, line);
distance = Measure.round(distance, 3);
return [
{
result: distance,
description: 'Distance'
}
];
}
static calculatePointPlane(point, plane) {
let distance = Measure.distancePointPlane(point.meta.position, plane);
distance = Measure.round(distance, 3);
return [
{
result: distance,
description: 'Distance'
}
];
}
static calculateLineLine(first, second) {
let distance = Measure.distanceLineLine(first, second);
distance = Measure.round(distance, 3);
let angle = Measure.degrees(Measure.angleLineLine(first, second));
angle = Measure.round(angle, 3);
return [
{
result: distance,
description: 'Distance'
},
{
result: angle,
description: 'Angle'
}
];
}
static calculateLinePlane(line, plane) {
let distance = Measure.distanceLinePlane(line, plane);
distance = Measure.round(distance, 3);
return [
{
result: distance,
description: 'Distance'
}
];
}
static calculateArcPlane(arc, plane) {
let distance = Measure.distanceArcPlane(arc, plane);
distance = Measure.round(distance, 3);
return [
{
result: distance,
description: 'Distance'
}
];
}
static calculatePlanePlane(plane1, plane2) {
let distance = Measure.distancePlanePlane(plane1, plane2);
distance = Measure.round(distance, 3);
let angle = Measure.degrees(Measure.anglePlanePlane(plane1, plane2));
angle = Measure.round(angle, 3);
return [
{
result: distance,
description: 'Distance'
},
{
result: angle,
description: 'Angle'
}
];
}
static isNullVector(vec) {
const tolerance = 1e-12;
if (Math.abs(vec.x) < tolerance && Math.abs(vec.y) < tolerance && Math.abs(vec.z) < tolerance) {
return true;
} else {
return false;
}
}
static distancePointPoint(point1, point2) {
return point1.meta.position.clone().distanceTo(point2.meta.position);
}
static distancePointLine(pointPosition, line) {
const vec = line.meta.start.clone().sub(line.meta.end);
const vecAB = line.meta.start.clone().sub(pointPosition);
let distance = 0;
const length = vec.length();
if (length > 0) {
distance = vec.cross(vecAB).length() / length;
}
return distance;
}
static distanceLineLine(line1, line2) {
const vec1 = line1.meta.start.clone().sub(line1.meta.end);
const vec2 = line2.meta.start.clone().sub(line2.meta.end);
const nVec = vec1.clone().cross(vec2);
let distance = 0;
if (Measure.isNullVector(nVec)) {
// lines are parallel
const vecAB = line1.meta.start.clone().sub(line2.meta.start);
const nVec2 = vecAB.clone().cross(vec1);
if (!Measure.isNullVector(nVec2)) {
// lines are not conincident
return Measure.distancePointLine(line1.meta.start, line2);
}
} else {
distance = Math.abs(nVec.clone().dot(line2.meta.start.clone().sub(line1.meta.start)) / nVec.length());
}
return distance;
}
static distanceLinePlane(line, plane) {
const vec = line.meta.start.clone().sub(line.meta.end);
const product = vec.dot(plane.meta.normal);
if (product < 1e-12) {
// line is parallel to plane
return Measure.distancePointPlane(line.meta.start, plane);
}
}
static distanceArcPlane(arc, plane) {
const vec = arc.meta.normal.clone().cross(plane.meta.normal);
if (Measure.isNullVector(vec)) {
// parallel
return Measure.distancePointPlane(arc.meta.center, plane);
}
}
static distancePointPlane(pointPosition, plane) {
const normal = plane.meta.normal.normalize();
const d = (-1) * plane.meta.pointOnPlane.clone().dot(normal);
const distance = Math.abs(normal.clone().dot(pointPosition) + d) / normal.length();
return distance;
}
static distancePlanePlane(plane1, plane2) {
const vec = plane1.meta.normal.clone().cross(plane2.meta.normal);
if (Measure.isNullVector(vec)) {
// parallel
return Measure.distancePointPlane(plane1.meta.pointOnPlane, plane2);
} else {
return 0;
}
}
static angleLineLine(line1, line2) {
const vec1 = line1.meta.start.clone().sub(line1.meta.end);
const vec2 = line2.meta.start.clone().sub(line2.meta.end);
const angle = vec1.angleTo(vec2);
return angle;
}
static anglePlanePlane(plane1, plane2) {
const normal1 = plane1.meta.normal;
const normal2 = plane2.meta.normal;
const angle = normal1.angleTo(normal2);
return angle;
}
static degrees(radians) {
return radians / Math.PI * 180;
}
static round(value, digits) {
const factor = Math.pow(10, digits);
return Math.round(value * factor) / factor;
}
}