Yeaaaa I love to swizzle! It’s so fun honestly that I feel like it’s worth adding in to vector types in languages that don’t have it if you can.
A couple years ago I wrote a mixin to facilitate this in Ruby, if anyone has use for that:
module Swizzleable
module ClassMethods
def swizz_masks
[
[:r, :g, :b, :a],
[:x, :y, :z, :w],
[:s, :t, :p, :q],
]
end
end
def self.included(klass)
klass.extend(ClassMethods)
min_max_swizz_len = 4
max_swizz_len = klass.swizz_masks.map(&:length).max
max_swizz_len = min_max_swizz_len if max_swizz_len < min_max_swizz_len
klass.swizz_masks.each do |comp_set|
1.upto(max_swizz_len) do |n|
comp_set.repeated_permutation(n).each do |comps|
klass.define_method(comps.join.to_sym) do
out = comp_set.zip(swizz_vals).to_h.fetch_values(*comps)
if klass.public_method_defined? :post_swizz
post_swizz(out)
else
out
end
end
end
comp_set.permutation(n).each do |comps|
klass.define_method((comps.join + '=').to_sym) do |args|
comps.map(&comp_set.method(:index)).zip(args).each do |ndx, arg|
if klass.public_method_defined? :swizz_assign
swizz_assign(ndx, arg)
else
send(:[]=, ndx, arg)
end
end
end
end
end
end
end
end
The code might look a little crazy but it’s easy to use—you just include
the module in your class and define a method swizz_vals
that returns an array with the components in xyzw
order (up to the dimension of the vector, it’s okay if it’s less than 4). By default swizzling returns the components in a plain array, but you can also define post_swizz
if you like, which will receive this array and can transform it before it gets passed back to the user. Likewise, by default assigning new values to components via swizzle assumes that the class supports normal array subscripting assignment for the object’s components ([]=
), but if that doesn’t work well for your class, you can define swizz_assign
, which receives the component index and new value and can do whatever is appropriate with it.
If you’re feeling saucy, you can monkey-patch the stdlib Vector
with this:
require 'matrix'
class Vector
include Swizzleable
def swizz_vals
(0...[4, size].min).map { self[_1] }
end
def post_swizz(arr)
self.class[*arr]
end
end
v = Vector[1, 2, 3, 4]
v.xy
#=> Vector[1, 2]
v.argb
#=> Vector[4, 1, 2, 3]
v.ptq
#=> Vector[3, 2, 4]
v.yz = [10, 12]
v.wzyx
#=> Vector[4, 12, 10, 1]
I feel like this is a nice demonstration of ZA POWAH of Ruby, and kind of underscores why I feel like it’s a bit underrated for game development maybe.
Also, if you’d rather swazzle than swizzle, this video might help.