hyper-scatter 0.1.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/dist-lib/controller/interaction_controller.d.ts +29 -0
  4. package/dist-lib/controller/interaction_controller.d.ts.map +1 -0
  5. package/dist-lib/controller/interaction_controller.js +286 -0
  6. package/dist-lib/controller/interaction_controller.js.map +1 -0
  7. package/dist-lib/core/dataset.d.ts +62 -0
  8. package/dist-lib/core/dataset.d.ts.map +1 -0
  9. package/dist-lib/core/dataset.js +152 -0
  10. package/dist-lib/core/dataset.js.map +1 -0
  11. package/dist-lib/core/lasso_simplify.d.ts +16 -0
  12. package/dist-lib/core/lasso_simplify.d.ts.map +1 -0
  13. package/dist-lib/core/lasso_simplify.js +173 -0
  14. package/dist-lib/core/lasso_simplify.js.map +1 -0
  15. package/dist-lib/core/math/euclidean.d.ts +31 -0
  16. package/dist-lib/core/math/euclidean.d.ts.map +1 -0
  17. package/dist-lib/core/math/euclidean.js +64 -0
  18. package/dist-lib/core/math/euclidean.js.map +1 -0
  19. package/dist-lib/core/math/poincare.d.ts +117 -0
  20. package/dist-lib/core/math/poincare.d.ts.map +1 -0
  21. package/dist-lib/core/math/poincare.js +321 -0
  22. package/dist-lib/core/math/poincare.js.map +1 -0
  23. package/dist-lib/core/rng.d.ts +18 -0
  24. package/dist-lib/core/rng.d.ts.map +1 -0
  25. package/dist-lib/core/rng.js +52 -0
  26. package/dist-lib/core/rng.js.map +1 -0
  27. package/dist-lib/core/selection/point_in_polygon.d.ts +30 -0
  28. package/dist-lib/core/selection/point_in_polygon.d.ts.map +1 -0
  29. package/dist-lib/core/selection/point_in_polygon.js +112 -0
  30. package/dist-lib/core/selection/point_in_polygon.js.map +1 -0
  31. package/dist-lib/core/types.d.ts +185 -0
  32. package/dist-lib/core/types.d.ts.map +1 -0
  33. package/dist-lib/core/types.js +53 -0
  34. package/dist-lib/core/types.js.map +1 -0
  35. package/dist-lib/impl_candidate/spatial_index.d.ts +45 -0
  36. package/dist-lib/impl_candidate/spatial_index.d.ts.map +1 -0
  37. package/dist-lib/impl_candidate/spatial_index.js +186 -0
  38. package/dist-lib/impl_candidate/spatial_index.js.map +1 -0
  39. package/dist-lib/impl_candidate/webgl_candidate.d.ts +283 -0
  40. package/dist-lib/impl_candidate/webgl_candidate.d.ts.map +1 -0
  41. package/dist-lib/impl_candidate/webgl_candidate.js +2276 -0
  42. package/dist-lib/impl_candidate/webgl_candidate.js.map +1 -0
  43. package/dist-lib/index.d.ts +11 -0
  44. package/dist-lib/index.d.ts.map +1 -0
  45. package/dist-lib/index.js +52 -0
  46. package/dist-lib/index.js.map +1 -0
  47. package/package.json +63 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Deterministic dataset generation for testing.
