UNPKG

awv3

Version:
429 lines (391 loc) 14.4 kB
/** * @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; } }