@fnndsc/orrery 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.
- package/README.md +12 -0
- package/dist/controls/cameraRig.d.ts +223 -0
- package/dist/controls/cameraRig.js +353 -0
- package/dist/controls/cameraRig.js.map +1 -0
- package/dist/controls/gestures.d.ts +133 -0
- package/dist/controls/gestures.js +282 -0
- package/dist/controls/gestures.js.map +1 -0
- package/dist/controls/index.d.ts +9 -0
- package/dist/controls/index.js +10 -0
- package/dist/controls/index.js.map +1 -0
- package/dist/controls/picker.d.ts +117 -0
- package/dist/controls/picker.js +167 -0
- package/dist/controls/picker.js.map +1 -0
- package/dist/controls/replay.d.ts +94 -0
- package/dist/controls/replay.js +147 -0
- package/dist/controls/replay.js.map +1 -0
- package/dist/draw/censusField.d.ts +113 -0
- package/dist/draw/censusField.js +241 -0
- package/dist/draw/censusField.js.map +1 -0
- package/dist/draw/constants.d.ts +13 -0
- package/dist/draw/constants.js +14 -0
- package/dist/draw/constants.js.map +1 -0
- package/dist/draw/geometry.d.ts +33 -0
- package/dist/draw/geometry.js +55 -0
- package/dist/draw/geometry.js.map +1 -0
- package/dist/draw/handoff.d.ts +11 -0
- package/dist/draw/handoff.js +12 -0
- package/dist/draw/handoff.js.map +1 -0
- package/dist/draw/handoffField.d.ts +74 -0
- package/dist/draw/handoffField.js +151 -0
- package/dist/draw/handoffField.js.map +1 -0
- package/dist/draw/index.d.ts +20 -0
- package/dist/draw/index.js +21 -0
- package/dist/draw/index.js.map +1 -0
- package/dist/draw/labelField.d.ts +63 -0
- package/dist/draw/labelField.js +149 -0
- package/dist/draw/labelField.js.map +1 -0
- package/dist/draw/lamps.d.ts +16 -0
- package/dist/draw/lamps.js +17 -0
- package/dist/draw/lamps.js.map +1 -0
- package/dist/draw/nebula.d.ts +8 -0
- package/dist/draw/nebula.js +28 -0
- package/dist/draw/nebula.js.map +1 -0
- package/dist/draw/palette.d.ts +41 -0
- package/dist/draw/palette.js +32 -0
- package/dist/draw/palette.js.map +1 -0
- package/dist/draw/sphereField.d.ts +98 -0
- package/dist/draw/sphereField.js +168 -0
- package/dist/draw/sphereField.js.map +1 -0
- package/dist/draw/starField.d.ts +127 -0
- package/dist/draw/starField.js +267 -0
- package/dist/draw/starField.js.map +1 -0
- package/dist/draw/stars.d.ts +65 -0
- package/dist/draw/stars.js +132 -0
- package/dist/draw/stars.js.map +1 -0
- package/dist/draw/timing.d.ts +11 -0
- package/dist/draw/timing.js +12 -0
- package/dist/draw/timing.js.map +1 -0
- package/dist/draw/tubeField.d.ts +131 -0
- package/dist/draw/tubeField.js +352 -0
- package/dist/draw/tubeField.js.map +1 -0
- package/dist/draw/tubes.d.ts +41 -0
- package/dist/draw/tubes.js +116 -0
- package/dist/draw/tubes.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/accretion.d.ts +31 -0
- package/dist/layout/accretion.js +384 -0
- package/dist/layout/accretion.js.map +1 -0
- package/dist/layout/constellations.d.ts +25 -0
- package/dist/layout/constellations.js +197 -0
- package/dist/layout/constellations.js.map +1 -0
- package/dist/layout/galaxy.d.ts +12 -0
- package/dist/layout/galaxy.js +99 -0
- package/dist/layout/galaxy.js.map +1 -0
- package/dist/layout/hierarchy.d.ts +45 -0
- package/dist/layout/hierarchy.js +464 -0
- package/dist/layout/hierarchy.js.map +1 -0
- package/dist/layout/index.d.ts +20 -0
- package/dist/layout/index.js +21 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/layout/layoutInput.d.ts +29 -0
- package/dist/layout/layoutInput.js +45 -0
- package/dist/layout/layoutInput.js.map +1 -0
- package/dist/layout/molecule.d.ts +64 -0
- package/dist/layout/molecule.js +155 -0
- package/dist/layout/molecule.js.map +1 -0
- package/dist/layout/pull.d.ts +94 -0
- package/dist/layout/pull.js +137 -0
- package/dist/layout/pull.js.map +1 -0
- package/dist/layout/ranked.d.ts +75 -0
- package/dist/layout/ranked.js +148 -0
- package/dist/layout/ranked.js.map +1 -0
- package/dist/layout/registry.d.ts +23 -0
- package/dist/layout/registry.js +165 -0
- package/dist/layout/registry.js.map +1 -0
- package/dist/layout/seeded.d.ts +32 -0
- package/dist/layout/seeded.js +48 -0
- package/dist/layout/seeded.js.map +1 -0
- package/dist/layout/types.d.ts +81 -0
- package/dist/layout/types.js +14 -0
- package/dist/layout/types.js.map +1 -0
- package/dist/scene/index.d.ts +6 -0
- package/dist/scene/index.js +7 -0
- package/dist/scene/index.js.map +1 -0
- package/dist/scene/orrery.d.ts +592 -0
- package/dist/scene/orrery.js +1555 -0
- package/dist/scene/orrery.js.map +1 -0
- package/dist/types/encoding.d.ts +43 -0
- package/dist/types/encoding.js +7 -0
- package/dist/types/encoding.js.map +1 -0
- package/dist/types/space.d.ts +12 -0
- package/dist/types/space.js +10 -0
- package/dist/types/space.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Picking: what lies under a pointer, however it is drawn.
|
|
3
|
+
*
|
|
4
|
+
* The drawers keep what they draw; the picker asks the scene for it as
|
|
5
|
+
* plain sources — meshes, stars and nebulae as points with a reach, a
|
|
6
|
+
* census that picks by ray — and answers with an id. In order: a solid
|
|
7
|
+
* sphere wins (an entered feed stands as spheres among stars), then a
|
|
8
|
+
* census member, then the nearest star within its drawn size, then a
|
|
9
|
+
* nebula the pointer is inside. A dimmed thing is scenery and takes no
|
|
10
|
+
* pointer; a halo wrapping a cluster is taken only when nothing solid lies
|
|
11
|
+
* along the ray.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import * as THREE from 'three';
|
|
16
|
+
/** A fingertip is wider than a cursor: the least a star reaches for one. */
|
|
17
|
+
export const TOUCH_REACH_PX = 14;
|
|
18
|
+
/** The least a star reaches for a cursor. */
|
|
19
|
+
export const CURSOR_REACH_PX = 4;
|
|
20
|
+
/**
|
|
21
|
+
* Finds what lies under a pointer.
|
|
22
|
+
*/
|
|
23
|
+
export class Picker {
|
|
24
|
+
camera;
|
|
25
|
+
world;
|
|
26
|
+
element;
|
|
27
|
+
sources;
|
|
28
|
+
starGlow;
|
|
29
|
+
raycaster = new THREE.Raycaster();
|
|
30
|
+
/**
|
|
31
|
+
* @param camera - The eye.
|
|
32
|
+
* @param world - The group every drawer hangs from.
|
|
33
|
+
* @param element - The canvas, for where a pointer falls on it.
|
|
34
|
+
* @param sources - What can be picked.
|
|
35
|
+
* @param starGlow - How far a star's glow reaches, in radii: its drawn size.
|
|
36
|
+
*/
|
|
37
|
+
constructor(camera, world, element, sources, starGlow) {
|
|
38
|
+
this.camera = camera;
|
|
39
|
+
this.world = world;
|
|
40
|
+
this.element = element;
|
|
41
|
+
this.sources = sources;
|
|
42
|
+
this.starGlow = starGlow;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Aims the ray from the eye through the pointer, the world current.
|
|
46
|
+
*
|
|
47
|
+
* @param at - The pointer.
|
|
48
|
+
* @returns The aimed raycaster (shared: read it before the next aim).
|
|
49
|
+
*/
|
|
50
|
+
ray_aim(at) {
|
|
51
|
+
const bounds = this.element.getBoundingClientRect();
|
|
52
|
+
const pointer = new THREE.Vector2(((at.clientX - bounds.left) / bounds.width) * 2 - 1, -((at.clientY - bounds.top) / bounds.height) * 2 + 1);
|
|
53
|
+
// A pick can land between a rebuild and its first render, when fresh
|
|
54
|
+
// meshes still carry identity matrices: bring them current.
|
|
55
|
+
this.world.updateMatrixWorld(true);
|
|
56
|
+
this.raycaster.setFromCamera(pointer, this.camera);
|
|
57
|
+
return this.raycaster;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The solid sphere under the pointer. A halo wraps its cluster, so its
|
|
61
|
+
* surface is hit before the spheres inside it: a solid hit anywhere along
|
|
62
|
+
* the ray wins, the halo only when nothing solid is.
|
|
63
|
+
*
|
|
64
|
+
* @param at - The pointer.
|
|
65
|
+
* @returns The mesh, or null.
|
|
66
|
+
*/
|
|
67
|
+
mesh_under(at) {
|
|
68
|
+
const hits = this.ray_aim(at)
|
|
69
|
+
.intersectObjects([...this.sources.meshes()])
|
|
70
|
+
.filter((hit) => hit.object.userData['dim'] !== true);
|
|
71
|
+
const solid = hits.find((hit) => hit.object.userData['halo'] !== true);
|
|
72
|
+
const object = (solid ?? hits[0])?.object;
|
|
73
|
+
return object instanceof THREE.Mesh ? object : null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The node behind the solid sphere under the pointer.
|
|
77
|
+
*
|
|
78
|
+
* @param at - The pointer.
|
|
79
|
+
* @returns Its id, or null.
|
|
80
|
+
*/
|
|
81
|
+
meshNode_under(at) {
|
|
82
|
+
const nodeId = this.mesh_under(at)?.userData['nodeId'];
|
|
83
|
+
return typeof nodeId === 'string' ? nodeId : null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The node under the pointer, however it is drawn: a solid sphere, a
|
|
87
|
+
* census member, or a star (or nebula) when nothing solid is.
|
|
88
|
+
*
|
|
89
|
+
* @param at - The pointer.
|
|
90
|
+
* @param touch - Whether a finger is asking: a star reaches farther for one.
|
|
91
|
+
* @returns Its id, or null.
|
|
92
|
+
*/
|
|
93
|
+
node_under(at, touch) {
|
|
94
|
+
const solid = this.meshNode_under(at);
|
|
95
|
+
if (solid !== null)
|
|
96
|
+
return solid;
|
|
97
|
+
const census = this.sources.census();
|
|
98
|
+
if (census !== null)
|
|
99
|
+
return census.pick(this.ray_aim(at));
|
|
100
|
+
return this.star_under(at, touch);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The star — or, over no star, the nebula — under the pointer, picked in
|
|
104
|
+
* screen space: a star is hit within its drawn size (never less than a
|
|
105
|
+
* few pixels, more for a finger), the nearest centre for its size wins.
|
|
106
|
+
*
|
|
107
|
+
* @param at - The pointer.
|
|
108
|
+
* @param touch - Whether a finger is asking.
|
|
109
|
+
* @returns Its id, or null.
|
|
110
|
+
*/
|
|
111
|
+
star_under(at, touch) {
|
|
112
|
+
const stars = this.sources.stars();
|
|
113
|
+
const nebulae = this.sources.nebulae();
|
|
114
|
+
const bounds = this.element.getBoundingClientRect();
|
|
115
|
+
const px = at.clientX - bounds.left;
|
|
116
|
+
const py = at.clientY - bounds.top;
|
|
117
|
+
this.world.updateMatrixWorld(true);
|
|
118
|
+
this.camera.updateMatrixWorld();
|
|
119
|
+
const perUnit = bounds.height / (2 * Math.tan((this.camera.fov * Math.PI) / 360));
|
|
120
|
+
// One matrix from the group's space to the screen's, one scratch
|
|
121
|
+
// reading: a census can hold tens of thousands of stars, and this runs
|
|
122
|
+
// on every pointer move.
|
|
123
|
+
const view = new THREE.Matrix4().multiplyMatrices(this.camera.matrixWorldInverse, this.world.matrixWorld);
|
|
124
|
+
const clip = new THREE.Matrix4().multiplyMatrices(this.camera.projectionMatrix, view);
|
|
125
|
+
const e = clip.elements;
|
|
126
|
+
const v = view.elements;
|
|
127
|
+
const screen = (p) => {
|
|
128
|
+
const depth = -(v[2] * p.x + v[6] * p.y + v[10] * p.z + v[14]);
|
|
129
|
+
if (depth <= 0)
|
|
130
|
+
return null;
|
|
131
|
+
const w = e[3] * p.x + e[7] * p.y + e[11] * p.z + e[15];
|
|
132
|
+
const x = (e[0] * p.x + e[4] * p.y + e[8] * p.z + e[12]) / w;
|
|
133
|
+
const y = (e[1] * p.x + e[5] * p.y + e[9] * p.z + e[13]) / w;
|
|
134
|
+
return { x: (x + 1) / 2 * bounds.width, y: (1 - y) / 2 * bounds.height, depth };
|
|
135
|
+
};
|
|
136
|
+
const floor = touch ? TOUCH_REACH_PX : CURSOR_REACH_PX;
|
|
137
|
+
let best = null;
|
|
138
|
+
let bestScore = Infinity;
|
|
139
|
+
for (const star of stars) {
|
|
140
|
+
if (star.dim)
|
|
141
|
+
continue;
|
|
142
|
+
const hit = screen(star.position);
|
|
143
|
+
if (hit === null)
|
|
144
|
+
continue;
|
|
145
|
+
const reach = Math.max(floor, (this.starGlow * star.radius * perUnit) / hit.depth);
|
|
146
|
+
const distance = Math.hypot(hit.x - px, hit.y - py);
|
|
147
|
+
if (distance > reach)
|
|
148
|
+
continue;
|
|
149
|
+
const score = distance / reach;
|
|
150
|
+
if (score < bestScore) {
|
|
151
|
+
bestScore = score;
|
|
152
|
+
best = star.id;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (best !== null)
|
|
156
|
+
return best;
|
|
157
|
+
for (const nebula of nebulae) {
|
|
158
|
+
const hit = screen(nebula.position);
|
|
159
|
+
if (hit === null)
|
|
160
|
+
continue;
|
|
161
|
+
if (Math.hypot(hit.x - px, hit.y - py) <= (nebula.radius * perUnit) / hit.depth)
|
|
162
|
+
return nebula.id;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"picker.js","sourceRoot":"","sources":["../../src/controls/picker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The replay: a space's history played back in the order it arrived.
|
|
3
|
+
*
|
|
4
|
+
* Every node the surface can date has an arrival time. A replay runs from
|
|
5
|
+
* the first arrival to the last in a fixed stretch of wall time (scaled by
|
|
6
|
+
* a speed), and says, frame by frame, which nodes have arrived since the
|
|
7
|
+
* last frame and which have left (a seek backwards). A node that arrives
|
|
8
|
+
* flashes, and the flash fades; the camera is untouched, the positions are
|
|
9
|
+
* the final ones, and a node with no arrival time stands throughout.
|
|
10
|
+
*
|
|
11
|
+
* Pure: a clock the caller can drive, no renderer.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/** How long, in wall time, a replay at speed 1 takes from first arrival to last. */
|
|
16
|
+
export declare const REPLAY_WALL_MS: number;
|
|
17
|
+
/** How long an arrival's flash lasts, in wall time. */
|
|
18
|
+
export declare const REPLAY_FLASH_MS: number;
|
|
19
|
+
/**
|
|
20
|
+
* One frame's news.
|
|
21
|
+
*
|
|
22
|
+
* @property at - The replay's moment, in the arrivals' own units (epoch ms).
|
|
23
|
+
* @property arrived - Nodes that arrived since the last frame.
|
|
24
|
+
* @property departed - Nodes that are no longer there (a seek backwards).
|
|
25
|
+
* @property done - The replay has reached the last arrival.
|
|
26
|
+
*/
|
|
27
|
+
export interface ReplayFrame {
|
|
28
|
+
at: number;
|
|
29
|
+
arrived: string[];
|
|
30
|
+
departed: string[];
|
|
31
|
+
done: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A replay's clock.
|
|
35
|
+
*/
|
|
36
|
+
export declare class ReplayClock {
|
|
37
|
+
private readonly clock;
|
|
38
|
+
private readonly order;
|
|
39
|
+
private readonly first;
|
|
40
|
+
private readonly last;
|
|
41
|
+
/** How many of `order` are shown. */
|
|
42
|
+
private shown;
|
|
43
|
+
/** The replay's moment. */
|
|
44
|
+
private moment;
|
|
45
|
+
private speed;
|
|
46
|
+
private running;
|
|
47
|
+
private lastWall;
|
|
48
|
+
private readonly flashes;
|
|
49
|
+
/**
|
|
50
|
+
* @param arrivals - When each node arrived (any monotonic unit; epoch ms
|
|
51
|
+
* for dates). Nodes absent from it stand throughout.
|
|
52
|
+
* @param clock - Wall-clock milliseconds; `Date.now` unless a test drives time.
|
|
53
|
+
*/
|
|
54
|
+
constructor(arrivals: ReadonlyMap<string, number>, clock?: () => number);
|
|
55
|
+
/** @returns The first and last arrivals. */
|
|
56
|
+
span(): [number, number];
|
|
57
|
+
/** @returns Every node with an arrival, in arrival order. */
|
|
58
|
+
ids(): string[];
|
|
59
|
+
/** @returns Whether the replay is advancing. */
|
|
60
|
+
playing(): boolean;
|
|
61
|
+
/** @returns The replay's moment. */
|
|
62
|
+
at(): number;
|
|
63
|
+
/**
|
|
64
|
+
* Plays on, at a speed: 1 crosses the whole history in {@link REPLAY_WALL_MS}.
|
|
65
|
+
*
|
|
66
|
+
* @param speed - How much faster than 1; a replay at its end starts over.
|
|
67
|
+
*/
|
|
68
|
+
play(speed?: number): void;
|
|
69
|
+
/** Holds the replay where it is. */
|
|
70
|
+
pause(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Moves the replay to a moment. Nodes arrived by then are shown at once,
|
|
73
|
+
* without a flash; nodes that arrive after it are hidden again.
|
|
74
|
+
*
|
|
75
|
+
* @param at - The moment, clamped to the span.
|
|
76
|
+
* @returns What changed.
|
|
77
|
+
*/
|
|
78
|
+
seek(at: number): ReplayFrame;
|
|
79
|
+
/**
|
|
80
|
+
* Advances by the wall time since the last frame, if playing. What arrives
|
|
81
|
+
* begins to flash; a replay that reaches the last arrival pauses there.
|
|
82
|
+
*
|
|
83
|
+
* @returns This frame's news.
|
|
84
|
+
*/
|
|
85
|
+
step(): ReplayFrame;
|
|
86
|
+
/**
|
|
87
|
+
* The nodes still flashing, and how bright: 1 as they arrive, fading to 0
|
|
88
|
+
* over {@link REPLAY_FLASH_MS}. A flash that has faded is forgotten, and
|
|
89
|
+
* reported once at 0 so its light can be put back.
|
|
90
|
+
*
|
|
91
|
+
* @returns Each flashing node's brightness.
|
|
92
|
+
*/
|
|
93
|
+
flashing(): Map<string, number>;
|
|
94
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The replay: a space's history played back in the order it arrived.
|
|
3
|
+
*
|
|
4
|
+
* Every node the surface can date has an arrival time. A replay runs from
|
|
5
|
+
* the first arrival to the last in a fixed stretch of wall time (scaled by
|
|
6
|
+
* a speed), and says, frame by frame, which nodes have arrived since the
|
|
7
|
+
* last frame and which have left (a seek backwards). A node that arrives
|
|
8
|
+
* flashes, and the flash fades; the camera is untouched, the positions are
|
|
9
|
+
* the final ones, and a node with no arrival time stands throughout.
|
|
10
|
+
*
|
|
11
|
+
* Pure: a clock the caller can drive, no renderer.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/** How long, in wall time, a replay at speed 1 takes from first arrival to last. */
|
|
16
|
+
export const REPLAY_WALL_MS = 30_000;
|
|
17
|
+
/** How long an arrival's flash lasts, in wall time. */
|
|
18
|
+
export const REPLAY_FLASH_MS = 900;
|
|
19
|
+
/**
|
|
20
|
+
* A replay's clock.
|
|
21
|
+
*/
|
|
22
|
+
export class ReplayClock {
|
|
23
|
+
clock;
|
|
24
|
+
order;
|
|
25
|
+
first;
|
|
26
|
+
last;
|
|
27
|
+
/** How many of `order` are shown. */
|
|
28
|
+
shown = 0;
|
|
29
|
+
/** The replay's moment. */
|
|
30
|
+
moment;
|
|
31
|
+
speed = 1;
|
|
32
|
+
running = false;
|
|
33
|
+
lastWall;
|
|
34
|
+
flashes = new Map();
|
|
35
|
+
/**
|
|
36
|
+
* @param arrivals - When each node arrived (any monotonic unit; epoch ms
|
|
37
|
+
* for dates). Nodes absent from it stand throughout.
|
|
38
|
+
* @param clock - Wall-clock milliseconds; `Date.now` unless a test drives time.
|
|
39
|
+
*/
|
|
40
|
+
constructor(arrivals, clock = Date.now) {
|
|
41
|
+
this.clock = clock;
|
|
42
|
+
this.order = [...arrivals].map(([id, at]) => ({ id, at })).sort((a, b) => a.at - b.at || (a.id < b.id ? -1 : 1));
|
|
43
|
+
this.first = this.order[0]?.at ?? 0;
|
|
44
|
+
this.last = this.order[this.order.length - 1]?.at ?? 0;
|
|
45
|
+
this.moment = this.first;
|
|
46
|
+
this.lastWall = clock();
|
|
47
|
+
}
|
|
48
|
+
/** @returns The first and last arrivals. */
|
|
49
|
+
span() {
|
|
50
|
+
return [this.first, this.last];
|
|
51
|
+
}
|
|
52
|
+
/** @returns Every node with an arrival, in arrival order. */
|
|
53
|
+
ids() {
|
|
54
|
+
return this.order.map((entry) => entry.id);
|
|
55
|
+
}
|
|
56
|
+
/** @returns Whether the replay is advancing. */
|
|
57
|
+
playing() {
|
|
58
|
+
return this.running;
|
|
59
|
+
}
|
|
60
|
+
/** @returns The replay's moment. */
|
|
61
|
+
at() {
|
|
62
|
+
return this.moment;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Plays on, at a speed: 1 crosses the whole history in {@link REPLAY_WALL_MS}.
|
|
66
|
+
*
|
|
67
|
+
* @param speed - How much faster than 1; a replay at its end starts over.
|
|
68
|
+
*/
|
|
69
|
+
play(speed = this.speed) {
|
|
70
|
+
this.speed = Math.max(0.05, speed);
|
|
71
|
+
if (this.moment >= this.last && this.shown >= this.order.length)
|
|
72
|
+
this.seek(this.first);
|
|
73
|
+
this.running = true;
|
|
74
|
+
this.lastWall = this.clock();
|
|
75
|
+
}
|
|
76
|
+
/** Holds the replay where it is. */
|
|
77
|
+
pause() {
|
|
78
|
+
this.running = false;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Moves the replay to a moment. Nodes arrived by then are shown at once,
|
|
82
|
+
* without a flash; nodes that arrive after it are hidden again.
|
|
83
|
+
*
|
|
84
|
+
* @param at - The moment, clamped to the span.
|
|
85
|
+
* @returns What changed.
|
|
86
|
+
*/
|
|
87
|
+
seek(at) {
|
|
88
|
+
this.moment = Math.max(this.first, Math.min(this.last, at));
|
|
89
|
+
const arrived = [];
|
|
90
|
+
const departed = [];
|
|
91
|
+
while (this.shown < this.order.length && this.order[this.shown].at <= this.moment) {
|
|
92
|
+
arrived.push(this.order[this.shown].id);
|
|
93
|
+
this.shown += 1;
|
|
94
|
+
}
|
|
95
|
+
while (this.shown > 0 && this.order[this.shown - 1].at > this.moment) {
|
|
96
|
+
this.shown -= 1;
|
|
97
|
+
const id = this.order[this.shown].id;
|
|
98
|
+
departed.push(id);
|
|
99
|
+
this.flashes.delete(id);
|
|
100
|
+
}
|
|
101
|
+
return { at: this.moment, arrived, departed, done: this.moment >= this.last };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Advances by the wall time since the last frame, if playing. What arrives
|
|
105
|
+
* begins to flash; a replay that reaches the last arrival pauses there.
|
|
106
|
+
*
|
|
107
|
+
* @returns This frame's news.
|
|
108
|
+
*/
|
|
109
|
+
step() {
|
|
110
|
+
const wall = this.clock();
|
|
111
|
+
const elapsed = wall - this.lastWall;
|
|
112
|
+
this.lastWall = wall;
|
|
113
|
+
if (!this.running)
|
|
114
|
+
return { at: this.moment, arrived: [], departed: [], done: this.moment >= this.last };
|
|
115
|
+
const perWall = (this.last - this.first) / REPLAY_WALL_MS * this.speed;
|
|
116
|
+
// A history of one moment is crossed at once.
|
|
117
|
+
const frame = this.seek(perWall > 0 ? this.moment + elapsed * perWall : this.last);
|
|
118
|
+
for (const id of frame.arrived)
|
|
119
|
+
this.flashes.set(id, wall);
|
|
120
|
+
if (frame.done)
|
|
121
|
+
this.running = false;
|
|
122
|
+
return frame;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The nodes still flashing, and how bright: 1 as they arrive, fading to 0
|
|
126
|
+
* over {@link REPLAY_FLASH_MS}. A flash that has faded is forgotten, and
|
|
127
|
+
* reported once at 0 so its light can be put back.
|
|
128
|
+
*
|
|
129
|
+
* @returns Each flashing node's brightness.
|
|
130
|
+
*/
|
|
131
|
+
flashing() {
|
|
132
|
+
const wall = this.clock();
|
|
133
|
+
const out = new Map();
|
|
134
|
+
for (const [id, began] of this.flashes) {
|
|
135
|
+
const t = (wall - began) / REPLAY_FLASH_MS;
|
|
136
|
+
if (t >= 1) {
|
|
137
|
+
this.flashes.delete(id);
|
|
138
|
+
out.set(id, 0);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
out.set(id, 1 - t);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/controls/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The census: every job of every stage.
|
|
3
|
+
*
|
|
4
|
+
* A stage that stands for many jobs is drawn as a shell of them around its
|
|
5
|
+
* place, each job its own sphere (or, under stars, its own point of light);
|
|
6
|
+
* each job is joined to a job of its parent stage — by tubes that carry
|
|
7
|
+
* the feed's pulses while there are few enough of them, by lines past that.
|
|
8
|
+
* A wave down the graph flares every job of a stage together.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import * as THREE from 'three';
|
|
13
|
+
import type { NodeState } from '../types/encoding.js';
|
|
14
|
+
import type { Palette } from './palette.js';
|
|
15
|
+
import type { StarField } from './starField.js';
|
|
16
|
+
import { type TubeField } from './tubeField.js';
|
|
17
|
+
/**
|
|
18
|
+
* One stage as the census draws it.
|
|
19
|
+
*
|
|
20
|
+
* @property id - The stage.
|
|
21
|
+
* @property position - Where it stands.
|
|
22
|
+
* @property radius - Its sphere's radius as a single node.
|
|
23
|
+
* @property count - How many jobs it stands for.
|
|
24
|
+
* @property color - Its hue.
|
|
25
|
+
* @property dim - Faint scenery.
|
|
26
|
+
* @property ember - Some of its jobs failed: under stars, drawn solid over the glow.
|
|
27
|
+
* @property state - What it is doing.
|
|
28
|
+
* @property parents - The stages it hangs from.
|
|
29
|
+
* @property ghost - An anchor, not a stage: it counts in the cloud's centre
|
|
30
|
+
* and in how deep a stage lies, and draws no jobs.
|
|
31
|
+
*/
|
|
32
|
+
export interface CensusNode {
|
|
33
|
+
id: string;
|
|
34
|
+
position: THREE.Vector3;
|
|
35
|
+
radius: number;
|
|
36
|
+
count: number;
|
|
37
|
+
color: THREE.Color;
|
|
38
|
+
dim: boolean;
|
|
39
|
+
ember: boolean;
|
|
40
|
+
state: NodeState;
|
|
41
|
+
parents: ReadonlyArray<string>;
|
|
42
|
+
ghost?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** Where a census cloud stands and how far it reaches, for framing it. */
|
|
45
|
+
export interface CensusCloud {
|
|
46
|
+
center: THREE.Vector3;
|
|
47
|
+
radius: number;
|
|
48
|
+
}
|
|
49
|
+
/** What a census draws with. */
|
|
50
|
+
export interface CensusHost {
|
|
51
|
+
parent: THREE.Object3D;
|
|
52
|
+
stars: StarField;
|
|
53
|
+
tubes: TubeField;
|
|
54
|
+
/** The display's pixel ratio, the canvas height in device pixels, the camera's field of view. */
|
|
55
|
+
starsView(): {
|
|
56
|
+
pixelRatio: number;
|
|
57
|
+
heightPx: number;
|
|
58
|
+
fovDeg: number;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Every job of every stage, drawn. */
|
|
62
|
+
export declare class CensusField {
|
|
63
|
+
private readonly host;
|
|
64
|
+
private mesh;
|
|
65
|
+
private base;
|
|
66
|
+
private memberIds;
|
|
67
|
+
private memberPositions;
|
|
68
|
+
/**
|
|
69
|
+
* @param host - What the census draws with.
|
|
70
|
+
*/
|
|
71
|
+
constructor(host: CensusHost);
|
|
72
|
+
/** Whether the census is drawn as instanced spheres (not stars). */
|
|
73
|
+
drawn(): boolean;
|
|
74
|
+
/** The stage behind each job, in draw order. */
|
|
75
|
+
ids(): ReadonlyArray<string>;
|
|
76
|
+
/** Where each job stands, in draw order. */
|
|
77
|
+
positions(): ReadonlyArray<THREE.Vector3>;
|
|
78
|
+
/**
|
|
79
|
+
* Draws the census.
|
|
80
|
+
*
|
|
81
|
+
* @param nodes - The stages.
|
|
82
|
+
* @param starring - Draw jobs as points of light, not spheres.
|
|
83
|
+
* @param palette - The colours.
|
|
84
|
+
* @returns Where the cloud stands and how far it reaches.
|
|
85
|
+
*/
|
|
86
|
+
build(nodes: ReadonlyArray<CensusNode>, starring: boolean, palette: Palette): CensusCloud;
|
|
87
|
+
/**
|
|
88
|
+
* Flares every job of a stage with the wave, written straight into the
|
|
89
|
+
* instance colours.
|
|
90
|
+
*
|
|
91
|
+
* @param fireAt - When a stage fires, by id; undefined when it does not.
|
|
92
|
+
* @param elapsed - Time since the wave set off.
|
|
93
|
+
* @param pulse - The flare's hue.
|
|
94
|
+
* @param flareMs - How long a flare lasts.
|
|
95
|
+
* @returns The latest a stage fires, so the caller knows when the wave is done.
|
|
96
|
+
*/
|
|
97
|
+
flare(fireAt: (id: string) => number | undefined, elapsed: number, pulse: THREE.Color, flareMs: number): number;
|
|
98
|
+
/**
|
|
99
|
+
* The stage behind the job a ray meets first; the touched job lights up.
|
|
100
|
+
*
|
|
101
|
+
* @param raycaster - The ray, aimed by the caller.
|
|
102
|
+
* @returns The stage, or null.
|
|
103
|
+
*/
|
|
104
|
+
pick(raycaster: THREE.Raycaster): string | null;
|
|
105
|
+
/** Forgets everything: the scene is being redrawn. */
|
|
106
|
+
clear(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Joins the jobs with tubes: each to the job of its parent stage it stands
|
|
109
|
+
* against, in the stage's hue (red into a failed one); a live stage
|
|
110
|
+
* streams, a finished run replays with every job of a stage together.
|
|
111
|
+
*/
|
|
112
|
+
private tubes_build;
|
|
113
|
+
}
|