diff options
author | Kristian Lyngstol <kristian@bohemians.org> | 2015-04-02 19:24:45 +0200 |
---|---|---|
committer | Kristian Lyngstol <kristian@bohemians.org> | 2015-04-02 19:24:45 +0200 |
commit | 0d8bba263dc195147d6fdb09662e7926f0a58b3e (patch) | |
tree | 4c570b4376c323e585120e7695b8715be7aa8881 /web/nms.gathering.org/speedometer/d3-master/test/math | |
parent | e4354b47bd8891c5b1ee591fdf74b3ca67eee461 (diff) |
Bump lots of changes
Diffstat (limited to 'web/nms.gathering.org/speedometer/d3-master/test/math')
6 files changed, 450 insertions, 0 deletions
diff --git a/web/nms.gathering.org/speedometer/d3-master/test/math/random-test.js b/web/nms.gathering.org/speedometer/d3-master/test/math/random-test.js new file mode 100644 index 0000000..1e00ec9 --- /dev/null +++ b/web/nms.gathering.org/speedometer/d3-master/test/math/random-test.js @@ -0,0 +1,132 @@ +var vows = require("vows"), + load = require("../load"), + assert = require("../assert"), + seedrandom = require("seedrandom"); + +var suite = vows.describe("d3.random"); + +var _random; + +// Testing a random number generator is a bit more complicated than testing +// deterministic code, so we use different techniques. +// +// If the RNG is correct, each test in this suite will pass with probability +// at least P. The tests have been designed so that P ≥ 98%. Specific values +// of P are given above each case. We use the seedrandom module to get around +// this non-deterministic aspect -- so it is safe to assume that if the tests +// fail, then d3's RNG is broken. +// +// See also: http://www.johndcook.com/Beautiful_Testing_ch10.pdf + +suite.addBatch({ + "random": { + topic: load("math/random").sandbox({Math: Math}).expression("d3.random"), + "(using seedrandom)": { + topic: function(random) { + _random = Math.random; + Math.seedrandom("a random seed."); + return random; + }, + "normal": { + "topic": function(random) { return random.normal(-43289, 38.8); }, + "has normal distribution": KSTest(normalCDF(-43289, 38.8)) + }, + "logNormal": { + "topic": function(random) { return random.logNormal(10, 2.5); }, + "has log-normal distribution": KSTest(logNormalCDF(10, 2.5)) + }, + "irwinHall": { + "topic": function(random) { return random.irwinHall(10); }, + "has Irwin-Hall distribution": KSTest(irwinHallCDF(10)) + }, + teardown: function() { + Math.random = _random; + } + } + } +}); + +/** + * A macro that that takes a RNG and performs a Kolmogorov-Smirnov test: + * asserts that the values generated by the RNG could be generated by the + * distribution with cumulative distribution function `cdf'. Each test runs in + * O(n log n) * O(cdf). + * + * Passes with P≈98%. + * + * @param cdf function(x) { returns CDF of the distribution evaluated at x } + * @param n number of sample points. Higher n = better evaluation, slower test. + * @return a function that asserts the rng produces values fitting the distribution + */ +function KSTest(cdf, n) { + return function(rng) { + var n = 1000; + var values = []; + for (var i = 0; i < n; i++) { + values.push(rng()); + } + values.sort(function(a, b) { return a - b; }); + + K_positive = -Infinity; // Identity of max() function + for (var i = 0; i < n; i++) { + var edf_i = i / n; // Empirical distribution function evaluated at x=values[i] + K_positive = Math.max(K_positive, edf_i - cdf(values[i])); + } + K_positive *= Math.sqrt(n); + + // Derivation of this interval is difficult. + // @see K-S test in Knuth's AoCP vol.2 + assert.inDelta(K_positive, 0.723255, 0.794145); + }; +} + +// Logistic approximation to normal CDF around N(mean, stddev). +function normalCDF(mean, stddev) { + return function(x) { + return 1 / (1 + Math.exp(-0.07056 * Math.pow((x-mean)/stddev, 3) - 1.5976 * (x-mean)/stddev)); + }; +} + +// See http://en.wikipedia.org/wiki/Log-normal_distribution#Similar_distributions +function logNormalCDF(mean, stddev) { + var exponent = Math.PI / (stddev * Math.sqrt(3)); + var numerator = Math.exp(mean); + return function(x) { + return 1 / (Math.pow(numerator / x, exponent) + 1); + }; +} + +function irwinHallCDF(n) { + var normalisingFactor = factorial(n); + + // Precompute binom(n, k), k=0..n for efficiency. (this array gets stored + // inside the closure, so it is only computed once) + var binoms = []; + for (var k = 0; k <= n; k++) { + binoms.push(binom(n, k)); + } + + // See CDF at http://en.wikipedia.org/wiki/Irwin–Hall_distribution + return function(x) { + var t = 0; + for (var k = 0; k < x; k++) { + t += Math.pow(-1, k % 2) * binoms[k] * Math.pow(x - k, n); + } + return t / normalisingFactor; + }; +} + +function factorial(n) { + var t = 1; + for (var i = 2; i <= n; i++) { + t *= i; + } + return t; +} + +function binom(n, k) { + if (k < 0 || k > n) return undefined; // only defined for 0 <= k <= n + return factorial(n) / (factorial(k) * factorial(n - k)); +} + +suite.export(module); diff --git a/web/nms.gathering.org/speedometer/d3-master/test/math/transform-null-matrix-test.html b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-null-matrix-test.html new file mode 100644 index 0000000..7a5eec3 --- /dev/null +++ b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-null-matrix-test.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<body> +<script src="../../d3.js"></script> +<script> + +var svg = d3.select("body").append("svg") + .attr("width", 960) + .attr("height", 500); + +var g = svg.append("g") + .attr("transform", "translate(100,100)") + .append("g") + .attr("transform", "matrix(1 0 0 1 0 0)rotate(0)"); + +var rect = g.append("rect") + .attr("x", -25) + .attr("y", -50) + .attr("width", 50) + .attr("height", 100); + +g.transition() + .duration(3000) + .attrTween("transform", d3.tween("matrix(1 0 0 1 100 100)rotate(360)", d3.interpolateString)); + +</script> diff --git a/web/nms.gathering.org/speedometer/d3-master/test/math/transform-null-test.html b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-null-test.html new file mode 100644 index 0000000..6211680 --- /dev/null +++ b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-null-test.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<body> +<script src="../../d3.js"></script> +<script> + +var svg = d3.select("body").append("svg") + .attr("width", 960) + .attr("height", 500); + +var g = svg.append("g") + .attr("transform", "translate(100,100)") + .append("g") + .attr("transform", "translate(0,0)rotate(0)"); + +var rect = g.append("rect") + .attr("x", -25) + .attr("y", -50) + .attr("width", 50) + .attr("height", 100); + +g.transition() + .duration(3000) + .attrTween("transform", d3.tween("translate(100,100)rotate(360)", d3.interpolateString)); + +</script> diff --git a/web/nms.gathering.org/speedometer/d3-master/test/math/transform-rotate-origin-test.html b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-rotate-origin-test.html new file mode 100644 index 0000000..5a006f5 --- /dev/null +++ b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-rotate-origin-test.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<body> +<script src="../../d3.js"></script> +<script> + +var width = 960, + height = 500; + +var cross = d3.svg.symbol() + .type("cross") + .size(10000); + +var svg = d3.select("body").append("svg") + .attr("width", width) + .attr("height", height) + .append("g") + .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")"); + +svg.append("path") + .style("fill", "steelblue") + .attr("d", cross); + +svg.append("path") + .style("fill", "darkgreen") + .attr("d", cross) + .attr("transform", "rotate(45 200,200)"); + +svg.append("path") + .style("fill", "white") + .style("fill-opacity", .2) + .style("stroke", "steelblue") + .style("stroke-width", "4px") + .attr("d", cross) + .transition() + .duration(1000) + .style("stroke", "darkgreen") + .attr("transform", "rotate(45 200,200)"); + +</script> diff --git a/web/nms.gathering.org/speedometer/d3-master/test/math/transform-rotate-test.html b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-rotate-test.html new file mode 100644 index 0000000..49fb571 --- /dev/null +++ b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-rotate-test.html @@ -0,0 +1,138 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + +table { + width: 960px; + border-spacing: 0; + border-collapse: collapse; +} + +th, td { + padding: 4px; +} + +th { + text-align: left; +} + +td { + border: solid 1px #ccc; + text-align: right; +} + +td.fail { + background: lightcoral; +} + +td.success { + background: lightgreen; +} + +</style> +<table> + <thead> + <th>start</th> + <th>end</th> + <th colspan=5>actual intermediate values</th> + <th>exp.</th> + <th>act.</th> + </thead> + <tbody> + </tbody> +</table> +<script src="../../d3.js"></script> +<script> + +var format = d3.format(",.2f"); + +var tests = [ + {start: 170, end: 225, expected: [ 170.00, -176.25, -162.50, -148.75, -135.00]}, + {start: 225, end: 170, expected: [-135.00, -148.75, -162.50, -176.25, 170.00]}, + {start: -170, end: -225, expected: [-170.00, 176.25, 162.50, 148.75, 135.00]}, + {start: -225, end: -170, expected: [ 135.00, 148.75, 162.50, 176.25, -170.00]}, + {start: -170, end: 170, expected: [-170.00, -175.00, 180.00, 175.00, 170.00]}, + {start: -170, end: 0, expected: [-170.00, -127.50, -85.00, -42.50, 0.00]}, + {start: 170, end: 0, expected: [ 170.00, 127.50, 85.00, 42.50, 0.00]}, + {start: -180, end: 90, expected: [ 180.00, 157.50, 135.00, 112.50, 90.00]}, + {start: 180, end: 90, expected: [ 180.00, 157.50, 135.00, 112.50, 90.00]}, + {start: -180, end: -90, expected: [-180.00, -157.50, -135.00, -112.50, -90.00]}, + {start: 180, end: -90, expected: [ 180.00, -157.50, -135.00, -112.50, -90.00]}, + {start: 780, end: -90, expected: [ 60.00, 22.50, -15.00, -52.50, -90.00]} +]; + +var tr = d3.select("tbody").selectAll("tr") + .data(tests) + .enter().append("tr"); + +tr.append("td") + .text(function(d) { return format(d.start); }); + +tr.append("td") + .text(function(d) { return format(d.end); }); + +tr.selectAll(".actual") + .data(function(d) { + var interpolate = d3.interpolateTransform("rotate(" + d.start + ")", "rotate(" + d.end + ")"); + return d.expected.map(function(expected, i) { + return { + expected: expected, + actual: d3.transform(interpolate(i / 4)).rotate + }; + }); + }) + .enter().append("td") + .text(function(d, i) { return format(d.actual); }) + .attr("class", function(d) { return Math.abs(d.actual - d.expected) < .01 ? "success" : "fail"; }); + +var ga = tr.append("td").attr("width", 40).append("svg") + .attr("width", 40) + .attr("height", 20) + .append("g") + .attr("transform", "translate(20,10)") + .append("g") + .each(animateExpected); + +ga.append("path") + .attr("d", d3.svg.symbol().type("cross").size(120)); + +ga.append("circle") + .attr("cx", 8) + .attr("r", 4); + +var gb = tr.append("td").attr("width", 40).append("svg") + .attr("width", 40) + .attr("height", 20) + .append("g") + .attr("transform", "translate(20,10)") + .append("g") + .each(animateActual); + +gb.append("path") + .attr("d", d3.svg.symbol().type("cross").size(120)); + +gb.append("circle") + .attr("cx", 8) + .attr("r", 4); + +function animateExpected(d) { + d3.select(this).transition() + .duration(2500) + .attrTween("transform", function(d) { + var a = d.start % 360, b = d.end % 360; + if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path + return d3.interpolateString("rotate(" + a + ")", "rotate(" + b + ")"); + }) + .each("end", animateExpected); +} + +function animateActual(d) { + d3.select(this) + .attr("transform", "rotate(" + d.start + ")") + .transition() + .duration(2500) + .attr("transform", "rotate(" + d.end + ")") + .each("end", animateActual); +} + +</script> diff --git a/web/nms.gathering.org/speedometer/d3-master/test/math/transform-test.html b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-test.html new file mode 100644 index 0000000..7e313d7 --- /dev/null +++ b/web/nms.gathering.org/speedometer/d3-master/test/math/transform-test.html @@ -0,0 +1,88 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Transform Test</title> +<style> + +th, +td { + border: solid #ccc 1px; +} + +</style> +<body> +<script src="../../d3.js"></script> +<script> + +var outcome = d3.select("body").append("p"); + +var g = d3.select("body").append("svg") + .attr("width", 10) + .attr("height", 10) + .append("g"); + +var results = d3.select("body").append("table") + .style("display", "none"); + +results.append("tr").selectAll("th") + .data(["Expected", "Actual", "Expected matrix", "Actual matrix"]) + .enter().append("th") + .text(function(d) { return d; }); + +var el = g[0][0], + v, + m, + a, + b, + failures = 0; + +for (var tx = -10; tx <= 10; tx += 5) { + for (var ty = -10; ty <= 10; ty += 5) { + for (var r = -180; r <= 180; r += 15) { + for (var skx = -45; skx <= 45; skx += 45) { + for (var sx = -2; sx <= 2; sx++) { + for (var sy = -2; sy <= 2; sy++) { + v = "translate(" + tx + "," + ty + ")rotate(" + r + ")skewX(" + skx + ")scale(" + sx + "," + sy + ")"; + g.attr("transform", v); + a = matrix(el); + g.attr("transform", d3.transform(v)); + b = matrix(el); + if (!deepEqual(a, b, 1e-6)) { + failures++; + results + .style("display", null) + .append("tr").selectAll("td") + .data([v, d3.transform(v), a, b]) + .enter().append("td") + .text(function(d) { return d; }); + } + } + } + } + } + } +} + +outcome.text(failures ? failures + " failures" : "Success!"); + +function matrix(el) { + var t = el.transform.baseVal.consolidate(); + if (t) { + var m = t.matrix; + return [m.a, m.b, m.c, m.d, m.e, m.f]; + } + return null; +} + +function deepEqual(actual, expected, epsilon) { + epsilon = epsilon || 0; + if (actual === expected) return true; + if (actual == null || expected == null) return false; + if (actual.length !== expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (Math.abs(actual[i] - expected[i]) > epsilon) return false; + } + return true; +} + +</script> |