// 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
}
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-15) {
- // No siginificant rotation to perform. Bail to avoid
+ 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