Skip to content

Commit f711a5f

Browse files
authored
Add Swizzle to all Vecs. (#39)
* Add swizzle based on macro. * More constructors. * Make it work with shady. * Morepretty. * Add ray tracer output. * Better typePrefix that works with all vector layouts.
1 parent 9196bbf commit f711a5f

5 files changed

Lines changed: 205 additions & 47 deletions

File tree

src/vmath.nim

Lines changed: 135 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
## default: ObjArray based
55
##
66

7-
import math, strutils
7+
import macros, math, strutils
88
export math except isNan
99

1010
{.push inline.}
@@ -230,13 +230,13 @@ elif true or defined(vmathObjArrayBased):
230230
GVec234[T] = GVec2[T] | GVec3[T] | GVec4[T]
231231

232232
template gvec2*[T](x, y: T): GVec2[T] =
233-
GVec2[T](arr:[T(x), T(y)])
233+
GVec2[T](arr: [T(x), T(y)])
234234

235235
template gvec3*[T](x, y, z: T): GVec3[T] =
236-
GVec3[T](arr:[T(x), T(y), T(z)])
236+
GVec3[T](arr: [T(x), T(y), T(z)])
237237

238238
template gvec4*[T](x, y, z, w: T): GVec4[T] =
239-
GVec4[T](arr:[T(x), T(y), T(z), T(w)])
239+
GVec4[T](arr: [T(x), T(y), T(z), T(w)])
240240

241241
template x*[T](a: var GVec2[T]): var T = a.arr[0]
242242
template y*[T](a: var GVec2[T]): var T = a.arr[1]
@@ -436,6 +436,9 @@ proc isNan*(x: SomeFloat): bool =
436436
## Returns true if number is a NaN.
437437
x != 0.0 and (x != x or x * 0.5 == x)
438438

439+
template lowerType(a: typed): string =
440+
($type(a)).toLowerAscii()
441+
439442
template genConstructor(lower, upper, typ: untyped) =
440443

441444
proc `lower 2`*(): `upper 2` = gvec2[typ](typ(0), typ(0))
@@ -457,58 +460,145 @@ template genConstructor(lower, upper, typ: untyped) =
457460
proc `lower 4`*[T](x: GVec4[T]): `upper 4` =
458461
gvec4[typ](typ(x[0]), typ(x[1]), typ(x[2]), typ(x[3]))
459462

460-
proc `lower 3`*[T](x: GVec2[T]): `upper 3` =
461-
gvec3[typ](typ(x[0]), typ(x[1]), 0)
462-
proc `lower 4`*[T](x: GVec3[T]): `upper 4` =
463-
gvec4[typ](typ(x[0]), typ(x[1]), typ(x[2]), 0)
463+
proc `lower 3`*[T](x: GVec2[T], z: T = 0): `upper 3` =
464+
gvec3[typ](typ(x[0]), typ(x[1]), z)
465+
proc `lower 4`*[T](x: GVec3[T], w: T = 0): `upper 4` =
466+
gvec4[typ](typ(x[0]), typ(x[1]), typ(x[2]), w)
467+
468+
proc `lower 4`*[T](a, b: GVec2[T]): `upper 4` =
469+
gvec4[typ](typ(a[0]), typ(a[1]), typ(b[0]), typ(b[1]))
464470

465471
proc `$`*(a: `upper 2`): string =
466-
($type(a)).toLowerAscii() & "(" & $a.x & ", " & $a.y & ")"
472+
lowerType(a) & "(" & $a.x & ", " & $a.y & ")"
467473
proc `$`*(a: `upper 3`): string =
468-
($type(a)).toLowerAscii() & "(" & $a.x & ", " & $a.y & ", " & $a.z & ")"
474+
lowerType(a) & "(" & $a.x & ", " & $a.y & ", " & $a.z & ")"
469475
proc `$`*(a: `upper 4`): string =
470-
($type(a)).toLowerAscii() & "(" & $a.x & ", " & $a.y & ", " & $a.z & ", " & $a.w & ")"
476+
lowerType(a) & "(" & $a.x & ", " & $a.y & ", " & $a.z & ", " & $a.w & ")"
471477

472478
genConstructor(bvec, BVec, bool)
473479
genConstructor(ivec, IVec, int32)
474480
genConstructor(uvec, UVec, uint32)
475481
genConstructor(vec, Vec, float32)
476482
genConstructor(dvec, DVec, float64)
477483

478-
proc xy*[T](a: GVec234[T]): GVec2[T] = gvec2[T](a.x, a.y)
479-
proc xz*[T](a: GVec234[T]): GVec2[T] = gvec2[T](a.x, a.z)
480-
proc yx*[T](a: GVec234[T]): GVec2[T] = gvec2[T](a.y, a.x)
481-
proc yz*[T](a: GVec234[T]): GVec2[T] = gvec2[T](a.y, a.z)
482-
proc zx*[T](a: GVec234[T]): GVec2[T] = gvec2[T](a.z, a.x)
483-
proc zy*[T](a: GVec234[T]): GVec2[T] = gvec2[T](a.z, a.y)
484-
485-
proc xxx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.x, a.x)
486-
proc xxy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.x, a.y)
487-
proc xxz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.x, a.z)
488-
proc xyx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.y, a.x)
489-
proc xyy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.y, a.y)
490-
proc xyz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.y, a.z)
491-
proc xzx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.z, a.x)
492-
proc xzy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.z, a.y)
493-
proc xzz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.x, a.z, a.z)
494-
proc yxx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.x, a.x)
495-
proc yxy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.x, a.y)
496-
proc yxz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.x, a.z)
497-
proc yyx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.y, a.x)
498-
proc yyy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.y, a.y)
499-
proc yyz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.y, a.z)
500-
proc yzx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.z, a.x)
501-
proc yzy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.z, a.y)
502-
proc yzz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.y, a.z, a.z)
503-
proc zxx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.x, a.x)
504-
proc zxy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.x, a.y)
505-
proc zxz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.x, a.z)
506-
proc zyx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.y, a.x)
507-
proc zyy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.y, a.y)
508-
proc zyz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.y, a.z)
509-
proc zzx*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.z, a.x)
510-
proc zzy*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.z, a.y)
511-
proc zzz*[T](a: GVec34[T]): GVec3[T] = gvec3[T](a.z, a.z, a.z)
484+
{.experimental: "dotOperators".}
485+
proc num(letter: char, fields: NimNode): int =
486+
## Given a swizzle character gives back the location number.
487+
case letter:
488+
of 'x', 'r', 's': 0
489+
of 'y', 'g', 't': 1
490+
of 'z', 'b', 'p': 2
491+
of 'w', 'a', 'q': 3
492+
else:
493+
error "invalid swizzle character: " & letter, fields
494+
quit()
495+
496+
proc typePrefix(node: NimNode): string =
497+
## Given a node of type GVec234 gives its prefix type.
498+
## IVec2 -> "i", DVec4 -> "d", Vec3 -> ""
499+
let typeName =
500+
when defined(vmathArrayBased):
501+
node.getType()[2].repr
502+
elif defined(vmathObjBased):
503+
node.getType()[2][0].getType().repr
504+
elif true or defined(vmathObjArrayBased):
505+
node.getType()[2][0].getType()[2].repr
506+
case typeName:
507+
of "bool": "b"
508+
of "int32": "i"
509+
of "uint32": "u"
510+
of "float32": ""
511+
of "float", "float64": "d"
512+
else:
513+
error "invalid vector type: " & typeName, node
514+
quit()
515+
516+
macro `.`*(v: GVec234, fields: untyped): untyped =
517+
## Adds support for swizzle getter.
518+
## x y z w
519+
## r g b a
520+
## s t p q
521+
## v.xyz, v.xxx, v.zyx ...
522+
## v.rgb, v.rrr, v.bgr ...
523+
## v.stp, v.sss, v.pts ...
524+
let swizzle = fields.repr
525+
let vec = ident(typePrefix(v) & "vec" & $swizzle.len)
526+
if swizzle.len == 1:
527+
let a = num(swizzle[0], fields)
528+
result = quote do:
529+
`v`[`a`]
530+
elif swizzle.len == 2:
531+
let
532+
a = num(swizzle[0], fields)
533+
b = num(swizzle[1], fields)
534+
result = quote do:
535+
`vec`(`v`[`a`], `v`[`b`])
536+
elif swizzle.len == 3:
537+
let
538+
a = num(swizzle[0], fields)
539+
b = num(swizzle[1], fields)
540+
c = num(swizzle[2], fields)
541+
result = quote do:
542+
`vec`(`v`[`a`], `v`[`b`], `v`[`c`])
543+
elif swizzle.len == 4:
544+
let
545+
a = num(swizzle[0], fields)
546+
b = num(swizzle[1], fields)
547+
c = num(swizzle[2], fields)
548+
d = num(swizzle[3], fields)
549+
result = quote do:
550+
`vec`(`v`[`a`], `v`[`b`], `v`[`c`], `v`[`d`])
551+
else:
552+
error "invalid number of swizzle characters: " & swizzle, fields
553+
554+
macro `.=`*(v: GVec234, fields: untyped, e: untyped): untyped =
555+
## Adds support for swizzle setter.
556+
## x y z w
557+
## r g b a
558+
## s t p q
559+
## v.xyz, v.xxx, v.zyx ...
560+
## v.rgb, v.rrr, v.bgr ...
561+
## v.stp, v.sss, v.pts ...
562+
let swizzle = fields.repr
563+
if swizzle.len == 1:
564+
let a = num(swizzle[0], fields)
565+
result = quote do:
566+
`v`[`a`] = `e`
567+
elif swizzle.len == 2:
568+
let
569+
a = num(swizzle[0], fields)
570+
b = num(swizzle[1], fields)
571+
result = quote do:
572+
block:
573+
let tmp = `e`
574+
`v`[`a`] = tmp[0]
575+
`v`[`b`] = tmp[1]
576+
elif swizzle.len == 3:
577+
let
578+
a = num(swizzle[0], fields)
579+
b = num(swizzle[1], fields)
580+
c = num(swizzle[2], fields)
581+
result = quote do:
582+
block:
583+
let tmp = `e`
584+
`v`[`a`] = tmp[0]
585+
`v`[`b`] = tmp[1]
586+
`v`[`c`] = tmp[2]
587+
elif swizzle.len == 4:
588+
let
589+
a = num(swizzle[0], fields)
590+
b = num(swizzle[1], fields)
591+
c = num(swizzle[2], fields)
592+
d = num(swizzle[3], fields)
593+
result = quote do:
594+
block:
595+
let tmp = `e`
596+
`v`[`a`] = tmp[0]
597+
`v`[`b`] = tmp[1]
598+
`v`[`c`] = tmp[2]
599+
`v`[`d`] = tmp[3]
600+
else:
601+
error "invalid number of swizzle characters: " & swizzle, fields
512602

