awv3
Version:
⚡ AWV3 embedded CAD
443 lines (405 loc) • 14.7 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.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
}
}