@fusion.dev/helix-graph 0.120.0 → 0.122.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 +194 -8
- package/dist/connect-graph/arrange.d.ts +20 -0
- package/dist/connect-graph/arrange.js +125 -0
- package/dist/connect-graph/connect-graph.d.ts +47 -5
- package/dist/connect-graph/connect-graph.js +86 -19
- package/dist/connect-graph/diff.d.ts +3 -1
- package/dist/connect-graph/diff.js +2 -1
- package/dist/connect-graph/looks.d.ts +29 -3
- package/dist/connect-graph/looks.js +24 -6
- package/dist/connect-graph/seed.d.ts +3 -0
- package/dist/connect-graph/seed.js +21 -4
- package/dist/connect-graph/shape-field.d.ts +47 -0
- package/dist/connect-graph/shape-field.js +172 -0
- package/dist/connect-graph/stencil.d.ts +50 -0
- package/dist/connect-graph/stencil.js +199 -0
- package/dist/cosmos/cosmos-driver.d.ts +67 -0
- package/dist/cosmos/cosmos-driver.js +419 -0
- package/dist/cosmos/index.d.ts +1 -0
- package/dist/cosmos/index.js +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/overlay/overlay.d.ts +77 -0
- package/dist/overlay/overlay.js +358 -0
- package/dist/shapes/index.d.ts +1 -0
- package/dist/shapes/index.js +1 -0
- package/dist/sigma/node-program.d.ts +71 -0
- package/dist/sigma/node-program.js +170 -0
- package/dist/sigma/sigma-driver.d.ts +31 -3
- package/dist/sigma/sigma-driver.js +125 -140
- package/dist/three/index.d.ts +1 -0
- package/dist/three/index.js +1 -0
- package/dist/three/three-driver.d.ts +32 -0
- package/dist/three/three-driver.js +714 -0
- package/package.json +47 -7
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GraphDriver } from '../connect-graph/connect-graph.js';
|
|
2
|
+
import type { NodeLook } from '../connect-graph/looks.js';
|
|
2
3
|
/** A graphology graph, as far as the driver calls it. */
|
|
3
4
|
export interface SigmaGraph {
|
|
4
5
|
addEdge(source: string, target: string, attributes?: Record<string, unknown>): unknown;
|
|
@@ -57,16 +58,19 @@ export interface SigmaRenderer {
|
|
|
57
58
|
x: number;
|
|
58
59
|
y: number;
|
|
59
60
|
} | undefined;
|
|
60
|
-
off(type: 'clickNode', listener: (payload: {
|
|
61
|
+
off(type: 'clickNode' | 'enterNode' | 'leaveNode', listener: (payload: {
|
|
61
62
|
node: string;
|
|
62
63
|
}) => void): unknown;
|
|
63
|
-
|
|
64
|
+
off(type: 'afterRender' | 'beforeRender', listener: () => void): unknown;
|
|
65
|
+
on(type: 'clickNode' | 'enterNode' | 'leaveNode', listener: (payload: {
|
|
64
66
|
node: string;
|
|
65
67
|
}) => void): unknown;
|
|
68
|
+
on(type: 'afterRender' | 'beforeRender', listener: () => void): unknown;
|
|
69
|
+
getSetting(name: 'nodeProgramClasses'): Record<string, unknown>;
|
|
66
70
|
refresh(): unknown;
|
|
67
71
|
resize(force?: boolean): unknown;
|
|
68
72
|
scaleSize(size?: number): number;
|
|
69
|
-
setSetting(name: 'defaultDrawNodeHover' | 'edgeReducer' | 'labelColor' | 'labelFont' | 'labelRenderedSizeThreshold' | 'nodeReducer', value: unknown): unknown;
|
|
73
|
+
setSetting(name: 'defaultDrawNodeHover' | 'edgeReducer' | 'labelColor' | 'labelFont' | 'defaultDrawNodeLabel' | 'labelGridCellSize' | 'labelRenderedSizeThreshold' | 'labelSize' | 'minEdgeThickness' | 'nodeProgramClasses' | 'nodeReducer', value: unknown): unknown;
|
|
70
74
|
viewportToFramedGraph(point: {
|
|
71
75
|
x: number;
|
|
72
76
|
y: number;
|
|
@@ -100,6 +104,30 @@ export interface SigmaDriverOptions {
|
|
|
100
104
|
* Without one, notes stay where the connector started them.
|
|
101
105
|
*/
|
|
102
106
|
layout?: ((graph: SigmaGraph) => SigmaLayout) | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* How heavily a note is drawn where it does not say: `solid`, filled, by
|
|
109
|
+
* default; `outlined`, a ring; `subtle`, a tinted fill with a ring.
|
|
110
|
+
*/
|
|
111
|
+
emphasis?: NodeLook['emphasis'];
|
|
112
|
+
/**
|
|
113
|
+
* What a note is drawn as made of where it does not say: flat by default,
|
|
114
|
+
* `glass`, a shaded sphere with a highlight and a lit rim, or `frosted`, a
|
|
115
|
+
* milky body with a soft fall of light.
|
|
116
|
+
*/
|
|
117
|
+
material?: NodeLook['material'];
|
|
118
|
+
/** A note's shape where it does not say: `circle` by default, `square`, `diamond` or `ring`. */
|
|
119
|
+
shape?: NodeLook['shape'];
|
|
120
|
+
/**
|
|
121
|
+
* Whether each group's name is drawn over its notes. On by default, which
|
|
122
|
+
* is how a reader finds their way while notes are named only on hover.
|
|
123
|
+
*/
|
|
124
|
+
groupLabels?: boolean | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* Which notes are named. `hover`, by default: the note under the pointer,
|
|
127
|
+
* the selected note and its neighbors. `hubs`: the busiest notes as well, at
|
|
128
|
+
* rest. `all`: every note, where its name does not cover another.
|
|
129
|
+
*/
|
|
130
|
+
labels?: 'all' | 'hover' | 'hubs' | undefined;
|
|
103
131
|
/** The longest a layout runs, in milliseconds, settled or not. 10 seconds by default, about where 10,000 notes settle. */
|
|
104
132
|
layoutLimit?: number | undefined;
|
|
105
133
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { ease, gesturesHeld, groupsOver, signalsOver } from '../overlay/overlay.js';
|
|
2
|
+
import { EMPHASES, HelixNodeProgram, MATERIALS, SHAPES } from './node-program.js';
|
|
1
3
|
const STEP = 250;
|
|
2
4
|
const ZOOM = 1.5;
|
|
3
5
|
const SAMPLE = 250;
|
|
4
6
|
const STILL = 0.004;
|
|
5
7
|
const HELD = 2;
|
|
6
8
|
const IDLE = 2000;
|
|
7
|
-
const pixels = (size) =>
|
|
9
|
+
const pixels = (size) => 1.6 + 1.35 * Math.max(size, 0);
|
|
8
10
|
const hoverOf = (looks) => (context, data, settings) => {
|
|
9
11
|
if (!data.label) {
|
|
10
12
|
return;
|
|
@@ -25,11 +27,33 @@ const hoverOf = (looks) => (context, data, settings) => {
|
|
|
25
27
|
context.fillStyle = looks.ink;
|
|
26
28
|
context.fillText(data.label, x, data.y + size / 3);
|
|
27
29
|
};
|
|
28
|
-
const TRAVEL = 700;
|
|
29
|
-
const PULSE = 450;
|
|
30
30
|
const GROW = 300;
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const labelOf = (looks, claim) => (context, data, settings) => {
|
|
32
|
+
if (!data.label) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const strong = data.strong === true;
|
|
36
|
+
const size = strong ? settings.labelSize : settings.labelSize - 1;
|
|
37
|
+
context.font = `${strong ? 600 : 500} ${size}px ${settings.labelFont}`;
|
|
38
|
+
const x = data.x + data.size + 4;
|
|
39
|
+
const y = data.y + size / 3;
|
|
40
|
+
const box = {
|
|
41
|
+
bottom: y + 4,
|
|
42
|
+
left: x - 2,
|
|
43
|
+
right: x + context.measureText(data.label).width + 2,
|
|
44
|
+
top: y - size,
|
|
45
|
+
};
|
|
46
|
+
if (!claim(box, data.forceLabel === true)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
context.lineJoin = 'round';
|
|
50
|
+
context.lineWidth = 3;
|
|
51
|
+
context.strokeStyle = looks.ground;
|
|
52
|
+
context.strokeText(data.label, x, y);
|
|
53
|
+
context.fillStyle = strong ? looks.ink : looks.quiet;
|
|
54
|
+
context.fillText(data.label, x, y);
|
|
55
|
+
};
|
|
56
|
+
const HUB = 6.5;
|
|
33
57
|
const positionsOf = (graph) => {
|
|
34
58
|
const positions = new Map();
|
|
35
59
|
graph.forEachNode((node, attributes) => {
|
|
@@ -41,35 +65,8 @@ export const sigmaDriver = (sigma, options = {}) => {
|
|
|
41
65
|
const graph = sigma.getGraph();
|
|
42
66
|
const container = sigma.getContainer();
|
|
43
67
|
const camera = sigma.getCamera();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const hold = (event) => {
|
|
47
|
-
if (mode === 'free') {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (mode === 'off') {
|
|
51
|
-
event.stopImmediatePropagation();
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (event instanceof WheelEvent) {
|
|
55
|
-
if (!(event.ctrlKey || event.metaKey)) {
|
|
56
|
-
event.stopImmediatePropagation();
|
|
57
|
-
}
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (typeof TouchEvent !== 'undefined' && event instanceof TouchEvent) {
|
|
61
|
-
if (event.touches.length < 2) {
|
|
62
|
-
event.stopImmediatePropagation();
|
|
63
|
-
}
|
|
64
|
-
else if (event.type === 'touchstart' && event.cancelable) {
|
|
65
|
-
event.preventDefault();
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
const held = ['wheel', 'touchstart', 'touchmove', 'mousedown'];
|
|
70
|
-
for (const type of held) {
|
|
71
|
-
container.addEventListener(type, hold, { capture: true, passive: false });
|
|
72
|
-
}
|
|
68
|
+
const programs = sigma.getSetting('nodeProgramClasses');
|
|
69
|
+
sigma.setSetting('nodeProgramClasses', { ...programs, helix: HelixNodeProgram });
|
|
73
70
|
const sized = typeof ResizeObserver === 'function'
|
|
74
71
|
? new ResizeObserver(() => {
|
|
75
72
|
sigma.resize(true);
|
|
@@ -77,10 +74,34 @@ export const sigmaDriver = (sigma, options = {}) => {
|
|
|
77
74
|
})
|
|
78
75
|
: undefined;
|
|
79
76
|
sized?.observe(container);
|
|
80
|
-
let
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
let hovered;
|
|
78
|
+
const enter = ({ node }) => {
|
|
79
|
+
hovered = node;
|
|
80
|
+
sigma.refresh();
|
|
81
|
+
};
|
|
82
|
+
const leave = () => {
|
|
83
|
+
hovered = undefined;
|
|
84
|
+
sigma.refresh();
|
|
85
|
+
};
|
|
86
|
+
sigma.on('enterNode', enter);
|
|
87
|
+
sigma.on('leaveNode', leave);
|
|
88
|
+
const naming = options.labels ?? 'hover';
|
|
89
|
+
let taken = [];
|
|
90
|
+
const claim = (box, must) => {
|
|
91
|
+
const free = taken.every((other) => box.right < other.left ||
|
|
92
|
+
box.left > other.right ||
|
|
93
|
+
box.bottom < other.top ||
|
|
94
|
+
box.top > other.bottom);
|
|
95
|
+
if (free || must) {
|
|
96
|
+
taken.push(box);
|
|
97
|
+
}
|
|
98
|
+
return free || must;
|
|
99
|
+
};
|
|
100
|
+
const clear = () => {
|
|
101
|
+
taken = [];
|
|
102
|
+
};
|
|
103
|
+
sigma.on('beforeRender', clear);
|
|
104
|
+
const held = gesturesHeld(container);
|
|
84
105
|
const onScreen = (id) => {
|
|
85
106
|
const data = sigma.getNodeDisplayData(id);
|
|
86
107
|
if (!data || data.hidden) {
|
|
@@ -89,76 +110,9 @@ export const sigmaDriver = (sigma, options = {}) => {
|
|
|
89
110
|
const at = sigma.framedGraphToViewport(data);
|
|
90
111
|
return { radius: sigma.scaleSize(data.size), x: at.x, y: at.y };
|
|
91
112
|
};
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
flights = [];
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const ratio = window.devicePixelRatio || 1;
|
|
99
|
-
const width = Math.round(container.clientWidth * ratio);
|
|
100
|
-
const height = Math.round(container.clientHeight * ratio);
|
|
101
|
-
if (overlay.width !== width || overlay.height !== height) {
|
|
102
|
-
overlay.width = width;
|
|
103
|
-
overlay.height = height;
|
|
104
|
-
}
|
|
105
|
-
context.setTransform(ratio, 0, 0, ratio, 0, 0);
|
|
106
|
-
context.clearRect(0, 0, width, height);
|
|
107
|
-
flights = flights.filter((flight) => {
|
|
108
|
-
const from = onScreen(flight.from);
|
|
109
|
-
const to = onScreen(flight.to);
|
|
110
|
-
const gone = (now - flight.started) / TRAVEL;
|
|
111
|
-
if (!from || !to || gone >= 1 + PULSE / TRAVEL) {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
context.save();
|
|
115
|
-
context.strokeStyle = flight.color;
|
|
116
|
-
context.fillStyle = flight.color;
|
|
117
|
-
if (gone < 1) {
|
|
118
|
-
if (flight.animate) {
|
|
119
|
-
const along = ease(gone);
|
|
120
|
-
const x = from.x + (to.x - from.x) * along;
|
|
121
|
-
const y = from.y + (to.y - from.y) * along;
|
|
122
|
-
context.globalAlpha = 0.6;
|
|
123
|
-
context.lineWidth = 2;
|
|
124
|
-
context.beginPath();
|
|
125
|
-
context.moveTo(from.x, from.y);
|
|
126
|
-
context.lineTo(x, y);
|
|
127
|
-
context.stroke();
|
|
128
|
-
context.globalAlpha = 1;
|
|
129
|
-
context.shadowBlur = 14;
|
|
130
|
-
context.shadowColor = flight.color;
|
|
131
|
-
context.beginPath();
|
|
132
|
-
context.arc(x, y, 5, 0, Math.PI * 2);
|
|
133
|
-
context.fill();
|
|
134
|
-
context.shadowBlur = 0;
|
|
135
|
-
context.fillStyle = 'rgba(255, 255, 255, 0.85)';
|
|
136
|
-
context.beginPath();
|
|
137
|
-
context.arc(x, y, 2, 0, Math.PI * 2);
|
|
138
|
-
context.fill();
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
context.globalAlpha = 1 - gone;
|
|
142
|
-
context.lineWidth = 2;
|
|
143
|
-
context.beginPath();
|
|
144
|
-
context.moveTo(from.x, from.y);
|
|
145
|
-
context.lineTo(to.x, to.y);
|
|
146
|
-
context.stroke();
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
else if (flight.animate) {
|
|
150
|
-
const opened = (gone - 1) / (PULSE / TRAVEL);
|
|
151
|
-
context.globalAlpha = 1 - opened;
|
|
152
|
-
context.lineWidth = 2.5;
|
|
153
|
-
context.beginPath();
|
|
154
|
-
context.arc(to.x, to.y, to.radius + 2 + opened * 16, 0, Math.PI * 2);
|
|
155
|
-
context.stroke();
|
|
156
|
-
}
|
|
157
|
-
context.restore();
|
|
158
|
-
return true;
|
|
159
|
-
});
|
|
160
|
-
flying = flights.length > 0 ? requestAnimationFrame(fly) : 0;
|
|
161
|
-
};
|
|
113
|
+
const signals = signalsOver(container, onScreen);
|
|
114
|
+
const groups = groupsOver(container, onScreen);
|
|
115
|
+
sigma.on('afterRender', groups.draw);
|
|
162
116
|
const grow = (sizes) => {
|
|
163
117
|
const started = performance.now();
|
|
164
118
|
const step = (now) => {
|
|
@@ -208,38 +162,83 @@ export const sigmaDriver = (sigma, options = {}) => {
|
|
|
208
162
|
}
|
|
209
163
|
}
|
|
210
164
|
},
|
|
165
|
+
fit: (ids, animate) => {
|
|
166
|
+
let low = { x: Number.POSITIVE_INFINITY, y: Number.POSITIVE_INFINITY };
|
|
167
|
+
let high = { x: Number.NEGATIVE_INFINITY, y: Number.NEGATIVE_INFINITY };
|
|
168
|
+
for (const id of ids) {
|
|
169
|
+
const at = sigma.getNodeDisplayData(id);
|
|
170
|
+
if (at) {
|
|
171
|
+
low = { x: Math.min(low.x, at.x), y: Math.min(low.y, at.y) };
|
|
172
|
+
high = { x: Math.max(high.x, at.x), y: Math.max(high.y, at.y) };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!Number.isFinite(low.x)) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const state = {
|
|
179
|
+
ratio: Math.min(Math.max(Math.max(high.x - low.x, high.y - low.y) * 2.4, 0.12), 1),
|
|
180
|
+
x: (low.x + high.x) / 2,
|
|
181
|
+
y: (low.y + high.y) / 2,
|
|
182
|
+
};
|
|
183
|
+
if (animate) {
|
|
184
|
+
camera.animate(state, { duration: STEP * 2 });
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
camera.setState(state);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
211
190
|
gestures: (next) => {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
191
|
+
held.set(next);
|
|
192
|
+
},
|
|
193
|
+
groups: (next) => {
|
|
194
|
+
if (options.groupLabels !== false) {
|
|
195
|
+
groups.set(next);
|
|
196
|
+
}
|
|
215
197
|
},
|
|
216
198
|
look: (looks) => {
|
|
217
|
-
|
|
199
|
+
signals.look(looks);
|
|
200
|
+
groups.look(looks);
|
|
218
201
|
sigma.setSetting('labelColor', { color: looks.ink });
|
|
202
|
+
sigma.setSetting('labelSize', 13);
|
|
203
|
+
sigma.setSetting('minEdgeThickness', 0.8);
|
|
219
204
|
sigma.setSetting('labelFont', looks.font);
|
|
220
205
|
sigma.setSetting('labelRenderedSizeThreshold', 0);
|
|
206
|
+
sigma.setSetting('labelGridCellSize', 130);
|
|
207
|
+
sigma.setSetting('defaultDrawNodeLabel', labelOf(looks, claim));
|
|
221
208
|
sigma.setSetting('defaultDrawNodeHover', hoverOf(looks));
|
|
222
209
|
sigma.setSetting('nodeReducer', (node, data) => {
|
|
223
210
|
const look = looks.node(node);
|
|
224
211
|
const grown = look.selected ? 1.5 : look.raised ? 1.25 : 1;
|
|
212
|
+
const named = look.label === 'always' ||
|
|
213
|
+
(look.label === 'room' &&
|
|
214
|
+
(look.raised ||
|
|
215
|
+
naming === 'all' ||
|
|
216
|
+
(naming === 'hubs' && typeof data.size === 'number' && data.size >= HUB)));
|
|
225
217
|
return {
|
|
226
218
|
...data,
|
|
227
219
|
color: look.color,
|
|
220
|
+
emphasisCode: EMPHASES[look.emphasis ?? options.emphasis ?? 'solid'],
|
|
228
221
|
forceLabel: look.label === 'always',
|
|
222
|
+
glow: look.glow,
|
|
229
223
|
hidden: look.hidden,
|
|
230
|
-
label:
|
|
224
|
+
label: named ? data.label : null,
|
|
225
|
+
materialCode: MATERIALS[look.material ?? options.material ?? 'flat'],
|
|
226
|
+
shapeCode: SHAPES[look.shape ?? options.shape ?? 'circle'],
|
|
231
227
|
size: typeof data.size === 'number' ? data.size * grown : data.size,
|
|
228
|
+
strong: look.selected || look.raised,
|
|
229
|
+
type: 'helix',
|
|
232
230
|
zIndex: look.selected ? 2 : look.raised ? 1 : 0,
|
|
233
231
|
};
|
|
234
232
|
});
|
|
235
233
|
sigma.setSetting('edgeReducer', (_edge, data) => {
|
|
236
234
|
const look = looks.link(String(data.from), String(data.to));
|
|
235
|
+
const lit = hovered !== undefined && (data.from === hovered || data.to === hovered);
|
|
237
236
|
return {
|
|
238
237
|
...data,
|
|
239
|
-
color: look.color,
|
|
238
|
+
color: lit ? looks.node(hovered).color : look.color,
|
|
240
239
|
hidden: look.hidden,
|
|
241
|
-
size: look.raised ? 1.
|
|
242
|
-
zIndex: look.raised ? 1 : 0,
|
|
240
|
+
size: look.raised || lit ? 1.6 : 0.8,
|
|
241
|
+
zIndex: look.raised || lit ? 1 : 0,
|
|
243
242
|
};
|
|
244
243
|
});
|
|
245
244
|
sigma.refresh();
|
|
@@ -261,33 +260,19 @@ export const sigmaDriver = (sigma, options = {}) => {
|
|
|
261
260
|
camera.setState(to);
|
|
262
261
|
}
|
|
263
262
|
},
|
|
264
|
-
signal: (from, to,
|
|
265
|
-
|
|
266
|
-
overlay = document.createElement('canvas');
|
|
267
|
-
overlay.setAttribute('aria-hidden', 'true');
|
|
268
|
-
overlay.style.cssText =
|
|
269
|
-
'position:absolute;inset:0;width:100%;height:100%;pointer-events:none';
|
|
270
|
-
container.append(overlay);
|
|
271
|
-
}
|
|
272
|
-
flights.push({ animate, color: color ?? ink, from, started: performance.now(), to });
|
|
273
|
-
if (flights.length > FLYING) {
|
|
274
|
-
flights = flights.slice(-FLYING);
|
|
275
|
-
}
|
|
276
|
-
if (!flying) {
|
|
277
|
-
flying = requestAnimationFrame(fly);
|
|
278
|
-
}
|
|
263
|
+
signal: (from, to, colors, animate) => {
|
|
264
|
+
signals.signal(from, to, colors, animate);
|
|
279
265
|
},
|
|
280
266
|
stop: () => {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
267
|
+
sigma.off('afterRender', groups.draw);
|
|
268
|
+
sigma.off('beforeRender', clear);
|
|
269
|
+
groups.stop();
|
|
270
|
+
signals.stop();
|
|
271
|
+
sigma.off('enterNode', enter);
|
|
272
|
+
sigma.off('leaveNode', leave);
|
|
273
|
+
sigma.setSetting('nodeProgramClasses', programs);
|
|
286
274
|
sized?.disconnect();
|
|
287
|
-
|
|
288
|
-
container.removeEventListener(type, hold, { capture: true });
|
|
289
|
-
}
|
|
290
|
-
container.style.touchAction = touchAction;
|
|
275
|
+
held.stop();
|
|
291
276
|
},
|
|
292
277
|
update: (changes, animate) => {
|
|
293
278
|
for (const id of changes.removeNodes) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type ThreeDriverOptions, threeDriver } from './three-driver.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { threeDriver } from './three-driver.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { GraphDriver } from '../connect-graph/connect-graph.js';
|
|
2
|
+
import type { NodeLook } from '../connect-graph/looks.js';
|
|
3
|
+
/** How the three.js driver behaves where the page does not say. */
|
|
4
|
+
export interface ThreeDriverOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Whether the graph turns slowly while nobody is handling it. On by
|
|
7
|
+
* default, and never for a reader who asks for less motion.
|
|
8
|
+
*/
|
|
9
|
+
autoRotate?: boolean | undefined;
|
|
10
|
+
/** Whether each group's name is drawn over its notes. On by default. */
|
|
11
|
+
groupLabels?: boolean | undefined;
|
|
12
|
+
/** The longest a layout runs, in milliseconds, settled or not. 10 seconds by default. */
|
|
13
|
+
layoutLimit?: number | undefined;
|
|
14
|
+
/** A note's shape where it does not say: `circle` by default, `square` or `diamond`. */
|
|
15
|
+
shape?: NodeLook['shape'];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Drives three.js for a Graph frame: the graph drawn in three dimensions, to
|
|
19
|
+
* be turned and looked into. Pass the box inside the frame the drawing goes
|
|
20
|
+
* in, and `connectGraph` fills it:
|
|
21
|
+
*
|
|
22
|
+
* ```js
|
|
23
|
+
* connectGraph(frame, threeDriver(box))
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* **A layout runs in three dimensions**, with d3-force-3d, where notes have no
|
|
27
|
+
* place; with a solid `stencil`, the notes are drawn in its shape and none
|
|
28
|
+
* runs. Dragging turns the graph, the wheel and a pinch bring it nearer, and
|
|
29
|
+
* it turns slowly by itself while nobody is handling it, unless the reader
|
|
30
|
+
* asks for less motion.
|
|
31
|
+
*/
|
|
32
|
+
export declare const threeDriver: (container: HTMLElement, options?: ThreeDriverOptions) => GraphDriver;
|