513603
proc `==`*[T](a, b: GVec2[T]): bool =
514604
a.x == b.x and a.y == b.y

tests/bench_raytracer.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
## MIT License
33
## Copyright (c) 2021 Edin Omeragic
44

5-
import chroma, math, times, pixie, vmath, benchy
5+
import benchy, chroma, math, pixie, times, vmath
66

77
{.push inline, noinit, checks: off.}
88

tests/bench_rep.nim

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ proc `[]=`(a: var Vec3ObjArr, i: int, v: float32) {.inline.} =
160160
proc set(a: var Vec3Tuple, i: int, v: float32) {.inline.} =
161161
cast[ptr float32](cast[uint64](a.addr) + i.uint64 * sizeof(float32).uint64)[] = v
162162

163-
164163
timeIt "vec3Obj[static]=", 1000:
165164
var v = vec3Obj(1, 2, 3)
166165
for i in 0 .. 1000000:

tests/raytracer.png

174 KB
Loading

tests/test.nim

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ block:
252252
_ = dvec3()
253253
_ = dvec4()
254254

255+
var a = vec3(vec2(1, 2), 3)
256+
doAssert a == vec3(1, 2, 3)
257+
258+
var b = vec4(vec3(1, 2, 3), 4)
259+
doAssert b == vec4(1, 2, 3, 4)
260+
261+
var c = vec4(vec2(1, 2), vec2(3, 4))
262+
doAssert c == vec4(1, 2, 3, 4)
263+
255264
block:
256265
# test $ string functions
257266
doAssert $bvec2(true, false) == "bvec2(true, false)"
@@ -278,6 +287,66 @@ block:
278287
echo vec3(1.0, 2.0, 3.0)
279288
echo vec4(1.0, 2.0, 3.0, 4.0)
280289

