var vows = require(“vows”),

load = require("../load"),
assert = require("../assert");

var suite = vows.describe(“d3.geom.voronoi”);

suite.addBatch({

"voronoi": {
  topic: load("geom/voronoi").expression("d3.geom.voronoi"),

  "the default voronoi layout": {
    topic: function(voronoi) {
      return voronoi();
    },
    "has no defined clip extent": function(v) {
      assert.isNull(v.clipExtent());
    },
    "has no defined size": function(v) {
      assert.isNull(v.size());
    },
    "returns the configured clip extent": function(v) {
      try {
        assert.deepEqual(v.clipExtent([[1, 2], [3, 4]]).clipExtent(), [[1, 2], [3, 4]]);
      } finally {
        v.clipExtent(null);
      }
    },
    "returns the configured size": function(v) {
      try {
        assert.deepEqual(v.size([1, 2]).size(), [1, 2]);
      } finally {
        v.size(null);
      }
    },
    "size implies a clip extent from [0, 0]": function(v) {
      try {
        assert.deepEqual(v.size([1, 2]).clipExtent(), [[0, 0], [1, 2]]);
      } finally {
        v.size(null);
      }
    },
    "clip extent implies a size, assuming [0, 0]": function(v) {
      try {
        assert.deepEqual(v.clipExtent([[1, 2], [3, 4]]).size(), [3, 4]);
      } finally {
        v.clipExtent(null);
      }
    },
    "has the default x-accessor, d[0]": function(v) {
      assert.strictEqual(v.x()([42, 43]), 42);
    },
    "has the default y-accessor, d[1]": function(v) {
      assert.strictEqual(v.y()([42, 43]), 43);
    },
    "of two points": {
      topic: function(v) {
        return v([[200, 200], [760, 300]]);
      },
      "returns two cells with the expected geometry": function(cells) {
        assert.inDelta(cells, [
          [[-178046.7857142857, 1e6], [179096.07142857145, -1e6], [-1e6, 1e6], [1e6, 1e6]],
          [[-178046.7857142857, 1e6], [179096.07142857145, -1e6], [-1e6, -1e6], [1e6, -1e6]]
        ], 1e-6);
      },
      "the returned cells are open polygons": function(cells) {
        cells.forEach(function(cell) {
          assert.greater(cell.length, 2);
          cell.forEach(function(point) {
            assert.equal(point.length, 2);
            assert.isNumber(point[0]);
            assert.isNumber(point[1]);
          });
          assert.notDeepEqual(cell[0], cell[cell.length - 1]);
        });
      },
      "the returned cells are not clipped to the layout size": function(cells) {
        var x0 = Infinity, x1 = -Infinity, y0 = Infinity, y1 = -Infinity;
        cells.forEach(function(cell) {
          cell.forEach(function(point) {
            if (point[0] < x0) x0 = point[0];
            if (point[0] > x1) x1 = point[0];
            if (point[1] < y0) y0 = point[1];
            if (point[1] > y1) y1 = point[1];
          });
        });
        assert.strictEqual(x0, -1e6);
        assert.strictEqual(x1, 1e6);
        assert.strictEqual(y0, -1e6);
        assert.strictEqual(y1, 1e6);
      },
      "the returned cells' point property points back to the input point": function(cells) {
        assert.deepEqual(cells.map(function(cell) { return cell.point; }), [[200, 200], [760, 300]]);
      }
    },
    "links": {
      "for two points": function(v) {
        assert.deepEqual(v.links([[200, 200], [760, 300]]), [
          {source: [200, 200], target: [760, 300]}
        ]);
      },
      "for three points": function(v) {
        assert.deepEqual(v.links([[200, 200], [500, 250], [760, 300]]), [
          {source: [200, 200], target: [760, 300]},
          {source: [500, 250], target: [760, 300]},
          {source: [200, 200], target: [500, 250]}
        ]);
      }
    },
    "triangles": {
      "for three points": function(v) {
        assert.deepEqual(asArray(v.triangles([[200, 200], [500, 250], [760, 300]])), [
          [[200, 200], [760, 300], [500, 250]]
        ]);
      }
    }
  },

  "a voronoi layout with custom x- and y-accessors": {
    topic: function(voronoi) {
      return voronoi()
          .x(function(d) { return d.x; })
          .y(43);
    },
    "observes the specified x-accessor, a function": function(v) {
      assert.strictEqual(v.x()({x: 42, y: 43}), 42);
    },
    "observes the specified y-accessor, a constant": function(v) {
      assert.strictEqual(v.y(), 43);
    },
    "of two points": {
      topic: function(v) {
        return v([{x: 200}, {x: 760}]);
      },
      "returns two cells with the expected geometry": function(cells) {
        assert.inDelta(cells, [
          [[480, 1e6], [480, -1e6], [-1e6, -1e6], [-1e6, 1e6]],
          [[480, -1e6], [480, 1e6], [1e6, -1e6], [1e6, 1e6]]
        ], 1e-6);
      }
    },
    "links": {
      topic: function(v) {
        return v.y(function(d) { return d.y; });
      },
      "for two points": function(v) {
        assert.deepEqual(v.links([{x: 200, y: 200}, {x: 760, y: 300}]), [
          {source: {x: 200, y: 200}, target: {x: 760, y: 300}}
        ]);
      },
      "for three points": function(v) {
        assert.deepEqual(v.links([{x: 200, y: 200}, {x: 500, y: 250}, {x: 760, y: 300}]), [
          {source: {x: 200, y: 200}, target: {x: 760, y: 300}},
          {source: {x: 500, y: 250}, target: {x: 760, y: 300}},
          {source: {x: 200, y: 200}, target: {x: 500, y: 250}}
        ]);
      }
    }
  },

  "a voronoi layout with clip extent [[0, 0], [960, 500]]": {
    topic: function(voronoi) {
      return voronoi()
          .x(function(d) { return d.x; })
          .y(function(d) { return d.y; })
          .clipExtent([[0, 0], [960, 500]]);
    },
    "of two points": {
      topic: function(v) {
        return v([{x: 200, y: 200}, {x: 760, y: 300}]);
      },
      "returns two cells with the expected geometry": function(cells) {
        assert.inDelta(cells, [
          [[435.35714285715324, 500], [524.6428571428696, 0], [0, 0], [0, 500]],
          [[960, 0], [960, 500], [435.35714285715324, 500], [524.6428571428696, 0]]
        ], 1e-6);
      },
      "the returned cells are clipped to the layout size": function(cells) {
        assert.isTrue(cells.every(function(cell) {
          return cell.every(function(point) {
            return point[0] >= 0 && point[0] <= 960
                && point[1] >= 0 && point[1] <= 500;
          });
        }));
      }
    }
  },

  "the default voronoi layout applied directly": {
    "with zero points": {
      "returns the empty array": function(voronoi) {
        assert.deepEqual(voronoi([]), []);
      }
    },
    "with one point": {
      "returns the semi-infinite bounding box": function(voronoi) {
        assert.deepEqual(asArray(voronoi([[50, 50]], 100, 100)), [[[-1000000,-1000000],[-1000000,1000000],[1000000,1000000],[1000000,-1000000]]]);
      }
    },
    "with two points": {
      "separated by a line at 90° (vertical)": function(voronoi) {
        assert.deepEqual(asArray(voronoi([[50, 25], [50, 75]], 100, 100)), [[[-1000000,50],[1000000,50],[-1000000,-1000000],[1000000,-1000000]],[[-1000000,50],[1000000,50],[-1000000,1000000],[1000000,1000000]]]);
        assert.deepEqual(asArray(voronoi([[50, 75], [50, 25]], 100, 100)), [[[-1000000,50],[1000000,50],[-1000000,1000000],[1000000,1000000]],[[-1000000,50],[1000000,50],[-1000000,-1000000],[1000000,-1000000]]]);
      },
      "separated by a line at 0° (horizontal)": function(voronoi) {
        assert.deepEqual(asArray(voronoi([[25, 50], [75, 50]], 100, 100)), [[[50,1000000],[50,-1000000],[-1000000,-1000000],[-1000000,1000000]],[[50,-1000000],[50,1000000],[1000000,-1000000],[1000000,1000000]]]);
        assert.deepEqual(asArray(voronoi([[75, 50], [25, 50]], 100, 100)), [[[50,-1000000],[50,1000000],[1000000,-1000000],[1000000,1000000]],[[50,1000000],[50,-1000000],[-1000000,-1000000],[-1000000,1000000]]]);
      },
      "separated by a line at 45° (diagonal)": function(voronoi) {
        assert.deepEqual(asArray(voronoi([[25, 25], [75, 75]], 100, 100)), [[[-999900,1000000],[1000100,-1000000],[-1000000,-1000000]],[[-999900,1000000],[1000100,-1000000],[1000000,1000000]]]);
        assert.deepEqual(asArray(voronoi([[75, 25], [25, 75]], 100, 100)), [[[-1000000,-1000000],[1000000,1000000],[1000000,-1000000]],[[-1000000,-1000000],[1000000,1000000],[-1000000,1000000]]]);
      },
      "separated by an arbitrary diagonal": function(voronoi) {
        assert.deepEqual(asArray(voronoi([[25, 25], [50, 75]], 100, 100)), [[[-1000000,500068.75],[1000000,-499931.25],[-1000000,-1000000],[1000000,-1000000]],[[-1000000,500068.75],[1000000,-499931.25],[-1000000,1000000],[1000000,1000000]]]);
        assert.deepEqual(asArray(voronoi([[25, 25], [75, 50]], 100, 100)), [[[-499931.25,1000000],[500068.75,-1000000],[-1000000,1000000],[1000000,1000000]], [[-499931.25,1000000],[500068.75,-1000000],[-1000000,-1000000],[1000000,-1000000]]]);
      }
    },
    "with three points": {
      "collinear": function(voronoi) {
        assert.deepEqual(asArray(voronoi([[25, 25], [50, 50], [75, 75]], 100, 100)), [[[-999925,1000000],[1000075,-1000000],[-1000000,-1000000]],[[-999925,1000000],[-999875,1000000],[1000125,-1000000],[1000075,-1000000]],[[-999875,1000000],[1000125,-1000000],[1000000,1000000]]]);
      }
    }
  }
}

});

suite.export(module);

function asArray(array) {

return Array.isArray(array) ? array.map(asArray) : array;

}