Skip to content

Instantly share code, notes, and snippets.

@randName
Created March 18, 2020 09:55
Show Gist options
  • Save randName/74378c84c4c356b1cec6c1a74e85c75c to your computer and use it in GitHub Desktop.
Save randName/74378c84c4c356b1cec6c1a74e85c75c to your computer and use it in GitHub Desktop.
GLSL-style swizzling in Javascript
const a = vec(1, 2, 3, 4)
console.log(a.length, a)
// standard property access
console.log(a[0], a.y, a.b, a.t)
// swizzling
console.log(a.xy, a.zyx, a.xxyy)
// same rules from GLSL apply
console.log(a.xyr, a.xxyyz)
// setters are also available
a.x = 3
a.yz = [1, 2]
console.log([...a])
// cloning works
const b = vec(...a)
a.x -= 3
b.x += 2
console.log([...a], [...b])
const masks = new Map(Object.entries({
xyzw: /^[xyzw]{1,4}$/,
rgba: /^[rgba]{1,4}$/,
stpq: /^[stpq]{1,4}$/,
}))
const propIndex = (p) => {
for (const mask of masks.keys()) {
const idx = mask.indexOf(p)
if (idx === -1) { continue }
return idx
}
return p
}
const propMask = (p) => {
for (const [mask, re] of masks) {
if (!re.test(p)) { continue }
return mask
}
return null
}
function getComponent (obj, prop) {
if (typeof prop === 'symbol' || prop > -1) {
return obj[prop]
} else if (prop.length === 1) {
return obj[propIndex(prop)]
}
const m = propMask(prop)
if (m) {
return prop.split('').map((c) => obj[m.indexOf(c)])
}
return obj[prop]
}
function setComponent (obj, prop, val) {
if (prop > -1) {
obj[prop] = val
return true
} else if (prop.length === 1) {
obj[propIndex(prop)] = val
return true
}
const plen = prop.length
if (plen === val.length) {
const m = propMask(prop)
if (m && plen === (new Set(prop)).size) {
for (let i = 0; i < plen; ++i) {
obj[m.indexOf(prop[i])] = val[i]
}
return true
}
}
return false
}
function vec (...components) {
return new Proxy(components, {
get: getComponent,
set: setComponent,
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment