diff --git a/src/generator/bezier.js b/src/generator/bezier.js index 177e91ea..f34c1593 100644 --- a/src/generator/bezier.js +++ b/src/generator/bezier.js @@ -14,23 +14,29 @@ const bezier = function(colors) { // linear interpolation [lab0, lab1] = colors.map(c => c.lab()); I = function(t) { - const lab = ([0, 1, 2].map((i) => lab0[i] + (t * (lab1[i] - lab0[i])))); - return new Color(lab, 'lab'); + const linearInterpolation = (x0, x1) => x0 + (t * (x1 - x0)); + const lab = ([0, 1, 2].map((i) => linearInterpolation(lab0[i], lab1[i]))); + const alpha = linearInterpolation(colors[0].alpha(), colors[1].alpha()); + return new Color(lab, 'lab').alpha(alpha); }; } else if (colors.length === 3) { // quadratic bezier interpolation [lab0, lab1, lab2] = colors.map(c => c.lab()); I = function(t) { - const lab = ([0, 1, 2].map((i) => ((1-t)*(1-t) * lab0[i]) + (2 * (1-t) * t * lab1[i]) + (t * t * lab2[i]))); - return new Color(lab, 'lab'); + const quadraticInterpolation = (x0, x1, x2) => ((1-t)*(1-t) * x0) + (2 * (1-t) * t * x1) + (t * t * x2) + const lab = ([0, 1, 2].map((i) => quadraticInterpolation(lab0[i], lab1[i], lab2[i]))); + const alpha = quadraticInterpolation(colors[0].alpha(), colors[1].alpha(), colors[2].alpha()); + return new Color(lab, 'lab').alpha( alpha ); }; } else if (colors.length === 4) { // cubic bezier interpolation let lab3; [lab0, lab1, lab2, lab3] = colors.map(c => c.lab()); I = function(t) { - const lab = ([0, 1, 2].map((i) => ((1-t)*(1-t)*(1-t) * lab0[i]) + (3 * (1-t) * (1-t) * t * lab1[i]) + (3 * (1-t) * t * t * lab2[i]) + (t*t*t * lab3[i]))); - return new Color(lab, 'lab'); + const cubicInterpolation = (x0, x1, x2, x3) => ((1-t)*(1-t)*(1-t) * x0) + (3 * (1-t) * (1-t) * t * x1) + (3 * (1-t) * t * t * x2) + (t*t*t * x3); + const lab = ([0, 1, 2].map((i) => cubicInterpolation(lab0[i], lab1[i], lab2[i], lab3[i]))); + const alpha = cubicInterpolation(colors[0].alpha(), colors[1].alpha(), colors[2].alpha(), colors[3].alpha()); + return new Color(lab, 'lab').alpha(alpha); }; } else if (colors.length === 5) { const I0 = bezier(colors.slice(0, 3)); diff --git a/test/bezier.js b/test/bezier.test.js similarity index 63% rename from test/bezier.js rename to test/bezier.test.js index e9d2c7fc..3c35629b 100644 --- a/test/bezier.js +++ b/test/bezier.test.js @@ -11,42 +11,42 @@ vows 'simple two color linear interpolation': { topic: { - f: chroma.bezier(['white', 'black']) + f: chroma.bezier(['white', chroma('black').alpha(0)]) }, 'starts from white'(topic) { assert.equal(topic.f(0).hex(), '#ffffff'); }, - 'ends in black'(topic) { assert.equal(topic.f(1).hex(), '#000000'); }, - 'center is grey'(topic) { assert.equal(topic.f(0.5).hex(), '#777777'); } + 'ends in transparent black'(topic) { assert.equal(topic.f(1).hex(), '#00000000'); }, + 'center is transluscent grey'(topic) { assert.equal(topic.f(0.5).hex(), '#77777780'); } }, 'three color quadratic bezier interpolation': { topic: { - f: chroma.bezier(['white', 'red', 'black']) + f: chroma.bezier(['white', chroma('red').alpha(.5), chroma('black').alpha(0)]) }, 'starts from white'(topic) { assert.equal(topic.f(0).hex(), '#ffffff'); }, - 'ends in black'(topic) { assert.equal(topic.f(1).hex(), '#000000'); }, - 'center is a greyish red'(topic) { assert.equal(topic.f(0.5).hex(), '#c45c44'); } + 'ends in transparent black'(topic) { assert.equal(topic.f(1).hex(), '#00000000'); }, + 'center is a transluscent greyish red'(topic) { assert.equal(topic.f(0.5).hex(), '#c45c4480'); } }, 'four color cubic bezier interpolation': { topic: { - f: chroma.bezier(['white', 'yellow', 'red', 'black']) + f: chroma.bezier(['white', chroma('yellow').alpha(1/3), chroma('red').alpha(2/3), chroma('black').alpha(0)]) }, 'starts from white'(topic) { assert.equal(topic.f(0).hex(), '#ffffff'); }, - 'ends in black'(topic) { assert.equal(topic.f(1).hex(), '#000000'); }, - '1st quarter'(topic) { assert.equal(topic.f(0.25).hex(), '#ffe085'); }, - 'center'(topic) { assert.equal(topic.f(0.5).hex(), '#e69735'); }, - '3rd quarter'(topic) { assert.equal(topic.f(0.75).hex(), '#914213'); } + 'ends in transparent black'(topic) { assert.equal(topic.f(1).hex(), '#00000000'); }, + '1st quarter'(topic) { assert.equal(topic.f(0.25).hex(), '#ffe085a7'); }, + 'center'(topic) { assert.equal(topic.f(0.5).hex(), '#e6973580'); }, + '3rd quarter'(topic) { assert.equal(topic.f(0.75).hex(), '#91421358'); } }, 'five color diverging quadratic bezier interpolation': { topic: { - f: chroma.bezier(['darkred', 'orange', 'snow', 'lightgreen', 'royalblue']) + f: chroma.bezier(['darkred', chroma('orange').alpha(.75), chroma('snow').alpha(.5), chroma('lightgreen').alpha(.25), chroma('royalblue').alpha(0)]) }, 'starts from darkred'(topic) { assert.equal(topic.f(0).hex(), '#8b0000'); }, - 'ends in royalblue'(topic) { assert.equal(topic.f(1).hex(), '#4169e1'); }, - 'center is snow'(topic) { assert.equal(topic.f(0.5).hex(), '#fffafa'); }, - '1st quarter'(topic) { assert.equal(topic.f(0.25).hex(), '#e9954e'); }, - '3rd quarter'(topic) { assert.equal(topic.f(0.75).hex(), '#a6cfc1'); } + 'ends in transparent royalblue'(topic) { assert.equal(topic.f(1).hex(), '#4169e100'); }, + 'center is snow'(topic) { assert.equal(topic.f(0.5).hex(), '#fffafa80'); }, + '1st quarter'(topic) { assert.equal(topic.f(0.25).hex(), '#e9954ebf'); }, + '3rd quarter'(topic) { assert.equal(topic.f(0.75).hex(), '#a6cfc140'); } }, 'using bezier in a chroma.scale': {