290+
block:
291+
# test swizzle vec
292+
var a = vec2(1, 2)
293+
doAssert a.x == 1.0
294+
doAssert a.y == 2.0
295+
doAssert a.yx == vec2(2, 1)
296+
doAssert a.gr == vec2(2, 1)
297+
doAssert a.ts == vec2(2, 1)
298+
doAssert a.xxx == vec3(1, 1, 1)
299+
300+
a.yx = vec2(-1, -2)
301+
doAssert a == vec2(-2, -1)
302+
303+
a.xx = vec2(-7, -3)
304+
doAssert a == vec2(-3, -1)
305+
306+
when compiles(a.xyzxyz):
307+
doAssert false
308+
309+
when compiles(a.z = 123):
310+
doAssert false
311+
312+
var b = vec4(1, 2, 3, 4)
313+
doAssert b == vec4(1, 2, 3, 4)
314+
b.wzyx = b
315+
doAssert b == vec4(4, 3, 2, 1)
316+
317+
b.g = 123
318+
doAssert b == vec4(4.0, 123.0, 2.0, 1.0)
319+
320+
block:
321+
# test swizzle dvec float64
322+
var a = dvec2(1, 2)
323+
doAssert a.x == 1.0
324+
doAssert a.y == 2.0
325+
doAssert a.yx == dvec2(2, 1)
326+
doAssert a.gr == dvec2(2, 1)
327+
doAssert a.ts == dvec2(2, 1)
328+
doAssert a.xxx == dvec3(1, 1, 1)
329+
330+
a.yx = dvec2(-1, -2)
331+
doAssert a == dvec2(-2, -1)
332+
333+
a.xx = dvec2(-7, -3)
334+
doAssert a == dvec2(-3, -1)
335+
336+
when compiles(a.xyzxyz):
337+
doAssert false
338+
339+
when compiles(a.z = 123):
340+
doAssert false
341+
342+
var b = dvec4(1, 2, 3, 4)
343+
doAssert b == dvec4(1, 2, 3, 4)
344+
b.wzyx = b
345+
doAssert b == dvec4(4, 3, 2, 1)
346+
347+
b.g = 123
348+
doAssert b == dvec4(4.0, 123.0, 2.0, 1.0)
349+
281350
block:
282351
# Test basic mat constructors.
283352
block:

0 commit comments

Comments
 (0)