-
Notifications
You must be signed in to change notification settings - Fork 520
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(modeling): added fromNoisyPoints() to plane
- Loading branch information
Showing
5 changed files
with
138 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import Plane from './type' | ||
import Vec3 from '../vec3/type' | ||
|
||
export default fromNoisyPoints | ||
|
||
declare function fromNoisyPoints(out: Plane, ...vertices: Array<Vec3>): Plane |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
const test = require('ava') | ||
const { fromNoisyPoints, create } = require('./index') | ||
|
||
const { compareVectors } = require('../../../test/helpers/index') | ||
|
||
test('plane: fromNoisyPoints() should return a new plane with correct values', (t) => { | ||
const obs1 = fromNoisyPoints(create(), [0, 0, 0], [1, 0, 0], [1, 1, 0]) | ||
t.true(compareVectors(obs1, [0, 0, 1, 0])) | ||
|
||
const obs2 = fromNoisyPoints(obs1, [0, 6, 0], [0, 2, 2], [0, 6, 6]) | ||
t.true(compareVectors(obs2, [1, 0, 0, 0])) | ||
|
||
// same vertices results in an invalid plane | ||
const obs3 = fromNoisyPoints(obs1, [0, 6, 0], [0, 6, 0], [0, 6, 0]) | ||
t.true(compareVectors(obs3, [0 / 0, 0 / 0, 0 / 0, 0 / 0])) | ||
|
||
// co-linear vertices | ||
const obs4 = fromNoisyPoints(obs1, [0, 0, 0], [1, 0, 0], [2, 0, 0], [0, 1, 0]) | ||
t.true(compareVectors(obs4, [0, 0, 1, 0])) | ||
|
||
// random vertices | ||
const obs5 = fromNoisyPoints(obs1, [0, 0, 0], [5, 1, -2], [3, -2, 4], [1, 1, 0]) | ||
t.true(compareVectors(obs5, [0.08054818365229491, 0.8764542170444571, 0.47469990050062555, 0.4185833634679763])) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters