diff --git a/test/env-assert.js b/test/env-assert.js index 637503e2..d5ba00da 100644 --- a/test/env-assert.js +++ b/test/env-assert.js @@ -34,7 +34,7 @@ assert.hslEqual = function(actual, h, s, l, message) { assert.pathEqual = function(actual, expected, message) { if (!pathEqual(actual, expected)) { - assert.fail(actual, expected, message || "expected {expected}, got {actual}", null, assert.pathEqual); + assert.fail(formatPath(actual), formatPath(expected), message || "expected {expected}, got {actual}", null, assert.pathEqual); } }; @@ -57,16 +57,25 @@ function pathEqual(a, b) { return true; } +var reNumber = /[-+]?(?:\d+\.\d+|\d+\.|\.\d+|\d+)(?:[eE][-]?\d+)?/g; + function parsePath(path) { - var re = /[-+]?(?:\d+\.\d+|\d+\.|\.\d+|\d+)(?:[eE][-]?\d+)?/g, parts = []; - for (var i = 0, s0 = 0, s1, m; m = re.exec(path); ++i) { + var parts = []; + reNumber.lastIndex = 0; + for (var i = 0, s0 = 0, s1, m; m = reNumber.exec(path); ++i) { if (m.index) { var part = path.substring(s0, s1 = m.index); if (!/^[, ]$/.test(part)) parts.push(part); } parts.push(parseFloat(m[0])); - s0 = re.lastIndex; + s0 = reNumber.lastIndex; } if (s0 < path.length) parts.push(path.substring(s0)); return parts; } + +function formatPath(path) { + return path.replace(reNumber, function(s) { + return Math.abs((s = +s) - Math.floor(s)) < 1e-6 ? Math.floor(s) : s.toFixed(6); + }); +} diff --git a/test/svg/area-radial-test.js b/test/svg/area-radial-test.js new file mode 100644 index 00000000..f21e8d04 --- /dev/null +++ b/test/svg/area-radial-test.js @@ -0,0 +1,200 @@ +require("../env"); +require("../../d3"); + +var vows = require("vows"), + assert = require("assert"); + +var suite = vows.describe("d3.svg.area.radial"); + +suite.addBatch({ + "area.radial": { + topic: function() { + return d3.svg.area.radial; + }, + + "radius is an alias for setting innerRadius and outerRadius": function(area) { + var a = area().radius(f); + function f() {} + assert.equal(a.radius(), f); + assert.equal(a.innerRadius(), f); + assert.equal(a.outerRadius(), f); + }, + "radius is an alias for getting outerRadius": function(area) { + var a = area().outerRadius(f); + function f() {} + assert.equal(a.radius(), f); + }, + + "angle is an alias for setting startAngle and endAngle": function(area) { + var a = area().angle(f); + function f() {} + assert.equal(a.angle(), f); + assert.equal(a.startAngle(), f); + assert.equal(a.endAngle(), f); + }, + "angle is an alias for getting endAngle": function(area) { + var a = area().endAngle(f); + function f() {} + assert.equal(a.angle(), f); + }, + + "innerRadius defaults to a function accessor": function(area) { + var a = area(); + assert.pathEqual(a([[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-10L16.829420,-10.806046L18.185949,8.322937L1.411200,9.899925L0,-10L0,-20L0,-20L0,-10Z"); + assert.typeOf(a.innerRadius(), "function"); + }, + "innerRadius can be defined as a constant": function(area) { + var a = area().innerRadius(30); + assert.pathEqual(a([[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-10L16.829420,-10.806046L18.185949,8.322937L1.411200,9.899925L0,-30L0,-30L0,-30L0,-30Z"); + assert.equal(a.innerRadius(), 30); + }, + "innerRadius can be defined as a function": function(area) { + var a = area().innerRadius(f), t = {}, dd = [], ii = [], tt = []; + function f(d, i) { dd.push(d); ii.push(i); tt.push(this); return 30; } + assert.pathEqual(a.call(t, [[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-10L16.829420,-10.806046L18.185949,8.322937L1.411200,9.899925L0,-30L0,-30L0,-30L0,-30Z"); + assert.deepEqual(dd, [[10, 0], [20, 1], [20, 2], [10, 3]], "expected data, got {actual}"); + assert.deepEqual(ii, [0, 1, 2, 3], "expected index, got {actual}"); + assert.deepEqual(tt, [t, t, t, t], "expected this, got {actual}"); + }, + + "outerRadius defaults to a function accessor": function(area) { + var a = area(); + assert.pathEqual(a([[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-10L16.829420,-10.806046L18.185949,8.322937L1.411200,9.899925L0,-10L0,-20L0,-20L0,-10Z"); + assert.typeOf(a.outerRadius(), "function"); + }, + "outerRadius can be defined as a constant": function(area) { + var a = area().outerRadius(30); + assert.pathEqual(a([[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-30L25.244130,-16.209069L27.278923,12.484405L4.233600,29.699775L0,-10L0,-20L0,-20L0,-10Z"); + assert.equal(a.outerRadius(), 30); + }, + "outerRadius can be defined as a function": function(area) { + var a = area().outerRadius(f), t = {}, dd = [], ii = [], tt = []; + function f(d, i) { dd.push(d); ii.push(i); tt.push(this); return 30; } + assert.pathEqual(a.call(t, [[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-30L25.244130,-16.209069L27.278923,12.484405L4.233600,29.699775L0,-10L0,-20L0,-20L0,-10Z"); + assert.deepEqual(dd, [[10, 0], [20, 1], [20, 2], [10, 3]], "expected data, got {actual}"); + assert.deepEqual(ii, [0, 1, 2, 3], "expected index, got {actual}"); + assert.deepEqual(tt, [t, t, t, t], "expected this, got {actual}"); + }, + + "startAngle defaults to zero": function(area) { + var a = area(); + assert.pathEqual(a([[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-10L16.829420,-10.806046L18.185949,8.322937L1.411200,9.899925L0,-10L0,-20L0,-20L0,-10Z"); + assert.equal(a.startAngle(), 0); + }, + "startAngle can be defined as a constant": function(area) { + var a = area().startAngle(Math.PI / 2); + assert.pathEqual(a([[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-10L16.829420,-10.806046L18.185949,8.322937L1.411200,9.899925L10,0L20,0L20,0L10,0Z"); + assert.equal(a.startAngle(), Math.PI / 2); + }, + "startAngle can be defined as a function": function(area) { + var a = area().startAngle(f), t = {}, dd = [], ii = [], tt = []; + function f(d, i) { dd.push(d); ii.push(i); tt.push(this); return Math.PI / 2; } + assert.pathEqual(a.call(t, [[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-10L16.829420,-10.806046L18.185949,8.322937L1.411200,9.899925L10,0L20,0L20,0L10,0Z"); + assert.deepEqual(dd, [[10, 0], [20, 1], [20, 2], [10, 3]], "expected data, got {actual}"); + assert.deepEqual(ii, [0, 1, 2, 3], "expected index, got {actual}"); + assert.deepEqual(tt, [t, t, t, t], "expected this, got {actual}"); + }, + + "endAngle defaults to a function accessor": function(area) { + var a = area(); + assert.pathEqual(a([[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-10L16.829420,-10.806046L18.185949,8.322937L1.411200,9.899925L0,-10L0,-20L0,-20L0,-10Z"); + assert.typeOf(a.endAngle(), "function"); + }, + "endAngle can be defined as a constant": function(area) { + var a = area().endAngle(Math.PI / 2); + assert.pathEqual(a([[10, 0], [20, 1], [20, 2], [10, 3]]), "M10,0L20,0L20,0L10,0L0,-10L0,-20L0,-20L0,-10Z"); + assert.equal(a.endAngle(), Math.PI / 2); + }, + "endAngle can be defined as a function": function(area) { + var a = area().endAngle(f), t = {}, dd = [], ii = [], tt = []; + function f(d, i) { dd.push(d); ii.push(i); tt.push(this); return Math.PI / 2; } + assert.pathEqual(a.call(t, [[10, 0], [20, 1], [20, 2], [10, 3]]), "M10,0L20,0L20,0L10,0L0,-10L0,-20L0,-20L0,-10Z"); + assert.deepEqual(dd, [[10, 0], [20, 1], [20, 2], [10, 3]], "expected data, got {actual}"); + assert.deepEqual(ii, [0, 1, 2, 3], "expected index, got {actual}"); + assert.deepEqual(tt, [t, t, t, t], "expected this, got {actual}"); + }, + + "if innerRadius === outerRadius, radius is only evaluated once per point": function(area) { + var a = area().radius(f), t = {}, dd = [], ii = [], tt = []; + function f(d, i) { dd.push(d); ii.push(i); tt.push(this); return 30; } + assert.pathEqual(a.call(t, [[10, 0], [20, 1], [20, 2], [10, 3]]), "M0,-30L25.244130,-16.209069L27.278923,12.484405L4.233600,29.699775L0,-30L0,-30L0,-30L0,-30Z"); + assert.deepEqual(dd, [[10, 0], [20, 1], [20, 2], [10, 3]], "expected data, got {actual}"); + assert.deepEqual(ii, [0, 1, 2, 3], "expected index, got {actual}"); + assert.deepEqual(tt, [t, t, t, t], "expected this, got {actual}"); + }, + "if startAngle === endAngle, angle is only evaluated once per point": function(area) { + var a = area().angle(f), t = {}, dd = [], ii = [], tt = []; + function f(d, i) { dd.push(d); ii.push(i); tt.push(this); return Math.PI / 2; } + assert.pathEqual(a.call(t, [[10, 0], [20, 1], [20, 2], [10, 3]]), "M10,0L20,0L20,0L10,0L10,0L20,0L20,0L10,0Z"); + assert.deepEqual(dd, [[10, 0], [20, 1], [20, 2], [10, 3]], "expected data, got {actual}"); + assert.deepEqual(ii, [0, 1, 2, 3], "expected index, got {actual}"); + assert.deepEqual(tt, [t, t, t, t], "expected this, got {actual}"); + }, + + "interpolate defaults to linear": function(area) { + assert.equal(area().interpolate(), "linear"); + }, + "interpolate can be defined as a constant": function(area) { + var a = area().interpolate("step-before"); + assert.pathEqual(a([[0, 0], [1, 1]]), "M0,0V-0.540302H0.841471L0,-1V0H0Z"); + assert.equal(a.interpolate(), "step-before"); + }, + + "tension defaults to .7": function(area) { + assert.equal(area().tension(), .7); + }, + "tension can be specified as a constant": function(area) { + var a = area().tension(.5); + assert.equal(a.tension(), .5); + }, + + "returns null if input points array is empty": function(area) { + assert.isNull(area()([])); + }, + + "interpolate(linear)": { + "supports linear interpolation": testInterpolation("linear") + }, + + "interpolate(step)": { + "supports step-before interpolation": testInterpolation("step-before"), + "supports step-after interpolation": testInterpolation("step-after") + }, + + "interpolate(basis)": { + "supports basis interpolation": testInterpolation("basis"), + "supports basis-open interpolation": testInterpolation("basis-open") + }, + + "interpolate(cardinal)": { + "supports cardinal interpolation": testInterpolation("cardinal"), + "supports cardinal-open interpolation": testInterpolation("cardinal-open") + }, + + "interpolate(monotone)": { + "supports monotone interpolation": testInterpolation("monotone") + } + } +}); + +// A radial area is just a transformation of a Cartesian line. +function testInterpolation(interpolate) { + var data = [[10, 0], [20, 1], [20, 2], [10, 3]]; + + var radial = d3.svg.area.radial() + .innerRadius(function(d) { return d[0]; }) + .outerRadius(function(d) { return d[0] * 2; }) + .angle(function(d) { return d[1]; }); + + var cartesian = d3.svg.area() + .x0(function(d) { return d[0] * Math.cos(d[1] - Math.PI / 2); }) + .x1(function(d) { return 2 * d[0] * Math.cos(d[1] - Math.PI / 2); }) + .y0(function(d) { return d[0] * Math.sin(d[1] - Math.PI / 2); }) + .y1(function(d) { return 2 * d[0] * Math.sin(d[1] - Math.PI / 2); }); + + return function() { + assert.pathEqual(radial.interpolate(interpolate)(data), cartesian.interpolate(interpolate)(data)); + }; +} + +suite.export(module); diff --git a/test/svg/area-test.js b/test/svg/area-test.js index 2d047fb2..706ba9b9 100644 --- a/test/svg/area-test.js +++ b/test/svg/area-test.js @@ -135,17 +135,17 @@ suite.addBatch({ assert.equal(area().interpolate(), "linear"); }, "interpolate can be defined as a constant": function(area) { - var l = area().interpolate("step-before"); - assert.pathEqual(l([[0, 0], [1, 1]]), "M0,0V1H1L1,0V0H0Z"); - assert.equal(l.interpolate(), "step-before"); + var a = area().interpolate("step-before"); + assert.pathEqual(a([[0, 0], [1, 1]]), "M0,0V1H1L1,0V0H0Z"); + assert.equal(a.interpolate(), "step-before"); }, "tension defaults to .7": function(area) { assert.equal(area().tension(), .7); }, "tension can be specified as a constant": function(area) { - var l = area().tension(.5); - assert.equal(l.tension(), .5); + var a = area().tension(.5); + assert.equal(a.tension(), .5); }, "returns null if input points array is empty": function(area) { diff --git a/test/svg/line-radial-test.js b/test/svg/line-radial-test.js index 96c68491..9e0855a6 100644 --- a/test/svg/line-radial-test.js +++ b/test/svg/line-radial-test.js @@ -111,12 +111,14 @@ suite.addBatch({ function testInterpolation(interpolate) { var data = [[10, 0], [20, 1], [20, 2], [10, 3]]; + var radial = d3.svg.line.radial(); + var cartesian = d3.svg.line() .x(function(d) { return d[0] * Math.cos(d[1] - Math.PI / 2); }) .y(function(d) { return d[0] * Math.sin(d[1] - Math.PI / 2); }); - return function(line) { - assert.pathEqual(line().interpolate(interpolate)(data), cartesian.interpolate(interpolate)(data)); + return function() { + assert.pathEqual(radial.interpolate(interpolate)(data), cartesian.interpolate(interpolate)(data)); }; }