3
+ */
4
+ import { SeededRNG } from './rng.js';
5
+ // For very large-N benchmarks we want determinism but also *reasonable* setup time.
6
+ // Dataset generation is not the performance target of this project; rendering and
7
+ // interaction are. For N in the multi-millions, avoid per-point trig/log-heavy
8
+ // sampling where possible.
9
+ const FAST_PATH_THRESHOLD_N = 2_000_000;
10
+ function gaussianFastApprox(rng, mean = 0, std = 1) {
11
+ // Irwin–Hall approximation: sum of uniforms approximates a normal.
12
+ // Using 6 uniforms is a good speed/quality tradeoff.
13
+ // Output has mean 0, variance 0.5 -> std ~0.707; we rescale to unit std.
14
+ const u = rng.random() + rng.random() + rng.random() +
15
+ rng.random() + rng.random() + rng.random();
16
+ const z = (u - 3.0) * 1.4142135623730951; // sqrt(2)
17
+ return mean + z * std;
18
+ }
19
+ function fillPoincareClusterCentersRandom(rng, labelCount, outCx, outCy, outJitterStd) {
20
+ // Random cluster centers inside disk with area-uniform radius.
21
+ // We still scale jitter by (1-|center|^2)/2 to keep clusters visually
22
+ // comparable in hyperbolic units.
23
+ const hyperbolicStd = 0.30;
24
+ for (let label = 0; label < labelCount; label++) {
25
+ const r = Math.sqrt(rng.random()) * 0.92;
26
+ const theta = rng.range(0, 2 * Math.PI);
27
+ const cx = r * Math.cos(theta);
28
+ const cy = r * Math.sin(theta);
29
+ outCx[label] = cx;
30
+ outCy[label] = cy;
31
+ const r2 = cx * cx + cy * cy;
32
+ outJitterStd[label] = hyperbolicStd * 0.5 * (1 - r2);
33
+ }
34
+ }
35
+ /**
36
+ * Generate a deterministic dataset.
37
+ */
38
+ export function generateDataset(config) {
39
+ const rng = new SeededRNG(config.seed);
40
+ const positions = new Float32Array(config.n * 2);
41
+ const labels = new Uint16Array(config.n);
42
+ const fast = config.n >= FAST_PATH_THRESHOLD_N;
43
+ const distribution = config.distribution ?? 'default';
44
+ if (config.geometry === 'euclidean') {
45
+ // Generate clustered Euclidean data
46
+ const clusterCenters = [];
47
+ for (let i = 0; i < config.labelCount; i++) {
48
+ clusterCenters.push({
49
+ x: rng.range(-1, 1),
50
+ y: rng.range(-1, 1),
51
+ });
52
+ }
53
+ for (let i = 0; i < config.n; i++) {
54
+ const label = rng.int(config.labelCount);
55
+ const center = clusterCenters[label];
56
+ // For huge N, avoid Box–Muller (log/cos/sqrt) per point.
57
+ // The approximation keeps overall visual structure and determinism.
58
+ const noiseX = fast ? gaussianFastApprox(rng, 0, 0.15) : rng.gaussian(0, 0.15);
59
+ const noiseY = fast ? gaussianFastApprox(rng, 0, 0.15) : rng.gaussian(0, 0.15);
60
+ positions[i * 2] = center.x + noiseX;
61
+ positions[i * 2 + 1] = center.y + noiseY;
62
+ labels[i] = label;
63
+ }
64
+ }
65
+ else {
66
+ // Generate Poincare disk data
67
+ // Points must be inside the unit disk (|z| < 1)
68
+ if (distribution === 'clustered') {
69
+ const cx = new Float32Array(config.labelCount);
70
+ const cy = new Float32Array(config.labelCount);
71
+ const jitterStd = new Float32Array(config.labelCount);
72
+ fillPoincareClusterCentersRandom(rng, config.labelCount, cx, cy, jitterStd);
73
+ for (let i = 0; i < config.n; i++) {
74
+ const label = rng.int(config.labelCount);
75
+ const j = jitterStd[label];
76
+ const noiseX = fast ? gaussianFastApprox(rng, 0, j) : rng.gaussian(0, j);
77
+ const noiseY = fast ? gaussianFastApprox(rng, 0, j) : rng.gaussian(0, j);
78
+ let x = cx[label] + noiseX;
79
+ let y = cy[label] + noiseY;
80
+ const rSq = x * x + y * y;
81
+ if (rSq >= 1) {
82
+ const invR = 0.999 / Math.sqrt(rSq);
83
+ x *= invR;
84
+ y *= invR;
85
+ }
86
+ positions[i * 2] = x;
87
+ positions[i * 2 + 1] = y;
88
+ labels[i] = label;
89
+ }
90
+ return {
91
+ n: config.n,
92
+ positions,
93
+ labels,
94
+ geometry: config.geometry,
95
+ };
96
+ }
97
+ // For huge N, avoid sin/cos per point by sampling angle from a lookup table.
98
+ // Deterministic (seeded) and close enough for stress/perf testing.
99
+ const tableSize = fast ? 4096 : 0;
100
+ const cosTable = tableSize ? new Float32Array(tableSize) : null;
101
+ const sinTable = tableSize ? new Float32Array(tableSize) : null;
102
+ if (tableSize && cosTable && sinTable) {
103
+ for (let t = 0; t < tableSize; t++) {
104
+ const theta = (t / tableSize) * 2 * Math.PI;
105
+ cosTable[t] = Math.cos(theta);
106
+ sinTable[t] = Math.sin(theta);
107
+ }
108
+ }
109
+ for (let i = 0; i < config.n; i++) {
110
+ const label = rng.int(config.labelCount);
111
+ let r;
112
+ if (config.boundaryStress) {
113
+ // Stress test: many points near boundary
114
+ r = rng.range(0.9, 0.999);
115
+ }
116
+ else {
117
+ // Normal distribution: use sqrt for uniform area distribution
118
+ // Then compress slightly to avoid exact boundary
119
+ r = Math.sqrt(rng.random()) * 0.95;
120
+ }
121
+ if (tableSize && cosTable && sinTable) {
122
+ const t = rng.int(tableSize);
123
+ positions[i * 2] = r * cosTable[t];
124
+ positions[i * 2 + 1] = r * sinTable[t];
125
+ }
126
+ else {
127
+ const theta = rng.range(0, 2 * Math.PI);
128
+ positions[i * 2] = r * Math.cos(theta);
129
+ positions[i * 2 + 1] = r * Math.sin(theta);
130
+ }
131
+ labels[i] = label;
132
+ }
133
+ }
134
+ return {
135
+ n: config.n,
136
+ positions,
137
+ labels,
138
+ geometry: config.geometry,
139
+ };
140
+ }
141
+ /**
142
+ * Standard test datasets.
143
+ */
144
+ export const STANDARD_DATASETS = {
145
+ euclidean_10k: { seed: 42, n: 10_000, labelCount: 10, geometry: 'euclidean' },
146
+ euclidean_100k: { seed: 42, n: 100_000, labelCount: 10, geometry: 'euclidean' },
147
+ euclidean_1m: { seed: 42, n: 1_000_000, labelCount: 10, geometry: 'euclidean' },
148
+ poincare_10k: { seed: 42, n: 10_000, labelCount: 10, geometry: 'poincare' },
149
+ poincare_100k: { seed: 42, n: 100_000, labelCount: 10, geometry: 'poincare' },
150
+ poincare_boundary_stress: { seed: 42, n: 100_000, labelCount: 10, geometry: 'poincare', boundaryStress: true },
151
+ };
152
+ //# sourceMappingURL=dataset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../../src/core/dataset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Lasso polygon simplification utilities.
3
+ *
4
+ * These are UI-level helpers (not benchmark-critical math) and are intended to
5
+ * match the "feel" of Embedding Atlas' lasso:
6
+ * - sample points while dragging
7
+ * - smooth (Chaikin)
8
+ * - simplify until under vertex budget
9
+ */
10
+ /**
11
+ * Simplify a closed polygon represented as an interleaved XY list.
12
+ *
13
+ * Returns a new `Float32Array` of interleaved XY coordinates.
14
+ */
15
+ export declare function simplifyPolygonData(points: ArrayLike<number>, maxVerts: number): Float32Array;
16
+ //# sourceMappingURL=lasso_simplify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lasso_simplify.d.ts","sourceRoot":"","sources":["../../src/core/lasso_simplify.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqDH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAiH7F"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Lasso polygon simplification utilities.
3
+ *
4
+ * These are UI-level helpers (not benchmark-critical math) and are intended to
5
+ * match the "feel" of Embedding Atlas' lasso:
6
+ * - sample points while dragging
7
+ * - smooth (Chaikin)
8
+ * - simplify until under vertex budget
9
+ */
10
+ function boundingRectXY(points) {
11
+ let xMin = Infinity;
12
+ let yMin = Infinity;
13
+ let xMax = -Infinity;
14
+ let yMax = -Infinity;
15
+ for (let i = 0; i < points.length; i += 2) {
16
+ const x = points[i];
17
+ const y = points[i + 1];
18
+ if (x < xMin)
19
+ xMin = x;
20
+ if (x > xMax)
21
+ xMax = x;
22
+ if (y < yMin)
23
+ yMin = y;
24
+ if (y > yMax)
25
+ yMax = y;
26
+ }
27
+ if (!Number.isFinite(xMin) || !Number.isFinite(yMin)) {
28
+ return { xMin: 0, yMin: 0, xMax: 0, yMax: 0 };
29
+ }
30
+ return { xMin, yMin, xMax, yMax };
31
+ }
32
+ function chaikinClosedXY(points, iterations) {
33
+ // Chaikin smoothing on a closed polygon.
34
+ let pts = Array.from(points);
35
+ let n = Math.floor(pts.length / 2);
36
+ if (n < 3)
37
+ return pts;
38
+ for (let it = 0; it < iterations; it++) {
39
+ n = Math.floor(pts.length / 2);
40
+ if (n < 3)
41
+ break;
42
+ const out = new Array(n * 4);
43
+ let w = 0;
44
+ for (let i = 0; i < n; i++) {
45
+ const i0 = i;
46
+ const i1 = (i + 1) % n;
47
+ const x0 = pts[i0 * 2];
48
+ const y0 = pts[i0 * 2 + 1];
49
+ const x1 = pts[i1 * 2];
50
+ const y1 = pts[i1 * 2 + 1];
51
+ // Q = 0.75*p0 + 0.25*p1
52
+ out[w++] = 0.75 * x0 + 0.25 * x1;
53
+ out[w++] = 0.75 * y0 + 0.25 * y1;
54
+ // R = 0.25*p0 + 0.75*p1
55
+ out[w++] = 0.25 * x0 + 0.75 * x1;
56
+ out[w++] = 0.25 * y0 + 0.75 * y1;
57
+ }
58
+ pts = out;
59
+ }
60
+ return pts;
61
+ }
62
+ /**
63
+ * Simplify a closed polygon represented as an interleaved XY list.
64
+ *
65
+ * Returns a new `Float32Array` of interleaved XY coordinates.
66
+ */
67
+ export function simplifyPolygonData(points, maxVerts) {
68
+ // Embedding Atlas strategy (ported conceptually):
69
+ // 1) Chaikin smoothing on the closed polygon.
70
+ // 2) Simplify with a tolerance derived from bounding box; increase until within budget.
71
+ const n0 = Math.floor(points.length / 2);
72
+ if (n0 <= maxVerts)
73
+ return new Float32Array(points);
74
+ // Safety: avoid pathological blow-ups if the user drags for a long time.
75
+ // If we have too many points, pre-subsample before smoothing.
76
+ const MAX_RAW = 2048;
77
+ let base = points;
78
+ if (n0 > MAX_RAW) {
79
+ const out = new Float32Array(MAX_RAW * 2);
80
+ for (let i = 0; i < MAX_RAW; i++) {
81
+ const src = Math.floor((i * n0) / MAX_RAW);
82
+ out[i * 2] = points[src * 2];
83
+ out[i * 2 + 1] = points[src * 2 + 1];
84
+ }
85
+ base = out;
86
+ }
87
+ const smoothed = chaikinClosedXY(base, 5);
88
+ const rect = boundingRectXY(smoothed);
89
+ const span = Math.max(rect.xMax - rect.xMin, rect.yMax - rect.yMin);
90
+ const distSqToSegment = (px, py, ax, ay, bx, by) => {
91
+ const abx = bx - ax;
92
+ const aby = by - ay;
93
+ const apx = px - ax;
94
+ const apy = py - ay;
95
+ const abLenSq = abx * abx + aby * aby;
96
+ let t = 0;
97
+ if (abLenSq > 1e-12) {
98
+ t = (apx * abx + apy * aby) / abLenSq;
99
+ if (t < 0)
100
+ t = 0;
101
+ else if (t > 1)
102
+ t = 1;
103
+ }
104
+ const qx = ax + t * abx;
105
+ const qy = ay + t * aby;
106
+ const dx = px - qx;
107
+ const dy = py - qy;
108
+ return dx * dx + dy * dy;
109
+ };
110
+ const rdp = (eps) => {
111
+ const m = Math.floor(smoothed.length / 2);
112
+ if (m <= 2)
113
+ return smoothed.slice();
114
+ const epsSq = eps * eps;
115
+ const keep = new Uint8Array(m);
116
+ keep[0] = 1;
117
+ keep[m - 1] = 1;
118
+ const stack = [0, m - 1];
119
+ while (stack.length > 0) {
120
+ const end = stack.pop();
121
+ const start = stack.pop();
122
+ const ax = smoothed[start * 2];
123
+ const ay = smoothed[start * 2 + 1];
124
+ const bx = smoothed[end * 2];
125
+ const by = smoothed[end * 2 + 1];
126
+ let maxD = -1;
127
+ let maxI = -1;
128
+ for (let i = start + 1; i < end; i++) {
129
+ const px = smoothed[i * 2];
130
+ const py = smoothed[i * 2 + 1];
131
+ const d = distSqToSegment(px, py, ax, ay, bx, by);
132
+ if (d > maxD) {
133
+ maxD = d;
134
+ maxI = i;
135
+ }
136
+ }
137
+ if (maxD > epsSq && maxI >= 0) {
138
+ keep[maxI] = 1;
139
+ stack.push(start, maxI);
140
+ stack.push(maxI, end);
141
+ }
142
+ }
143
+ const out = [];
144
+ for (let i = 0; i < m; i++) {
145
+ if (!keep[i])
146
+ continue;
147
+ out.push(smoothed[i * 2], smoothed[i * 2 + 1]);
148
+ }
149
+ return out;
150
+ };
151
+ // Match Embedding Atlas: start with tolerance ~= bbox/100, then increase slowly.
152
+ let tol = Math.max(1e-12, span / 100);
153
+ let simplified = rdp(tol);
154
+ let it = 0;
155
+ while (Math.floor(simplified.length / 2) > maxVerts && it < 20) {
156
+ tol *= 1.1;
157
+ simplified = rdp(tol);
158
+ it++;
159
+ }
160
+ // Final safety: if still too large, fall back to uniform subsampling.
161
+ const n = Math.floor(simplified.length / 2);
162
+ if (n > maxVerts) {
163
+ const out = new Float32Array(maxVerts * 2);
164
+ for (let i = 0; i < maxVerts; i++) {
165
+ const src = Math.floor((i * n) / maxVerts);
166
+ out[i * 2] = simplified[src * 2];
167
+ out[i * 2 + 1] = simplified[src * 2 + 1];
168
+ }
169
+ return out;
170
+ }
171
+ return new Float32Array(simplified);
172
+ }
173
+ //# sourceMappingURL=lasso_simplify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lasso_simplify.js","sourceRoot":"","sources":["../../src/core/lasso_simplify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Euclidean geometry utilities.
3
+ */
4
+ import { EuclideanViewState } from '../types.js';
5
+ /**
6
+ * Create a default Euclidean view state.
7
+ */
8
+ export declare function createEuclideanView(): EuclideanViewState;
9
+ /**
10
+ * Project a data point to screen coordinates.
11
+ */
12
+ export declare function projectEuclidean(dataX: number, dataY: number, view: EuclideanViewState, width: number, height: number): {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ /**
17
+ * Unproject screen coordinates to data space.
18
+ */
19
+ export declare function unprojectEuclidean(screenX: number, screenY: number, view: EuclideanViewState, width: number, height: number): {
20
+ x: number;
21
+ y: number;
22
+ };
23
+ /**
24
+ * Apply pan to view (anchor-invariant: point under cursor stays under cursor).
25
+ */
26
+ export declare function panEuclidean(view: EuclideanViewState, deltaScreenX: number, deltaScreenY: number, width: number, height: number): EuclideanViewState;
27
+ /**
28
+ * Apply zoom to view (anchor-invariant: point under cursor stays under cursor).
29
+ */
30
+ export declare function zoomEuclidean(view: EuclideanViewState, anchorScreenX: number, anchorScreenY: number, delta: number, width: number, height: number): EuclideanViewState;
31
+ //# sourceMappingURL=euclidean.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"euclidean.d.ts","sourceRoot":"","sources":["../../../src/core/math/euclidean.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,CAOxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,kBAAkB,EACxB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,kBAAkB,EACxB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,kBAAkB,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,kBAAkB,CAOpB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,kBAAkB,EACxB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,kBAAkB,CAmBpB"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Euclidean geometry utilities.
3
+ */
4
+ /**
5
+ * Create a default Euclidean view state.
6
+ */
7
+ export function createEuclideanView() {
8
+ return {
9
+ type: 'euclidean',
10
+ centerX: 0,
11
+ centerY: 0,
12
+ zoom: 1,
13
+ };
14
+ }
15
+ /**
16
+ * Project a data point to screen coordinates.
17
+ */
18
+ export function projectEuclidean(dataX, dataY, view, width, height) {
19
+ const scale = Math.min(width, height) * 0.4 * view.zoom;
20
+ const x = width / 2 + (dataX - view.centerX) * scale;
21
+ const y = height / 2 - (dataY - view.centerY) * scale; // flip Y
22
+ return { x, y };
23
+ }
24
+ /**
25
+ * Unproject screen coordinates to data space.
26
+ */
27
+ export function unprojectEuclidean(screenX, screenY, view, width, height) {
28
+ const scale = Math.min(width, height) * 0.4 * view.zoom;
29
+ const x = view.centerX + (screenX - width / 2) / scale;
30
+ const y = view.centerY - (screenY - height / 2) / scale; // flip Y
31
+ return { x, y };
32
+ }
33
+ /**
34
+ * Apply pan to view (anchor-invariant: point under cursor stays under cursor).
35
+ */
36
+ export function panEuclidean(view, deltaScreenX, deltaScreenY, width, height) {
37
+ const scale = Math.min(width, height) * 0.4 * view.zoom;
38
+ return {
39
+ ...view,
40
+ centerX: view.centerX - deltaScreenX / scale,
41
+ centerY: view.centerY + deltaScreenY / scale, // flip Y
42
+ };
43
+ }
44
+ /**
45
+ * Apply zoom to view (anchor-invariant: point under cursor stays under cursor).
46
+ */
47
+ export function zoomEuclidean(view, anchorScreenX, anchorScreenY, delta, width, height) {
48
+ // Get data point under anchor before zoom
49
+ const anchorData = unprojectEuclidean(anchorScreenX, anchorScreenY, view, width, height);
50
+ // Apply zoom (delta > 0 = zoom in)
51
+ const zoomFactor = Math.pow(1.1, delta);
52
+ const newZoom = Math.max(0.1, Math.min(100, view.zoom * zoomFactor));
53
+ // Calculate new center so anchor point stays in place
54
+ const newScale = Math.min(width, height) * 0.4 * newZoom;
55
+ const newCenterX = anchorData.x - (anchorScreenX - width / 2) / newScale;
56
+ const newCenterY = anchorData.y + (anchorScreenY - height / 2) / newScale;
57
+ return {
58
+ ...view,
59
+ centerX: newCenterX,
60
+ centerY: newCenterY,
61
+ zoom: newZoom,
62
+ };
63
+ }
64
+ //# sourceMappingURL=euclidean.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"euclidean.js","sourceRoot":"","sources":["../../../src/core/math/euclidean.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Poincare disk (hyperbolic) geometry utilities.
3
+ *
4
+ * The Poincare disk model represents hyperbolic space inside the unit disk.
5
+ * Points are represented as complex numbers z with |z| < 1.
6
+ *
7
+ * Isometries (distance-preserving transformations) are Mobius transformations
8
+ * of the form: f(z) = (z - a) / (1 - conj(a) * z) where |a| < 1
9
+ *
10
+ * This maps point 'a' to the origin, which is our "camera translation".
11
+ *
12
+ * References:
13
+ * - https://math.libretexts.org/Bookshelves/Geometry/Geometry_with_an_Introduction_to_Cosmic_Topology_(Hitchman)/05:_Hyperbolic_Geometry/5.01:_The_Poincare_Disk_Model
14
+ * - https://geoopt.readthedocs.io/en/latest/extended/stereographic.html
15
+ */
16
+ import { HyperbolicViewState } from '../types.js';
17
+ /**
18
+ * Create a default hyperbolic view state (centered at origin).
19
+ */
20
+ export declare function createHyperbolicView(): HyperbolicViewState;
21
+ /**
22
+ * Apply Mobius transformation: T_a(z) = (z - a) / (1 - conj(a) * z)
23
+ * This is the hyperbolic isometry that translates 'a' to the origin.
24
+ *
25
+ * Mathematical derivation:
26
+ * - Numerator: z - a (complex subtraction)
27
+ * - Denominator: 1 - conj(a) * z where conj(a) = ax - i*ay
28
+ */
29
+ export declare function mobiusTransform(zx: number, zy: number, ax: number, ay: number): {
30
+ x: number;
31
+ y: number;
32
+ };
33
+ /**
34
+ * Inverse Mobius transformation.
35
+ * If forward is T_a(z) = (z - a) / (1 - conj(a) * z)
36
+ * Then inverse is T_a^{-1}(w) = (w + a) / (1 + conj(a) * w)
37
+ */
38
+ export declare function inverseMobiusTransform(wx: number, wy: number, ax: number, ay: number): {
39
+ x: number;
40
+ y: number;
41
+ };
42
+ /**
43
+ * Mobius addition (gyroaddition) in the Poincare ball.
44
+ * Formula from geoopt for curvature k = -1:
45
+ *
46
+ * x ⊕ y = ((1 + 2⟨x,y⟩ + ‖y‖²)x + (1 - ‖x‖²)y) / (1 + 2⟨x,y⟩ + ‖x‖²‖y‖²)
47
+ *
48
+ * where ⟨x,y⟩ is the real inner product (dot product).
49
+ */
50
+ export declare function mobiusAdd(ax: number, ay: number, bx: number, by: number): {
51
+ x: number;
52
+ y: number;
53
+ };
54
+ /**
55
+ * Mobius negation: -x in the gyrogroup is just negation.
56
+ */
57
+ export declare function mobiusNeg(x: number, y: number): {
58
+ x: number;
59
+ y: number;
60
+ };
61
+ /**
62
+ * Mobius subtraction: x ⊖ y = x ⊕ (-y)
63
+ */
64
+ export declare function mobiusSub(ax: number, ay: number, bx: number, by: number): {
65
+ x: number;
66
+ y: number;
67
+ };
68
+ /**
69
+ * Project a Poincare disk point to screen coordinates.
70
+ */
71
+ export declare function projectPoincare(dataX: number, dataY: number, view: HyperbolicViewState, width: number, height: number): {
72
+ x: number;
73
+ y: number;
74
+ };
75
+ /**
76
+ * Unproject screen coordinates to Poincare disk (data space).
77
+ */
78
+ export declare function unprojectPoincare(screenX: number, screenY: number, view: HyperbolicViewState, width: number, height: number): {
79
+ x: number;
80
+ y: number;
81
+ };
82
+ /**
83
+ * Apply pan to hyperbolic view (anchor-invariant).
84
+ *
85
+ * The point under the cursor at drag start should stay under the cursor
86
+ * throughout the drag. This is the correct "natural" drag behavior.
87
+ *
88
+ * Algorithm:
89
+ * 1. Find the data point p under the start screen position
90
+ * 2. Get the target disk position d2 from end screen position
91
+ * 3. Solve for new camera a' such that T_{a'}(p) = d2
92
+ */
93
+ export declare function panPoincare(view: HyperbolicViewState, startScreenX: number, startScreenY: number, endScreenX: number, endScreenY: number, width: number, height: number): HyperbolicViewState;
94
+ /**
95
+ * Apply zoom to hyperbolic view (anchor-invariant).
96
+ *
97
+ * Display zoom scales the visual representation while keeping the
98
+ * data point under the cursor stationary.
99
+ */
100
+ export declare function zoomPoincare(view: HyperbolicViewState, anchorScreenX: number, anchorScreenY: number, delta: number, width: number, height: number): HyperbolicViewState;
101
+ /**
102
+ * Hyperbolic distance between two points in the Poincare disk.
103
+ * d(z1, z2) = 2 * arctanh(|z1 - z2| / |1 - conj(z1) * z2|)
104
+ */
105
+ export declare function hyperbolicDistance(x1: number, y1: number, x2: number, y2: number): number;
106
+ /**
107
+ * Geodesic interpolation between two points.
108
+ * gamma(t) = x ⊕ (t ⊗ ((-x) ⊕ y))
109
+ *
110
+ * For simplicity, we use a linear approximation in disk space
111
+ * which is accurate for small distances.
112
+ */
113
+ export declare function geodesicInterpolate(x1: number, y1: number, x2: number, y2: number, t: number): {
114
+ x: number;
115
+ y: number;
116
+ };
117
+ //# sourceMappingURL=poincare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poincare.d.ts","sourceRoot":"","sources":["../../../src/core/math/poincare.ts"],"names":[],"mappings":"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"}