| import {Adder} from "d3-array"; |
| import {asin, atan2, cos, degrees, epsilon, epsilon2, hypot, radians, sin, sqrt} from "./math.js"; |
| import noop from "./noop.js"; |
| import stream from "./stream.js"; |
|
|
| var W0, W1, |
| X0, Y0, Z0, |
| X1, Y1, Z1, |
| X2, Y2, Z2, |
| lambda00, phi00, |
| x0, y0, z0; |
|
|
| var centroidStream = { |
| sphere: noop, |
| point: centroidPoint, |
| lineStart: centroidLineStart, |
| lineEnd: centroidLineEnd, |
| polygonStart: function() { |
| centroidStream.lineStart = centroidRingStart; |
| centroidStream.lineEnd = centroidRingEnd; |
| }, |
| polygonEnd: function() { |
| centroidStream.lineStart = centroidLineStart; |
| centroidStream.lineEnd = centroidLineEnd; |
| } |
| }; |
|
|
| |
| function centroidPoint(lambda, phi) { |
| lambda *= radians, phi *= radians; |
| var cosPhi = cos(phi); |
| centroidPointCartesian(cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)); |
| } |
|
|
| function centroidPointCartesian(x, y, z) { |
| ++W0; |
| X0 += (x - X0) / W0; |
| Y0 += (y - Y0) / W0; |
| Z0 += (z - Z0) / W0; |
| } |
|
|
| function centroidLineStart() { |
| centroidStream.point = centroidLinePointFirst; |
| } |
|
|
| function centroidLinePointFirst(lambda, phi) { |
| lambda *= radians, phi *= radians; |
| var cosPhi = cos(phi); |
| x0 = cosPhi * cos(lambda); |
| y0 = cosPhi * sin(lambda); |
| z0 = sin(phi); |
| centroidStream.point = centroidLinePoint; |
| centroidPointCartesian(x0, y0, z0); |
| } |
|
|
| function centroidLinePoint(lambda, phi) { |
| lambda *= radians, phi *= radians; |
| var cosPhi = cos(phi), |
| x = cosPhi * cos(lambda), |
| y = cosPhi * sin(lambda), |
| z = sin(phi), |
| w = atan2(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); |
| W1 += w; |
| X1 += w * (x0 + (x0 = x)); |
| Y1 += w * (y0 + (y0 = y)); |
| Z1 += w * (z0 + (z0 = z)); |
| centroidPointCartesian(x0, y0, z0); |
| } |
|
|
| function centroidLineEnd() { |
| centroidStream.point = centroidPoint; |
| } |
|
|
| |
| |
| function centroidRingStart() { |
| centroidStream.point = centroidRingPointFirst; |
| } |
|
|
| function centroidRingEnd() { |
| centroidRingPoint(lambda00, phi00); |
| centroidStream.point = centroidPoint; |
| } |
|
|
| function centroidRingPointFirst(lambda, phi) { |
| lambda00 = lambda, phi00 = phi; |
| lambda *= radians, phi *= radians; |
| centroidStream.point = centroidRingPoint; |
| var cosPhi = cos(phi); |
| x0 = cosPhi * cos(lambda); |
| y0 = cosPhi * sin(lambda); |
| z0 = sin(phi); |
| centroidPointCartesian(x0, y0, z0); |
| } |
|
|
| function centroidRingPoint(lambda, phi) { |
| lambda *= radians, phi *= radians; |
| var cosPhi = cos(phi), |
| x = cosPhi * cos(lambda), |
| y = cosPhi * sin(lambda), |
| z = sin(phi), |
| cx = y0 * z - z0 * y, |
| cy = z0 * x - x0 * z, |
| cz = x0 * y - y0 * x, |
| m = hypot(cx, cy, cz), |
| w = asin(m), |
| v = m && -w / m; |
| X2.add(v * cx); |
| Y2.add(v * cy); |
| Z2.add(v * cz); |
| W1 += w; |
| X1 += w * (x0 + (x0 = x)); |
| Y1 += w * (y0 + (y0 = y)); |
| Z1 += w * (z0 + (z0 = z)); |
| centroidPointCartesian(x0, y0, z0); |
| } |
|
|
| export default function(object) { |
| W0 = W1 = |
| X0 = Y0 = Z0 = |
| X1 = Y1 = Z1 = 0; |
| X2 = new Adder(); |
| Y2 = new Adder(); |
| Z2 = new Adder(); |
| stream(object, centroidStream); |
|
|
| var x = +X2, |
| y = +Y2, |
| z = +Z2, |
| m = hypot(x, y, z); |
|
|
| |
| if (m < epsilon2) { |
| x = X1, y = Y1, z = Z1; |
| |
| if (W1 < epsilon) x = X0, y = Y0, z = Z0; |
| m = hypot(x, y, z); |
| |
| if (m < epsilon2) return [NaN, NaN]; |
| } |
|
|
| return [atan2(y, x) * degrees, asin(z / m) * degrees]; |
| } |
|
|