Source: vector3d.js

  1. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2. /* Vector handling functions (c) Chris Veness 2011-2016 */
  3. /* MIT Licence */
  4. /* www.movable-type.co.uk/scripts/geodesy/docs/module-vector3d.html */
  5. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  6. /**
  7. * Library of 3-d vector manipulation routines.
  8. *
  9. * @module vector3d
  10. */
  11. /* Vector3d - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  12. /**
  13. * Functions for manipulating generic 3-d vectors.
  14. *
  15. * Functions return vectors as return results, so that operations can be chained.
  16. * @example var v = v1.cross(v2).dot(v3) // ≡ v1×v2⋅v3
  17. */
  18. class Vector3d { // note prototype-based class not inheritance-based class
  19. /**
  20. * Creates a 3-d vector.
  21. *
  22. * @param {number} x - X component of vector.
  23. * @param {number} y - Y component of vector.
  24. * @param {number} z - Z component of vector.
  25. *
  26. * @example
  27. * import Vector3d from 'vector3d';
  28. * var v = new Vector3d(0.267, 0.535, 0.802);
  29. */
  30. constructor(x, y, z) {
  31. this.x = Number(x);
  32. this.y = Number(y);
  33. this.z = Number(z);
  34. }
  35. /**
  36. * Adds supplied vector to ‘this’ vector.
  37. *
  38. * @param {Vector3d} v - Vector to be added to this vector.
  39. * @returns {Vector3d} Vector representing sum of this and v.
  40. */
  41. plus(v) {
  42. if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');
  43. return new Vector3d(this.x + v.x, this.y + v.y, this.z + v.z);
  44. }
  45. /**
  46. * Subtracts supplied vector from ‘this’ vector.
  47. *
  48. * @param {Vector3d} v - Vector to be subtracted from this vector.
  49. * @returns {Vector3d} Vector representing difference between this and v.
  50. */
  51. minus(v) {
  52. if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');
  53. return new Vector3d(this.x - v.x, this.y - v.y, this.z - v.z);
  54. }
  55. /**
  56. * Multiplies ‘this’ vector by a scalar value.
  57. *
  58. * @param {number} x - Factor to multiply this vector by.
  59. * @returns {Vector3d} Vector scaled by x.
  60. */
  61. times(x) {
  62. x = Number(x);
  63. return new Vector3d(this.x * x, this.y * x, this.z * x);
  64. }
  65. /**
  66. * Divides ‘this’ vector by a scalar value.
  67. *
  68. * @param {number} x - Factor to divide this vector by.
  69. * @returns {Vector3d} Vector divided by x.
  70. */
  71. dividedBy(x) {
  72. x = Number(x);
  73. return new Vector3d(this.x / x, this.y / x, this.z / x);
  74. }
  75. /**
  76. * Multiplies ‘this’ vector by the supplied vector using dot (scalar) product.
  77. *
  78. * @param {Vector3d} v - Vector to be dotted with this vector.
  79. * @returns {number} Dot product of ‘this’ and v.
  80. */
  81. dot(v) {
  82. if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');
  83. return this.x * v.x + this.y * v.y + this.z * v.z;
  84. }
  85. /**
  86. * Multiplies ‘this’ vector by the supplied vector using cross (vector) product.
  87. *
  88. * @param {Vector3d} v - Vector to be crossed with this vector.
  89. * @returns {Vector3d} Cross product of ‘this’ and v.
  90. */
  91. cross(v) {
  92. if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');
  93. var x = this.y * v.z - this.z * v.y;
  94. var y = this.z * v.x - this.x * v.z;
  95. var z = this.x * v.y - this.y * v.x;
  96. return new Vector3d(x, y, z);
  97. }
  98. /**
  99. * Negates a vector to point in the opposite direction
  100. *
  101. * @returns {Vector3d} Negated vector.
  102. */
  103. negate() {
  104. return new Vector3d(-this.x, -this.y, -this.z);
  105. }
  106. /**
  107. * Length (magnitude or norm) of ‘this’ vector
  108. *
  109. * @returns {number} Magnitude of this vector.
  110. */
  111. get length() {
  112. return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
  113. }
  114. /**
  115. * Normalizes a vector to its unit vector
  116. * – if the vector is already unit or is zero magnitude, this is a no-op.
  117. *
  118. * @returns {Vector3d} Normalised version of this vector.
  119. */
  120. unit() {
  121. var norm = this.length;
  122. if (norm == 1) return this;
  123. if (norm == 0) return this;
  124. var x = this.x / norm;
  125. var y = this.y / norm;
  126. var z = this.z / norm;
  127. return new Vector3d(x, y, z);
  128. }
  129. /**
  130. * Calculates the angle between ‘this’ vector and supplied vector.
  131. *
  132. * @param {Vector3d} v
  133. * @param {Vector3d} [vSign] - If supplied (and out of plane of this and v), angle is signed +ve if
  134. * this->v is clockwise looking along vSign, -ve in opposite direction (otherwise unsigned angle).
  135. * @returns {number} Angle (in radians) between this vector and supplied vector.
  136. */
  137. angleTo(v, vSign) {
  138. if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');
  139. var sinθ = this.cross(v).length;
  140. var cosθ = this.dot(v);
  141. if (vSign !== undefined) {
  142. if (!(vSign instanceof Vector3d)) throw new TypeError('vSign is not Vector3d object');
  143. // use vSign as reference to get sign of sinθ
  144. sinθ = this.cross(v).dot(vSign) < 0 ? -sinθ : sinθ;
  145. }
  146. return Math.atan2(sinθ, cosθ);
  147. }
  148. /**
  149. * Rotates ‘this’ point around an axis by a specified angle.
  150. *
  151. * @param {Vector3d} axis - The axis being rotated around.
  152. * @param {number} angle - The angle of rotation (in degrees).
  153. * @returns {Vector3d} The rotated point.
  154. */
  155. rotateAround(axis, angle) {
  156. if (!(axis instanceof Vector3d)) throw new TypeError('axis is not Vector3d object');
  157. var θ = angle.toRadians();
  158. // en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
  159. // en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix
  160. var p = this.unit();
  161. var a = axis.unit();
  162. var s = Math.sin(θ);
  163. var c = Math.cos(θ);
  164. var t = 1-c;
  165. var x = a.x, y = a.y, z = a.z;
  166. var r = [ // rotation matrix for rotation about supplied axis
  167. [ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],
  168. [ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],
  169. [ t*x*z - s*y, t*y*z + s*x, t*z*z + c ],
  170. ];
  171. // multiply r × p
  172. var rp = [
  173. r[0][0]*p.x + r[0][1]*p.y + r[0][2]*p.z,
  174. r[1][0]*p.x + r[1][1]*p.y + r[1][2]*p.z,
  175. r[2][0]*p.x + r[2][1]*p.y + r[2][2]*p.z,
  176. ];
  177. var p2 = new Vector3d(rp[0], rp[1], rp[2]);
  178. return p2;
  179. // qv en.wikipedia.org/wiki/Rodrigues'_rotation_formula...
  180. }
  181. /**
  182. * String representation of vector.
  183. *
  184. * @param {number} [dp=3] - Number of decimal places to be used.
  185. * @returns {string} Vector represented as [x,y,z].
  186. */
  187. toString(dp=3) {
  188. var str = '[' + this.x.toFixed(dp) + ',' + this.y.toFixed(dp) + ',' + this.z.toFixed(dp) + ']';
  189. return str;
  190. }
  191. }
  192. export default Vector3d
  193. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */