celestial-chart 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,71 @@
1
+ /* NOT PART OF THE BUILD.
2
+ *
3
+ * Nothing in the library calls `timezones()`, and it is never attached to the
4
+ * `Celestial` object either — it was unreachable upstream as well.
5
+ * Modularisation made that visible: no module imports it, so the bundler leaves
6
+ * it out. Its `topojson` dependency would only have been in the bundle for this.
7
+ *
8
+ * Kept rather than deleted: if timezone lookup is ever needed, this is where to
9
+ * start. It would then have to become an ES module, with topojson-client added
10
+ * as a dependency.
11
+ */
12
+ function timezones() {
13
+ var cfg = settings.set(),
14
+ world, timezone;
15
+ loadJson(cfg.datapath + "timezones.json", function(error, json) {
16
+ world = topojson.feature(json, json.objects.timezones);
17
+
18
+ Celestial.container.selectAll(".timezones")
19
+ .data(world.features)
20
+ .enter().append("path")
21
+ .attr("class", "tz");
22
+ });
23
+
24
+ function getTimezone(pos) {
25
+ var tz;
26
+ Celestial.container.selectAll(".tz").each( function(d,i) {
27
+ if (pointInPolygon(pos, d.geometry.coordinates[0])) {
28
+ tz = getMinutes(d.properties.zone);
29
+ return false;
30
+ }
31
+ });
32
+ return tz;
33
+ }
34
+
35
+ function getMinutes(s) {
36
+ if (!s) return;
37
+ /*var tza = s.match(/UTC([\+\-])(\d+)\:(\d+)/);
38
+ if (tza === null) return;
39
+ var tzm = parseInt(tza[2]) * 60 + parseInt(tza[3]);
40
+ if (tza[1] === "-") tzm *= -1;*/
41
+ return parseFloat(s) * 60;
42
+ }
43
+
44
+ function pointInPolygon(p, polygon) {
45
+ var isInside = false;
46
+ var minX = polygon[0][0], maxX = polygon[0][0];
47
+ var minY = polygon[0][1], maxY = polygon[0][1];
48
+ for (var n = 1; n < polygon.length; n++) {
49
+ var q = polygon[n];
50
+ minX = Math.min(q[0], minX);
51
+ maxX = Math.max(q[0], maxX);
52
+ minY = Math.min(q[1], minY);
53
+ maxY = Math.max(q[1], maxY);
54
+ }
55
+
56
+ if (p[0] < minX || p[0] > maxX || p[1] < minY || p[1] > maxY) {
57
+ return false;
58
+ }
59
+
60
+ var i = 0, j = polygon.length - 1;
61
+ for (i, j; i < polygon.length; j = i++) {
62
+ if ( (polygon[i][1] > p[1]) != (polygon[j][1] > p[1]) &&
63
+ p[0] < (polygon[j][0] - polygon[i][0]) * (p[1] - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) + polygon[i][0] ) {
64
+ isInside = !isInside;
65
+ }
66
+ }
67
+
68
+ return isInside;
69
+ }
70
+ Celestial.getTimezone = getTimezone;
71
+ }
@@ -0,0 +1,94 @@
1
+ import { Celestial } from "./core.js";
2
+ import { eulerAngles } from "./projection.js";
3
+
4
+ var τ = Math.PI*2,
5
+ halfπ = Math.PI/2,
6
+ deg2rad = Math.PI/180;
7
+
8
+
9
+ //Transform equatorial into any coordinates, degrees
10
+ function transformDeg(c, euler) {
11
+ var res = transform( c.map( function(d) { return d * deg2rad; } ), euler);
12
+ return res.map( function(d) { return d / deg2rad; } );
13
+ }
14
+
15
+ //Transform equatorial into any coordinates, radians
16
+ function transform(c, euler) {
17
+ var x, y, z, β, γ, λ, φ, dψ, ψ, θ,
18
+ ε = 1.0e-5;
19
+
20
+ if (!euler) return c;
21
+
22
+ λ = c[0]; // celestial longitude 0..2pi
23
+ if (λ < 0) λ += τ;
24
+ φ = c[1]; // celestial latitude -pi/2..pi/2
25
+
26
+ λ -= euler[0]; // celestial longitude - celestial coordinates of the native pole
27
+ β = euler[1]; // inclination between the poles (colatitude)
28
+ γ = euler[2]; // native coordinates of the celestial pole
29
+
30
+ x = Math.sin(φ) * Math.sin(β) - Math.cos(φ) * Math.cos(β) * Math.cos(λ);
31
+ if (Math.abs(x) < ε) {
32
+ x = -Math.cos(φ + β) + Math.cos(φ) * Math.cos(β) * (1 - Math.cos(λ));
33
+ }
34
+ y = -Math.cos(φ) * Math.sin(λ);
35
+
36
+ if (x !== 0 || y !== 0) {
37
+ dψ = Math.atan2(y, x);
38
+ } else {
39
+ dψ = λ - Math.PI;
40
+ }
41
+ ψ = (γ + dψ);
42
+ if (ψ > Math.PI) ψ -= τ;
43
+
44
+ if (λ % Math.PI === 0) {
45
+ θ = φ + Math.cos(λ) * β;
46
+ if (θ > halfπ) θ = Math.PI - θ;
47
+ if (θ < -halfπ) θ = -Math.PI - θ;
48
+ } else {
49
+ z = Math.sin(φ) * Math.cos(β) + Math.cos(φ) * Math.sin(β) * Math.cos(λ);
50
+ if (Math.abs(z) > 0.99) {
51
+ θ = Math.abs(Math.acos(Math.sqrt(x*x+y*y)));
52
+ if (z < 0) θ *= -1;
53
+ } else {
54
+ θ = Math.asin(z);
55
+ }
56
+ }
57
+
58
+ return [ψ, θ];
59
+ }
60
+
61
+
62
+ function getAngles(coords) {
63
+ if (coords === null || coords.length <= 0) return [0,0,0];
64
+ var rot = eulerAngles.equatorial;
65
+ if (!coords[2]) coords[2] = 0;
66
+ return [rot[0] - coords[0], rot[1] - coords[1], rot[2] + coords[2]];
67
+ }
68
+
69
+
70
+ var euler = {
71
+ "ecliptic": [-90.0, 23.4393, 90.0],
72
+ "inverse ecliptic": [90.0, 23.4393, -90.0],
73
+ "galactic": [-167.1405, 62.8717, 122.9319],
74
+ "inverse galactic": [122.9319, 62.8717, -167.1405],
75
+ "supergalactic": [283.7542, 74.2911, 26.4504],
76
+ "inverse supergalactic": [26.4504, 74.2911, 283.7542],
77
+ "init": function () {
78
+ for (var key in this) {
79
+ if (this[key].constructor == Array) {
80
+ this[key] = this[key].map( function(val) { return val * deg2rad; } );
81
+ }
82
+ }
83
+ },
84
+ "add": function(name, ang) {
85
+ if (!ang || !name || ang.length !== 3 || this.hasOwnProperty(name)) return;
86
+ this[name] = ang.map( function(val) { return val * deg2rad; } );
87
+ return this[name];
88
+ }
89
+ };
90
+
91
+ euler.init();
92
+ Celestial.euler = function () { return euler; };
93
+
94
+ export { deg2rad, euler, getAngles, halfπ, transformDeg, τ };
package/src/util.js ADDED
@@ -0,0 +1,275 @@
1
+ import * as d3 from "./d3.js";
2
+ import { deg2rad, halfπ, τ } from "./transform.js";
3
+
4
+ function px(n) { return n + "px"; }
5
+ function Round(x, dg) { return(Math.round(Math.pow(10,dg)*x)/Math.pow(10,dg)); }
6
+ function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
7
+ function pad(n) { return n < 10 ? '0' + n : n; }
8
+
9
+
10
+ // The bare `hasOwnProperty` resolved through the global object; in a module
11
+ // that is misleading, so it is spelled out.
12
+ var hasOwn = Object.prototype.hasOwnProperty;
13
+ function has(o, key) { return o !== null && hasOwn.call(o, key); }
14
+ function when(o, key, val) { return o !== null && hasOwn.call(o, key) ? o[key] : val; }
15
+ function isNumber(n) { return n !== null && !isNaN(parseFloat(n)) && isFinite(n); }
16
+ function isArray(o) { return o !== null && Object.prototype.toString.call(o) === "[object Array]"; }
17
+ function isObject(o) { var type = typeof o; return type === 'function' || type === 'object' && !!o; }
18
+ function isFunction(o) { return typeof o == 'function' || false; }
19
+ // d3.functor was removed in v4. It did exactly one thing: turned anything that
20
+ // was not a function into a constant function.
21
+ function functor(o) { return isFunction(o) ? o : function() { return o; }; }
22
+
23
+ // Since v5 d3.json returns a Promise instead of invoking a callback. The shape
24
+ // of the call sites is preserved — the old (error, json) form stays — so that
25
+ // the loading logic is unchanged and the effect of the D3 upgrade stays
26
+ // separable from everything else.
27
+ // d3-queue was a separate package, unmaintained since v5. This is all the
28
+ // library uses of its interface: defer(fn) to enqueue a task, await(cb) for the
29
+ // end. Every defer runs synchronously, before the await.
30
+ // The v3 selection.classed({name: boolean, ...}) form was removed in v4;
31
+ // v7 only understands a classed(names, boolean) pair.
32
+ function classes_(sel, obj) {
33
+ for (var k in obj) { if (has(obj, k)) sel.classed(k, obj[k]); }
34
+ return sel;
35
+ }
36
+
37
+ // The same for the object form of attr and style: v3 accepted a whole property
38
+ // map, v4+ only a (name, value) pair. Left unhandled, the call runs AS A GETTER
39
+ // and the next link in the chain is no longer working on a selection — silently,
40
+ // without an exception, right up to the first use.
41
+ function attrs(sel, obj) {
42
+ for (var k in obj) { if (has(obj, k)) sel.attr(k, obj[k]); }
43
+ return sel;
44
+ }
45
+
46
+ function styles_(sel, obj) {
47
+ for (var k in obj) { if (has(obj, k)) sel.style(k, obj[k]); }
48
+ return sel;
49
+ }
50
+
51
+ function taskQueue(concurrency) {
52
+ var tasks = [], running = 0, next = 0, error_ = null, done = null;
53
+
54
+ function startNext() {
55
+ while (!error_ && running < concurrency && next < tasks.length) {
56
+ running++;
57
+ tasks[next++](function (e) {
58
+ running--;
59
+ if (e && !error_) { error_ = e; return done(error_); }
60
+ if (error_) return;
61
+ if (running === 0 && next === tasks.length) return done(null);
62
+ startNext();
63
+ });
64
+ }
65
+ }
66
+
67
+ return {
68
+ defer: function (fn) { tasks.push(fn); return this; },
69
+ await: function (cb) {
70
+ done = cb;
71
+ if (!tasks.length) cb(null);
72
+ else startNext();
73
+ return this;
74
+ }
75
+ };
76
+ }
77
+
78
+ function loadJson(url, callback) {
79
+ return d3.json(url).then(
80
+ function(json) { callback(null, json); },
81
+ function(error) { callback(error || new Error("failed to load: " + url)); }
82
+ );
83
+ }
84
+ function isValidDate(d) { return d && d instanceof Date && !isNaN(d); }
85
+ function fileExists(url) {
86
+ var http = new XMLHttpRequest();
87
+ http.open('HEAD', url, false);
88
+ http.send();
89
+ return http.status != 404;
90
+ }
91
+
92
+ function findPos(o) {
93
+ var l = 0, t = 0;
94
+ if (o.offsetParent) {
95
+ do {
96
+ l += o.offsetLeft;
97
+ t += o.offsetTop;
98
+ } while ((o = o.offsetParent) !== null);
99
+ }
100
+ return [l, t];
101
+ }
102
+
103
+ function hasParent(t, id){
104
+ while(t.parentNode){
105
+ if(t.id === id) return true;
106
+ t = t.parentNode;
107
+ }
108
+ return false;
109
+ }
110
+
111
+ function attach(node, event, func) {
112
+ if (node.addEventListener) node.addEventListener(event, func, false);
113
+ else node.attachEvent("on" + event, func);
114
+ }
115
+
116
+ function stopPropagation(e) {
117
+ if (typeof e.stopPropagation != "undefined") e.stopPropagation();
118
+ else e.cancelBubble = true;
119
+ }
120
+
121
+ function dateDiff(dt1, dt2, type) {
122
+ var diff = dt2.valueOf() - dt1.valueOf(),
123
+ tp = type || "d";
124
+ switch (tp) {
125
+ case 'y': case 'yr': diff /= 31556926080; break;
126
+ case 'm': case 'mo': diff /= 2629800000; break;
127
+ case 'd': case 'dy': diff /= 86400000; break;
128
+ case 'h': case 'hr': diff /= 3600000; break;
129
+ case 'n': case 'mn': diff /= 60000; break;
130
+ case 's': case 'sec': diff /= 1000; break;
131
+ case 'ms': break;
132
+ }
133
+ return Math.floor(diff);
134
+ }
135
+
136
+ function dateParse(s) {
137
+ if (!s) return;
138
+ var t = s.split(".");
139
+ if (t.length < 1) return;
140
+ t = t[0].split("-");
141
+ t[0] = t[0].replace(/\D/g, "");
142
+ if (!t[0]) return;
143
+ t[1] = t[1] ? t[1].replace(/\D/g, "") : "1";
144
+ t[2] = t[2] ? t[2].replace(/\D/g, "") : "1";
145
+ //Fraction -> h:m:s
146
+ return new Date(Date.UTC(t[0], t[1]-1, t[2]));
147
+ }
148
+
149
+
150
+ function interpolateAngle(a1, a2, t) {
151
+ a1 = (a1*deg2rad +τ) % τ;
152
+ a2 = (a2*deg2rad + τ) % τ;
153
+ if (Math.abs(a1 - a2) > Math.PI) {
154
+ if (a1 > a2) a1 = a1 - τ;
155
+ else if (a2 > a1) a2 = a2 - τ;
156
+ }
157
+ return d3.interpolateNumber(a1/deg2rad, a2/deg2rad);
158
+ }
159
+
160
+ var Trig = {
161
+ sinh: function (val) { return (Math.pow(Math.E, val)-Math.pow(Math.E, -val))/2; },
162
+ cosh: function (val) { return (Math.pow(Math.E, val)+Math.pow(Math.E, -val))/2; },
163
+ tanh: function (val) { return 2.0 / (1.0 + Math.exp(-2.0 * val)) - 1.0; },
164
+ asinh: function (val) { return Math.log(val + Math.sqrt(val * val + 1)); },
165
+ acosh: function (val) { return Math.log(val + Math.sqrt(val * val - 1)); },
166
+ // The JS `%` keeps the sign of the dividend, so a single shift is only enough
167
+ // while the input does not go below -2*pi (respectively -3*pi). The mean
168
+ // orbital elements do grow large as one moves away from J2000, and there the
169
+ // old formula returned a negative — that is, un-normalised — angle. Shifting
170
+ // twice is correct for every input.
171
+ normalize0: function(val) { return (((val + Math.PI) % (Math.PI*2)) + Math.PI*2) % (Math.PI*2) - Math.PI; },
172
+ normalize: function(val) { return ((val % (Math.PI*2)) + Math.PI*2) % (Math.PI*2); },
173
+
174
+ cartesian: function(p) {
175
+ var ϕ = p[0], θ = halfπ - p[1], r = p[2];
176
+ return {"x": r * Math.sin(θ) * Math.cos(ϕ), "y": r * Math.sin(θ) * Math.sin(ϕ), "z": r * Math.cos(θ)};
177
+ },
178
+ // WARNING: nothing in the library calls the following four methods.
179
+ // `spherical` is moreover not the inverse of `cartesian`: it uses `atan`
180
+ // instead of `atan2` (losing the quadrant, and dividing by zero when x = 0),
181
+ // and the second value it returns is a polar distance, not a latitude. Left
182
+ // untouched because it has no callers — but no new code should build on it
183
+ // until somebody puts it right.
184
+ spherical: function(p) {
185
+ var r = Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z),
186
+ θ = Math.atan(p.y / p.x),
187
+ ϕ = Math.acos(p.z / r);
188
+ return [θ / deg2rad, ϕ / deg2rad, r];
189
+ },
190
+ distance: function(p1, p2) {
191
+ return Math.acos(Math.sin(p1[1])*Math.sin(p2[1]) + Math.cos(p1[1])*Math.cos(p2[1])*Math.cos(p1[0]-p2[0]));
192
+ }
193
+ };
194
+
195
+ var epsilon = 1e-6,
196
+ halfPi = Math.PI / 2,
197
+ quarterPi = Math.PI / 4,
198
+ tau = Math.PI * 2;
199
+
200
+ function cartesian(spherical) {
201
+ var lambda = spherical[0], phi = spherical[1], cosPhi = Math.cos(phi);
202
+ return [cosPhi * Math.cos(lambda), cosPhi * Math.sin(lambda), Math.sin(phi)];
203
+ }
204
+
205
+ function cartesianCross(a, b) {
206
+ return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
207
+ }
208
+
209
+ function cartesianNormalizeInPlace(d) {
210
+ var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
211
+ d[0] /= l; d[1] /= l; d[2] /= l;
212
+ }
213
+
214
+ function longitude(point) {
215
+ if (Math.abs(point[0]) <= Math.PI)
216
+ return point[0];
217
+ else
218
+ return sign(point[0]) * ((Math.abs(point[0]) + Math.PI) % tau - Math.PI);
219
+ }
220
+
221
+ function polygonContains(polygon, point) {
222
+ var lambda = longitude(point),
223
+ phi = point[1],
224
+ sinPhi = Math.sin(phi),
225
+ normal = [Math.sin(lambda), -Math.cos(lambda), 0],
226
+ angle = 0,
227
+ winding = 0,
228
+ sum = 0;
229
+
230
+ if (sinPhi === 1) phi = halfPi + epsilon;
231
+ else if (sinPhi === -1) phi = -halfPi - epsilon;
232
+
233
+ for (var i = 0, n = polygon.length; i < n; ++i) {
234
+ if (!(m = (ring = polygon[i]).length)) continue;
235
+ var ring,
236
+ m,
237
+ point0 = ring[m - 1],
238
+ lambda0 = longitude(point0),
239
+ phi0 = point0[1] / 2 + quarterPi,
240
+ sinPhi0 = Math.sin(phi0),
241
+ cosPhi0 = Math.cos(phi0),
242
+ point1, cosPhi1, sinPhi1, lambda1;
243
+
244
+ for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
245
+ point1 = ring[j];
246
+ lambda1 = longitude(point1);
247
+ var phi1 = point1[1] / 2 + quarterPi;
248
+ sinPhi1 = Math.sin(phi1);
249
+ cosPhi1 = Math.cos(phi1);
250
+ var delta = lambda1 - lambda0,
251
+ sign = delta >= 0 ? 1 : -1,
252
+ absDelta = sign * delta,
253
+ antimeridian = absDelta > Math.PI,
254
+ k = sinPhi0 * sinPhi1;
255
+
256
+ sum += Math.atan2(k * sign * Math.sin(absDelta), cosPhi0 * cosPhi1 + k * Math.cos(absDelta));
257
+ angle += antimeridian ? delta + sign * tau : delta;
258
+
259
+ if ((antimeridian ^ lambda0) >= (lambda ^ lambda1) >= lambda) {
260
+ var arc = cartesianCross(cartesian(point0), cartesian(point1));
261
+ cartesianNormalizeInPlace(arc);
262
+ var intersection = cartesianCross(normal, arc);
263
+ cartesianNormalizeInPlace(intersection);
264
+ var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * Math.asin(intersection[2]);
265
+ if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
266
+ winding += antimeridian ^ delta >= 0 ? 1 : -1;
267
+ }
268
+ }
269
+ }
270
+ }
271
+
272
+ return (angle < -epsilon || angle < epsilon && sum < -epsilon) ^ (winding & 1);
273
+ }
274
+
275
+ export { Round, Trig, attrs, dateDiff, dateParse, taskQueue, findPos, functor, has, hasParent, interpolateAngle, isArray, isNumber, isObject, isValidDate, loadJson, classes_, pad, px, styles_ };