From: Scott Worley Date: Mon, 31 Dec 2012 08:54:08 +0000 (-0800) Subject: Handle opposite vectors in rotate_onto X-Git-Url: http://git.scottworley.com/nt3d/commitdiff_plain/df8f7c913f82a09f187927f8f201844a4e738060?ds=sidebyside Handle opposite vectors in rotate_onto And detect them in extrude() and avoid calling rotate_onto for the second rotation in that case. --- diff --git a/nt3d.js b/nt3d.js index 7198e22..7e0fa25 100644 --- a/nt3d.js +++ b/nt3d.js @@ -181,7 +181,16 @@ nt3d = { // 2. Rotate around shapenormali so that [1,0,0] // becomes pathnormali. - loop = this.rotate_onto(loop, shapex, pathnormali); + if (!this.opposite(shapex, pathnormali)) { + loop = this.rotate_onto(loop, shapex, pathnormali); + } else { + // Rare edge case: When shapex and pathnormali are + // opposite, rotate_onto cannot cross them to get + // an axis of rotation. In this case, we (extrude) + // already know what to do -- just rotate PI around + // shapenormali! + loop = this.rotate_about_origin(loop, shapenormali, Math.PI); + } // (This would probably be faster and more numerically stable // if the two rotations were applied as one combined operation @@ -283,17 +292,40 @@ nt3d = { } return rotated; }, + angle_epsilon: 1e-7, + opposite: function(a, b) { + // Do a and b point in exactly opposite directions? + return Math.abs(this.angle_between(this.unit(a), this.unit(b)) - Math.PI) < this.angle_epsilon; + }, rotate_onto: function(points, a, b) { // Rotate points such that a (in points-space) maps onto b // by crossing a and b to get a rotation axis and using // angle_between to get a rotation angle. var angle = this.angle_between(this.unit(a), this.unit(b)); - if (Math.abs(angle) < 1e-7) { + var abs_angle = Math.abs(angle); + if (Math.abs(angle) < this.angle_epsilon) { // No significant rotation to perform. Bail to avoid // NaNs and numerical error return points; } - var axis = this.unit(this.cross(a, b)); + var axis; + if (Math.abs(abs_angle - Math.PI) < this.angle_epsilon) { + // a and b point in opposite directions, so + // we cannot cross them. So just pick something. + // If the caller wishes to avoid this behaviour, + // they should check with this.opposite() first. + axis = this.project_to_orthogonal(a, [1,0,0]); + console.log("rotate_onto: a and b are opposite! If you carefully chose them to meet some other constraint, you will be sad! Arbitrarily using axis [1,0,0] ->", axis); + if (this.magnitude(axis) < this.angle_epsilon) { + // Oh, double bad luck! Our arbitrary choice + // lines up too! A second, orthogonal arbitrary + // choice is now guaranteed to succeed. + axis = this.project_to_orthogonal(a, [0,1,0]); + console.log("rotate_onto: Double bad luck! Arbitrarily using axis [0,1,0] ->", axis); + } + } else { + axis = this.unit(this.cross(a, b)); + } return this.rotate_about_origin(points, axis, angle); }, rotate: function(points, center, axis, angle) { // axis must be a unit vector