import “../core/noop”; import “../math/trigonometry”; import “geo”; import “stream”;

d3.geo.centroid = function(object) {

d3_geo_centroidW0 = d3_geo_centroidW1 =
d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
d3.geo.stream(object, d3_geo_centroid);

var x = d3_geo_centroidX2,
    y = d3_geo_centroidY2,
    z = d3_geo_centroidZ2,
    m = x * x + y * y + z * z;

// If the area-weighted centroid is undefined, fall back to length-weighted centroid.
if (m < ε2) {
  x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
  // If the feature has zero length, fall back to arithmetic mean of point vectors.
  if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
  m = x * x + y * y + z * z;
  // If the feature still has an undefined centroid, then return.
  if (m < ε2) return [NaN, NaN];
}

return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees];

};

var d3_geo_centroidW0,

d3_geo_centroidW1,
d3_geo_centroidX0,
d3_geo_centroidY0,
d3_geo_centroidZ0,
d3_geo_centroidX1,
d3_geo_centroidY1,
d3_geo_centroidZ1,
d3_geo_centroidX2,
d3_geo_centroidY2,
d3_geo_centroidZ2;

var d3_geo_centroid = {

sphere: d3_noop,
point: d3_geo_centroidPoint,
lineStart: d3_geo_centroidLineStart,
lineEnd: d3_geo_centroidLineEnd,
polygonStart: function() {
  d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
},
polygonEnd: function() {
  d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
}

};

// Arithmetic mean of Cartesian vectors. function d3_geo_centroidPoint(λ, φ) {

λ *= d3_radians;
var cosφ = Math.cos(φ *= d3_radians);
d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));

}

function d3_geo_centroidPointXYZ(x, y, z) {

++d3_geo_centroidW0;
d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;

}

function d3_geo_centroidLineStart() {

var x0, y0, z0; // previous point

d3_geo_centroid.point = function(λ, φ) {
  λ *= d3_radians;
  var cosφ = Math.cos(φ *= d3_radians);
  x0 = cosφ * Math.cos(λ);
  y0 = cosφ * Math.sin(λ);
  z0 = Math.sin(φ);
  d3_geo_centroid.point = nextPoint;
  d3_geo_centroidPointXYZ(x0, y0, z0);
};

function nextPoint(λ, φ) {
  λ *= d3_radians;
  var cosφ = Math.cos(φ *= d3_radians),
      x = cosφ * Math.cos(λ),
      y = cosφ * Math.sin(λ),
      z = Math.sin(φ),
      w = Math.atan2(
        Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w),
        x0 * x + y0 * y + z0 * z);
  d3_geo_centroidW1 += w;
  d3_geo_centroidX1 += w * (x0 + (x0 = x));
  d3_geo_centroidY1 += w * (y0 + (y0 = y));
  d3_geo_centroidZ1 += w * (z0 + (z0 = z));
  d3_geo_centroidPointXYZ(x0, y0, z0);
}

}

function d3_geo_centroidLineEnd() {

d3_geo_centroid.point = d3_geo_centroidPoint;

}

// See J. E. Brock, The Inertia Tensor for a Spherical Triangle, // J. Applied Mechanics 42, 239 (1975). function d3_geo_centroidRingStart() {

var λ00, φ00, // first point
    x0, y0, z0; // previous point

d3_geo_centroid.point = function(λ, φ) {
  λ00 = λ, φ00 = φ;
  d3_geo_centroid.point = nextPoint;
  λ *= d3_radians;
  var cosφ = Math.cos(φ *= d3_radians);
  x0 = cosφ * Math.cos(λ);
  y0 = cosφ * Math.sin(λ);
  z0 = Math.sin(φ);
  d3_geo_centroidPointXYZ(x0, y0, z0);
};

d3_geo_centroid.lineEnd = function() {
  nextPoint(λ00, φ00);
  d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
  d3_geo_centroid.point = d3_geo_centroidPoint;
};

function nextPoint(λ, φ) {
  λ *= d3_radians;
  var cosφ = Math.cos(φ *= d3_radians),
      x = cosφ * Math.cos(λ),
      y = cosφ * Math.sin(λ),
      z = Math.sin(φ),
      cx = y0 * z - z0 * y,
      cy = z0 * x - x0 * z,
      cz = x0 * y - y0 * x,
      m = Math.sqrt(cx * cx + cy * cy + cz * cz),
      u = x0 * x + y0 * y + z0 * z,
      v = m && -d3_acos(u) / m, // area weight
      w = Math.atan2(m, u); // line weight
  d3_geo_centroidX2 += v * cx;
  d3_geo_centroidY2 += v * cy;
  d3_geo_centroidZ2 += v * cz;
  d3_geo_centroidW1 += w;
  d3_geo_centroidX1 += w * (x0 + (x0 = x));
  d3_geo_centroidY1 += w * (y0 + (y0 = y));
  d3_geo_centroidZ1 += w * (z0 + (z0 = z));
  d3_geo_centroidPointXYZ(x0, y0, z0);
}

}