@fusion.dev/helix-graph 0.213.0 → 0.215.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 -5
- package/dist/overlay/overlay.d.ts +4 -2
- package/dist/overlay/overlay.js +2 -2
- package/dist/sigma/edge-program.d.ts +14 -77
- package/dist/sigma/edge-program.js +36 -306
- package/dist/sigma/index.d.ts +2 -1
- package/dist/sigma/index.js +1 -1
- package/dist/sigma/node-program.d.ts +19 -49
- package/dist/sigma/node-program.js +65 -155
- package/dist/sigma/sigma-driver.d.ts +28 -8
- package/dist/sigma/sigma-driver.js +142 -135
- package/dist/sigma/sigma-options.d.ts +72 -0
- package/dist/sigma/sigma-options.js +36 -0
- package/package.json +11 -7
|
@@ -1,115 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export const SHAPES = {
|
|
2
|
+
circle: 'helixCircle',
|
|
3
|
+
diamond: 'helixDiamond',
|
|
4
|
+
ring: 'helixCircle',
|
|
5
|
+
square: 'helixSquare',
|
|
6
|
+
};
|
|
4
7
|
export const EMPHASES = { outlined: 1, solid: 0, subtle: 2 };
|
|
5
8
|
export const MATERIALS = { flat: 0, frosted: 2, glass: 1, mark: 3 };
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
attribute float a_size;
|
|
12
|
-
attribute float a_shape;
|
|
13
|
-
attribute float a_emphasis;
|
|
14
|
-
attribute float a_material;
|
|
15
|
-
attribute float a_glow;
|
|
16
|
-
attribute float a_angle;
|
|
17
|
-
|
|
18
|
-
uniform mat3 u_matrix;
|
|
19
|
-
uniform float u_sizeRatio;
|
|
20
|
-
uniform float u_correctionRatio;
|
|
21
|
-
|
|
22
|
-
varying vec4 v_color;
|
|
23
|
-
varying vec2 v_diffVector;
|
|
24
|
-
varying float v_radius;
|
|
25
|
-
varying float v_shape;
|
|
26
|
-
varying float v_emphasis;
|
|
27
|
-
varying float v_material;
|
|
28
|
-
varying float v_glow;
|
|
29
|
-
|
|
30
|
-
const float bias = 255.0 / 254.0;
|
|
31
|
-
|
|
32
|
-
void main() {
|
|
33
|
-
float size = a_size * u_correctionRatio / u_sizeRatio * 4.0;
|
|
34
|
-
vec2 diffVector = size * ${REACH.toFixed(2)} * vec2(cos(a_angle), sin(a_angle));
|
|
35
|
-
gl_Position = vec4((u_matrix * vec3(a_position + diffVector, 1)).xy, 0, 1);
|
|
36
|
-
v_diffVector = diffVector;
|
|
37
|
-
v_radius = size / 2.0;
|
|
38
|
-
v_shape = a_shape;
|
|
39
|
-
v_emphasis = a_emphasis;
|
|
40
|
-
v_material = a_material;
|
|
41
|
-
v_glow = a_glow;
|
|
42
|
-
#ifdef PICKING_MODE
|
|
43
|
-
v_color = a_id;
|
|
44
|
-
#else
|
|
45
|
-
v_color = a_color;
|
|
46
|
-
#endif
|
|
47
|
-
v_color.a *= bias;
|
|
9
|
+
export const DIAMOND = 0.86 / Math.SQRT1_2;
|
|
10
|
+
const circle = {
|
|
11
|
+
glsl: `
|
|
12
|
+
float sdf_helixCircle(vec2 uv, float size) {
|
|
13
|
+
return length(uv) - size;
|
|
48
14
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
varying float v_material;
|
|
59
|
-
varying float v_glow;
|
|
60
|
-
|
|
61
|
-
uniform float u_correctionRatio;
|
|
62
|
-
|
|
63
|
-
/* How far a point is outside the note's shape, in radii: below zero inside. */
|
|
64
|
-
float outside(vec2 p, float shape) {
|
|
65
|
-
if (shape > 0.5 && shape < 1.5) {
|
|
66
|
-
vec2 q = abs(p) - vec2(0.7);
|
|
67
|
-
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - 0.18;
|
|
68
|
-
}
|
|
69
|
-
if (shape > 1.5 && shape < 2.5) {
|
|
70
|
-
return (abs(p.x) + abs(p.y)) * 0.7071 - 0.86;
|
|
71
|
-
}
|
|
72
|
-
return length(p) - 1.0;
|
|
15
|
+
`,
|
|
16
|
+
name: 'helixCircle',
|
|
17
|
+
uniforms: [],
|
|
18
|
+
};
|
|
19
|
+
const square = {
|
|
20
|
+
glsl: `
|
|
21
|
+
float sdf_helixSquare(vec2 uv, float size) {
|
|
22
|
+
vec2 q = abs(uv) - vec2(0.7 * size);
|
|
23
|
+
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - 0.18 * size;
|
|
73
24
|
}
|
|
25
|
+
`,
|
|
26
|
+
inradiusFactor: 0.88 / (0.7 * Math.SQRT2 + 0.18),
|
|
27
|
+
name: 'helixSquare',
|
|
28
|
+
uniforms: [],
|
|
29
|
+
};
|
|
30
|
+
const diamond = {
|
|
31
|
+
glsl: `
|
|
32
|
+
float sdf_helixDiamond(vec2 uv, float size) {
|
|
33
|
+
return (abs(uv.x) + abs(uv.y)) * 0.7071 - 0.7071 * size;
|
|
34
|
+
}
|
|
35
|
+
`,
|
|
36
|
+
inradiusFactor: Math.SQRT1_2,
|
|
37
|
+
name: 'helixDiamond',
|
|
38
|
+
uniforms: [],
|
|
39
|
+
};
|
|
40
|
+
const { FLOAT, UNSIGNED_BYTE } = WebGL2RenderingContext;
|
|
41
|
+
const look = {
|
|
42
|
+
attributes: [
|
|
43
|
+
{ name: 'helixColor', normalized: true, size: 4, source: 'color', type: UNSIGNED_BYTE },
|
|
44
|
+
{ name: 'helixEmphasis', size: 1, source: 'emphasisCode', type: FLOAT },
|
|
45
|
+
{ name: 'helixMaterial', size: 1, source: 'materialCode', type: FLOAT },
|
|
46
|
+
],
|
|
47
|
+
glsl: `
|
|
48
|
+
vec4 layer_helix(vec4 v_helixColor, float v_helixEmphasis, float v_helixMaterial) {
|
|
49
|
+
vec2 p = context.uv;
|
|
50
|
+
float d = context.sdf;
|
|
51
|
+
float aa = context.aaWidth;
|
|
74
52
|
|
|
75
|
-
void main(void) {
|
|
76
|
-
vec2 p = v_diffVector / v_radius;
|
|
77
|
-
float d = outside(p, v_shape);
|
|
78
|
-
|
|
79
|
-
#ifdef PICKING_MODE
|
|
80
|
-
if (d > 0.0) discard;
|
|
81
|
-
gl_FragColor = v_color;
|
|
82
|
-
#else
|
|
83
|
-
float aa = u_correctionRatio * 2.0 / v_radius;
|
|
84
|
-
float inside = 1.0 - smoothstep(-aa, 0.0, d);
|
|
85
|
-
|
|
86
|
-
/* A ring is a circle drawn outlined, whatever its emphasis says. */
|
|
87
|
-
float emphasis = v_shape > 2.5 ? 1.0 : v_emphasis;
|
|
88
53
|
float edge = smoothstep(-0.32 - aa, -0.32, d);
|
|
89
|
-
float fill =
|
|
54
|
+
float fill = v_helixEmphasis > 1.5 ? mix(0.3, 1.0, edge) : v_helixEmphasis > 0.5 ? edge : 1.0;
|
|
90
55
|
|
|
91
|
-
vec3 color =
|
|
56
|
+
vec3 color = v_helixColor.rgb;
|
|
92
57
|
float alpha = 1.0;
|
|
93
58
|
/* How large the note is drawn, in pixels: a highlight that suits a hub is
|
|
94
59
|
a speck of noise on a leaf, so the detail grows in with the size. */
|
|
95
|
-
float drawn = smoothstep(4.0, 14.0,
|
|
60
|
+
float drawn = smoothstep(4.0, 14.0, 1.0 / context.pixelToUV);
|
|
96
61
|
/* Lit from above on the left, as every surface here is. */
|
|
97
62
|
float lit = clamp(0.5 + 0.5 * dot(normalize(vec2(-0.45, 0.9)), p), 0.0, 1.0);
|
|
98
63
|
/* A fine rim a shade darker, so notes that touch stay two. */
|
|
99
64
|
float rim = smoothstep(-0.2 - aa, -0.2, d);
|
|
100
|
-
if (
|
|
65
|
+
if (v_helixMaterial > 0.5 && v_helixMaterial < 1.5) {
|
|
101
66
|
/* Glass: shaded like a sphere, a highlight, and light at its rim. */
|
|
102
67
|
color *= mix(0.72, 1.12, lit);
|
|
103
68
|
float highlight = exp(-pow(length(p - vec2(-0.32, 0.38)) / 0.26, 2.0));
|
|
104
69
|
color = mix(color, vec3(1.0), highlight * mix(0.25, 0.6, drawn));
|
|
105
70
|
color = mix(color, vec3(1.0), rim * 0.22 * drawn);
|
|
106
71
|
alpha = 0.94;
|
|
107
|
-
} else if (
|
|
108
|
-
/* Mark: a flat dot of its color, as a marker on a table is.
|
|
109
|
-
|
|
110
|
-
drawn around it below. */
|
|
72
|
+
} else if (v_helixMaterial > 2.5) {
|
|
73
|
+
/* Mark: a flat dot of its color, as a marker on a table is. What says it
|
|
74
|
+
is a mark is the band and the light its backdrop draws around it. */
|
|
111
75
|
alpha = 1.0;
|
|
112
|
-
} else if (
|
|
76
|
+
} else if (v_helixMaterial > 1.5) {
|
|
113
77
|
/* Frosted: a milky body, a soft fall of light, and no sharp highlight. */
|
|
114
78
|
color = mix(color * mix(0.86, 1.06, lit), vec3(1.0), 0.16);
|
|
115
79
|
color = mix(color, vec3(1.0), rim * 0.14);
|
|
@@ -123,68 +87,14 @@ void main(void) {
|
|
|
123
87
|
color = mix(color, color * 0.72, rim * 0.6);
|
|
124
88
|
}
|
|
125
89
|
|
|
126
|
-
|
|
127
|
-
/* The glow, outside the shape: a crisp ring just clear of the edge and a
|
|
128
|
-
soft light falling away beyond it, both in the note's own color. */
|
|
129
|
-
float reach = ${(REACH - 1).toFixed(2)};
|
|
130
|
-
float ring = 1.0 - smoothstep(0.0, 0.07 + aa, abs(d - 0.32));
|
|
131
|
-
/* The light falls away more slowly and further than it did, so the
|
|
132
|
-
selected note sits in a bloom rather than wearing a ring: the halo the
|
|
133
|
-
aura gives the stars its pointer gathers. */
|
|
134
|
-
float falloff = exp(-d * 1.7) * (1.0 - smoothstep(reach * 0.8, reach, d));
|
|
135
|
-
float halo = v_glow * step(0.0, d) * clamp(ring * 0.8 + falloff * 0.85, 0.0, 1.0) * v_color.a;
|
|
136
|
-
/* A mark's own halo, selected or not: a band of its color at a fifth just
|
|
137
|
-
outside the dot, and a soft glow beyond, the way a marker on a table
|
|
138
|
-
carries a ring of its color and a light around it. */
|
|
139
|
-
float band = (1.0 - smoothstep(0.34, 0.42, d)) * 0.22;
|
|
140
|
-
float light = exp(-d * 2.0) * (1.0 - smoothstep(reach * 0.8, reach, d)) * 0.3;
|
|
141
|
-
float mark = v_material > 2.5 ? step(0.0, d) * (band + light) * v_color.a : 0.0;
|
|
142
|
-
halo = clamp(halo + mark, 0.0, 1.0);
|
|
143
|
-
/* Premultiplied, as Sigma blends: the body over its glow. */
|
|
144
|
-
gl_FragColor = vec4(color * body + v_color.rgb * halo * (1.0 - body), body + halo * (1.0 - body));
|
|
145
|
-
#endif
|
|
146
|
-
}
|
|
147
|
-
`;
|
|
148
|
-
const { FLOAT, UNSIGNED_BYTE } = WebGLRenderingContext;
|
|
149
|
-
const UNIFORMS = ['u_sizeRatio', 'u_correctionRatio', 'u_matrix'];
|
|
150
|
-
export class HelixNodeProgram extends NodeProgram {
|
|
151
|
-
getDefinition() {
|
|
152
|
-
return {
|
|
153
|
-
ATTRIBUTES: [
|
|
154
|
-
{ name: 'a_position', size: 2, type: FLOAT },
|
|
155
|
-
{ name: 'a_size', size: 1, type: FLOAT },
|
|
156
|
-
{ name: 'a_color', normalized: true, size: 4, type: UNSIGNED_BYTE },
|
|
157
|
-
{ name: 'a_id', normalized: true, size: 4, type: UNSIGNED_BYTE },
|
|
158
|
-
{ name: 'a_shape', size: 1, type: FLOAT },
|
|
159
|
-
{ name: 'a_emphasis', size: 1, type: FLOAT },
|
|
160
|
-
{ name: 'a_material', size: 1, type: FLOAT },
|
|
161
|
-
{ name: 'a_glow', size: 1, type: FLOAT },
|
|
162
|
-
],
|
|
163
|
-
CONSTANT_ATTRIBUTES: [{ name: 'a_angle', size: 1, type: FLOAT }],
|
|
164
|
-
CONSTANT_DATA: [[0], [(2 * Math.PI) / 3], [(4 * Math.PI) / 3]],
|
|
165
|
-
FRAGMENT_SHADER_SOURCE: FRAGMENT,
|
|
166
|
-
METHOD: WebGLRenderingContext.TRIANGLES,
|
|
167
|
-
UNIFORMS,
|
|
168
|
-
VERTEX_SHADER_SOURCE: VERTEX,
|
|
169
|
-
VERTICES: 3,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
processVisibleItem(nodeIndex, startIndex, data) {
|
|
173
|
-
const array = this.array;
|
|
174
|
-
let at = startIndex;
|
|
175
|
-
array[at++] = data.x;
|
|
176
|
-
array[at++] = data.y;
|
|
177
|
-
array[at++] = data.size;
|
|
178
|
-
array[at++] = floatColor(data.color);
|
|
179
|
-
array[at++] = nodeIndex;
|
|
180
|
-
array[at++] = data.shapeCode ?? SHAPES.circle;
|
|
181
|
-
array[at++] = data.emphasisCode ?? EMPHASES.solid;
|
|
182
|
-
array[at++] = data.materialCode ?? MATERIALS.flat;
|
|
183
|
-
array[at] = data.glow ?? 0;
|
|
184
|
-
}
|
|
185
|
-
setUniforms(params, { gl, uniformLocations, }) {
|
|
186
|
-
gl.uniform1f(uniformLocations.u_correctionRatio, params.correctionRatio);
|
|
187
|
-
gl.uniform1f(uniformLocations.u_sizeRatio, params.sizeRatio);
|
|
188
|
-
gl.uniformMatrix3fv(uniformLocations.u_matrix, false, params.matrix);
|
|
189
|
-
}
|
|
90
|
+
return vec4(color, fill * alpha * v_helixColor.a);
|
|
190
91
|
}
|
|
92
|
+
`,
|
|
93
|
+
name: 'helix',
|
|
94
|
+
uniforms: [],
|
|
95
|
+
};
|
|
96
|
+
export const nodePrimitives = () => ({
|
|
97
|
+
backdrop: {},
|
|
98
|
+
layers: [look],
|
|
99
|
+
shapes: [circle, square, diamond],
|
|
100
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GraphDriver } from '../connect-graph/connect-graph.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type GraphLooks, type NodeLook } from '../connect-graph/looks.js';
|
|
3
|
+
import { type Named, type OnScreen } from '../overlay/overlay.js';
|
|
3
4
|
/**
|
|
4
5
|
* A graphology graph, as far as the driver calls it. Each member is
|
|
5
6
|
* graphology's own, as its documentation describes it.
|
|
@@ -34,6 +35,8 @@ export interface SigmaCamera {
|
|
|
34
35
|
x: number;
|
|
35
36
|
y: number;
|
|
36
37
|
};
|
|
38
|
+
off(type: 'updated', listener: () => void): unknown;
|
|
39
|
+
on(type: 'updated', listener: () => void): unknown;
|
|
37
40
|
setState(state: {
|
|
38
41
|
ratio?: number;
|
|
39
42
|
x?: number;
|
|
@@ -60,19 +63,23 @@ export interface SigmaRenderer<G extends SigmaGraph = SigmaGraph> {
|
|
|
60
63
|
y: number;
|
|
61
64
|
};
|
|
62
65
|
getNodeDisplayData(node: string): {
|
|
63
|
-
hidden?: boolean;
|
|
64
66
|
size: number;
|
|
67
|
+
visibility?: string;
|
|
65
68
|
x: number;
|
|
66
69
|
y: number;
|
|
67
70
|
} | undefined;
|
|
71
|
+
/** The primitives the renderer was made with, which say whether `sigmaOptions()` made it. */
|
|
72
|
+
getPrimitives(): {
|
|
73
|
+
nodes?: object | undefined;
|
|
74
|
+
};
|
|
68
75
|
off(type: 'clickNode' | 'enterNode' | 'leaveNode', listener: (payload: {
|
|
69
76
|
node: string;
|
|
70
77
|
}) => void): unknown;
|
|
71
|
-
off(type: 'afterRender' | '
|
|
78
|
+
off(type: 'afterRender' | 'clickStage', listener: () => void): unknown;
|
|
72
79
|
on(type: 'clickNode' | 'enterNode' | 'leaveNode', listener: (payload: {
|
|
73
80
|
node: string;
|
|
74
81
|
}) => void): unknown;
|
|
75
|
-
on(type: 'afterRender' | '
|
|
82
|
+
on(type: 'afterRender' | 'clickStage', listener: () => void): unknown;
|
|
76
83
|
getSetting(name: SigmaSetting): unknown;
|
|
77
84
|
refresh(options?: {
|
|
78
85
|
partialGraph?: {
|
|
@@ -82,7 +89,7 @@ export interface SigmaRenderer<G extends SigmaGraph = SigmaGraph> {
|
|
|
82
89
|
}): unknown;
|
|
83
90
|
resize(force?: boolean): unknown;
|
|
84
91
|
scaleSize(size?: number): number;
|
|
85
|
-
setSetting(name: SigmaSetting, value:
|
|
92
|
+
setSetting(name: SigmaSetting, value: boolean): unknown;
|
|
86
93
|
viewportToFramedGraph(point: {
|
|
87
94
|
x: number;
|
|
88
95
|
y: number;
|
|
@@ -92,7 +99,7 @@ export interface SigmaRenderer<G extends SigmaGraph = SigmaGraph> {
|
|
|
92
99
|
};
|
|
93
100
|
}
|
|
94
101
|
/** The settings the driver sets, and puts back as they were when it stops. */
|
|
95
|
-
type SigmaSetting = '
|
|
102
|
+
type SigmaSetting = 'enableCameraPanning' | 'enableCameraZooming';
|
|
96
103
|
/** A layout that runs over a graphology graph until it is stopped, as ForceAtlas2's worker does. */
|
|
97
104
|
export interface SigmaLayout {
|
|
98
105
|
kill(): void;
|
|
@@ -146,15 +153,28 @@ export interface SigmaDriverOptions<G extends SigmaGraph = SigmaGraph> {
|
|
|
146
153
|
/** The longest a layout runs, in milliseconds, settled or not. 10 seconds by default, about where 10,000 notes settle. */
|
|
147
154
|
layoutLimit?: number | undefined;
|
|
148
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* The notes to name this frame, in the order the overlay makes room for them:
|
|
158
|
+
* the ones that must be named first, the selected note, the one under the
|
|
159
|
+
* pointer and the one the keys are on, then the larger notes, as Sigma's grid
|
|
160
|
+
* kept the larger note's name. Named as `labels` says: the selected note's
|
|
161
|
+
* neighbors where there is room, and the rest only as the page asked.
|
|
162
|
+
*/
|
|
163
|
+
export declare const namedOf: (graph: SigmaGraph, looks: GraphLooks, labels: 'all' | 'hover' | 'hubs', hovered: string | undefined, onScreen: OnScreen) => Named[];
|
|
149
164
|
/**
|
|
150
165
|
* Drives Sigma.js for a Graph frame. Make the renderer with an empty
|
|
151
|
-
* graphology graph and pass it here, and `connectGraph`
|
|
166
|
+
* graphology graph and `sigmaOptions()`, pass it here, and `connectGraph`
|
|
167
|
+
* fills it:
|
|
152
168
|
*
|
|
153
169
|
* ```js
|
|
154
|
-
* const sigma = new Sigma(new Graph(), container)
|
|
170
|
+
* const sigma = new Sigma(new Graph(), container, sigmaOptions())
|
|
155
171
|
* connectGraph(frame, sigmaDriver(sigma, { layout }))
|
|
156
172
|
* ```
|
|
157
173
|
*
|
|
174
|
+
* **`sigmaOptions()` is what draws the notes and links as Helix draws them.**
|
|
175
|
+
* Sigma 4 takes its programs and reducers only when it is made, so the driver
|
|
176
|
+
* cannot add them to a renderer made without them, and refuses one.
|
|
177
|
+
*
|
|
158
178
|
* **Neither Sigma nor graphology is a dependency.** The driver types the
|
|
159
179
|
* renderer by the calls it makes on it, so a page brings whichever version it
|
|
160
180
|
* has, and a spec holds those shapes to the real classes. The renderer is the
|