ga.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /**
  2. * This is a 2D Projective Geometric Algebra implementation.
  3. *
  4. * For wider context on geometric algebra visit see https://bivector.net.
  5. *
  6. * For this specific algebra see cheatsheet https://bivector.net/2DPGA.pdf.
  7. *
  8. * Converted from generator written by enki, with a ton of added on top.
  9. *
  10. * This library uses 8-vectors to represent points, directions and lines
  11. * in 2D space.
  12. *
  13. * An array `[a, b, c, d, e, f, g, h]` represents a n(8)vector:
  14. * a + b*e0 + c*e1 + d*e2 + e*e01 + f*e20 + g*e12 + h*e012
  15. *
  16. * See GAPoint, GALine, GADirection and GATransform modules for common
  17. * operations.
  18. */
  19. export type Point = NVector;
  20. export type Direction = NVector;
  21. export type Line = NVector;
  22. export type Transform = NVector;
  23. export const point = (x: number, y: number): Point => [0, 0, 0, 0, y, x, 1, 0];
  24. export const origin = (): Point => [0, 0, 0, 0, 0, 0, 1, 0];
  25. export const direction = (x: number, y: number): Direction => {
  26. const norm = Math.hypot(x, y); // same as `inorm(direction(x, y))`
  27. return [0, 0, 0, 0, y / norm, x / norm, 0, 0];
  28. };
  29. export const offset = (x: number, y: number): Direction => [
  30. 0,
  31. 0,
  32. 0,
  33. 0,
  34. y,
  35. x,
  36. 0,
  37. 0,
  38. ];
  39. /// This is the "implementation" part of the library
  40. type NVector = readonly [
  41. number,
  42. number,
  43. number,
  44. number,
  45. number,
  46. number,
  47. number,
  48. number,
  49. ];
  50. // These are labels for what each number in an nvector represents
  51. const NVECTOR_BASE = ["1", "e0", "e1", "e2", "e01", "e20", "e12", "e012"];
  52. // Used to represent points, lines and transformations
  53. export const nvector = (value: number = 0, index: number = 0): NVector => {
  54. const result = [0, 0, 0, 0, 0, 0, 0, 0];
  55. if (index < 0 || index > 7) {
  56. throw new Error(`Expected \`index\` between 0 and 7, got \`${index}\``);
  57. }
  58. if (value !== 0) {
  59. result[index] = value;
  60. }
  61. return result as unknown as NVector;
  62. };
  63. const STRING_EPSILON = 0.000001;
  64. export const toString = (nvector: NVector): string => {
  65. const result = nvector
  66. .map((value, index) =>
  67. Math.abs(value) > STRING_EPSILON
  68. ? value.toFixed(7).replace(/(\.|0+)$/, "") +
  69. (index > 0 ? NVECTOR_BASE[index] : "")
  70. : null,
  71. )
  72. .filter((representation) => representation != null)
  73. .join(" + ");
  74. return result === "" ? "0" : result;
  75. };
  76. // Reverse the order of the basis blades.
  77. export const reverse = (nvector: NVector): NVector => [
  78. nvector[0],
  79. nvector[1],
  80. nvector[2],
  81. nvector[3],
  82. -nvector[4],
  83. -nvector[5],
  84. -nvector[6],
  85. -nvector[7],
  86. ];
  87. // Poincare duality operator.
  88. export const dual = (nvector: NVector): NVector => [
  89. nvector[7],
  90. nvector[6],
  91. nvector[5],
  92. nvector[4],
  93. nvector[3],
  94. nvector[2],
  95. nvector[1],
  96. nvector[0],
  97. ];
  98. // Clifford Conjugation
  99. export const conjugate = (nvector: NVector): NVector => [
  100. nvector[0],
  101. -nvector[1],
  102. -nvector[2],
  103. -nvector[3],
  104. -nvector[4],
  105. -nvector[5],
  106. -nvector[6],
  107. nvector[7],
  108. ];
  109. // Main involution
  110. export const involute = (nvector: NVector): NVector => [
  111. nvector[0],
  112. -nvector[1],
  113. -nvector[2],
  114. -nvector[3],
  115. nvector[4],
  116. nvector[5],
  117. nvector[6],
  118. -nvector[7],
  119. ];
  120. // Multivector addition
  121. export const add = (a: NVector, b: NVector | number): NVector => {
  122. if (isNumber(b)) {
  123. return [a[0] + b, a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
  124. }
  125. return [
  126. a[0] + b[0],
  127. a[1] + b[1],
  128. a[2] + b[2],
  129. a[3] + b[3],
  130. a[4] + b[4],
  131. a[5] + b[5],
  132. a[6] + b[6],
  133. a[7] + b[7],
  134. ];
  135. };
  136. // Multivector subtraction
  137. export const sub = (a: NVector, b: NVector | number): NVector => {
  138. if (isNumber(b)) {
  139. return [a[0] - b, a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
  140. }
  141. return [
  142. a[0] - b[0],
  143. a[1] - b[1],
  144. a[2] - b[2],
  145. a[3] - b[3],
  146. a[4] - b[4],
  147. a[5] - b[5],
  148. a[6] - b[6],
  149. a[7] - b[7],
  150. ];
  151. };
  152. // The geometric product.
  153. export const mul = (a: NVector, b: NVector | number): NVector => {
  154. if (isNumber(b)) {
  155. return [
  156. a[0] * b,
  157. a[1] * b,
  158. a[2] * b,
  159. a[3] * b,
  160. a[4] * b,
  161. a[5] * b,
  162. a[6] * b,
  163. a[7] * b,
  164. ];
  165. }
  166. return [
  167. mulScalar(a, b),
  168. b[1] * a[0] +
  169. b[0] * a[1] -
  170. b[4] * a[2] +
  171. b[5] * a[3] +
  172. b[2] * a[4] -
  173. b[3] * a[5] -
  174. b[7] * a[6] -
  175. b[6] * a[7],
  176. b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6],
  177. b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6],
  178. b[4] * a[0] +
  179. b[2] * a[1] -
  180. b[1] * a[2] +
  181. b[7] * a[3] +
  182. b[0] * a[4] +
  183. b[6] * a[5] -
  184. b[5] * a[6] +
  185. b[3] * a[7],
  186. b[5] * a[0] -
  187. b[3] * a[1] +
  188. b[7] * a[2] +
  189. b[1] * a[3] -
  190. b[6] * a[4] +
  191. b[0] * a[5] +
  192. b[4] * a[6] +
  193. b[2] * a[7],
  194. b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6],
  195. b[7] * a[0] +
  196. b[6] * a[1] +
  197. b[5] * a[2] +
  198. b[4] * a[3] +
  199. b[3] * a[4] +
  200. b[2] * a[5] +
  201. b[1] * a[6] +
  202. b[0] * a[7],
  203. ];
  204. };
  205. export const mulScalar = (a: NVector, b: NVector): number =>
  206. b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6];
  207. // The outer/exterior/wedge product.
  208. export const meet = (a: NVector, b: NVector): NVector => [
  209. b[0] * a[0],
  210. b[1] * a[0] + b[0] * a[1],
  211. b[2] * a[0] + b[0] * a[2],
  212. b[3] * a[0] + b[0] * a[3],
  213. b[4] * a[0] + b[2] * a[1] - b[1] * a[2] + b[0] * a[4],
  214. b[5] * a[0] - b[3] * a[1] + b[1] * a[3] + b[0] * a[5],
  215. b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6],
  216. b[7] * a[0] +
  217. b[6] * a[1] +
  218. b[5] * a[2] +
  219. b[4] * a[3] +
  220. b[3] * a[4] +
  221. b[2] * a[5] +
  222. b[1] * a[6],
  223. ];
  224. // The regressive product.
  225. export const join = (a: NVector, b: NVector): NVector => [
  226. joinScalar(a, b),
  227. a[1] * b[7] + a[4] * b[5] - a[5] * b[4] + a[7] * b[1],
  228. a[2] * b[7] - a[4] * b[6] + a[6] * b[4] + a[7] * b[2],
  229. a[3] * b[7] + a[5] * b[6] - a[6] * b[5] + a[7] * b[3],
  230. a[4] * b[7] + a[7] * b[4],
  231. a[5] * b[7] + a[7] * b[5],
  232. a[6] * b[7] + a[7] * b[6],
  233. a[7] * b[7],
  234. ];
  235. export const joinScalar = (a: NVector, b: NVector): number =>
  236. a[0] * b[7] +
  237. a[1] * b[6] +
  238. a[2] * b[5] +
  239. a[3] * b[4] +
  240. a[4] * b[3] +
  241. a[5] * b[2] +
  242. a[6] * b[1] +
  243. a[7] * b[0];
  244. // The inner product.
  245. export const dot = (a: NVector, b: NVector): NVector => [
  246. b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6],
  247. b[1] * a[0] +
  248. b[0] * a[1] -
  249. b[4] * a[2] +
  250. b[5] * a[3] +
  251. b[2] * a[4] -
  252. b[3] * a[5] -
  253. b[7] * a[6] -
  254. b[6] * a[7],
  255. b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6],
  256. b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6],
  257. b[4] * a[0] + b[7] * a[3] + b[0] * a[4] + b[3] * a[7],
  258. b[5] * a[0] + b[7] * a[2] + b[0] * a[5] + b[2] * a[7],
  259. b[6] * a[0] + b[0] * a[6],
  260. b[7] * a[0] + b[0] * a[7],
  261. ];
  262. export const norm = (a: NVector): number =>
  263. Math.sqrt(Math.abs(a[0] * a[0] - a[2] * a[2] - a[3] * a[3] + a[6] * a[6]));
  264. export const inorm = (a: NVector): number =>
  265. Math.sqrt(Math.abs(a[7] * a[7] - a[5] * a[5] - a[4] * a[4] + a[1] * a[1]));
  266. export const normalized = (a: NVector): NVector => {
  267. const n = norm(a);
  268. if (n === 0 || n === 1) {
  269. return a;
  270. }
  271. const sign = a[6] < 0 ? -1 : 1;
  272. return mul(a, sign / n);
  273. };
  274. export const inormalized = (a: NVector): NVector => {
  275. const n = inorm(a);
  276. if (n === 0 || n === 1) {
  277. return a;
  278. }
  279. return mul(a, 1 / n);
  280. };
  281. const isNumber = (a: any): a is number => typeof a === "number";
  282. export const E0: NVector = nvector(1, 1);
  283. export const E1: NVector = nvector(1, 2);
  284. export const E2: NVector = nvector(1, 3);
  285. export const E01: NVector = nvector(1, 4);
  286. export const E20: NVector = nvector(1, 5);
  287. export const E12: NVector = nvector(1, 6);
  288. export const E012: NVector = nvector(1, 7);
  289. export const I = E012;