ga.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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 function point(x: number, y: number): Point {
  24. return [0, 0, 0, 0, y, x, 1, 0];
  25. }
  26. export function origin(): Point {
  27. return [0, 0, 0, 0, 0, 0, 1, 0];
  28. }
  29. export function direction(x: number, y: number): Direction {
  30. const norm = Math.hypot(x, y); // same as `inorm(direction(x, y))`
  31. return [0, 0, 0, 0, y / norm, x / norm, 0, 0];
  32. }
  33. export function offset(x: number, y: number): Direction {
  34. return [0, 0, 0, 0, y, x, 0, 0];
  35. }
  36. /// This is the "implementation" part of the library
  37. type NVector = readonly [
  38. number,
  39. number,
  40. number,
  41. number,
  42. number,
  43. number,
  44. number,
  45. number,
  46. ];
  47. // These are labels for what each number in an nvector represents
  48. const NVECTOR_BASE = ["1", "e0", "e1", "e2", "e01", "e20", "e12", "e012"];
  49. // Used to represent points, lines and transformations
  50. export function nvector(value: number = 0, index: number = 0): NVector {
  51. const result = [0, 0, 0, 0, 0, 0, 0, 0];
  52. if (index < 0 || index > 7) {
  53. throw new Error(`Expected \`index\` betwen 0 and 7, got \`${index}\``);
  54. }
  55. if (value !== 0) {
  56. result[index] = value;
  57. }
  58. return (result as unknown) as NVector;
  59. }
  60. const STRING_EPSILON = 0.000001;
  61. export function toString(nvector: NVector): string {
  62. const result = nvector
  63. .map((value, index) =>
  64. Math.abs(value) > STRING_EPSILON
  65. ? value.toFixed(7).replace(/(\.|0+)$/, "") +
  66. (index > 0 ? NVECTOR_BASE[index] : "")
  67. : null,
  68. )
  69. .filter((representation) => representation != null)
  70. .join(" + ");
  71. return result === "" ? "0" : result;
  72. }
  73. // Reverse the order of the basis blades.
  74. export function reverse(nvector: NVector): NVector {
  75. return [
  76. nvector[0],
  77. nvector[1],
  78. nvector[2],
  79. nvector[3],
  80. -nvector[4],
  81. -nvector[5],
  82. -nvector[6],
  83. -nvector[7],
  84. ];
  85. }
  86. // Poincare duality operator.
  87. export function dual(nvector: NVector): NVector {
  88. return [
  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. }
  99. // Clifford Conjugation
  100. export function conjugate(nvector: NVector): NVector {
  101. return [
  102. nvector[0],
  103. -nvector[1],
  104. -nvector[2],
  105. -nvector[3],
  106. -nvector[4],
  107. -nvector[5],
  108. -nvector[6],
  109. nvector[7],
  110. ];
  111. }
  112. // Main involution
  113. export function involute(nvector: NVector): NVector {
  114. return [
  115. nvector[0],
  116. -nvector[1],
  117. -nvector[2],
  118. -nvector[3],
  119. nvector[4],
  120. nvector[5],
  121. nvector[6],
  122. -nvector[7],
  123. ];
  124. }
  125. // Multivector addition
  126. export function add(a: NVector, b: NVector | number): NVector {
  127. if (isNumber(b)) {
  128. return [a[0] + b, a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
  129. }
  130. return [
  131. a[0] + b[0],
  132. a[1] + b[1],
  133. a[2] + b[2],
  134. a[3] + b[3],
  135. a[4] + b[4],
  136. a[5] + b[5],
  137. a[6] + b[6],
  138. a[7] + b[7],
  139. ];
  140. }
  141. // Multivector subtraction
  142. export function sub(a: NVector, b: NVector | number): NVector {
  143. if (isNumber(b)) {
  144. return [a[0] - b, a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
  145. }
  146. return [
  147. a[0] - b[0],
  148. a[1] - b[1],
  149. a[2] - b[2],
  150. a[3] - b[3],
  151. a[4] - b[4],
  152. a[5] - b[5],
  153. a[6] - b[6],
  154. a[7] - b[7],
  155. ];
  156. }
  157. // The geometric product.
  158. export function mul(a: NVector, b: NVector | number): NVector {
  159. if (isNumber(b)) {
  160. return [
  161. a[0] * b,
  162. a[1] * b,
  163. a[2] * b,
  164. a[3] * b,
  165. a[4] * b,
  166. a[5] * b,
  167. a[6] * b,
  168. a[7] * b,
  169. ];
  170. }
  171. return [
  172. mulScalar(a, b),
  173. b[1] * a[0] +
  174. b[0] * a[1] -
  175. b[4] * a[2] +
  176. b[5] * a[3] +
  177. b[2] * a[4] -
  178. b[3] * a[5] -
  179. b[7] * a[6] -
  180. b[6] * a[7],
  181. b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6],
  182. b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6],
  183. b[4] * a[0] +
  184. b[2] * a[1] -
  185. b[1] * a[2] +
  186. b[7] * a[3] +
  187. b[0] * a[4] +
  188. b[6] * a[5] -
  189. b[5] * a[6] +
  190. b[3] * a[7],
  191. b[5] * a[0] -
  192. b[3] * a[1] +
  193. b[7] * a[2] +
  194. b[1] * a[3] -
  195. b[6] * a[4] +
  196. b[0] * a[5] +
  197. b[4] * a[6] +
  198. b[2] * a[7],
  199. b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6],
  200. b[7] * a[0] +
  201. b[6] * a[1] +
  202. b[5] * a[2] +
  203. b[4] * a[3] +
  204. b[3] * a[4] +
  205. b[2] * a[5] +
  206. b[1] * a[6] +
  207. b[0] * a[7],
  208. ];
  209. }
  210. export function mulScalar(a: NVector, b: NVector): number {
  211. return b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6];
  212. }
  213. // The outer/exterior/wedge product.
  214. export function meet(a: NVector, b: NVector): NVector {
  215. return [
  216. b[0] * a[0],
  217. b[1] * a[0] + b[0] * a[1],
  218. b[2] * a[0] + b[0] * a[2],
  219. b[3] * a[0] + b[0] * a[3],
  220. b[4] * a[0] + b[2] * a[1] - b[1] * a[2] + b[0] * a[4],
  221. b[5] * a[0] - b[3] * a[1] + b[1] * a[3] + b[0] * a[5],
  222. b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6],
  223. b[7] * a[0] +
  224. b[6] * a[1] +
  225. b[5] * a[2] +
  226. b[4] * a[3] +
  227. b[3] * a[4] +
  228. b[2] * a[5] +
  229. b[1] * a[6],
  230. ];
  231. }
  232. // The regressive product.
  233. export function join(a: NVector, b: NVector): NVector {
  234. return [
  235. joinScalar(a, b),
  236. a[1] * b[7] + a[4] * b[5] - a[5] * b[4] + a[7] * b[1],
  237. a[2] * b[7] - a[4] * b[6] + a[6] * b[4] + a[7] * b[2],
  238. a[3] * b[7] + a[5] * b[6] - a[6] * b[5] + a[7] * b[3],
  239. a[4] * b[7] + a[7] * b[4],
  240. a[5] * b[7] + a[7] * b[5],
  241. a[6] * b[7] + a[7] * b[6],
  242. a[7] * b[7],
  243. ];
  244. }
  245. export function joinScalar(a: NVector, b: NVector): number {
  246. return (
  247. a[0] * b[7] +
  248. a[1] * b[6] +
  249. a[2] * b[5] +
  250. a[3] * b[4] +
  251. a[4] * b[3] +
  252. a[5] * b[2] +
  253. a[6] * b[1] +
  254. a[7] * b[0]
  255. );
  256. }
  257. // The inner product.
  258. export function dot(a: NVector, b: NVector): NVector {
  259. return [
  260. b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6],
  261. b[1] * a[0] +
  262. b[0] * a[1] -
  263. b[4] * a[2] +
  264. b[5] * a[3] +
  265. b[2] * a[4] -
  266. b[3] * a[5] -
  267. b[7] * a[6] -
  268. b[6] * a[7],
  269. b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6],
  270. b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6],
  271. b[4] * a[0] + b[7] * a[3] + b[0] * a[4] + b[3] * a[7],
  272. b[5] * a[0] + b[7] * a[2] + b[0] * a[5] + b[2] * a[7],
  273. b[6] * a[0] + b[0] * a[6],
  274. b[7] * a[0] + b[0] * a[7],
  275. ];
  276. }
  277. export function norm(a: NVector): number {
  278. return Math.sqrt(
  279. Math.abs(a[0] * a[0] - a[2] * a[2] - a[3] * a[3] + a[6] * a[6]),
  280. );
  281. }
  282. export function inorm(a: NVector): number {
  283. return Math.sqrt(
  284. Math.abs(a[7] * a[7] - a[5] * a[5] - a[4] * a[4] + a[1] * a[1]),
  285. );
  286. }
  287. export function normalized(a: NVector): NVector {
  288. const n = norm(a);
  289. if (n === 0 || n === 1) {
  290. return a;
  291. }
  292. const sign = a[6] < 0 ? -1 : 1;
  293. return mul(a, sign / n);
  294. }
  295. export function inormalized(a: NVector): NVector {
  296. const n = inorm(a);
  297. if (n === 0 || n === 1) {
  298. return a;
  299. }
  300. return mul(a, 1 / n);
  301. }
  302. function isNumber(a: any): a is number {
  303. return typeof a === "number";
  304. }
  305. export const E0: NVector = nvector(1, 1);
  306. export const E1: NVector = nvector(1, 2);
  307. export const E2: NVector = nvector(1, 3);
  308. export const E01: NVector = nvector(1, 4);
  309. export const E20: NVector = nvector(1, 5);
  310. export const E12: NVector = nvector(1, 6);
  311. export const E012: NVector = nvector(1, 7);
  312. export const I = E012;