diff --git a/packages/typegpu/src/data/vectorOps.ts b/packages/typegpu/src/data/vectorOps.ts index ad519a0eb..7daaeac85 100644 --- a/packages/typegpu/src/data/vectorOps.ts +++ b/packages/typegpu/src/data/vectorOps.ts @@ -239,9 +239,7 @@ export const VectorOps = { ( e1: T, e2: T, - ) => T extends wgsl.AnyVec2Instance ? wgsl.v2b - : T extends wgsl.AnyVec3Instance ? wgsl.v3b - : wgsl.v4b + ) => wgsl.MatchingBoolInstance >, lt: { @@ -272,9 +270,7 @@ export const VectorOps = { ( e1: T, e2: T, - ) => T extends wgsl.AnyVec2Instance ? wgsl.v2b - : T extends wgsl.AnyVec3Instance ? wgsl.v3b - : wgsl.v4b + ) => wgsl.MatchingBoolInstance >, or: { @@ -1243,9 +1239,7 @@ export const VectorOps = { ( f: T, t: T, - c: T extends wgsl.AnyVec2Instance ? wgsl.v2b - : T extends wgsl.AnyVec3Instance ? wgsl.v3b - : wgsl.v4b, + c: wgsl.MatchingBoolInstance, ) => T >, diff --git a/packages/typegpu/src/data/wgslTypes.ts b/packages/typegpu/src/data/wgslTypes.ts index 9e2e02ecd..38a40b7df 100644 --- a/packages/typegpu/src/data/wgslTypes.ts +++ b/packages/typegpu/src/data/wgslTypes.ts @@ -658,6 +658,16 @@ export type AnyIntegerVecInstance = v2i | v2u | v3i | v3u | v4i | v4u; export type AnyBooleanVecInstance = v2b | v3b | v4b; +export type MatchingBoolInstance = T extends + AnyVecInstance + ? T['kind'] extends `vec${infer TDim extends 2 | 3 | 4}${string}` ? { + 2: v2b; + 3: v3b; + 4: v4b; + }[TDim] + : never + : boolean; + export type AnySignedVecInstance = | v2i | v2f diff --git a/packages/typegpu/src/std/boolean.ts b/packages/typegpu/src/std/boolean.ts index 973c3f498..bb356c5d1 100644 --- a/packages/typegpu/src/std/boolean.ts +++ b/packages/typegpu/src/std/boolean.ts @@ -14,11 +14,13 @@ import { type AnyVecInstance, type AnyWgslData, isVecInstance, + type MatchingBoolInstance, type v2b, type v3b, type v4b, } from '../data/wgslTypes.ts'; import { $internal } from '../shared/symbols.ts'; +import { unify } from '../tgsl/conversion.ts'; import { sub } from './operators.ts'; function correspondingBooleanVectorSchema(dataType: AnyData) { @@ -48,8 +50,20 @@ export const allEq = dualImpl({ codegenImpl: (lhs, rhs) => stitch`all(${lhs} == ${rhs})`, }); -const cpuEq = (lhs: T, rhs: T) => - VectorOps.eq[lhs.kind](lhs, rhs); +function cpuEq(lhs: number, rhs: number): boolean; +function cpuEq( + lhs: T, + rhs: T, +): MatchingBoolInstance; +function cpuEq( + lhs: T, + rhs: T, +): MatchingBoolInstance { + if (typeof lhs !== 'number' && typeof rhs !== 'number') { + return VectorOps.eq[lhs.kind](lhs, rhs) as MatchingBoolInstance; + } + return (lhs === rhs) as MatchingBoolInstance; +} /** * Checks **component-wise** whether `lhs == rhs`. @@ -62,10 +76,13 @@ const cpuEq = (lhs: T, rhs: T) => */ export const eq = dualImpl({ name: 'eq', - signature: (...argTypes) => ({ - argTypes, - returnType: correspondingBooleanVectorSchema(argTypes[0]), - }), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ + argTypes: uargs, + returnType: correspondingBooleanVectorSchema(uargs[0]), + }); + }, normalImpl: cpuEq, codegenImpl: (lhs, rhs) => stitch`(${lhs} == ${rhs})`, }); @@ -89,23 +106,43 @@ export const ne = dualImpl({ codegenImpl: (lhs, rhs) => stitch`(${lhs} != ${rhs})`, }); -const cpuLt = (lhs: T, rhs: T) => - VectorOps.lt[lhs.kind](lhs, rhs); +function cpuLt(lhs: number, rhs: number): boolean; +function cpuLt( + lhs: T, + rhs: T, +): MatchingBoolInstance; +function cpuLt( + lhs: T, + rhs: T, +): MatchingBoolInstance { + if (typeof lhs !== 'number' && typeof rhs !== 'number') { + return VectorOps.lt[lhs.kind](lhs, rhs) as MatchingBoolInstance; + } + return (lhs < rhs) as MatchingBoolInstance; +} /** * Checks **component-wise** whether `lhs < rhs`. - * This function does **not** return `bool`, for that use-case, wrap the result in `all`. - * @example + * @example ```ts * lt(vec2f(0.0, 0.0), vec2f(0.0, 1.0)) // returns vec2b(false, true) * lt(vec3u(0, 1, 2), vec3u(2, 1, 0)) // returns vec3b(true, false, false) * all(lt(vec4i(1, 2, 3, 4), vec4i(2, 3, 4, 5))) // returns true + * ``` + * + * Also accepts scalar values for the sake of generic use, but it's better to use the `<` operator. + * @example ```ts + * lt(1, 2) // returns true + * ``` */ export const lt = dualImpl({ name: 'lt', - signature: (...argTypes) => ({ - argTypes, - returnType: correspondingBooleanVectorSchema(argTypes[0]), - }), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ + argTypes: uargs, + returnType: correspondingBooleanVectorSchema(uargs[0]), + }); + }, normalImpl: cpuLt, codegenImpl: (lhs, rhs) => stitch`(${lhs} < ${rhs})`, }); @@ -120,15 +157,30 @@ export const lt = dualImpl({ */ export const le = dualImpl({ name: 'le', - signature: (...argTypes) => ({ - argTypes, - returnType: correspondingBooleanVectorSchema(argTypes[0]), - }), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ + argTypes: uargs, + returnType: correspondingBooleanVectorSchema(uargs[0]), + }); + }, normalImpl: (lhs: T, rhs: T) => cpuOr(cpuLt(lhs, rhs), cpuEq(lhs, rhs)), codegenImpl: (lhs, rhs) => stitch`(${lhs} <= ${rhs})`, }); +function cpuGt(lhs: number, rhs: number): boolean; +function cpuGt( + lhs: T, + rhs: T, +): MatchingBoolInstance; +function cpuGt( + lhs: T, + rhs: T, +): MatchingBoolInstance { + return cpuAnd(cpuNot(cpuLt(lhs, rhs)), cpuNot(cpuEq(lhs, rhs))); +} + /** * Checks **component-wise** whether `lhs > rhs`. * This function does **not** return `bool`, for that use-case, wrap the result in `all`. @@ -139,12 +191,14 @@ export const le = dualImpl({ */ export const gt = dualImpl({ name: 'gt', - signature: (...argTypes) => ({ - argTypes, - returnType: correspondingBooleanVectorSchema(argTypes[0]), - }), - normalImpl: (lhs: T, rhs: T) => - cpuAnd(cpuNot(cpuLt(lhs, rhs)), cpuNot(cpuEq(lhs, rhs))), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ + argTypes: uargs, + returnType: correspondingBooleanVectorSchema(uargs[0]), + }); + }, + normalImpl: cpuGt, codegenImpl: (lhs, rhs) => stitch`(${lhs} > ${rhs})`, }); @@ -158,10 +212,13 @@ export const gt = dualImpl({ */ export const ge = dualImpl({ name: 'ge', - signature: (...argTypes) => ({ - argTypes: argTypes, - returnType: correspondingBooleanVectorSchema(argTypes[0]), - }), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ + argTypes: uargs, + returnType: correspondingBooleanVectorSchema(uargs[0]), + }); + }, normalImpl: (lhs: T, rhs: T) => cpuNot(cpuLt(lhs, rhs)), codegenImpl: (lhs, rhs) => stitch`(${lhs} >= ${rhs})`, @@ -169,8 +226,14 @@ export const ge = dualImpl({ // logical ops -const cpuNot = (value: T): T => - VectorOps.neg[value.kind](value); +function cpuNot(value: boolean): boolean; +function cpuNot(value: T): T; +function cpuNot(value: T): T { + if (typeof value === 'boolean') { + return (!value) as T; + } + return VectorOps.neg[value.kind](value) as T; +} /** * Returns **component-wise** `!value`. @@ -185,8 +248,14 @@ export const not = dualImpl({ codegenImpl: (arg) => stitch`!(${arg})`, }); -const cpuOr = (lhs: T, rhs: T) => - VectorOps.or[lhs.kind](lhs, rhs); +function cpuOr(lhs: boolean, rhs: boolean): boolean; +function cpuOr(lhs: T, rhs: T): T; +function cpuOr(lhs: T, rhs: T): T { + if (typeof lhs !== 'boolean' && typeof rhs !== 'boolean') { + return VectorOps.or[lhs.kind](lhs, rhs) as T; + } + return lhs || rhs; +} /** * Returns **component-wise** logical `or` result. @@ -196,13 +265,19 @@ const cpuOr = (lhs: T, rhs: T) => */ export const or = dualImpl({ name: 'or', - signature: (...argTypes) => ({ argTypes, returnType: argTypes[0] }), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ argTypes: uargs, returnType: uargs[0] }); + }, normalImpl: cpuOr, codegenImpl: (lhs, rhs) => stitch`(${lhs} | ${rhs})`, }); -const cpuAnd = (lhs: T, rhs: T) => - cpuNot(cpuOr(cpuNot(lhs), cpuNot(rhs))); +function cpuAnd(lhs: boolean, rhs: boolean): boolean; +function cpuAnd(lhs: T, rhs: T): T; +function cpuAnd(lhs: T, rhs: T): T { + return cpuNot(cpuOr(cpuNot(lhs), cpuNot(rhs))); +} /** * Returns **component-wise** logical `and` result. @@ -212,7 +287,10 @@ const cpuAnd = (lhs: T, rhs: T) => */ export const and = dualImpl({ name: 'and', - signature: (...argTypes) => ({ argTypes, returnType: argTypes[0] }), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ argTypes: uargs, returnType: uargs[0] }); + }, normalImpl: cpuAnd, codegenImpl: (lhs, rhs) => stitch`(${lhs} & ${rhs})`, }); diff --git a/packages/typegpu/src/std/numeric.ts b/packages/typegpu/src/std/numeric.ts index d3243c29b..00640b3cd 100644 --- a/packages/typegpu/src/std/numeric.ts +++ b/packages/typegpu/src/std/numeric.ts @@ -68,7 +68,7 @@ export const abs = dualImpl({ }); function cpuAcos(value: number): number; -function cpuAcos(value: T): T; +function cpuAcos(value: T): T; function cpuAcos(value: T): T { if (typeof value === 'number') { return Math.acos(value) as T; @@ -84,7 +84,7 @@ export const acos = dualImpl({ }); function cpuAcosh(value: number): number; -function cpuAcosh(value: T): T; +function cpuAcosh(value: T): T; function cpuAcosh(value: T): T { if (typeof value === 'number') { return Math.acosh(value) as T; @@ -100,7 +100,7 @@ export const acosh = dualImpl({ }); function cpuAsin(value: number): number; -function cpuAsin(value: T): T; +function cpuAsin(value: T): T; function cpuAsin(value: T): T { if (typeof value === 'number') { return Math.asin(value) as T; @@ -116,7 +116,7 @@ export const asin = dualImpl({ }); function cpuAsinh(value: number): number; -function cpuAsinh(value: T): T; +function cpuAsinh(value: T): T; function cpuAsinh(value: T): T { if (typeof value === 'number') { return Math.asinh(value) as T; @@ -132,7 +132,7 @@ export const asinh = dualImpl({ }); function cpuAtan(value: number): number; -function cpuAtan(value: T): T; +function cpuAtan(value: T): T; function cpuAtan(value: T): T { if (typeof value === 'number') { return Math.atan(value) as T; @@ -148,7 +148,7 @@ export const atan = dualImpl({ }); function cpuAtanh(value: number): number; -function cpuAtanh(value: T): T; +function cpuAtanh(value: T): T; function cpuAtanh(value: T): T { if (typeof value === 'number') { return Math.atanh(value) as T; @@ -164,7 +164,7 @@ export const atanh = dualImpl({ }); function cpuAtan2(y: number, x: number): number; -function cpuAtan2(y: T, x: T): T; +function cpuAtan2(y: T, x: T): T; function cpuAtan2(y: T, x: T): T { if (typeof y === 'number' && typeof x === 'number') { return Math.atan2(y, x) as T; @@ -189,7 +189,7 @@ export const atan2 = dualImpl({ }); function cpuCeil(value: number): number; -function cpuCeil(value: T): T; +function cpuCeil(value: T): T; function cpuCeil(value: T): T { if (typeof value === 'number') { return Math.ceil(value) as T; @@ -228,7 +228,7 @@ export const clamp = dualImpl({ }); function cpuCos(value: number): number; -function cpuCos(value: T): T; +function cpuCos(value: T): T; function cpuCos(value: T): T { if (typeof value === 'number') { return Math.cos(value) as T; @@ -244,7 +244,7 @@ export const cos = dualImpl({ }); function cpuCosh(value: number): number; -function cpuCosh(value: T): T; +function cpuCosh(value: T): T; function cpuCosh(value: T): T { if (typeof value === 'number') { return Math.cosh(value) as T; @@ -260,7 +260,9 @@ export const cosh = dualImpl({ }); function cpuCountLeadingZeros(value: number): number; -function cpuCountLeadingZeros(value: T): T; +function cpuCountLeadingZeros( + value: T, +): T; function cpuCountLeadingZeros( value: T, ): T { @@ -277,7 +279,7 @@ export const countLeadingZeros = dualImpl({ }); function cpuCountOneBits(value: number): number; -function cpuCountOneBits(value: T): T; +function cpuCountOneBits(value: T): T; function cpuCountOneBits( value: T, ): T { @@ -294,7 +296,9 @@ export const countOneBits = dualImpl({ }); function cpuCountTrailingZeros(value: number): number; -function cpuCountTrailingZeros(value: T): T; +function cpuCountTrailingZeros( + value: T, +): T; function cpuCountTrailingZeros( value: T, ): T { @@ -319,7 +323,7 @@ export const cross = dualImpl({ }); function cpuDegrees(value: number): number; -function cpuDegrees(value: T): T; +function cpuDegrees(value: T): T; function cpuDegrees(value: T): T { if (typeof value === 'number') { return ((value * 180) / Math.PI) as T; @@ -349,7 +353,10 @@ export const determinant = dualImpl({ }); function cpuDistance(a: number, b: number): number; -function cpuDistance(a: T, b: T): number; +function cpuDistance( + a: T, + b: T, +): number; function cpuDistance( a: T, b: T, @@ -406,7 +413,7 @@ export const dot4I8Packed = dualImpl({ }); function cpuExp(value: number): number; -function cpuExp(value: T): T; +function cpuExp(value: T): T; function cpuExp(value: T): T { if (typeof value === 'number') { return Math.exp(value) as T; @@ -422,7 +429,7 @@ export const exp = dualImpl({ }); function cpuExp2(value: number): number; -function cpuExp2(value: T): T; +function cpuExp2(value: T): T; function cpuExp2(value: T): T { if (typeof value === 'number') { return (2 ** value) as T; @@ -438,7 +445,7 @@ export const exp2 = dualImpl({ }); function cpuExtractBits(e: number, offset: number, count: number): number; -function cpuExtractBits( +function cpuExtractBits( e: T, offset: number, count: number, @@ -479,7 +486,9 @@ export const faceForward = dualImpl({ }); function cpuFirstLeadingBit(value: number): number; -function cpuFirstLeadingBit(value: T): T; +function cpuFirstLeadingBit( + value: T, +): T; function cpuFirstLeadingBit( value: T, ): T { @@ -496,7 +505,9 @@ export const firstLeadingBit = dualImpl({ }); function cpuFirstTrailingBit(value: number): number; -function cpuFirstTrailingBit(value: T): T; +function cpuFirstTrailingBit( + value: T, +): T; function cpuFirstTrailingBit( value: T, ): T { @@ -513,7 +524,7 @@ export const firstTrailingBit = dualImpl({ }); function cpuFloor(value: number): number; -function cpuFloor(value: T): T; +function cpuFloor(value: T): T; function cpuFloor(value: T): T { if (typeof value === 'number') { return Math.floor(value) as T; @@ -529,7 +540,7 @@ export const floor = dualImpl({ }); function cpuFma(e1: number, e2: number, e3: number): number; -function cpuFma(e1: T, e2: T, e3: T): T; +function cpuFma(e1: T, e2: T, e3: T): T; function cpuFma( e1: T, e2: T, @@ -554,7 +565,7 @@ export const fma = dualImpl({ }); function cpuFract(value: number): number; -function cpuFract(value: T): T; +function cpuFract(value: T): T; function cpuFract(value: T): T { if (typeof value === 'number') { return (value - Math.floor(value)) as T; @@ -620,7 +631,7 @@ function cpuInsertBits( offset: number, count: number, ): number; -function cpuInsertBits( +function cpuInsertBits( e: T, newbits: T, offset: number, @@ -649,7 +660,7 @@ export const insertBits = dualImpl({ }); function cpuInverseSqrt(value: number): number; -function cpuInverseSqrt(value: T): T; +function cpuInverseSqrt(value: T): T; function cpuInverseSqrt(value: T): T { if (typeof value === 'number') { return (1 / Math.sqrt(value)) as T; @@ -708,7 +719,7 @@ export const ldexp = dualImpl({ }); function cpuLength(value: number): number; -function cpuLength(value: T): number; +function cpuLength(value: T): number; function cpuLength(value: T): number { if (typeof value === 'number') { return Math.abs(value); @@ -727,7 +738,7 @@ export const length = dualImpl({ }); function cpuLog(value: number): number; -function cpuLog(value: T): T; +function cpuLog(value: T): T; function cpuLog(value: T): T { if (typeof value === 'number') { return Math.log(value) as T; @@ -743,7 +754,7 @@ export const log = dualImpl({ }); function cpuLog2(value: number): number; -function cpuLog2(value: T): T; +function cpuLog2(value: T): T; function cpuLog2(value: T): T { if (typeof value === 'number') { return Math.log2(value) as T; @@ -759,7 +770,7 @@ export const log2 = dualImpl({ }); function cpuMax(a: number, b: number): number; -function cpuMax(a: T, b: T): T; +function cpuMax(a: T, b: T): T; function cpuMax(a: T, b: T): T { if (typeof a === 'number') { return Math.max(a, b as number) as T; @@ -781,7 +792,7 @@ export const max = dualImpl({ }); function cpuMin(a: number, b: number): number; -function cpuMin(a: T, b: T): T; +function cpuMin(a: T, b: T): T; function cpuMin(a: T, b: T): T { if (typeof a === 'number') { return Math.min(a, b as number) as T; @@ -804,7 +815,7 @@ export const min = dualImpl({ function cpuMix(e1: number, e2: number, e3: number): number; function cpuMix(e1: T, e2: T, e3: number): T; -function cpuMix(e1: T, e2: T, e3: T): T; +function cpuMix(e1: T, e2: T, e3: T): T; function cpuMix( e1: T, e2: T, @@ -828,7 +839,10 @@ function cpuMix( export const mix = dualImpl({ name: 'mix', - signature: (e1, e2, e3) => ({ argTypes: [e1, e2, e3], returnType: e1 }), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ argTypes: uargs, returnType: uargs[0] }); + }, normalImpl: cpuMix, codegenImpl: (e1, e2, e3) => stitch`mix(${e1}, ${e2}, ${e3})`, }); @@ -845,17 +859,14 @@ const ModfResult = { vec4h: abstruct({ fract: vec4h, whole: vec4h }), } as const; -type ModfOverload = { - (value: number): Infer; - ( - value: T, - ): Infer; -}; +export type ModfResultFor = T extends AnyFloatVecInstance + ? Infer + : Infer; function cpuModf(e: number): Infer; -function cpuModf( +function cpuModf( e: T, -): Infer; +): ModfResultFor; function cpuModf( value: T, ): Infer { @@ -864,7 +875,7 @@ function cpuModf( ); } -export const modf: ModfOverload = dualImpl({ +export const modf = dualImpl({ name: 'modf', signature: (e) => { const returnType = ModfResult[e.type as keyof typeof ModfResult]; @@ -890,7 +901,7 @@ export const normalize = dualImpl({ }); function powCpu(base: number, exponent: number): number; -function powCpu( +function powCpu( base: T, exponent: T, ): T; @@ -921,7 +932,9 @@ export const pow = dualImpl({ }); function cpuQuantizeToF16(value: number): number; -function cpuQuantizeToF16(value: T): T; +function cpuQuantizeToF16( + value: T, +): T; function cpuQuantizeToF16( value: T, ): T { @@ -984,7 +997,7 @@ export const refract = createDualImpl( ); function cpuReverseBits(value: number): number; -function cpuReverseBits(value: T): T; +function cpuReverseBits(value: T): T; function cpuReverseBits(value: T): T { throw new Error( 'CPU implementation for reverseBits not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', @@ -999,7 +1012,7 @@ export const reverseBits = dualImpl({ }); function cpuRound(value: number): number; -function cpuRound(value: T): T; +function cpuRound(value: T): T; function cpuRound(value: T): T { if (typeof value === 'number') { return Math.round(value) as T; @@ -1017,7 +1030,7 @@ export const round = dualImpl({ }); function cpuSaturate(value: number): number; -function cpuSaturate(value: T): T; +function cpuSaturate(value: T): T; function cpuSaturate(value: T): T { if (typeof value === 'number') { return Math.max(0, Math.min(1, value)) as T; @@ -1035,7 +1048,7 @@ export const saturate = dualImpl({ }); function cpuSign(e: number): number; -function cpuSign(e: T): T; +function cpuSign(e: T): T; function cpuSign(e: T): T { if (typeof e === 'number') { return Math.sign(e) as T; @@ -1051,7 +1064,7 @@ export const sign = dualImpl({ }); function cpuSin(value: number): number; -function cpuSin(value: T): T; +function cpuSin(value: T): T; function cpuSin(value: T): T { if (typeof value === 'number') { return Math.sin(value) as T; @@ -1067,7 +1080,7 @@ export const sin = dualImpl({ }); function cpuSinh(value: number): number; -function cpuSinh(value: T): T; +function cpuSinh(value: T): T; function cpuSinh(value: T): T { if (typeof value === 'number') { return Math.sinh(value) as T; @@ -1085,7 +1098,7 @@ export const sinh = dualImpl({ }); function cpuSmoothstep(edge0: number, edge1: number, x: number): number; -function cpuSmoothstep( +function cpuSmoothstep( edge0: T, edge1: T, x: T, @@ -1121,7 +1134,7 @@ export const smoothstep = dualImpl({ }); function cpuSqrt(value: number): number; -function cpuSqrt(value: T): T; +function cpuSqrt(value: T): T; function cpuSqrt(value: T): T { if (typeof value === 'number') { return Math.sqrt(value) as T; @@ -1158,7 +1171,7 @@ export const step = dualImpl({ }); function cpuTan(value: number): number; -function cpuTan(value: T): T; +function cpuTan(value: T): T; function cpuTan(value: T): T { if (typeof value === 'number') { return Math.tan(value) as T; @@ -1176,7 +1189,7 @@ export const tan = dualImpl({ }); function cpuTanh(value: number): number; -function cpuTanh(value: T): T; +function cpuTanh(value: T): T; function cpuTanh(value: T): T { if (typeof value === 'number') { return Math.tanh(value) as T; @@ -1203,7 +1216,7 @@ export const transpose = dualImpl({ }); function cpuTrunc(value: number): number; -function cpuTrunc(value: T): T; +function cpuTrunc(value: T): T; function cpuTrunc(value: T): T { throw new Error( 'CPU implementation for trunc not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', diff --git a/packages/typegpu/tests/std/boolean/eq.test.ts b/packages/typegpu/tests/std/boolean/eq.test.ts index bf1a080f7..899062eeb 100644 --- a/packages/typegpu/tests/std/boolean/eq.test.ts +++ b/packages/typegpu/tests/std/boolean/eq.test.ts @@ -1,49 +1,53 @@ import { describe, expect, it } from 'vitest'; -import { - vec2b, - vec2f, - vec2u, - vec3b, - vec3f, - vec3u, - vec4b, - vec4f, - vec4u, -} from '../../../src/data/index.ts'; +import * as d from '../../../src/data/index.ts'; import { eq } from '../../../src/std/index.ts'; describe('eq', () => { + it('compares numbers', () => { + expect(eq(10, 10)).toStrictEqual(true); + expect(eq(1, 0)).toStrictEqual(false); + expect(eq(10, 20)).toStrictEqual(false); + }); + it('compares integer vectors', () => { - expect(eq(vec2u(1, 0), vec2u(0, 0))).toStrictEqual(vec2b(false, true)); - expect(eq(vec3u(10, 20, 40), vec3u(10, 20, 30))).toStrictEqual( - vec3b(true, true, false), + expect(eq(d.vec2u(1, 0), d.vec2u(0, 0))).toStrictEqual( + d.vec2b(false, true), + ); + expect(eq(d.vec3u(10, 20, 40), d.vec3u(10, 20, 30))).toStrictEqual( + d.vec3b(true, true, false), ); - expect(eq(vec4u(1, 2, 3, 4), vec4u(4, 2, 3, 1))).toStrictEqual( - vec4b(false, true, true, false), + expect(eq(d.vec4u(1, 2, 3, 4), d.vec4u(4, 2, 3, 1))).toStrictEqual( + d.vec4b(false, true, true, false), ); }); it('compares float vectors', () => { - expect(eq(vec2f(0.1, 1.1), vec2f(0.1, 0))).toStrictEqual( - vec2b(true, false), + expect(eq(d.vec2f(0.1, 1.1), d.vec2f(0.1, 0))).toStrictEqual( + d.vec2b(true, false), ); - expect(eq(vec3f(1.2, 2.3, 3.4), vec3f(2.3, 3.2, 3.4))).toStrictEqual( - vec3b(false, false, true), + expect(eq(d.vec3f(1.2, 2.3, 3.4), d.vec3f(2.3, 3.2, 3.4))).toStrictEqual( + d.vec3b(false, false, true), ); expect( - eq(vec4f(0.1, -0.2, -0.3, 0.4), vec4f(0.1, 0.2, 0.3, 0.4)), - ).toStrictEqual(vec4b(true, false, false, true)); + eq(d.vec4f(0.1, -0.2, -0.3, 0.4), d.vec4f(0.1, 0.2, 0.3, 0.4)), + ).toStrictEqual(d.vec4b(true, false, false, true)); }); it('compares boolean vectors', () => { - expect(eq(vec2b(false, false), vec2b(false, true))).toStrictEqual( - vec2b(true, false), + expect(eq(d.vec2b(false, false), d.vec2b(false, true))).toStrictEqual( + d.vec2b(true, false), ); expect( - eq(vec3b(true, true, true), vec3b(false, false, true)), - ).toStrictEqual(vec3b(false, false, true)); + eq(d.vec3b(true, true, true), d.vec3b(false, false, true)), + ).toStrictEqual(d.vec3b(false, false, true)); expect( - eq(vec4b(false, true, false, true), vec4b(false, false, true, true)), - ).toStrictEqual(vec4b(true, false, false, true)); + eq(d.vec4b(false, true, false, true), d.vec4b(false, false, true, true)), + ).toStrictEqual(d.vec4b(true, false, false, true)); + }); + + it('accepts unions', () => { + const a = 1 as number | d.v3f; + const b = 0 as number | d.v3f; + expect(eq(a, b)).toStrictEqual(false); }); }); diff --git a/packages/typegpu/tests/std/boolean/gt.test.ts b/packages/typegpu/tests/std/boolean/gt.test.ts index cfc2928ec..386e48f45 100644 --- a/packages/typegpu/tests/std/boolean/gt.test.ts +++ b/packages/typegpu/tests/std/boolean/gt.test.ts @@ -1,37 +1,41 @@ import { describe, expect, it } from 'vitest'; -import { - vec2b, - vec2f, - vec2i, - vec3b, - vec3f, - vec3i, - vec4b, - vec4f, - vec4i, -} from '../../../src/data/index.ts'; +import * as d from '../../../src/data/index.ts'; import { gt } from '../../../src/std/index.ts'; describe('gt', () => { + it('compares numbers', () => { + expect(gt(1, 0)).toStrictEqual(true); + expect(gt(1, 1)).toStrictEqual(false); + expect(gt(0, 1)).toStrictEqual(false); + }); + it('compares integer vectors', () => { - expect(gt(vec2i(1, -1), vec2i(0, 0))).toStrictEqual(vec2b(true, false)); - expect(gt(vec3i(20, 20, 20), vec3i(10, 20, 30))).toStrictEqual( - vec3b(true, false, false), + expect(gt(d.vec2i(1, -1), d.vec2i(0, 0))).toStrictEqual( + d.vec2b(true, false), + ); + expect(gt(d.vec3i(20, 20, 20), d.vec3i(10, 20, 30))).toStrictEqual( + d.vec3b(true, false, false), ); - expect(gt(vec4i(1, 2, 3, 4), vec4i(4, 2, 3, 1))).toStrictEqual( - vec4b(false, false, false, true), + expect(gt(d.vec4i(1, 2, 3, 4), d.vec4i(4, 2, 3, 1))).toStrictEqual( + d.vec4b(false, false, false, true), ); }); it('compares float vectors', () => { - expect(gt(vec2f(0.1, 2.1), vec2f(0.1, 2))).toStrictEqual( - vec2b(false, true), + expect(gt(d.vec2f(0.1, 2.1), d.vec2f(0.1, 2))).toStrictEqual( + d.vec2b(false, true), ); - expect(gt(vec3f(1.2, 3.3, 3.4), vec3f(2.3, 3.2, 3.4))).toStrictEqual( - vec3b(false, true, false), + expect(gt(d.vec3f(1.2, 3.3, 3.4), d.vec3f(2.3, 3.2, 3.4))).toStrictEqual( + d.vec3b(false, true, false), ); expect( - gt(vec4f(1.1, -1.2, -0.3, 0.4), vec4f(0.1, 0.2, 0.3, 0.4)), - ).toStrictEqual(vec4b(true, false, false, false)); + gt(d.vec4f(1.1, -1.2, -0.3, 0.4), d.vec4f(0.1, 0.2, 0.3, 0.4)), + ).toStrictEqual(d.vec4b(true, false, false, false)); + }); + + it('accepts unions', () => { + const a = 1 as number | d.v3f; + const b = 0 as number | d.v3f; + expect(gt(a, b)).toStrictEqual(true); }); }); diff --git a/packages/typegpu/tests/std/boolean/lt.test.ts b/packages/typegpu/tests/std/boolean/lt.test.ts index 493d080b9..0b37b99ef 100644 --- a/packages/typegpu/tests/std/boolean/lt.test.ts +++ b/packages/typegpu/tests/std/boolean/lt.test.ts @@ -1,37 +1,51 @@ import { describe, expect, it } from 'vitest'; -import { - vec2b, - vec2f, - vec2i, - vec3b, - vec3f, - vec3i, - vec4b, - vec4f, - vec4i, -} from '../../../src/data/index.ts'; +import * as d from '../../../src/data/index.ts'; import { lt } from '../../../src/std/index.ts'; +import { asWgsl } from '../../utils/parseResolved.ts'; describe('lt', () => { + it('compares numbers', () => { + expect(lt(1, 0)).toStrictEqual(false); + expect(lt(1, 1)).toStrictEqual(false); + expect(lt(0, 1)).toStrictEqual(true); + }); + it('compares integer vectors', () => { - expect(lt(vec2i(1, -1), vec2i(0, 0))).toStrictEqual(vec2b(false, true)); - expect(lt(vec3i(10, 20, 20), vec3i(10, 20, 30))).toStrictEqual( - vec3b(false, false, true), + expect(lt(d.vec2i(1, -1), d.vec2i(0, 0))).toStrictEqual( + d.vec2b(false, true), ); - expect(lt(vec4i(1, 2, 3, 4), vec4i(4, 2, 3, 1))).toStrictEqual( - vec4b(true, false, false, false), + expect(lt(d.vec3i(10, 20, 20), d.vec3i(10, 20, 30))).toStrictEqual( + d.vec3b(false, false, true), + ); + expect(lt(d.vec4i(1, 2, 3, 4), d.vec4i(4, 2, 3, 1))).toStrictEqual( + d.vec4b(true, false, false, false), ); }); it('compares float vectors', () => { - expect(lt(vec2f(0.1, 1.1), vec2f(0.1, 2))).toStrictEqual( - vec2b(false, true), + expect(lt(d.vec2f(0.1, 1.1), d.vec2f(0.1, 2))).toStrictEqual( + d.vec2b(false, true), ); - expect(lt(vec3f(1.2, 2.3, 3.4), vec3f(2.3, 3.2, 3.4))).toStrictEqual( - vec3b(true, true, false), + expect(lt(d.vec3f(1.2, 2.3, 3.4), d.vec3f(2.3, 3.2, 3.4))).toStrictEqual( + d.vec3b(true, true, false), ); expect( - lt(vec4f(0.1, -0.2, -0.3, 0.4), vec4f(0.1, 0.2, 0.3, 0.4)), - ).toStrictEqual(vec4b(false, true, true, false)); + lt(d.vec4f(0.1, -0.2, -0.3, 0.4), d.vec4f(0.1, 0.2, 0.3, 0.4)), + ).toStrictEqual(d.vec4b(false, true, true, false)); + }); + + it('accepts unions', () => { + const a = 1 as number | d.v3f; + const b = 0 as number | d.v3f; + expect(lt(a, b)).toStrictEqual(false); + }); +}); + +describe('lt (on GPU)', () => { + it('works', () => { + expect(asWgsl(() => { + 'kernel'; + return lt(1, 0); + })).toMatchInlineSnapshot(`""`); }); }); diff --git a/packages/typegpu/tests/std/boolean/not.test.ts b/packages/typegpu/tests/std/boolean/not.test.ts index b248a27d7..ddcb27e4b 100644 --- a/packages/typegpu/tests/std/boolean/not.test.ts +++ b/packages/typegpu/tests/std/boolean/not.test.ts @@ -1,9 +1,14 @@ import { describe, expect, it } from 'vitest'; -import { vec2b, vec3b, vec4b } from '../../../src/data/index.ts'; +import { type v2b, vec2b, vec3b, vec4b } from '../../../src/data/index.ts'; import { not } from '../../../src/std/boolean.ts'; -describe('neg', () => { - it('negates', () => { +describe('not', () => { + it('negates boolean', () => { + expect(not(true)).toStrictEqual(false); + expect(not(false)).toStrictEqual(true); + }); + + it('negates vectors', () => { expect(not(vec2b(true, false))).toStrictEqual(vec2b(false, true)); expect(not(vec3b(false, false, true))).toStrictEqual( vec3b(true, true, false), @@ -12,4 +17,9 @@ describe('neg', () => { vec4b(false, false, true, true), ); }); + + it('accepts unions', () => { + const v = false as boolean | v2b; + expect(not(v)).toStrictEqual(true); + }); });