@fusion.dev/helix-graph 0.214.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
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ The elements themselves are on the
|
|
|
13
13
|
|
|
14
14
|
```js
|
|
15
15
|
import { connectGraph } from '@fusion.dev/helix-graph'
|
|
16
|
-
import { sigmaDriver } from '@fusion.dev/helix-graph/sigma'
|
|
16
|
+
import { sigmaDriver, sigmaOptions } from '@fusion.dev/helix-graph/sigma'
|
|
17
17
|
import Graph from 'graphology'
|
|
18
18
|
import forceAtlas2 from 'graphology-layout-forceatlas2'
|
|
19
19
|
import FA2Layout from 'graphology-layout-forceatlas2/worker'
|
|
@@ -25,7 +25,7 @@ frame.links = links // [{ source, target, label }]
|
|
|
25
25
|
|
|
26
26
|
// The box the drawing goes in: a <div> inside the frame, sized by it.
|
|
27
27
|
const box = frame.querySelector('div')
|
|
28
|
-
const sigma = new Sigma(new Graph(), box)
|
|
28
|
+
const sigma = new Sigma(new Graph(), box, sigmaOptions())
|
|
29
29
|
const connection = connectGraph(
|
|
30
30
|
frame,
|
|
31
31
|
sigmaDriver(sigma, {
|
|
@@ -408,7 +408,7 @@ connectGraph(frame, isometricDriver(frame.querySelector('div')))
|
|
|
408
408
|
| Import | What |
|
|
409
409
|
|---|---|
|
|
410
410
|
| `@fusion.dev/helix-graph` | `communitiesOf`, `connectGraph`, `diffGraph`, `fitToStencil`, `graphColorsFrom`, `groupByCommunity`, `linkKey`, `seedPositions`, and the types `ConnectGraphOptions`, `DrawnLink`, `DrawnNode`, `GraphChanges`, `GraphColors`, `GraphConnection`, `GraphDriver`, `GraphGroup`, `GraphLooks`, `GraphPoint`, `GraphReach`, `GraphStencil`, `GraphTone`, `GroupByCommunityOptions`, `LinkLook`, `NodeLook`, `SeedLink`, `SeedNode`, `ShapeField` and `StencilFrame` |
|
|
411
|
-
| `@fusion.dev/helix-graph/sigma` | `sigmaDriver(sigma, options)`, and the types `SigmaDriverOptions`, `SigmaGraph`, `SigmaLayout` and `SigmaRenderer` |
|
|
411
|
+
| `@fusion.dev/helix-graph/sigma` | `sigmaDriver(sigma, options)`, `sigmaOptions(overrides)`, the options to make the renderer with, and the types `HelixEdgeData`, `SigmaDriverOptions`, `SigmaGraph`, `SigmaLayout`, `SigmaOptionsOverrides` and `SigmaRenderer` |
|
|
412
412
|
| `@fusion.dev/helix-graph/cosmos` | `cosmosDriver(graph, box, options)`, and the types `CosmosDriverOptions` and `CosmosGraph` |
|
|
413
413
|
| `@fusion.dev/helix-graph/three` | `threeDriver(box, options)`, and the type `ThreeDriverOptions` |
|
|
414
414
|
| `@fusion.dev/helix-graph/isometric` | `isometricDriver(box, options)`, `ISOMETRIC`, the camera it starts from, and the types `Camera`, `IsometricDriver` and `IsometricDriverOptions` |
|
|
@@ -420,8 +420,15 @@ connectGraph(frame, isometricDriver(frame.querySelector('div')))
|
|
|
420
420
|
driver types its renderer by the calls it makes on it, so a page brings
|
|
421
421
|
whichever version it has, and a spec holds those shapes to the real classes,
|
|
422
422
|
so a renamed method fails a typecheck here before it reaches a page. `@fusion.dev/helix-graph/sigma` imports
|
|
423
|
-
Sigma's rendering module for the
|
|
424
|
-
Sigma
|
|
423
|
+
Sigma's rendering module for the line and arrowhead the links are drawn with,
|
|
424
|
+
which is why Sigma 4 is a peer dependency.
|
|
425
|
+
|
|
426
|
+
**Make the renderer with `sigmaOptions()`.** Sigma 4 takes the programs a
|
|
427
|
+
note and a link are drawn with, and the reducers that draw them as the frame
|
|
428
|
+
says, only when it is made, so `sigmaDriver` refuses a renderer made without
|
|
429
|
+
them. `sigmaOptions({ settings })` sets Sigma's own settings over Helix's.
|
|
430
|
+
Sigma draws no names: the driver names notes over the drawing, as it names
|
|
431
|
+
groups.
|
|
425
432
|
|
|
426
433
|
| `connectGraph` option | Default | What else it takes |
|
|
427
434
|
|---|---|---|
|
|
@@ -65,14 +65,16 @@ export declare const nameStart: (at: {
|
|
|
65
65
|
/** A note to name over the drawing: its name, and whether it is the one the reader is on. */
|
|
66
66
|
export interface Named {
|
|
67
67
|
id: string;
|
|
68
|
+
/** Whether the name is drawn even where it covers another. The strong ones by default. */
|
|
69
|
+
must?: boolean | undefined;
|
|
68
70
|
name: string;
|
|
69
71
|
strong: boolean;
|
|
70
72
|
}
|
|
71
73
|
/**
|
|
72
74
|
* **Names on a canvas of their own**, for a renderer that draws none: each
|
|
73
75
|
* note given, beside it, ringed in the ground's color so it reads over the
|
|
74
|
-
* links. The
|
|
75
|
-
* order given.
|
|
76
|
+
* links. The ones that must be named always, and the rest where there is
|
|
77
|
+
* room, in the order given.
|
|
76
78
|
*/
|
|
77
79
|
export declare const namesOver: (container: HTMLElement, onScreen: OnScreen) => {
|
|
78
80
|
/** The boxes the names last drawn take. */
|
package/dist/overlay/overlay.js
CHANGED
|
@@ -315,7 +315,7 @@ export const namesOver = (container, onScreen) => {
|
|
|
315
315
|
taken = chromeOver(container);
|
|
316
316
|
const rightToLeft = isRtl(container);
|
|
317
317
|
context.textAlign = 'left';
|
|
318
|
-
for (const { id, name, strong } of named) {
|
|
318
|
+
for (const { id, must, name, strong } of named) {
|
|
319
319
|
const at = onScreen(id);
|
|
320
320
|
if (!at || looks.node(id).hidden) {
|
|
321
321
|
continue;
|
|
@@ -331,7 +331,7 @@ export const namesOver = (container, onScreen) => {
|
|
|
331
331
|
});
|
|
332
332
|
const x = nameStart(at, width, boxAt, taken, container.clientWidth, rightToLeft);
|
|
333
333
|
const box = boxAt(x);
|
|
334
|
-
if (!clearOf(box, taken) && !strong) {
|
|
334
|
+
if (!clearOf(box, taken) && !(must ?? strong)) {
|
|
335
335
|
continue;
|
|
336
336
|
}
|
|
337
337
|
taken.push(box);
|
|
@@ -1,87 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/** What the edge program reads off a link, beyond what Sigma gives every link. */
|
|
1
|
+
import type { EdgePrimitives } from 'sigma/primitives';
|
|
2
|
+
/** What the edge layer reads off a link, beyond what Sigma gives every link. */
|
|
4
3
|
export interface HelixEdgeData {
|
|
5
4
|
/** How strongly the link glows, from 0 to 1: a link on the selected note's path. */
|
|
6
5
|
glow?: number;
|
|
7
6
|
/** Whether the link is drawn heavy, with an arrowhead, as a link the selection lights is. */
|
|
8
7
|
strong?: boolean;
|
|
9
8
|
}
|
|
10
|
-
declare const UNIFORMS: readonly ['u_matrix', 'u_zoomRatio', 'u_sizeRatio', 'u_correctionRatio', 'u_pixelRatio', 'u_feather', 'u_minEdgeThickness'];
|
|
11
9
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* How far past the line's own width a glowing link is drawn, as a multiple of
|
|
11
|
+
* that width, which is the room its glow has. **Sigma draws nothing outside a
|
|
12
|
+
* link's width**, so a glowing link is given the width of its glow and the
|
|
13
|
+
* layer draws the line in its middle.
|
|
15
14
|
*/
|
|
16
|
-
export declare
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
name: string;
|
|
20
|
-
size: number;
|
|
21
|
-
type: 5126;
|
|
22
|
-
normalized?: never;
|
|
23
|
-
} | {
|
|
24
|
-
name: string;
|
|
25
|
-
normalized: boolean;
|
|
26
|
-
size: number;
|
|
27
|
-
type: 5121;
|
|
28
|
-
})[];
|
|
29
|
-
CONSTANT_ATTRIBUTES: {
|
|
30
|
-
name: string;
|
|
31
|
-
size: number;
|
|
32
|
-
type: 5126;
|
|
33
|
-
}[];
|
|
34
|
-
CONSTANT_DATA: number[][];
|
|
35
|
-
FRAGMENT_SHADER_SOURCE: string;
|
|
36
|
-
METHOD: 4;
|
|
37
|
-
UNIFORMS: readonly ["u_matrix", "u_zoomRatio", "u_sizeRatio", "u_correctionRatio", "u_pixelRatio", "u_feather", "u_minEdgeThickness"];
|
|
38
|
-
VERTEX_SHADER_SOURCE: string;
|
|
39
|
-
VERTICES: number;
|
|
40
|
-
};
|
|
41
|
-
processVisibleItem(edgeIndex: number, startIndex: number, sourceData: NodeDisplayData, targetData: NodeDisplayData, data: EdgeDisplayData & HelixEdgeData): void;
|
|
42
|
-
setUniforms(params: RenderParams, { gl, uniformLocations, }: {
|
|
43
|
-
gl: WebGLRenderingContext;
|
|
44
|
-
uniformLocations: Record<string, WebGLUniformLocation>;
|
|
45
|
-
}): void;
|
|
46
|
-
}
|
|
47
|
-
declare const HEAD_UNIFORMS: readonly ['u_matrix', 'u_sizeRatio', 'u_correctionRatio', 'u_minEdgeThickness', 'u_lengthToThicknessRatio', 'u_widenessToThicknessRatio'];
|
|
15
|
+
export declare const REACH = 4;
|
|
16
|
+
/** How many times its line's width a link that glows fully is drawn: the line and its glow either side. */
|
|
17
|
+
export declare const SPREAD: number;
|
|
48
18
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
19
|
+
* The links' primitives: a straight line drawn by Helix's layer, and a head
|
|
20
|
+
* on the strong ones only, at the note a link arrives at. Every link already
|
|
21
|
+
* reads in its direction by its gradient, and a head on each of a graph's
|
|
22
|
+
* thousands of links is a field of chevrons.
|
|
53
23
|
*/
|
|
54
|
-
export declare
|
|
55
|
-
getDefinition(): {
|
|
56
|
-
ATTRIBUTES: ({
|
|
57
|
-
normalized?: never;
|
|
58
|
-
name: string;
|
|
59
|
-
size: number;
|
|
60
|
-
type: 5126;
|
|
61
|
-
} | {
|
|
62
|
-
name: string;
|
|
63
|
-
normalized: boolean;
|
|
64
|
-
size: number;
|
|
65
|
-
type: 5121;
|
|
66
|
-
})[];
|
|
67
|
-
CONSTANT_ATTRIBUTES: {
|
|
68
|
-
name: string;
|
|
69
|
-
size: number;
|
|
70
|
-
type: 5126;
|
|
71
|
-
}[];
|
|
72
|
-
CONSTANT_DATA: number[][];
|
|
73
|
-
FRAGMENT_SHADER_SOURCE: string;
|
|
74
|
-
METHOD: 4;
|
|
75
|
-
UNIFORMS: readonly ["u_matrix", "u_sizeRatio", "u_correctionRatio", "u_minEdgeThickness", "u_lengthToThicknessRatio", "u_widenessToThicknessRatio"];
|
|
76
|
-
VERTEX_SHADER_SOURCE: string;
|
|
77
|
-
VERTICES: number;
|
|
78
|
-
};
|
|
79
|
-
processVisibleItem(edgeIndex: number, startIndex: number, sourceData: NodeDisplayData, targetData: NodeDisplayData, data: EdgeDisplayData & HelixEdgeData): void;
|
|
80
|
-
setUniforms(params: RenderParams, { gl, uniformLocations, }: {
|
|
81
|
-
gl: WebGLRenderingContext;
|
|
82
|
-
uniformLocations: Record<string, WebGLUniformLocation>;
|
|
83
|
-
}): void;
|
|
84
|
-
}
|
|
85
|
-
/** The line and its head, as one program a link is drawn with. */
|
|
86
|
-
export declare const HelixEdgeProgram: EdgeProgramType;
|
|
87
|
-
export {};
|
|
24
|
+
export declare const edgePrimitives: () => EdgePrimitives;
|
|
@@ -1,316 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
uniform float u_sizeRatio;
|
|
17
|
-
uniform float u_zoomRatio;
|
|
18
|
-
uniform float u_pixelRatio;
|
|
19
|
-
uniform float u_correctionRatio;
|
|
20
|
-
uniform float u_minEdgeThickness;
|
|
21
|
-
uniform float u_feather;
|
|
22
|
-
|
|
23
|
-
varying vec4 v_color;
|
|
24
|
-
varying vec2 v_normal;
|
|
25
|
-
varying float v_thickness;
|
|
26
|
-
varying float v_feather;
|
|
27
|
-
varying float v_along;
|
|
28
|
-
varying float v_glow;
|
|
29
|
-
varying float v_spread;
|
|
30
|
-
|
|
31
|
-
const float bias = 255.0 / 254.0;
|
|
32
|
-
|
|
33
|
-
void main() {
|
|
34
|
-
float minThickness = u_minEdgeThickness;
|
|
35
|
-
|
|
36
|
-
vec2 normal = a_normal * a_normalCoef;
|
|
37
|
-
vec2 position = a_positionStart * (1.0 - a_positionCoef) + a_positionEnd * a_positionCoef;
|
|
38
|
-
|
|
39
|
-
float normalLength = length(normal);
|
|
40
|
-
vec2 unitNormal = normal / normalLength;
|
|
41
|
-
|
|
42
|
-
/* At least the minimum thickness on screen, in the WebGL referential, as
|
|
43
|
-
Sigma's own rectangle does it. */
|
|
44
|
-
float pixelsThickness = max(normalLength, minThickness * u_sizeRatio);
|
|
45
|
-
float webGLThickness = pixelsThickness * u_correctionRatio / u_sizeRatio;
|
|
46
|
-
|
|
47
|
-
/* The quad reaches past the line by the room its glow needs, and only
|
|
48
|
-
where it glows: a resting link's quad is the line's own width. */
|
|
49
|
-
float spread = 1.0 + a_glow * ${REACH.toFixed(1)};
|
|
50
|
-
gl_Position = vec4((u_matrix * vec3(position + unitNormal * webGLThickness * spread, 1)).xy, 0, 1);
|
|
51
|
-
|
|
52
|
-
v_thickness = webGLThickness / u_zoomRatio;
|
|
53
|
-
v_normal = unitNormal;
|
|
54
|
-
v_feather = u_feather * u_correctionRatio / u_zoomRatio / u_pixelRatio * 2.0;
|
|
55
|
-
v_along = a_positionCoef;
|
|
56
|
-
v_glow = a_glow;
|
|
57
|
-
v_spread = spread;
|
|
58
|
-
|
|
59
|
-
#ifdef PICKING_MODE
|
|
60
|
-
v_color = a_id;
|
|
61
|
-
#else
|
|
62
|
-
v_color = a_color;
|
|
63
|
-
#endif
|
|
64
|
-
|
|
65
|
-
v_color.a *= bias;
|
|
66
|
-
}
|
|
67
|
-
`;
|
|
68
|
-
const FRAGMENT = `
|
|
69
|
-
precision mediump float;
|
|
70
|
-
|
|
71
|
-
varying vec4 v_color;
|
|
72
|
-
varying vec2 v_normal;
|
|
73
|
-
varying float v_thickness;
|
|
74
|
-
varying float v_feather;
|
|
75
|
-
varying float v_along;
|
|
76
|
-
varying float v_glow;
|
|
77
|
-
varying float v_spread;
|
|
78
|
-
|
|
79
|
-
void main(void) {
|
|
80
|
-
#ifdef PICKING_MODE
|
|
81
|
-
gl_FragColor = v_color;
|
|
82
|
-
#else
|
|
83
|
-
/* Across the quad, from the line's middle out: the normal interpolates
|
|
84
|
-
through zero at the middle, so its length is the distance in half-widths
|
|
85
|
-
of the quad, and the quad is the line widened by its spread. */
|
|
86
|
-
float dist = length(v_normal) * v_spread * v_thickness;
|
|
87
|
-
float line = 1.0 - smoothstep(v_thickness - v_feather, v_thickness, dist);
|
|
1
|
+
import { extremityArrow, pathLine } from 'sigma/rendering';
|
|
2
|
+
export const REACH = 4;
|
|
3
|
+
export const SPREAD = 1 + REACH;
|
|
4
|
+
const HEAD = { length: 2.5 / SPREAD, width: 2 / SPREAD };
|
|
5
|
+
const { FLOAT } = WebGL2RenderingContext;
|
|
6
|
+
const look = {
|
|
7
|
+
attributes: [{ name: 'helixGlow', size: 1, source: 'glow', type: FLOAT }],
|
|
8
|
+
glsl: `
|
|
9
|
+
vec4 layer_helix(EdgeContext ctx) {
|
|
10
|
+
/* Across the link, from its middle out, in the width it is drawn at: the
|
|
11
|
+
line's own width and, where it glows, the glow either side. */
|
|
12
|
+
float spread = 1.0 + v_helixGlow * ${REACH.toFixed(1)};
|
|
13
|
+
float halfWidth = ctx.thickness * 0.5 / spread;
|
|
14
|
+
float dist = ctx.sdf + ctx.thickness * 0.5;
|
|
15
|
+
float line = v_helixGlow > 0.0 ? 1.0 - smoothstep(halfWidth - ctx.aaWidth, halfWidth, dist) : 1.0;
|
|
88
16
|
|
|
89
17
|
/* The glow, outside the line: a soft light falling away from its edge, in
|
|
90
18
|
the link's own color, as a note's halo falls away from its rim. */
|
|
91
|
-
float past = max(dist -
|
|
92
|
-
float halo =
|
|
19
|
+
float past = max(dist - halfWidth, 0.0) / max(halfWidth * ${REACH.toFixed(1)}, 0.0001);
|
|
20
|
+
float halo = v_helixGlow * (1.0 - line) * exp(-past * 3.2) * (1.0 - smoothstep(0.7, 1.0, past)) * 0.55;
|
|
93
21
|
|
|
94
22
|
/* Faint where the link leaves and full where it arrives, so the direction
|
|
95
23
|
reads along every link, and the one lit by the selection as a path. */
|
|
96
|
-
float along = mix(0.4, 1.0, smoothstep(0.0, 1.0,
|
|
97
|
-
|
|
98
|
-
float alpha = (line + halo) * along * v_color.a;
|
|
99
|
-
/* Premultiplied, as Sigma blends. */
|
|
100
|
-
gl_FragColor = vec4(v_color.rgb * alpha, alpha);
|
|
101
|
-
#endif
|
|
102
|
-
}
|
|
103
|
-
`;
|
|
104
|
-
const { FLOAT, UNSIGNED_BYTE } = WebGLRenderingContext;
|
|
105
|
-
const UNIFORMS = [
|
|
106
|
-
'u_matrix',
|
|
107
|
-
'u_zoomRatio',
|
|
108
|
-
'u_sizeRatio',
|
|
109
|
-
'u_correctionRatio',
|
|
110
|
-
'u_pixelRatio',
|
|
111
|
-
'u_feather',
|
|
112
|
-
'u_minEdgeThickness',
|
|
113
|
-
];
|
|
114
|
-
export class HelixEdgeLineProgram extends EdgeProgram {
|
|
115
|
-
getDefinition() {
|
|
116
|
-
return {
|
|
117
|
-
ATTRIBUTES: [
|
|
118
|
-
{ name: 'a_positionStart', size: 2, type: FLOAT },
|
|
119
|
-
{ name: 'a_positionEnd', size: 2, type: FLOAT },
|
|
120
|
-
{ name: 'a_normal', size: 2, type: FLOAT },
|
|
121
|
-
{ name: 'a_color', normalized: true, size: 4, type: UNSIGNED_BYTE },
|
|
122
|
-
{ name: 'a_id', normalized: true, size: 4, type: UNSIGNED_BYTE },
|
|
123
|
-
{ name: 'a_glow', size: 1, type: FLOAT },
|
|
124
|
-
],
|
|
125
|
-
CONSTANT_ATTRIBUTES: [
|
|
126
|
-
{ name: 'a_positionCoef', size: 1, type: FLOAT },
|
|
127
|
-
{ name: 'a_normalCoef', size: 1, type: FLOAT },
|
|
128
|
-
],
|
|
129
|
-
CONSTANT_DATA: [
|
|
130
|
-
[0, 1],
|
|
131
|
-
[0, -1],
|
|
132
|
-
[1, 1],
|
|
133
|
-
[1, 1],
|
|
134
|
-
[0, -1],
|
|
135
|
-
[1, -1],
|
|
136
|
-
],
|
|
137
|
-
FRAGMENT_SHADER_SOURCE: FRAGMENT,
|
|
138
|
-
METHOD: WebGLRenderingContext.TRIANGLES,
|
|
139
|
-
UNIFORMS,
|
|
140
|
-
VERTEX_SHADER_SOURCE: VERTEX,
|
|
141
|
-
VERTICES: 6,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
processVisibleItem(edgeIndex, startIndex, sourceData, targetData, data) {
|
|
145
|
-
const thickness = data.size || 1;
|
|
146
|
-
const dx = targetData.x - sourceData.x;
|
|
147
|
-
const dy = targetData.y - sourceData.y;
|
|
148
|
-
let len = dx * dx + dy * dy;
|
|
149
|
-
let n1 = 0;
|
|
150
|
-
let n2 = 0;
|
|
151
|
-
if (len) {
|
|
152
|
-
len = 1 / Math.sqrt(len);
|
|
153
|
-
n1 = -dy * len * thickness;
|
|
154
|
-
n2 = dx * len * thickness;
|
|
155
|
-
}
|
|
156
|
-
const array = this.array;
|
|
157
|
-
let at = startIndex;
|
|
158
|
-
array[at++] = sourceData.x;
|
|
159
|
-
array[at++] = sourceData.y;
|
|
160
|
-
array[at++] = targetData.x;
|
|
161
|
-
array[at++] = targetData.y;
|
|
162
|
-
array[at++] = n1;
|
|
163
|
-
array[at++] = n2;
|
|
164
|
-
array[at++] = floatColor(data.color);
|
|
165
|
-
array[at++] = edgeIndex;
|
|
166
|
-
array[at] = data.glow ?? 0;
|
|
167
|
-
}
|
|
168
|
-
setUniforms(params, { gl, uniformLocations, }) {
|
|
169
|
-
gl.uniformMatrix3fv(uniformLocations.u_matrix, false, params.matrix);
|
|
170
|
-
gl.uniform1f(uniformLocations.u_zoomRatio, params.zoomRatio);
|
|
171
|
-
gl.uniform1f(uniformLocations.u_sizeRatio, params.sizeRatio);
|
|
172
|
-
gl.uniform1f(uniformLocations.u_correctionRatio, params.correctionRatio);
|
|
173
|
-
gl.uniform1f(uniformLocations.u_pixelRatio, params.pixelRatio);
|
|
174
|
-
gl.uniform1f(uniformLocations.u_feather, params.antiAliasingFeather);
|
|
175
|
-
gl.uniform1f(uniformLocations.u_minEdgeThickness, params.minEdgeThickness);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
const HEAD_VERTEX = `
|
|
179
|
-
attribute vec2 a_position;
|
|
180
|
-
attribute vec2 a_normal;
|
|
181
|
-
attribute float a_radius;
|
|
182
|
-
attribute vec3 a_barycentric;
|
|
183
|
-
attribute vec4 a_id;
|
|
184
|
-
attribute vec4 a_color;
|
|
185
|
-
|
|
186
|
-
uniform mat3 u_matrix;
|
|
187
|
-
uniform float u_sizeRatio;
|
|
188
|
-
uniform float u_correctionRatio;
|
|
189
|
-
uniform float u_minEdgeThickness;
|
|
190
|
-
uniform float u_lengthToThicknessRatio;
|
|
191
|
-
uniform float u_widenessToThicknessRatio;
|
|
192
|
-
|
|
193
|
-
varying vec4 v_color;
|
|
194
|
-
|
|
195
|
-
const float bias = 255.0 / 254.0;
|
|
196
|
-
|
|
197
|
-
void main() {
|
|
198
|
-
float minThickness = u_minEdgeThickness;
|
|
199
|
-
|
|
200
|
-
float normalLength = length(a_normal);
|
|
201
|
-
vec2 unitNormal = a_normal / normalLength;
|
|
202
|
-
|
|
203
|
-
/* As Sigma's own arrowhead: the thickness on screen, the note's radius,
|
|
204
|
-
and the head's length and width from the thickness. */
|
|
205
|
-
float pixelsThickness = max(normalLength / u_sizeRatio, minThickness);
|
|
206
|
-
float webGLThickness = pixelsThickness * u_correctionRatio;
|
|
207
|
-
float webGLNodeRadius = a_radius * 2.0 * u_correctionRatio / u_sizeRatio;
|
|
208
|
-
float webGLArrowHeadLength = webGLThickness * u_lengthToThicknessRatio * 2.0;
|
|
209
|
-
float webGLArrowHeadThickness = webGLThickness * u_widenessToThicknessRatio;
|
|
24
|
+
float along = mix(0.4, 1.0, smoothstep(0.0, 1.0, ctx.t));
|
|
210
25
|
|
|
211
|
-
float
|
|
212
|
-
float db = a_barycentric.y;
|
|
213
|
-
float dc = a_barycentric.z;
|
|
26
|
+
float alpha = (line + halo) * along;
|
|
214
27
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
28
|
+
/* The head, solid, narrowing from its base to its tip. Sigma's own cut
|
|
29
|
+
there is the line's whole width near the base, glow and all. */
|
|
30
|
+
if (v_zone > 1.5) {
|
|
31
|
+
float headHalf = ctx.thickness * v_headWidthRatio * 0.5 * (1.0 - v_zoneT);
|
|
32
|
+
alpha = max(alpha, 1.0 - smoothstep(headHalf - ctx.aaWidth, headHalf, dist));
|
|
33
|
+
}
|
|
219
34
|
|
|
220
|
-
|
|
221
|
-
+ db * (-(webGLNodeRadius + webGLArrowHeadLength) * unitNormal.x + webGLArrowHeadThickness * unitNormal.y)
|
|
222
|
-
+ dc * (-(webGLNodeRadius + webGLArrowHeadLength) * unitNormal.x - webGLArrowHeadThickness * unitNormal.y)
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
gl_Position = vec4((u_matrix * vec3(a_position + delta, 1)).xy, 0, 1);
|
|
226
|
-
|
|
227
|
-
#ifdef PICKING_MODE
|
|
228
|
-
v_color = a_id;
|
|
229
|
-
#else
|
|
230
|
-
v_color = a_color;
|
|
231
|
-
#endif
|
|
232
|
-
|
|
233
|
-
v_color.a *= bias;
|
|
234
|
-
}
|
|
235
|
-
`;
|
|
236
|
-
const HEAD_FRAGMENT = `
|
|
237
|
-
precision mediump float;
|
|
238
|
-
|
|
239
|
-
varying vec4 v_color;
|
|
240
|
-
|
|
241
|
-
void main(void) {
|
|
242
|
-
#ifdef PICKING_MODE
|
|
243
|
-
gl_FragColor = v_color;
|
|
244
|
-
#else
|
|
245
|
-
/* Premultiplied, as Sigma blends. */
|
|
246
|
-
gl_FragColor = vec4(v_color.rgb * v_color.a, v_color.a);
|
|
247
|
-
#endif
|
|
248
|
-
}
|
|
249
|
-
`;
|
|
250
|
-
const HEAD_UNIFORMS = [
|
|
251
|
-
'u_matrix',
|
|
252
|
-
'u_sizeRatio',
|
|
253
|
-
'u_correctionRatio',
|
|
254
|
-
'u_minEdgeThickness',
|
|
255
|
-
'u_lengthToThicknessRatio',
|
|
256
|
-
'u_widenessToThicknessRatio',
|
|
257
|
-
];
|
|
258
|
-
const HEAD = { length: 2.5, wideness: 2 };
|
|
259
|
-
export class HelixArrowHeadProgram extends EdgeProgram {
|
|
260
|
-
getDefinition() {
|
|
261
|
-
return {
|
|
262
|
-
ATTRIBUTES: [
|
|
263
|
-
{ name: 'a_position', size: 2, type: FLOAT },
|
|
264
|
-
{ name: 'a_normal', size: 2, type: FLOAT },
|
|
265
|
-
{ name: 'a_radius', size: 1, type: FLOAT },
|
|
266
|
-
{ name: 'a_color', normalized: true, size: 4, type: UNSIGNED_BYTE },
|
|
267
|
-
{ name: 'a_id', normalized: true, size: 4, type: UNSIGNED_BYTE },
|
|
268
|
-
],
|
|
269
|
-
CONSTANT_ATTRIBUTES: [{ name: 'a_barycentric', size: 3, type: FLOAT }],
|
|
270
|
-
CONSTANT_DATA: [
|
|
271
|
-
[1, 0, 0],
|
|
272
|
-
[0, 1, 0],
|
|
273
|
-
[0, 0, 1],
|
|
274
|
-
],
|
|
275
|
-
FRAGMENT_SHADER_SOURCE: HEAD_FRAGMENT,
|
|
276
|
-
METHOD: WebGLRenderingContext.TRIANGLES,
|
|
277
|
-
UNIFORMS: HEAD_UNIFORMS,
|
|
278
|
-
VERTEX_SHADER_SOURCE: HEAD_VERTEX,
|
|
279
|
-
VERTICES: 3,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
processVisibleItem(edgeIndex, startIndex, sourceData, targetData, data) {
|
|
283
|
-
const thickness = data.size || 1;
|
|
284
|
-
const dx = targetData.x - sourceData.x;
|
|
285
|
-
const dy = targetData.y - sourceData.y;
|
|
286
|
-
let len = dx * dx + dy * dy;
|
|
287
|
-
let n1 = 0;
|
|
288
|
-
let n2 = 0;
|
|
289
|
-
if (len) {
|
|
290
|
-
len = 1 / Math.sqrt(len);
|
|
291
|
-
n1 = -dy * len * thickness;
|
|
292
|
-
n2 = dx * len * thickness;
|
|
293
|
-
}
|
|
294
|
-
const array = this.array;
|
|
295
|
-
let at = startIndex;
|
|
296
|
-
array[at++] = targetData.x;
|
|
297
|
-
array[at++] = targetData.y;
|
|
298
|
-
array[at++] = -n1;
|
|
299
|
-
array[at++] = -n2;
|
|
300
|
-
array[at++] = targetData.size || 1;
|
|
301
|
-
array[at++] = floatColor(data.strong ? data.color : NONE);
|
|
302
|
-
array[at] = edgeIndex;
|
|
303
|
-
}
|
|
304
|
-
setUniforms(params, { gl, uniformLocations, }) {
|
|
305
|
-
gl.uniformMatrix3fv(uniformLocations.u_matrix, false, params.matrix);
|
|
306
|
-
gl.uniform1f(uniformLocations.u_sizeRatio, params.sizeRatio);
|
|
307
|
-
gl.uniform1f(uniformLocations.u_correctionRatio, params.correctionRatio);
|
|
308
|
-
gl.uniform1f(uniformLocations.u_minEdgeThickness, params.minEdgeThickness);
|
|
309
|
-
gl.uniform1f(uniformLocations.u_lengthToThicknessRatio, HEAD.length);
|
|
310
|
-
gl.uniform1f(uniformLocations.u_widenessToThicknessRatio, HEAD.wideness);
|
|
311
|
-
}
|
|
35
|
+
return vec4(v_color.rgb, alpha * v_color.a);
|
|
312
36
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
37
|
+
`,
|
|
38
|
+
name: 'helix',
|
|
39
|
+
uniforms: [],
|
|
40
|
+
};
|
|
41
|
+
export const edgePrimitives = () => ({
|
|
42
|
+
defaultHead: 'none',
|
|
43
|
+
extremities: [extremityArrow({ lengthRatio: HEAD.length, widthRatio: HEAD.width })],
|
|
44
|
+
layers: [look],
|
|
45
|
+
paths: [pathLine()],
|
|
46
|
+
});
|
package/dist/sigma/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export type { HelixEdgeData } from './edge-program.js';
|
|
2
2
|
export { type SigmaCamera, type SigmaDriverOptions, type SigmaGraph, type SigmaLayout, type SigmaRenderer, sigmaDriver, } from './sigma-driver.js';
|
|
3
|
+
export { type SigmaOptionsOverrides, sigmaOptions } from './sigma-options.js';
|
package/dist/sigma/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/** The shapes a note is drawn in, numbered as the shader reads them. */
|
|
1
|
+
import type { NodePrimitives } from 'sigma/primitives';
|
|
2
|
+
/** The shapes a note is drawn in, by the name each is registered under. A ring is a circle drawn outlined. */
|
|
4
3
|
export declare const SHAPES: {
|
|
5
|
-
readonly circle:
|
|
6
|
-
readonly diamond:
|
|
7
|
-
readonly ring:
|
|
8
|
-
readonly square:
|
|
4
|
+
readonly circle: 'helixCircle';
|
|
5
|
+
readonly diamond: 'helixDiamond';
|
|
6
|
+
readonly ring: 'helixCircle';
|
|
7
|
+
readonly square: 'helixSquare';
|
|
9
8
|
};
|
|
10
9
|
/** How heavily a note is drawn, numbered as the shader reads them. */
|
|
11
10
|
export declare const EMPHASES: {
|
|
@@ -20,53 +19,24 @@ export declare const MATERIALS: {
|
|
|
20
19
|
readonly glass: 1;
|
|
21
20
|
readonly mark: 3;
|
|
22
21
|
};
|
|
23
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* How much larger a diamond is drawn than its size says. **Sigma draws a note
|
|
24
|
+
* in a square as wide as the note**, and a diamond's tips are its widest
|
|
25
|
+
* points: one that reached past the square, as Helix's did at 1.22 radii, was
|
|
26
|
+
* cut off there. So the diamond is drawn to the square's edge and the note
|
|
27
|
+
* made larger by the reach it lost, and it covers what it covered before.
|
|
28
|
+
*/
|
|
29
|
+
export declare const DIAMOND: number;
|
|
30
|
+
/** What the node layer reads off a note, beyond what Sigma gives every note. */
|
|
24
31
|
export interface HelixNodeData {
|
|
25
32
|
/** `EMPHASES`. */
|
|
26
33
|
emphasisCode?: number;
|
|
27
|
-
/** How strongly the note glows, from 0 to 1: the selected note's halo. */
|
|
28
|
-
glow?: number;
|
|
29
34
|
/** `MATERIALS`. */
|
|
30
35
|
materialCode?: number;
|
|
31
|
-
/** `SHAPES`. */
|
|
32
|
-
shapeCode?: number;
|
|
33
36
|
}
|
|
34
|
-
declare const UNIFORMS: readonly ['u_sizeRatio', 'u_correctionRatio', 'u_matrix'];
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* press beside a note is not a press on it.
|
|
38
|
+
* The notes' primitives: Helix's shapes and the layer that draws a note's
|
|
39
|
+
* emphasis and material. Pressing reads the shape, so a press beside a note is
|
|
40
|
+
* not a press on it.
|
|
40
41
|
*/
|
|
41
|
-
export declare
|
|
42
|
-
getDefinition(): {
|
|
43
|
-
ATTRIBUTES: ({
|
|
44
|
-
name: string;
|
|
45
|
-
size: number;
|
|
46
|
-
type: 5126;
|
|
47
|
-
normalized?: never;
|
|
48
|
-
} | {
|
|
49
|
-
name: string;
|
|
50
|
-
normalized: boolean;
|
|
51
|
-
size: number;
|
|
52
|
-
type: 5121;
|
|
53
|
-
})[];
|
|
54
|
-
CONSTANT_ATTRIBUTES: {
|
|
55
|
-
name: string;
|
|
56
|
-
size: number;
|
|
57
|
-
type: 5126;
|
|
58
|
-
}[];
|
|
59
|
-
CONSTANT_DATA: number[][];
|
|
60
|
-
FRAGMENT_SHADER_SOURCE: string;
|
|
61
|
-
METHOD: 4;
|
|
62
|
-
UNIFORMS: readonly ["u_sizeRatio", "u_correctionRatio", "u_matrix"];
|
|
63
|
-
VERTEX_SHADER_SOURCE: string;
|
|
64
|
-
VERTICES: number;
|
|
65
|
-
};
|
|
66
|
-
processVisibleItem(nodeIndex: number, startIndex: number, data: NodeDisplayData & HelixNodeData): void;
|
|
67
|
-
setUniforms(params: RenderParams, { gl, uniformLocations, }: {
|
|
68
|
-
gl: WebGLRenderingContext;
|
|
69
|
-
uniformLocations: Record<string, WebGLUniformLocation>;
|
|
70
|
-
}): void;
|
|
71
|
-
}
|
|
72
|
-
export {};
|
|
42
|
+
export declare const nodePrimitives: () => NodePrimitives;
|