From 044f42d025e87ab20ee691c1ccdd7383bf5c7cd5 Mon Sep 17 00:00:00 2001 From: reczkok <66403540+reczkok@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:25:52 +0200 Subject: [PATCH] Swizzle rework using Proxy (#268) --- packages/typegpu/src/data/vector.ts | 595 ++++++++++++++++++++++------ 1 file changed, 484 insertions(+), 111 deletions(-) diff --git a/packages/typegpu/src/data/vector.ts b/packages/typegpu/src/data/vector.ts index 66c3e3447..b43ce4d77 100644 --- a/packages/typegpu/src/data/vector.ts +++ b/packages/typegpu/src/data/vector.ts @@ -103,36 +103,11 @@ function makeVecSchema( return Object.assign(construct, VecSchema); } -abstract class Swizzle2Impl { - abstract make2(x: number, y: number): T2; - - abstract readonly x: number; - abstract readonly y: number; - - get xx(): T2 { - return this.make2(this.x, this.x); - } - - get xy(): T2 { - return this.make2(this.x, this.y); - } - - get yx(): T2 { - return this.make2(this.y, this.x); - } - - get yy(): T2 { - return this.make2(this.y, this.y); - } -} - -abstract class vec2Impl extends Swizzle2Impl implements vec2 { +abstract class vec2Impl implements vec2 { constructor( public x: number, public y: number, - ) { - super(); - } + ) {} *[Symbol.iterator]() { yield this.x; @@ -140,44 +115,60 @@ abstract class vec2Impl extends Swizzle2Impl implements vec2 { } } -class vec2fImpl extends vec2Impl implements vec2f { +class vec2fImpl extends vec2Impl { readonly kind = 'vec2f'; make2(x: number, y: number): vec2f { - return new vec2fImpl(x, y); + return new vec2fImpl(x, y) as unknown as vec2f; + } + + make3(x: number, y: number, z: number): vec3f { + return new vec3fImpl(x, y, z) as unknown as vec3f; + } + + make4(x: number, y: number, z: number, w: number): vec4f { + return new vec4fImpl(x, y, z, w) as unknown as vec4f; } } -class vec2iImpl extends vec2Impl implements vec2i { +class vec2iImpl extends vec2Impl { readonly kind = 'vec2i'; make2(x: number, y: number): vec2i { - return new vec2iImpl(x, y); + return new vec2iImpl(x, y) as unknown as vec2i; + } + + make3(x: number, y: number, z: number): vec3i { + return new vec3iImpl(x, y, z) as unknown as vec3i; + } + + make4(x: number, y: number, z: number, w: number): vec4i { + return new vec4iImpl(x, y, z, w) as unknown as vec4i; } } -class vec2uImpl extends vec2Impl implements vec2u { +class vec2uImpl extends vec2Impl { readonly kind = 'vec2u'; make2(x: number, y: number): vec2u { - return new vec2uImpl(x, y); + return new vec2uImpl(x, y) as unknown as vec2u; } -} -abstract class Swizzle3Impl extends Swizzle2Impl { - abstract make3(x: number, y: number, z: number): T3; + make3(x: number, y: number, z: number): vec3u { + return new vec3uImpl(x, y, z) as unknown as vec3u; + } - abstract readonly z: number; + make4(x: number, y: number, z: number, w: number): vec4u { + return new vec4uImpl(x, y, z, w) as unknown as vec4u; + } } -abstract class vec3Impl extends Swizzle3Impl implements vec3 { +abstract class vec3Impl implements vec3 { constructor( public x: number, public y: number, public z: number, - ) { - super(); - } + ) {} *[Symbol.iterator]() { yield this.x; @@ -186,60 +177,61 @@ abstract class vec3Impl extends Swizzle3Impl implements vec3 { } } -class vec3fImpl extends vec3Impl implements vec3f { +class vec3fImpl extends vec3Impl { readonly kind = 'vec3f'; make2(x: number, y: number): vec2f { - return new vec2fImpl(x, y); + return new vec2fImpl(x, y) as unknown as vec2f; } make3(x: number, y: number, z: number): vec3f { - return new vec3fImpl(x, y, z); + return new vec3fImpl(x, y, z) as unknown as vec3f; + } + + make4(x: number, y: number, z: number, w: number): vec4f { + return new vec4fImpl(x, y, z, w) as unknown as vec4f; } } -class vec3iImpl extends vec3Impl implements vec3i { +class vec3iImpl extends vec3Impl { readonly kind = 'vec3i'; make2(x: number, y: number): vec2i { - return new vec2iImpl(x, y); + return new vec2iImpl(x, y) as unknown as vec2i; } make3(x: number, y: number, z: number): vec3i { - return new vec3iImpl(x, y, z); + return new vec3iImpl(x, y, z) as unknown as vec3i; + } + + make4(x: number, y: number, z: number, w: number): vec4i { + return new vec4iImpl(x, y, z, w) as unknown as vec4i; } } -class vec3uImpl extends vec3Impl implements vec3u { +class vec3uImpl extends vec3Impl { readonly kind = 'vec3u'; make2(x: number, y: number): vec2u { - return new vec2uImpl(x, y); + return new vec2uImpl(x, y) as unknown as vec2u; } make3(x: number, y: number, z: number): vec3u { - return new vec3uImpl(x, y, z); + return new vec3uImpl(x, y, z) as unknown as vec3u; } -} - -abstract class Swizzle4Impl extends Swizzle3Impl { - abstract make4(x: number, y: number, z: number, w: number): T4; - abstract readonly w: number; + make4(x: number, y: number, z: number, w: number): vec4u { + return new vec4uImpl(x, y, z, w) as unknown as vec4u; + } } -abstract class vec4Impl - extends Swizzle4Impl - implements vec4 -{ +abstract class vec4Impl implements vec4 { constructor( public x: number, public y: number, public z: number, public w: number, - ) { - super(); - } + ) {} *[Symbol.iterator]() { yield this.x; @@ -249,67 +241,366 @@ abstract class vec4Impl } } -class vec4fImpl extends vec4Impl implements vec4f { +class vec4fImpl extends vec4Impl { readonly kind = 'vec4f'; make2(x: number, y: number): vec2f { - return new vec2fImpl(x, y); + return new vec2fImpl(x, y) as unknown as vec2f; } make3(x: number, y: number, z: number): vec3f { - return new vec3fImpl(x, y, z); + return new vec3fImpl(x, y, z) as unknown as vec3f; } make4(x: number, y: number, z: number, w: number): vec4f { - return new vec4fImpl(x, y, z, w); + return new vec4fImpl(x, y, z, w) as unknown as vec4f; } } -class vec4iImpl extends vec4Impl implements vec4i { +class vec4iImpl extends vec4Impl { readonly kind = 'vec4i'; make2(x: number, y: number): vec2i { - return new vec2iImpl(x, y); + return new vec2iImpl(x, y) as unknown as vec2i; } make3(x: number, y: number, z: number): vec3i { - return new vec3iImpl(x, y, z); + return new vec3iImpl(x, y, z) as unknown as vec3i; } make4(x: number, y: number, z: number, w: number): vec4i { - return new vec4iImpl(x, y, z, w); + return new vec4iImpl(x, y, z, w) as unknown as vec4i; } } -class vec4uImpl extends vec4Impl implements vec4u { +class vec4uImpl extends vec4Impl { readonly kind = 'vec4u'; make2(x: number, y: number): vec2u { - return new vec2uImpl(x, y); + return new vec2uImpl(x, y) as unknown as vec2u; } make3(x: number, y: number, z: number): vec3u { - return new vec3uImpl(x, y, z); + return new vec3uImpl(x, y, z) as unknown as vec3u; } make4(x: number, y: number, z: number, w: number): vec4u { - return new vec4uImpl(x, y, z, w); + return new vec4uImpl(x, y, z, w) as unknown as vec4u; } } -interface Swizzle2 { - readonly xx: T2; // TODO: Create setter - readonly xy: T2; // TODO: Create setter - readonly yx: T2; // TODO: Create setter - readonly yy: T2; // TODO: Create setter -} - -interface Swizzle3 extends Swizzle2 { - // TODO: Implement -} - -interface Swizzle4 extends Swizzle3 { - // TODO: Implement +interface Swizzle2 { + readonly xx: T2; + readonly xy: T2; + readonly yx: T2; + readonly yy: T2; + + readonly xxx: T3; + readonly xxy: T3; + readonly xyx: T3; + readonly xyy: T3; + readonly yxx: T3; + readonly yxy: T3; + readonly yyx: T3; + readonly yyy: T3; + + readonly xxxx: T4; + readonly xxxy: T4; + readonly xxyx: T4; + readonly xxyy: T4; + readonly xyxx: T4; + readonly xyxy: T4; + readonly xyyx: T4; + readonly xyyy: T4; + readonly yxxx: T4; + readonly yxxy: T4; + readonly yxyx: T4; + readonly yxyy: T4; + readonly yyxx: T4; + readonly yyxy: T4; + readonly yyyx: T4; + readonly yyyy: T4; +} + +interface Swizzle3 extends Swizzle2 { + readonly xz: T2; + readonly yz: T2; + readonly zx: T2; + readonly zy: T2; + readonly zz: T2; + + readonly xxz: T3; + readonly xyz: T3; + readonly xzx: T3; + readonly xzy: T3; + readonly xzz: T3; + readonly yxz: T3; + readonly yyz: T3; + readonly yzx: T3; + readonly yzy: T3; + readonly yzz: T3; + readonly zxx: T3; + readonly zxy: T3; + readonly zxz: T3; + readonly zyx: T3; + readonly zyy: T3; + readonly zyz: T3; + readonly zzx: T3; + readonly zzy: T3; + readonly zzz: T3; + + readonly xxxz: T4; + readonly xxyz: T4; + readonly xxzx: T4; + readonly xxzy: T4; + readonly xxzz: T4; + readonly xyxz: T4; + readonly xyyz: T4; + readonly xyzx: T4; + readonly xyzy: T4; + readonly xyzz: T4; + readonly xzxx: T4; + readonly xzxy: T4; + readonly xzxz: T4; + readonly xzyx: T4; + readonly xzyy: T4; + readonly xzyz: T4; + readonly xzzx: T4; + readonly xzzy: T4; + readonly xzzz: T4; + readonly yxxz: T4; + readonly yxyz: T4; + readonly yxzx: T4; + readonly yxzy: T4; + readonly yxzz: T4; + readonly yyxz: T4; + readonly yyyz: T4; + readonly yyzx: T4; + readonly yyzy: T4; + readonly yyzz: T4; + readonly yzxx: T4; + readonly yzxy: T4; + readonly yzxz: T4; + readonly yzyx: T4; + readonly yzyy: T4; + readonly yzyz: T4; + readonly yzzx: T4; + readonly yzzy: T4; + readonly yzzz: T4; + readonly zxxx: T4; + readonly zxxy: T4; + readonly zxxz: T4; + readonly zxyx: T4; + readonly zxyy: T4; + readonly zxyz: T4; + readonly zxzx: T4; + readonly zxzy: T4; + readonly zxzz: T4; + readonly zyxx: T4; + readonly zyxy: T4; + readonly zyxz: T4; + readonly zyyx: T4; + readonly zyyy: T4; + readonly zyyz: T4; + readonly zyzx: T4; + readonly zyzy: T4; + readonly zyzz: T4; + readonly zzxx: T4; + readonly zzxy: T4; + readonly zzxz: T4; + readonly zzyx: T4; + readonly zzyy: T4; + readonly zzyz: T4; + readonly zzzx: T4; + readonly zzzy: T4; + readonly zzzz: T4; +} + +interface Swizzle4 extends Swizzle3 { + readonly yw: T2; + readonly zw: T2; + readonly wx: T2; + readonly wy: T2; + readonly wz: T2; + readonly ww: T2; + + readonly xxw: T3; + readonly xyw: T3; + readonly xzw: T3; + readonly xwx: T3; + readonly xwy: T3; + readonly xwz: T3; + readonly xww: T3; + readonly yxw: T3; + readonly yyw: T3; + readonly yzw: T3; + readonly ywx: T3; + readonly ywy: T3; + readonly ywz: T3; + readonly yww: T3; + readonly zxw: T3; + readonly zyw: T3; + readonly zzw: T3; + readonly zwx: T3; + readonly zwy: T3; + readonly zwz: T3; + readonly zww: T3; + readonly wxx: T3; + readonly wxz: T3; + readonly wxy: T3; + readonly wyy: T3; + readonly wyz: T3; + readonly wzz: T3; + readonly wwx: T3; + readonly wwy: T3; + readonly wwz: T3; + readonly www: T3; + + readonly xxxw: T4; + readonly xxyw: T4; + readonly xxzw: T4; + readonly xxwx: T4; + readonly xxwy: T4; + readonly xxwz: T4; + readonly xxww: T4; + readonly xyxw: T4; + readonly xyyw: T4; + readonly xyzw: T4; + readonly xywx: T4; + readonly xywy: T4; + readonly xywz: T4; + readonly xyww: T4; + readonly xzxw: T4; + readonly xzyw: T4; + readonly xzzw: T4; + readonly xzwx: T4; + readonly xzwy: T4; + readonly xzwz: T4; + readonly xzww: T4; + readonly xwxx: T4; + readonly xwxy: T4; + readonly xwxz: T4; + readonly xwyy: T4; + readonly xwyz: T4; + readonly xwzz: T4; + readonly xwwx: T4; + readonly xwwy: T4; + readonly xwwz: T4; + readonly xwww: T4; + readonly yxxw: T4; + readonly yxyw: T4; + readonly yxzw: T4; + readonly yxwx: T4; + readonly yxwy: T4; + readonly yxwz: T4; + readonly yxww: T4; + readonly yyxw: T4; + readonly yyyw: T4; + readonly yyzw: T4; + readonly yywx: T4; + readonly yywy: T4; + readonly yywz: T4; + readonly yyww: T4; + readonly yzxw: T4; + readonly yzyw: T4; + readonly yzzw: T4; + readonly yzwx: T4; + readonly yzwy: T4; + readonly yzwz: T4; + readonly yzww: T4; + readonly ywxx: T4; + readonly ywxy: T4; + readonly ywxz: T4; + readonly ywxw: T4; + readonly ywyy: T4; + readonly ywyz: T4; + readonly ywzz: T4; + readonly ywwx: T4; + readonly ywwy: T4; + readonly ywwz: T4; + readonly ywww: T4; + readonly zxxw: T4; + readonly zxyw: T4; + readonly zxzw: T4; + readonly zxwx: T4; + readonly zxwy: T4; + readonly zxwz: T4; + readonly zxww: T4; + readonly zyxw: T4; + readonly zyyw: T4; + readonly zyzw: T4; + readonly zywx: T4; + readonly zywy: T4; + readonly zywz: T4; + readonly zyww: T4; + readonly zzxw: T4; + readonly zzyw: T4; + readonly zzzw: T4; + readonly zzwx: T4; + readonly zzwy: T4; + readonly zzwz: T4; + readonly zzww: T4; + readonly zwxx: T4; + readonly zwxy: T4; + readonly zwxz: T4; + readonly zwxw: T4; + readonly zwyy: T4; + readonly zwyz: T4; + readonly zwzz: T4; + readonly zwwx: T4; + readonly zwwy: T4; + readonly zwwz: T4; + readonly zwww: T4; + readonly wxxx: T4; + readonly wxxy: T4; + readonly wxxz: T4; + readonly wxxw: T4; + readonly wxyx: T4; + readonly wxyy: T4; + readonly wxyz: T4; + readonly wxyw: T4; + readonly wxzx: T4; + readonly wxzy: T4; + readonly wxzz: T4; + readonly wxzw: T4; + readonly wxwx: T4; + readonly wxwy: T4; + readonly wxwz: T4; + readonly wxww: T4; + readonly wyxx: T4; + readonly wyxy: T4; + readonly wyxz: T4; + readonly wyxw: T4; + readonly wyyy: T4; + readonly wyyz: T4; + readonly wyzw: T4; + readonly wywx: T4; + readonly wywy: T4; + readonly wywz: T4; + readonly wyww: T4; + readonly wzxx: T4; + readonly wzxy: T4; + readonly wzxz: T4; + readonly wzxw: T4; + readonly wzyy: T4; + readonly wzyz: T4; + readonly wzzy: T4; + readonly wzzw: T4; + readonly wzwx: T4; + readonly wzwy: T4; + readonly wzwz: T4; + readonly wzww: T4; + readonly wwxx: T4; + readonly wwxy: T4; + readonly wwxz: T4; + readonly wwxw: T4; + readonly wwyy: T4; + readonly wwyz: T4; + readonly wwzz: T4; + readonly wwwx: T4; + readonly wwwy: T4; + readonly wwwz: T4; + readonly wwww: T4; } interface vec2 { @@ -333,6 +624,70 @@ interface vec4 { [Symbol.iterator](): Iterator; } +const vecProxyHandler: ProxyHandler = { + get: (target, prop) => { + if (typeof prop === 'symbol') { + return Reflect.get(target, prop); + } + + const targetAsVec4 = target as unknown as vec4uImpl; + const values = new Array(prop.length) as number[]; + + let idx = 0; + for (const char of prop as string) { + switch (char) { + case 'x': + values[idx] = targetAsVec4.x; + break; + case 'y': + values[idx] = targetAsVec4.y; + break; + case 'z': + values[idx] = targetAsVec4.z; + break; + case 'w': + values[idx] = targetAsVec4.w; + break; + default: + return Reflect.get(targetAsVec4, prop); + } + idx++; + } + + if (prop.length === 4) { + return new Proxy( + targetAsVec4.make4( + values[0] as number, + values[1] as number, + values[2] as number, + values[3] as number, + ), + vecProxyHandler, + ); + } + + if (prop.length === 3) { + return new Proxy( + targetAsVec4.make3( + values[0] as number, + values[1] as number, + values[2] as number, + ), + vecProxyHandler, + ); + } + + if (prop.length === 2) { + return new Proxy( + targetAsVec4.make2(values[0] as number, values[1] as number), + vecProxyHandler, + ); + } + + return Reflect.get(target, prop); + }, +}; + // ---------- // Public API // ---------- @@ -353,28 +708,28 @@ export interface vecBase { [Symbol.iterator](): Iterator; } -export interface vec2f extends vec2, Swizzle2 { +export interface vec2f extends vec2, Swizzle2 { /** use to distinguish between vectors of the same size on the type level */ kind: 'vec2f'; } -export interface vec2i extends vec2, Swizzle2 { +export interface vec2i extends vec2, Swizzle2 { /** use to distinguish between vectors of the same size on the type level */ kind: 'vec2i'; } -export interface vec2u extends vec2, Swizzle2 { +export interface vec2u extends vec2, Swizzle2 { /** use to distinguish between vectors of the same size on the type level */ kind: 'vec2u'; } -export interface vec3f extends vec3, Swizzle3 { +export interface vec3f extends vec3, Swizzle3 { /** use to distinguish between vectors of the same size on the type level */ kind: 'vec3f'; } -export interface vec3i extends vec3, Swizzle3 { +export interface vec3i extends vec3, Swizzle3 { /** use to distinguish between vectors of the same size on the type level */ kind: 'vec3i'; } -export interface vec3u extends vec3, Swizzle3 { +export interface vec3u extends vec3, Swizzle3 { /** use to distinguish between vectors of the same size on the type level */ kind: 'vec3u'; } @@ -402,8 +757,10 @@ export const vec2f = makeVecSchema({ byteAlignment: 8, length: 2, label: 'vec2f', - make: (x: number, y: number) => new vec2fImpl(x, y), - makeFromScalar: (x) => new vec2fImpl(x, x), + make: (x: number, y: number) => + new Proxy(new vec2fImpl(x, y), vecProxyHandler) as vec2f, + makeFromScalar: (x) => + new Proxy(new vec2fImpl(x, x), vecProxyHandler) as vec2f, }) as Vec2f; export type Vec2i = TgpuData & @@ -416,8 +773,10 @@ export const vec2i = makeVecSchema({ byteAlignment: 8, length: 2, label: 'vec2i', - make: (x: number, y: number) => new vec2iImpl(x, y), - makeFromScalar: (x) => new vec2iImpl(x, x), + make: (x: number, y: number) => + new Proxy(new vec2iImpl(x, y), vecProxyHandler) as vec2i, + makeFromScalar: (x) => + new Proxy(new vec2iImpl(x, x), vecProxyHandler) as vec2i, }) as Vec2i; export type Vec2u = TgpuData & @@ -430,8 +789,10 @@ export const vec2u = makeVecSchema({ byteAlignment: 8, length: 2, label: 'vec2u', - make: (x: number, y: number) => new vec2uImpl(x, y), - makeFromScalar: (x) => new vec2uImpl(x, x), + make: (x: number, y: number) => + new Proxy(new vec2uImpl(x, y), vecProxyHandler) as vec2u, + makeFromScalar: (x) => + new Proxy(new vec2uImpl(x, x), vecProxyHandler) as vec2u, }) as Vec2u; export type Vec3f = TgpuData & @@ -444,8 +805,10 @@ export const vec3f = makeVecSchema({ byteAlignment: 16, length: 3, label: 'vec3f', - make: (x, y, z) => new vec3fImpl(x, y, z), - makeFromScalar: (x) => new vec3fImpl(x, x, x), + make: (x, y, z) => + new Proxy(new vec3fImpl(x, y, z), vecProxyHandler) as vec3f, + makeFromScalar: (x) => + new Proxy(new vec3fImpl(x, x, x), vecProxyHandler) as vec3f, }) as Vec3f; export type Vec3i = TgpuData & @@ -458,8 +821,10 @@ export const vec3i = makeVecSchema({ byteAlignment: 16, length: 3, label: 'vec3i', - make: (x, y, z) => new vec3iImpl(x, y, z), - makeFromScalar: (x) => new vec3iImpl(x, x, x), + make: (x, y, z) => + new Proxy(new vec3iImpl(x, y, z), vecProxyHandler) as vec3i, + makeFromScalar: (x) => + new Proxy(new vec3iImpl(x, x, x), vecProxyHandler) as vec3i, }) as Vec3i; export type Vec3u = TgpuData & @@ -472,8 +837,10 @@ export const vec3u = makeVecSchema({ byteAlignment: 16, length: 3, label: 'vec3u', - make: (x, y, z) => new vec3uImpl(x, y, z), - makeFromScalar: (x) => new vec3uImpl(x, x, x), + make: (x, y, z) => + new Proxy(new vec3uImpl(x, y, z), vecProxyHandler) as vec3u, + makeFromScalar: (x) => + new Proxy(new vec3uImpl(x, x, x), vecProxyHandler) as vec3u, }) as Vec3u; export type Vec4f = TgpuData & @@ -486,8 +853,10 @@ export const vec4f = makeVecSchema({ byteAlignment: 16, length: 4, label: 'vec4f', - make: (x, y, z, w) => new vec4fImpl(x, y, z, w), - makeFromScalar: (x) => new vec4fImpl(x, x, x, x), + make: (x, y, z, w) => + new Proxy(new vec4fImpl(x, y, z, w), vecProxyHandler) as vec4f, + makeFromScalar: (x) => + new Proxy(new vec4fImpl(x, x, x, x), vecProxyHandler) as vec4f, }) as Vec4f; export type Vec4i = TgpuData & @@ -500,8 +869,10 @@ export const vec4i = makeVecSchema({ byteAlignment: 16, length: 4, label: 'vec4i', - make: (x, y, z, w) => new vec4iImpl(x, y, z, w), - makeFromScalar: (x) => new vec4iImpl(x, x, x, x), + make: (x, y, z, w) => + new Proxy(new vec4iImpl(x, y, z, w), vecProxyHandler) as vec4i, + makeFromScalar: (x) => + new Proxy(new vec4iImpl(x, x, x, x), vecProxyHandler) as vec4i, }) as Vec4i; export type Vec4u = TgpuData & @@ -514,6 +885,8 @@ export const vec4u = makeVecSchema({ byteAlignment: 16, length: 4, label: 'vec4u', - make: (x, y, z, w) => new vec4uImpl(x, y, z, w), - makeFromScalar: (x) => new vec4uImpl(x, x, x, x), + make: (x, y, z, w) => + new Proxy(new vec4uImpl(x, y, z, w), vecProxyHandler) as vec4u, + makeFromScalar: (x) => + new Proxy(new vec4uImpl(x, x, x, x), vecProxyHandler) as vec4u, }) as Vec4u;