-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathcox.js
121 lines (108 loc) · 3.22 KB
/
cox.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { geoProjection as projection, geoStream } from "d3-geo";
import { scan } from "d3-array";
import { asin, degrees, epsilon, sqrt } from "./math.js";
import { lagrangeRaw } from "./lagrange.js";
import { complexAdd, complexMul, complexNorm2, complexPow } from "./complex.js";
// w1 = gamma(1/n) * gamma(1 - 2/n) / n / gamma(1 - 1/n)
// https://blocks.roadtolarissa.com/Fil/852557838117687bbd985e4b38ff77d4
const w = [-1 / 2, sqrt(3) / 2],
w1 = [1.7666387502854533, 0],
m = 0.3 * 0.3;
// Approximate \int _0 ^sm(z) dt / (1 - t^3)^(2/3)
// sm maps a triangle to a disc, sm^-1 does the opposite
function sm_1(z) {
// rotate to have s ~= 1
const rot = complexPow(
w,
scan(
[0, 1, 2].map(function(i) {
return -complexMul(z, complexPow(w, [i, 0]))[0];
})
)
);
let y = complexMul(rot, z);
y = [1 - y[0], -y[1]];
// McIlroy formula 5 p6 and table for F3 page 16
const F0 = [
1.44224957030741,
0.240374928384568,
0.0686785509670194,
0.0178055502507087,
0.00228276285265497,
-1.48379585422573e-3,
-1.64287728109203e-3,
-1.02583417082273e-3,
-4.83607537673571e-4,
-1.67030822094781e-4,
-2.45024395166263e-5,
2.14092375450951e-5,
2.55897270486771e-5,
1.73086854400834e-5,
8.72756299984649e-6,
3.18304486798473e-6,
4.79323894565283e-7,
-4.58968389565456e-7,
-5.62970586787826e-7,
-3.92135372833465e-7
];
let F = [0, 0];
for (let i = F0.length; i--; ) F = complexAdd([F0[i], 0], complexMul(F, y));
let k = complexMul(
complexAdd(w1, complexMul([-F[0], -F[1]], complexPow(y, 1 - 2 / 3))),
complexMul(rot, rot)
);
// when we are close to [0,0] we switch to another approximation:
// https://www.wolframalpha.com/input/?i=(-2%2F3+choose+k)++*+(-1)%5Ek++%2F+(k%2B1)+with+k%3D0,1,2,3,4
// the difference is _very_ tiny but necessary
// if we want projection(0,0) === [0,0]
const n = complexNorm2(z);
if (n < m) {
const H0 = [
1,
1 / 3,
5 / 27,
10 / 81,
22 / 243 //…
];
const z3 = complexPow(z, [3, 0]);
let h = [0, 0];
for (let i = H0.length; i--; ) h = complexAdd([H0[i], 0], complexMul(h, z3));
h = complexMul(h, z);
k = complexAdd(complexMul(k, [n / m, 0]), complexMul(h, [1 - n / m, 0]));
}
return k;
}
const lagrange1_2 = lagrangeRaw ? lagrangeRaw(0.5) : null;
export function coxRaw(lambda, phi) {
const s = lagrange1_2(lambda, phi);
const t = sm_1([s[1] / 2, s[0] / 2]);
return [t[1], t[0]];
}
// the Sphere should go *exactly* to the vertices of the triangles
// because they are singular points
function sphere() {
const c = 2 * asin(1 / sqrt(5)) * degrees;
return {
type: "Polygon",
coordinates: [
[[0, 90], [-180, -c + epsilon], [0, -90], [180, -c + epsilon], [0, 90]]
]
};
}
export default function() {
const p = projection(coxRaw);
const stream_ = p.stream;
p.stream = function(stream) {
const rotate = p.rotate(),
rotateStream = stream_(stream),
sphereStream = (p.rotate([0, 0]), stream_(stream));
p.rotate(rotate);
rotateStream.sphere = function() {
geoStream(sphere(), sphereStream);
};
return rotateStream;
};
return p
.scale(188.305)
.translate([480, 333.167]);
}