UNPKG

@jscad/modeling

Version:

Constructive Solid Geometry (CSG) Library for JSCAD

107 lines (90 loc) 2.37 kB
const vec3 = require('../vec3') const fromNormalAndPoint = require('./fromNormalAndPoint') /** * Create a best-fit plane from the given noisy vertices. * * NOTE: There are two possible orientations for every plane. * This function always produces positive orientations. * * See http://www.ilikebigbits.com for the original discussion * * @param {Plane} out - receiving plane * @param {Array} vertices - list of vertices in any order or position * @returns {Plane} out * @alias module:modeling/maths/plane.fromNoisyPoints */ const fromNoisyPoints = (out, ...vertices) => { out[0] = 0.0 out[1] = 0.0 out[2] = 0.0 out[3] = 0.0 // calculate the centroid of the vertices // NOTE: out is the centriod const n = vertices.length vertices.forEach((v) => { vec3.add(out, out, v) }) vec3.scale(out, out, 1.0 / n) // Calculate full 3x3 covariance matrix, excluding symmetries let xx = 0.0 let xy = 0.0 let xz = 0.0 let yy = 0.0 let yz = 0.0 let zz = 0.0 const vn = vec3.create() vertices.forEach((v) => { // NOTE: out is the centriod vec3.subtract(vn, v, out) xx += vn[0] * vn[0] xy += vn[0] * vn[1] xz += vn[0] * vn[2] yy += vn[1] * vn[1] yz += vn[1] * vn[2] zz += vn[2] * vn[2] }) xx /= n xy /= n xz /= n yy /= n yz /= n zz /= n // Calculate the smallest Eigenvector of the covariance matrix // which becomes the plane normal vn[0] = 0.0 vn[1] = 0.0 vn[2] = 0.0 // weighted directional vector const wdv = vec3.create() // X axis let det = yy * zz - yz * yz wdv[0] = det wdv[1] = xz * yz - xy * zz wdv[2] = xy * yz - xz * yy let weight = det * det vec3.add(vn, vn, vec3.scale(wdv, wdv, weight)) // Y axis det = xx * zz - xz * xz wdv[0] = xz * yz - xy * zz wdv[1] = det wdv[2] = xy * xz - yz * xx weight = det * det if (vec3.dot(vn, wdv) < 0.0) { weight = -weight } vec3.add(vn, vn, vec3.scale(wdv, wdv, weight)) // Z axis det = xx * yy - xy * xy wdv[0] = xy * yz - xz * yy wdv[1] = xy * xz - yz * xx wdv[2] = det weight = det * det if (vec3.dot(vn, wdv) < 0.0) { weight = -weight } vec3.add(vn, vn, vec3.scale(wdv, wdv, weight)) // create the plane from normal and centriod // NOTE: out is the centriod return fromNormalAndPoint(out, vn, out) } module.exports = fromNoisyPoints