Skip to content

feat(typegpu/std): implement cosh, acosh, exp2 #1317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 5, 2025
33 changes: 33 additions & 0 deletions packages/typegpu/src/data/vectorOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,17 @@ export const VectorOps = {
vec4u: unary4u(Math.acos),
} as Record<VecKind, <T extends vBase>(v: T) => T>,

acosh: {
vec2f: unary2f(Math.acosh),
vec2h: unary2h(Math.acosh),

vec3f: unary3f(Math.acosh),
vec3h: unary3h(Math.acosh),

vec4f: unary4f(Math.acosh),
vec4h: unary4h(Math.acosh),
} as Record<VecKind, <T extends vBase>(v: T) => T>,

asin: {
vec2f: unary2f(Math.asin),
vec2h: unary2h(Math.asin),
Expand Down Expand Up @@ -1014,6 +1025,17 @@ export const VectorOps = {
vec4h: unary4h(Math.cos),
} as Record<VecKind, <T extends vBase>(v: T) => T>,

cosh: {
vec2f: unary2f(Math.cosh),
vec2h: unary2h(Math.cosh),

vec3f: unary3f(Math.cosh),
vec3h: unary3h(Math.cosh),

vec4f: unary4f(Math.cosh),
vec4h: unary4h(Math.cosh),
} as Record<VecKind, <T extends vBase>(v: T) => T>,

exp: {
vec2f: unary2f(Math.exp),
vec2h: unary2h(Math.exp),
Expand All @@ -1025,6 +1047,17 @@ export const VectorOps = {
vec4h: unary4h(Math.exp),
} as Record<VecKind, <T extends vBase>(v: T) => T>,

exp2: {
vec2f: unary2f((val) => 2 ** val),
vec2h: unary2h((val) => 2 ** val),

vec3f: unary3f((val) => 2 ** val),
vec3h: unary3h((val) => 2 ** val),

vec4f: unary4f((val) => 2 ** val),
vec4h: unary4h((val) => 2 ** val),
} as Record<VecKind, <T extends vBase>(v: T) => T>,

fract: {
vec2f: unary2f((value) => value - Math.floor(value)),
vec2h: unary2h((value) => value - Math.floor(value)),
Expand Down
3 changes: 3 additions & 0 deletions packages/typegpu/src/std/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ export {
// builtin functions
abs,
acos,
acosh,
asin,
atan2,
ceil,
clamp,
cos,
cosh,
cross,
distance,
dot,
exp,
exp2,
floor,
fract,
length,
Expand Down
60 changes: 54 additions & 6 deletions packages/typegpu/src/std/numeric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,23 +268,39 @@ export const acos = createDualImpl(
if (typeof value === 'number') {
return Math.acos(value) as T;
}
return VectorOps.acos[(value as AnyFloatVecInstance).kind](
value as never,
) as T;
return VectorOps.acos[value.kind](value) as T;
},
// GPU implementation
(value) => snip(`acos(${value.value})`, value.dataType),
);

/**
* @privateRemarks
* https://www.w3.org/TR/WGSL/#acosh-builtin
*/
export const acosh = createDualImpl(
// CPU implementation
<T extends AnyFloatVecInstance | number>(value: T): T => {
if (typeof value === 'number') {
return Math.acosh(value) as T;
}
return VectorOps.acosh[value.kind](value) as T;
},
// GPU implementation
(value) => snip(`acosh(${value.value})`, value.dataType),
);

/**
* @privateRemarks
* https://www.w3.org/TR/WGSL/#asin-builtin
*/
export const asin = createDualImpl(
// CPU implementation
<T extends AnyFloatVecInstance | number>(value: T): T => {
if (typeof value === 'number') {
return Math.asin(value) as T;
}
return VectorOps.asin[(value as AnyFloatVecInstance).kind](
value as never,
) as T;
return VectorOps.asin[value.kind](value) as T;
},
// GPU implementation
(value) => snip(`asin(${value.value})`, value.dataType),
Expand Down Expand Up @@ -343,6 +359,22 @@ export const cos = createDualImpl(
(value) => snip(`cos(${value.value})`, value.dataType),
);

/**
* @privateRemarks
* https://www.w3.org/TR/WGSL/#cosh-builtin
*/
export const cosh = createDualImpl(
// CPU implementation
<T extends AnyFloatVecInstance | number>(value: T): T => {
if (typeof value === 'number') {
return Math.cosh(value) as T;
}
return VectorOps.cosh[value.kind](value) as T;
},
// GPU implementation
(value) => snip(`cosh(${value.value})`, value.dataType),
);

/**
* @privateRemarks
* https://www.w3.org/TR/WGSL/#cross-builtin
Expand Down Expand Up @@ -498,6 +530,22 @@ export const exp = createDualImpl(
(value) => snip(`exp(${value.value})`, value.dataType),
);

/**
* @privateRemarks
* https://www.w3.org/TR/WGSL/#exp2-builtin
*/
export const exp2 = createDualImpl(
// CPU implementation
<T extends AnyFloatVecInstance | number>(value: T): T => {
if (typeof value === 'number') {
return (2 ** value) as T;
}
return VectorOps.exp2[value.kind](value) as T;
},
// GPU implementation
(value) => snip(`exp2(${value.value})`, value.dataType),
);

type PowOverload = {
(base: number, exponent: number): number;
<T extends AnyFloatVecInstance>(base: T, exponent: T): T;
Expand Down
35 changes: 35 additions & 0 deletions packages/typegpu/tests/std/numeric/acosh.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it } from 'vitest';
import { vec2f, vec3f, vec4f } from '../../../src/data/index.ts';
import { acosh, isCloseTo } from '../../../src/std/index.ts';

describe('acosh', () => {
it('computes acosh of a number', () => {
expect(acosh(1)).toBeCloseTo(0);
expect(acosh(Math.cosh(1))).toBeCloseTo(1);
expect(acosh(Math.cosh(-1))).toBeCloseTo(1);
});

it('computes acosh of vec2f', () => {
const input = vec2f(1, Math.cosh(1));
const expected = vec2f(Math.acosh(1), 1);
expect(isCloseTo(acosh(input), expected)).toBe(true);
});

it('tests acosh(cosh())', () => {
const input = vec2f(1, Math.cosh(1));
const expected = vec2f(Math.acosh(1), Math.acosh(Math.cosh(1)));
expect(isCloseTo(acosh(input), expected)).toBe(true);
});

it('computes acosh of vec3f', () => {
const input = vec3f(1, Math.cosh(1), Math.cosh(-1));
const expected = vec3f(Math.acosh(1), 1, 1);
expect(isCloseTo(acosh(input), expected)).toBe(true);
});

it('computes acosh of vec4f', () => {
const input = vec4f(1, Math.cosh(1), Math.cosh(-1), Math.cosh(2));
const expected = vec4f(Math.acosh(1), 1, 1, 2);
expect(isCloseTo(acosh(input), expected)).toBe(true);
});
});
34 changes: 34 additions & 0 deletions packages/typegpu/tests/std/numeric/cosh.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, expect, it } from 'vitest';
import { vec2f, vec3f, vec4f } from '../../../src/data/index.ts';
import { cosh, isCloseTo } from '../../../src/std/index.ts';

describe('cosh', () => {
it('computes cosh of a number', () => {
expect(cosh(0)).toBeCloseTo(1);
expect(cosh(1)).toBeCloseTo(Math.cosh(1));
expect(cosh(-1)).toBeCloseTo(Math.cosh(-1));
});

it('computes cosh of vec2f', () => {
const input = vec2f(0, 1);
const expected = vec2f(Math.cosh(0), Math.cosh(1));
expect(isCloseTo(cosh(input), expected)).toBe(true);
});

it('computes cosh of vec3f', () => {
const input = vec3f(0, 1, -1);
const expected = vec3f(Math.cosh(0), Math.cosh(1), Math.cosh(-1));
expect(isCloseTo(cosh(input), expected)).toBe(true);
});

it('computes cosh of vec4f', () => {
const input = vec4f(0, 1, -1, 2);
const expected = vec4f(
Math.cosh(0),
Math.cosh(1),
Math.cosh(-1),
Math.cosh(2),
);
expect(isCloseTo(cosh(input), expected)).toBe(true);
});
});
66 changes: 66 additions & 0 deletions packages/typegpu/tests/std/numeric/exp2.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, it } from 'vitest';
import {
vec2f,
vec2h,
vec3f,
vec3h,
vec4f,
vec4h,
} from '../../../src/data/index.ts';
import { isCloseTo } from '../../../src/std/index.ts';
import { exp2 } from '../../../src/std/numeric.ts';

describe('exp2', () => {
it('computes exp2 of a number', () => {
expect(exp2(0)).toBeCloseTo(1);
expect(exp2(1)).toBeCloseTo(2);
expect(exp2(2)).toBeCloseTo(4);
expect(exp2(-1)).toBeCloseTo(0.5);
expect(exp2(-2)).toBeCloseTo(0.25);
});

it('computes exp2 of vec2f', () => {
const input = vec2f(0, 1);
const expected = vec2f(2 ** 0, 2 ** 1);
expect(isCloseTo(exp2(input), expected)).toBe(true);
});

it('computes exp2 of vec3f', () => {
const input = vec3f(0, 1, -1);
const expected = vec3f(2 ** 0, 2 ** 1, 2 ** -1);
expect(isCloseTo(exp2(input), expected)).toBe(true);
});

it('computes exp2 of vec4f', () => {
const input = vec4f(0, 1, -1, 2);
const expected = vec4f(2 ** 0, 2 ** 1, 2 ** -1, 2 ** 2);
expect(isCloseTo(exp2(input), expected)).toBe(true);
});

it('computes exp2 of vec2h', () => {
const input = vec2h(0, 1);
const expected = vec2h(2 ** 0, 2 ** 1);
const result = exp2(input);
expect(result.x).toBeCloseTo(expected.x);
expect(result.y).toBeCloseTo(expected.y);
});

it('computes exp2 of vec3h', () => {
const input = vec3h(0, 1, -1);
const expected = vec3h(2 ** 0, 2 ** 1, 2 ** -1);
const result = exp2(input);
expect(result.x).toBeCloseTo(expected.x);
expect(result.y).toBeCloseTo(expected.y);
expect(result.z).toBeCloseTo(expected.z);
});

it('computes exp2 of vec4h', () => {
const input = vec4h(0, 1, -1, 2);
const expected = vec4h(2 ** 0, 2 ** 1, 2 ** -1, 2 ** 2);
const result = exp2(input);
expect(result.x).toBeCloseTo(expected.x);
expect(result.y).toBeCloseTo(expected.y);
expect(result.z).toBeCloseTo(expected.z);
expect(result.w).toBeCloseTo(expected.w);
});
});