UNPKG

awv3

Version:
443 lines (405 loc) 14.7 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.items, 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 } }