import “../core/identity”; import “../core/rebind”; import “../math/trigonometry”; import “clip-antimeridian”; import “clip-circle”; import “clip-view”; import “compose”; import “geo”; import “path”; import “resample”; import “rotation”; import “stream”;

d3.geo.projection = d3_geo_projection; d3.geo.projectionMutator = d3_geo_projectionMutator;

function d3_geo_projection(project) {

return d3_geo_projectionMutator(function() { return project; })();

}

function d3_geo_projectionMutator(projectAt) {

var project,
    rotate,
    projectRotate,
    projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }),
    k = 150, // scale
    x = 480, y = 250, // translate
    λ = 0, φ = 0, // center
    δλ = 0, δφ = 0, δγ = 0, // rotate
    δx, δy, // center
    preclip = d3_geo_clipAntimeridian,
    postclip = d3_identity,
    clipAngle = null,
    clipExtent = null,
    stream;

function projection(point) {
  point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
  return [point[0] * k + δx, δy - point[1] * k];
}

function invert(point) {
  point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
  return point && [point[0] * d3_degrees, point[1] * d3_degrees];
}

projection.stream = function(output) {
  if (stream) stream.valid = false;
  stream = d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(output))));
  stream.valid = true; // allow caching by d3.geo.path
  return stream;
};

projection.clipAngle = function(_) {
  if (!arguments.length) return clipAngle;
  preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
  return invalidate();
};

projection.clipExtent = function(_) {
  if (!arguments.length) return clipExtent;
  clipExtent = _;
  postclip = _ == null ? d3_identity : d3_geo_clipView(_[0][0], _[0][1], _[1][0], _[1][1]);
  return invalidate();
};

projection.scale = function(_) {
  if (!arguments.length) return k;
  k = +_;
  return reset();
};

projection.translate = function(_) {
  if (!arguments.length) return [x, y];
  x = +_[0];
  y = +_[1];
  return reset();
};

projection.center = function(_) {
  if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees];
  λ = _[0] % 360 * d3_radians;
  φ = _[1] % 360 * d3_radians;
  return reset();
};

projection.rotate = function(_) {
  if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees];
  δλ = _[0] % 360 * d3_radians;
  δφ = _[1] % 360 * d3_radians;
  δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
  return reset();
};

d3.rebind(projection, projectResample, "precision");

function reset() {
  projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
  var center = project(λ, φ);
  δx = x - center[0] * k;
  δy = y + center[1] * k;
  return invalidate();
}

function invalidate() {
  if (stream) {
    stream.valid = false;
    stream = null;
  }
  return projection;
}

return function() {
  project = projectAt.apply(this, arguments);
  projection.invert = project.invert && invert;
  return reset();
};

}

function d3_geo_projectionRadiansRotate(rotate, stream) {

return {
  point: function(x, y) {
    y = rotate(x * d3_radians, y * d3_radians), x = y[0];
    stream.point(x > π ? x - 2 * π : x < -π ? x + 2 * π : x, y[1]);
  },
  sphere: function() { stream.sphere(); },
  lineStart: function() { stream.lineStart(); },
  lineEnd: function() { stream.lineEnd(); },
  polygonStart: function() { stream.polygonStart(); },
  polygonEnd: function() { stream.polygonEnd(); }
};

}