123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- /**
- * This is a 2D Projective Geometric Algebra implementation.
- *
- * For wider context on geometric algebra visit see https://bivector.net.
- *
- * For this specific algebra see cheatsheet https://bivector.net/2DPGA.pdf.
- *
- * Converted from generator written by enki, with a ton of added on top.
- *
- * This library uses 8-vectors to represent points, directions and lines
- * in 2D space.
- *
- * An array `[a, b, c, d, e, f, g, h]` represents a n(8)vector:
- * a + b*e0 + c*e1 + d*e2 + e*e01 + f*e20 + g*e12 + h*e012
- *
- * See GAPoint, GALine, GADirection and GATransform modules for common
- * operations.
- */
- export type Point = NVector;
- export type Direction = NVector;
- export type Line = NVector;
- export type Transform = NVector;
- export const point = (x: number, y: number): Point => [0, 0, 0, 0, y, x, 1, 0];
- export const origin = (): Point => [0, 0, 0, 0, 0, 0, 1, 0];
- export const direction = (x: number, y: number): Direction => {
- const norm = Math.hypot(x, y); // same as `inorm(direction(x, y))`
- return [0, 0, 0, 0, y / norm, x / norm, 0, 0];
- };
- export const offset = (x: number, y: number): Direction => [
- 0,
- 0,
- 0,
- 0,
- y,
- x,
- 0,
- 0,
- ];
- /// This is the "implementation" part of the library
- type NVector = readonly [
- number,
- number,
- number,
- number,
- number,
- number,
- number,
- number,
- ];
- // These are labels for what each number in an nvector represents
- const NVECTOR_BASE = ["1", "e0", "e1", "e2", "e01", "e20", "e12", "e012"];
- // Used to represent points, lines and transformations
- export const nvector = (value: number = 0, index: number = 0): NVector => {
- const result = [0, 0, 0, 0, 0, 0, 0, 0];
- if (index < 0 || index > 7) {
- throw new Error(`Expected \`index\` between 0 and 7, got \`${index}\``);
- }
- if (value !== 0) {
- result[index] = value;
- }
- return result as unknown as NVector;
- };
- const STRING_EPSILON = 0.000001;
- export const toString = (nvector: NVector): string => {
- const result = nvector
- .map((value, index) =>
- Math.abs(value) > STRING_EPSILON
- ? value.toFixed(7).replace(/(\.|0+)$/, "") +
- (index > 0 ? NVECTOR_BASE[index] : "")
- : null,
- )
- .filter((representation) => representation != null)
- .join(" + ");
- return result === "" ? "0" : result;
- };
- // Reverse the order of the basis blades.
- export const reverse = (nvector: NVector): NVector => [
- nvector[0],
- nvector[1],
- nvector[2],
- nvector[3],
- -nvector[4],
- -nvector[5],
- -nvector[6],
- -nvector[7],
- ];
- // Poincare duality operator.
- export const dual = (nvector: NVector): NVector => [
- nvector[7],
- nvector[6],
- nvector[5],
- nvector[4],
- nvector[3],
- nvector[2],
- nvector[1],
- nvector[0],
- ];
- // Clifford Conjugation
- export const conjugate = (nvector: NVector): NVector => [
- nvector[0],
- -nvector[1],
- -nvector[2],
- -nvector[3],
- -nvector[4],
- -nvector[5],
- -nvector[6],
- nvector[7],
- ];
- // Main involution
- export const involute = (nvector: NVector): NVector => [
- nvector[0],
- -nvector[1],
- -nvector[2],
- -nvector[3],
- nvector[4],
- nvector[5],
- nvector[6],
- -nvector[7],
- ];
- // Multivector addition
- export const add = (a: NVector, b: NVector | number): NVector => {
- if (isNumber(b)) {
- return [a[0] + b, a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
- }
- return [
- a[0] + b[0],
- a[1] + b[1],
- a[2] + b[2],
- a[3] + b[3],
- a[4] + b[4],
- a[5] + b[5],
- a[6] + b[6],
- a[7] + b[7],
- ];
- };
- // Multivector subtraction
- export const sub = (a: NVector, b: NVector | number): NVector => {
- if (isNumber(b)) {
- return [a[0] - b, a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
- }
- return [
- a[0] - b[0],
- a[1] - b[1],
- a[2] - b[2],
- a[3] - b[3],
- a[4] - b[4],
- a[5] - b[5],
- a[6] - b[6],
- a[7] - b[7],
- ];
- };
- // The geometric product.
- export const mul = (a: NVector, b: NVector | number): NVector => {
- if (isNumber(b)) {
- return [
- a[0] * b,
- a[1] * b,
- a[2] * b,
- a[3] * b,
- a[4] * b,
- a[5] * b,
- a[6] * b,
- a[7] * b,
- ];
- }
- return [
- mulScalar(a, b),
- b[1] * a[0] +
- b[0] * a[1] -
- b[4] * a[2] +
- b[5] * a[3] +
- b[2] * a[4] -
- b[3] * a[5] -
- b[7] * a[6] -
- b[6] * a[7],
- b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6],
- b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6],
- b[4] * a[0] +
- b[2] * a[1] -
- b[1] * a[2] +
- b[7] * a[3] +
- b[0] * a[4] +
- b[6] * a[5] -
- b[5] * a[6] +
- b[3] * a[7],
- b[5] * a[0] -
- b[3] * a[1] +
- b[7] * a[2] +
- b[1] * a[3] -
- b[6] * a[4] +
- b[0] * a[5] +
- b[4] * a[6] +
- b[2] * a[7],
- b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6],
- b[7] * a[0] +
- b[6] * a[1] +
- b[5] * a[2] +
- b[4] * a[3] +
- b[3] * a[4] +
- b[2] * a[5] +
- b[1] * a[6] +
- b[0] * a[7],
- ];
- };
- export const mulScalar = (a: NVector, b: NVector): number =>
- b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6];
- // The outer/exterior/wedge product.
- export const meet = (a: NVector, b: NVector): NVector => [
- b[0] * a[0],
- b[1] * a[0] + b[0] * a[1],
- b[2] * a[0] + b[0] * a[2],
- b[3] * a[0] + b[0] * a[3],
- b[4] * a[0] + b[2] * a[1] - b[1] * a[2] + b[0] * a[4],
- b[5] * a[0] - b[3] * a[1] + b[1] * a[3] + b[0] * a[5],
- b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6],
- b[7] * a[0] +
- b[6] * a[1] +
- b[5] * a[2] +
- b[4] * a[3] +
- b[3] * a[4] +
- b[2] * a[5] +
- b[1] * a[6],
- ];
- // The regressive product.
- export const join = (a: NVector, b: NVector): NVector => [
- joinScalar(a, b),
- a[1] * b[7] + a[4] * b[5] - a[5] * b[4] + a[7] * b[1],
- a[2] * b[7] - a[4] * b[6] + a[6] * b[4] + a[7] * b[2],
- a[3] * b[7] + a[5] * b[6] - a[6] * b[5] + a[7] * b[3],
- a[4] * b[7] + a[7] * b[4],
- a[5] * b[7] + a[7] * b[5],
- a[6] * b[7] + a[7] * b[6],
- a[7] * b[7],
- ];
- export const joinScalar = (a: NVector, b: NVector): number =>
- a[0] * b[7] +
- a[1] * b[6] +
- a[2] * b[5] +
- a[3] * b[4] +
- a[4] * b[3] +
- a[5] * b[2] +
- a[6] * b[1] +
- a[7] * b[0];
- // The inner product.
- export const dot = (a: NVector, b: NVector): NVector => [
- b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6],
- b[1] * a[0] +
- b[0] * a[1] -
- b[4] * a[2] +
- b[5] * a[3] +
- b[2] * a[4] -
- b[3] * a[5] -
- b[7] * a[6] -
- b[6] * a[7],
- b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6],
- b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6],
- b[4] * a[0] + b[7] * a[3] + b[0] * a[4] + b[3] * a[7],
- b[5] * a[0] + b[7] * a[2] + b[0] * a[5] + b[2] * a[7],
- b[6] * a[0] + b[0] * a[6],
- b[7] * a[0] + b[0] * a[7],
- ];
- export const norm = (a: NVector): number =>
- Math.sqrt(Math.abs(a[0] * a[0] - a[2] * a[2] - a[3] * a[3] + a[6] * a[6]));
- export const inorm = (a: NVector): number =>
- Math.sqrt(Math.abs(a[7] * a[7] - a[5] * a[5] - a[4] * a[4] + a[1] * a[1]));
- export const normalized = (a: NVector): NVector => {
- const n = norm(a);
- if (n === 0 || n === 1) {
- return a;
- }
- const sign = a[6] < 0 ? -1 : 1;
- return mul(a, sign / n);
- };
- export const inormalized = (a: NVector): NVector => {
- const n = inorm(a);
- if (n === 0 || n === 1) {
- return a;
- }
- return mul(a, 1 / n);
- };
- const isNumber = (a: any): a is number => typeof a === "number";
- export const E0: NVector = nvector(1, 1);
- export const E1: NVector = nvector(1, 2);
- export const E2: NVector = nvector(1, 3);
- export const E01: NVector = nvector(1, 4);
- export const E20: NVector = nvector(1, 5);
- export const E12: NVector = nvector(1, 6);
- export const E012: NVector = nvector(1, 7);
- export const I = E012;
|