var cnst = (value) => (_x, _y, _z) => value; var variable = (name) => (x, y, z) => (name === "x" ? x : name === "y" ? y : z); var one = cnst(1); var two = cnst(2); var three = cnst(3); var binaryOp = (op) => (f, g) => (x, y, z) => op(f(x, y, z), g(x, y, z)); var ternaryOp = (op) => (f, g, h) => (x, y, z) => op(f(x, y, z), g(x, y, z), h(x, y, z)); var quaternaryOp = (op) => (f, g, h, k) => (x, y, z) => op(f(x, y, z), g(x, y, z), h(x, y, z), k(x, y, z)); var quinaryOp = (op) => (f, g, h, i, j) => (x, y, z) => op(f(x, y, z), g(x, y, z), h(x, y, z), i(x, y, z), j(x, y, z)); var add = binaryOp((a, b) => a + b); var subtract = binaryOp((a, b) => a - b); var multiply = binaryOp((a, b) => a * b); var divide = binaryOp((a, b) => a / b); var negate = (f) => (x, y, z) => -f(x, y, z); var clamp = ternaryOp((v, mn, mx) => Math.min(Math.max(v, mn), mx)); var wrap = ternaryOp( (v, mn, mx) => mn + ((((v - mn) % (mx - mn)) + (mx - mn)) % (mx - mn)), ); var softClamp = quaternaryOp( (v, mn, mx, lambda) => mn + (mx - mn) / (1 + Math.exp(lambda * ((mx + mn) / 2 - v))), ); var argMin3 = ternaryOp((a, b, c) => [a, b, c].indexOf(Math.min(a, b, c))); var argMax3 = ternaryOp((a, b, c) => [a, b, c].indexOf(Math.max(a, b, c))); var argMin5 = quinaryOp((a, b, c, d, e) => [a, b, c, d, e].indexOf(Math.min(a, b, c, d, e)), ); var argMax5 = quinaryOp((a, b, c, d, e) => [a, b, c, d, e].indexOf(Math.max(a, b, c, d, e)), ); var tokenize = (expression) => { var tokens = []; var i = 0; while (i < expression.length) { while (i < expression.length && expression[i] === " ") i++; var start = i; while (i < expression.length && expression[i] !== " ") i++; if (i > start) tokens.push(expression.slice(start, i)); } return tokens; }; var unaryOp = (op) => (f) => (x, y, z) => op(f(x, y, z)); var arcTan = unaryOp(Math.atan); var arcTan2 = binaryOp(Math.atan2); var sin = unaryOp(Math.sin); var cos = unaryOp(Math.cos); var ARITIES = { "+": 2, "-": 2, "*": 2, "/": 2, negate: 1, clamp: 3, wrap: 3, softClamp: 4, argMin3: 3, argMax3: 3, argMin5: 5, argMax5: 5, atan: 1, arcTan: 1, atan2: 2, arcTan2: 2, sin: 1, cos: 1, }; var OPERATIONS = { "+": add, "-": subtract, "*": multiply, "/": divide, negate: negate, clamp: clamp, wrap: wrap, softClamp: softClamp, argMin3: argMin3, argMax3: argMax3, argMin5: argMin5, argMax5: argMax5, atan: arcTan, arcTan: arcTan, atan2: arcTan2, arcTan2: arcTan2, sin: sin, cos: cos, }; var NAMED_CONSTS = { one: one, two: two, three: three }; var parse = (expression) => { var tokens = tokenize(expression); var stack = []; for (var token of tokens) { if (token in OPERATIONS) { var arity = ARITIES[token]; var args = stack.splice(-arity); stack.push(OPERATIONS[token](...args)); } else if (token in NAMED_CONSTS) { stack.push(NAMED_CONSTS[token]); } else if (token === "x" || token === "y" || token === "z") { stack.push(variable(token)); } else { stack.push(cnst(parseFloat(token))); } } return stack[0]; };