123 lines
3.0 KiB
JavaScript
123 lines
3.0 KiB
JavaScript
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];
|
|
};
|