<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script type="text/javascript">
function value(curve, x) {
for (var i = 0; i < curve.length && curve[i][0] <= x; i++)
;
if (i == curve.length)
i--;
var x1 = curve[i-1][0];
var y1 = curve[i-1][1];
var x2 = curve[i][0];
var y2 = curve[i][1];
if (x2 == x1)
return y1;
return y1+(y2-y1)/(x2-x1)*(x-x1);
}
function comp() {
//
// Convert curves from Pianoteq's format to JS array
//
var f_ptq = JSON.parse(document.getElementById("f_ptq_txt").value
.replace(/.*(\[.+\]).*/,"$1")
.replace(';', ','));
var g_ptq = JSON.parse(document.getElementById("g_ptq_txt").value
.replace(/.*(\[.+\]).*/,"$1")
.replace(';', ','));
var f = [[0,0]];
var f_size = f_ptq.length/2;
for (var i = 0; i < f_size; i++) {
f.push([f_ptq[i], f_ptq[i+f_size]]);
}
if (f[f_size][0] != 127)
f.push([127,f[f_size][1]]);
var g = [[0,0]];
var g_size = g_ptq.length/2;
for (var i = 0; i < g_size; i++) {
g.push([g_ptq[i], g_ptq[i+g_size]]);
}
if (g[g_size][0] != 127)
g.push([127,g[g_size][1]]);
//
// Find sufficient set of x'es for the composition curve
//
var g_inv = g.map(function(p) {return [p[1], p[0]];})
.sort(function(p, q) {return p[0]-q[0];});
var g_xs = g.map(function(p) {return p[0];});
var f_xs = f.map(function(p) {return value(g_inv, p[0]);});
var xs = g_xs.concat(f_xs)
.sort(function(a, b) {return a-b;})
.filter(function(x, i, a) {return i === 0 || a[i] != a[i-1];})
.filter(function(x) {return x <= 127;});
//
// Calculate composition curve
//
var fg = [];
xs.forEach(function(x) {
fg.push([x, value(f, value(g, x))]);
});
//
// Output using Pianoteq's format
//
var out = '[';
for (var i = 0; i < fg.length; i++) {
if (i != 0) out += ', ';
out += (fg[i][0]).toFixed();
}
out += '; ';
for (var i = 0; i < fg.length; i++) {
if (i != 0) out += ', ';
out += (fg[i][1]).toFixed();
}
out += ']';
document.getElementById("out_ptq_txt").value = out;
}
function go() {
try {
comp();
}
catch (e) {
document.getElementById("out_ptq_txt").value = "Error! Sorry..."
}
}
</script>
</head>
<body>
Preset's curve<br><i>f =</i> <input type="text" id="f_ptq_txt" size="70" value="[0, 127; 0, 127]">
<br><br>
Keyboard's curve<br><i>g = </i> <input type="text" id="g_ptq_txt" size="70" value="[0, 127; 0, 127]">
<br><br>
<button onclick="go()">Calculate</button>
<br><br>
Composition<br><i>f∘g = </i> <input type="text" id="out_ptq_txt" size="70" value="[0, 127; 0, 127]">
</body>
</html>
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |