math.scalar.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. import { Clamp, Lerp, NormalizeRadians, RandomRange, ToHex, WithinEpsilon } from "./math.scalar.functions.js";
  2. /**
  3. * Scalar computation library
  4. */
  5. export class Scalar {
  6. /**
  7. * Returns -1 if value is negative and +1 is value is positive.
  8. * @param value the value
  9. * @returns the value itself if it's equal to zero.
  10. */
  11. static Sign(value) {
  12. value = +value; // convert to a number
  13. if (value === 0 || isNaN(value)) {
  14. return value;
  15. }
  16. return value > 0 ? 1 : -1;
  17. }
  18. /**
  19. * the log2 of value.
  20. * @param value the value to compute log2 of
  21. * @returns the log2 of value.
  22. */
  23. static Log2(value) {
  24. return Math.log(value) * Math.LOG2E;
  25. }
  26. /**
  27. * the floor part of a log2 value.
  28. * @param value the value to compute log2 of
  29. * @returns the log2 of value.
  30. */
  31. static ILog2(value) {
  32. if (Math.log2) {
  33. return Math.floor(Math.log2(value));
  34. }
  35. if (value < 0) {
  36. return NaN;
  37. }
  38. else if (value === 0) {
  39. return -Infinity;
  40. }
  41. let n = 0;
  42. if (value < 1) {
  43. while (value < 1) {
  44. n++;
  45. value = value * 2;
  46. }
  47. n = -n;
  48. }
  49. else if (value > 1) {
  50. while (value > 1) {
  51. n++;
  52. value = Math.floor(value / 2);
  53. }
  54. }
  55. return n;
  56. }
  57. /**
  58. * Loops the value, so that it is never larger than length and never smaller than 0.
  59. *
  60. * This is similar to the modulo operator but it works with floating point numbers.
  61. * For example, using 3.0 for t and 2.5 for length, the result would be 0.5.
  62. * With t = 5 and length = 2.5, the result would be 0.0.
  63. * Note, however, that the behaviour is not defined for negative numbers as it is for the modulo operator
  64. * @param value the value
  65. * @param length the length
  66. * @returns the looped value
  67. */
  68. static Repeat(value, length) {
  69. return value - Math.floor(value / length) * length;
  70. }
  71. /**
  72. * Normalize the value between 0.0 and 1.0 using min and max values
  73. * @param value value to normalize
  74. * @param min max to normalize between
  75. * @param max min to normalize between
  76. * @returns the normalized value
  77. */
  78. static Normalize(value, min, max) {
  79. return (value - min) / (max - min);
  80. }
  81. /**
  82. * Denormalize the value from 0.0 and 1.0 using min and max values
  83. * @param normalized value to denormalize
  84. * @param min max to denormalize between
  85. * @param max min to denormalize between
  86. * @returns the denormalized value
  87. */
  88. static Denormalize(normalized, min, max) {
  89. return normalized * (max - min) + min;
  90. }
  91. /**
  92. * Calculates the shortest difference between two given angles given in degrees.
  93. * @param current current angle in degrees
  94. * @param target target angle in degrees
  95. * @returns the delta
  96. */
  97. static DeltaAngle(current, target) {
  98. let num = Scalar.Repeat(target - current, 360.0);
  99. if (num > 180.0) {
  100. num -= 360.0;
  101. }
  102. return num;
  103. }
  104. /**
  105. * PingPongs the value t, so that it is never larger than length and never smaller than 0.
  106. * @param tx value
  107. * @param length length
  108. * @returns The returned value will move back and forth between 0 and length
  109. */
  110. static PingPong(tx, length) {
  111. const t = Scalar.Repeat(tx, length * 2.0);
  112. return length - Math.abs(t - length);
  113. }
  114. /**
  115. * Interpolates between min and max with smoothing at the limits.
  116. *
  117. * This function interpolates between min and max in a similar way to Lerp. However, the interpolation will gradually speed up
  118. * from the start and slow down toward the end. This is useful for creating natural-looking animation, fading and other transitions.
  119. * @param from from
  120. * @param to to
  121. * @param tx value
  122. * @returns the smooth stepped value
  123. */
  124. static SmoothStep(from, to, tx) {
  125. let t = Scalar.Clamp(tx);
  126. t = -2.0 * t * t * t + 3.0 * t * t;
  127. return to * t + from * (1.0 - t);
  128. }
  129. /**
  130. * Moves a value current towards target.
  131. *
  132. * This is essentially the same as Mathf.Lerp but instead the function will ensure that the speed never exceeds maxDelta.
  133. * Negative values of maxDelta pushes the value away from target.
  134. * @param current current value
  135. * @param target target value
  136. * @param maxDelta max distance to move
  137. * @returns resulting value
  138. */
  139. static MoveTowards(current, target, maxDelta) {
  140. let result = 0;
  141. if (Math.abs(target - current) <= maxDelta) {
  142. result = target;
  143. }
  144. else {
  145. result = current + Scalar.Sign(target - current) * maxDelta;
  146. }
  147. return result;
  148. }
  149. /**
  150. * Same as MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees.
  151. *
  152. * Variables current and target are assumed to be in degrees. For optimization reasons, negative values of maxDelta
  153. * are not supported and may cause oscillation. To push current away from a target angle, add 180 to that angle instead.
  154. * @param current current value
  155. * @param target target value
  156. * @param maxDelta max distance to move
  157. * @returns resulting angle
  158. */
  159. static MoveTowardsAngle(current, target, maxDelta) {
  160. const num = Scalar.DeltaAngle(current, target);
  161. let result = 0;
  162. if (-maxDelta < num && num < maxDelta) {
  163. result = target;
  164. }
  165. else {
  166. target = current + num;
  167. result = Scalar.MoveTowards(current, target, maxDelta);
  168. }
  169. return result;
  170. }
  171. /**
  172. * Same as Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees.
  173. * The parameter t is clamped to the range [0, 1]. Variables a and b are assumed to be in degrees.
  174. * @param start start value
  175. * @param end target value
  176. * @param amount amount to lerp between
  177. * @returns the lerped value
  178. */
  179. static LerpAngle(start, end, amount) {
  180. let num = Scalar.Repeat(end - start, 360.0);
  181. if (num > 180.0) {
  182. num -= 360.0;
  183. }
  184. return start + num * Clamp(amount);
  185. }
  186. /**
  187. * Calculates the linear parameter t that produces the interpolant value within the range [a, b].
  188. * @param a start value
  189. * @param b target value
  190. * @param value value between a and b
  191. * @returns the inverseLerp value
  192. */
  193. static InverseLerp(a, b, value) {
  194. let result = 0;
  195. if (a != b) {
  196. result = Clamp((value - a) / (b - a));
  197. }
  198. else {
  199. result = 0.0;
  200. }
  201. return result;
  202. }
  203. /**
  204. * Returns a new scalar located for "amount" (float) on the Hermite spline defined by the scalars "value1", "value3", "tangent1", "tangent2".
  205. * @see http://mathworld.wolfram.com/HermitePolynomial.html
  206. * @param value1 defines the first control point
  207. * @param tangent1 defines the first tangent
  208. * @param value2 defines the second control point
  209. * @param tangent2 defines the second tangent
  210. * @param amount defines the amount on the interpolation spline (between 0 and 1)
  211. * @returns hermite result
  212. */
  213. static Hermite(value1, tangent1, value2, tangent2, amount) {
  214. const squared = amount * amount;
  215. const cubed = amount * squared;
  216. const part1 = 2.0 * cubed - 3.0 * squared + 1.0;
  217. const part2 = -2.0 * cubed + 3.0 * squared;
  218. const part3 = cubed - 2.0 * squared + amount;
  219. const part4 = cubed - squared;
  220. return value1 * part1 + value2 * part2 + tangent1 * part3 + tangent2 * part4;
  221. }
  222. /**
  223. * Returns a new scalar which is the 1st derivative of the Hermite spline defined by the scalars "value1", "value2", "tangent1", "tangent2".
  224. * @param value1 defines the first control point
  225. * @param tangent1 defines the first tangent
  226. * @param value2 defines the second control point
  227. * @param tangent2 defines the second tangent
  228. * @param time define where the derivative must be done
  229. * @returns 1st derivative
  230. */
  231. static Hermite1stDerivative(value1, tangent1, value2, tangent2, time) {
  232. const t2 = time * time;
  233. return (t2 - time) * 6 * value1 + (3 * t2 - 4 * time + 1) * tangent1 + (-t2 + time) * 6 * value2 + (3 * t2 - 2 * time) * tangent2;
  234. }
  235. /**
  236. * This function returns percentage of a number in a given range.
  237. *
  238. * RangeToPercent(40,20,60) will return 0.5 (50%)
  239. * RangeToPercent(34,0,100) will return 0.34 (34%)
  240. * @param number to convert to percentage
  241. * @param min min range
  242. * @param max max range
  243. * @returns the percentage
  244. */
  245. static RangeToPercent(number, min, max) {
  246. return (number - min) / (max - min);
  247. }
  248. /**
  249. * This function returns number that corresponds to the percentage in a given range.
  250. *
  251. * PercentToRange(0.34,0,100) will return 34.
  252. * @param percent to convert to number
  253. * @param min min range
  254. * @param max max range
  255. * @returns the number
  256. */
  257. static PercentToRange(percent, min, max) {
  258. return (max - min) * percent + min;
  259. }
  260. /**
  261. * Returns the highest common factor of two integers.
  262. * @param a first parameter
  263. * @param b second parameter
  264. * @returns HCF of a and b
  265. */
  266. static HCF(a, b) {
  267. const r = a % b;
  268. if (r === 0) {
  269. return b;
  270. }
  271. return Scalar.HCF(b, r);
  272. }
  273. }
  274. /**
  275. * Two pi constants convenient for computation.
  276. */
  277. Scalar.TwoPi = Math.PI * 2;
  278. /**
  279. * Boolean : true if the absolute difference between a and b is lower than epsilon (default = 1.401298E-45)
  280. * @param a number
  281. * @param b number
  282. * @param epsilon (default = 1.401298E-45)
  283. * @returns true if the absolute difference between a and b is lower than epsilon (default = 1.401298E-45)
  284. */
  285. Scalar.WithinEpsilon = WithinEpsilon;
  286. /**
  287. * Returns a string : the upper case translation of the number i to hexadecimal.
  288. * @param i number
  289. * @returns the upper case translation of the number i to hexadecimal.
  290. */
  291. Scalar.ToHex = ToHex;
  292. /**
  293. * Returns the value itself if it's between min and max.
  294. * Returns min if the value is lower than min.
  295. * Returns max if the value is greater than max.
  296. * @param value the value to clmap
  297. * @param min the min value to clamp to (default: 0)
  298. * @param max the max value to clamp to (default: 1)
  299. * @returns the clamped value
  300. */
  301. Scalar.Clamp = Clamp;
  302. /**
  303. * Creates a new scalar with values linearly interpolated of "amount" between the start scalar and the end scalar.
  304. * @param start start value
  305. * @param end target value
  306. * @param amount amount to lerp between
  307. * @returns the lerped value
  308. */
  309. Scalar.Lerp = Lerp;
  310. /**
  311. * Returns a random float number between and min and max values
  312. * @param min min value of random
  313. * @param max max value of random
  314. * @returns random value
  315. */
  316. Scalar.RandomRange = RandomRange;
  317. /**
  318. * Returns the angle converted to equivalent value between -Math.PI and Math.PI radians.
  319. * @param angle The angle to normalize in radian.
  320. * @returns The converted angle.
  321. */
  322. Scalar.NormalizeRadians = NormalizeRadians;
  323. //# sourceMappingURL=math.scalar.js.map