@fusion.dev/helix-graph 0.121.0 → 0.123.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +75 -2
  2. package/dist/connect-graph/arrange.d.ts +4 -2
  3. package/dist/connect-graph/arrange.js +45 -12
  4. package/dist/connect-graph/connect-graph.d.ts +10 -1
  5. package/dist/connect-graph/connect-graph.js +13 -4
  6. package/dist/connect-graph/diff.d.ts +2 -0
  7. package/dist/connect-graph/diff.js +2 -1
  8. package/dist/connect-graph/seed.d.ts +3 -0
  9. package/dist/connect-graph/seed.js +10 -2
  10. package/dist/connect-graph/shape-field.d.ts +61 -0
  11. package/dist/connect-graph/shape-field.js +172 -0
  12. package/dist/connect-graph/stencil.d.ts +56 -17
  13. package/dist/connect-graph/stencil.js +160 -79
  14. package/dist/connect-graph/surface-web.d.ts +18 -0
  15. package/dist/connect-graph/surface-web.js +99 -0
  16. package/dist/cosmos/cosmos-driver.js +8 -56
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/overlay/overlay.d.ts +18 -0
  20. package/dist/overlay/overlay.js +63 -1
  21. package/dist/shapes/flat.d.ts +28 -0
  22. package/dist/shapes/flat.js +63 -0
  23. package/dist/shapes/index.d.ts +5 -0
  24. package/dist/shapes/index.js +5 -0
  25. package/dist/shapes/model.d.ts +12 -0
  26. package/dist/shapes/model.js +31 -0
  27. package/dist/shapes/solids.d.ts +28 -0
  28. package/dist/shapes/solids.js +47 -0
  29. package/dist/shapes/triangles.d.ts +20 -0
  30. package/dist/shapes/triangles.js +78 -0
  31. package/dist/three/index.d.ts +1 -0
  32. package/dist/three/index.js +1 -0
  33. package/dist/three/surface.d.ts +13 -0
  34. package/dist/three/surface.js +112 -0
  35. package/dist/three/three-driver.d.ts +32 -0
  36. package/dist/three/three-driver.js +797 -0
  37. package/package.json +28 -7
@@ -0,0 +1,63 @@
1
+ import { FLAT, fieldFromCells, fieldFromPath, } from '../connect-graph/shape-field.js';
2
+ const pathOf = (element) => {
3
+ const number = (name) => Number(element.getAttribute(name) ?? 0);
4
+ switch (element.localName) {
5
+ case 'path':
6
+ return element.getAttribute('d') ?? '';
7
+ case 'rect': {
8
+ const [x, y, width, height] = [number('x'), number('y'), number('width'), number('height')];
9
+ return `M${x} ${y}h${width}v${height}h${-width}Z`;
10
+ }
11
+ case 'circle':
12
+ case 'ellipse': {
13
+ const [cx, cy] = [number('cx'), number('cy')];
14
+ const rx = element.localName === 'circle' ? number('r') : number('rx');
15
+ const ry = element.localName === 'circle' ? number('r') : number('ry');
16
+ return `M${cx - rx} ${cy}a${rx} ${ry} 0 1 0 ${rx * 2} 0a${rx} ${ry} 0 1 0 ${-rx * 2} 0Z`;
17
+ }
18
+ case 'polygon': {
19
+ const points = element.getAttribute('points')?.trim() ?? '';
20
+ return points ? `M${points}Z` : '';
21
+ }
22
+ default:
23
+ return '';
24
+ }
25
+ };
26
+ export const shapeFromSvg = (svg, resolution = FLAT) => {
27
+ const element = typeof svg === 'string'
28
+ ? new DOMParser().parseFromString(svg, 'image/svg+xml').documentElement
29
+ : svg;
30
+ const [, , width = 0, height = 0] = (element.getAttribute('viewBox') ?? '')
31
+ .trim()
32
+ .split(/[\s,]+/)
33
+ .map(Number);
34
+ const box = {
35
+ height: height || Number(element.getAttribute('height') ?? 0),
36
+ width: width || Number(element.getAttribute('width') ?? 0),
37
+ };
38
+ const path = [...element.querySelectorAll('path, rect, circle, ellipse, polygon')]
39
+ .map(pathOf)
40
+ .join(' ');
41
+ return fieldFromPath(path, box.width || 1, box.height || 1, resolution);
42
+ };
43
+ export const shapeFromImage = (image, { resolution = FLAT, threshold = 0.5 } = {}) => {
44
+ const size = (value) => typeof value === 'number' ? value : value.baseVal.value;
45
+ const width = Math.max(size(image.width), 1);
46
+ const height = Math.max(size(image.height), 1);
47
+ const cell = Math.max(width, height) / resolution;
48
+ const nx = Math.max(1, Math.ceil(width / cell));
49
+ const ny = Math.max(1, Math.ceil(height / cell));
50
+ const canvas = document.createElement('canvas');
51
+ canvas.width = nx;
52
+ canvas.height = ny;
53
+ const context = canvas.getContext('2d', { willReadFrequently: true });
54
+ const cells = new Uint8Array(nx * ny);
55
+ if (context) {
56
+ context.drawImage(image, 0, 0, nx, ny);
57
+ const alpha = context.getImageData(0, 0, nx, ny).data;
58
+ for (let at = 0; at < cells.length; at++) {
59
+ cells[at] = (alpha[at * 4 + 3] ?? 0) / 255 >= threshold ? 1 : 0;
60
+ }
61
+ }
62
+ return fieldFromCells(cells, [nx, ny, 1], cell, { depth: 0, height, width });
63
+ };
@@ -0,0 +1,5 @@
1
+ export { fieldFromInside as shapeFromInside, fieldFromPath as shapeFromPath, inflate, type ShapeField, } from '../connect-graph/shape-field.js';
2
+ export { type ImageOptions, shapeFromImage, shapeFromSvg } from './flat.js';
3
+ export { shapeFromModel } from './model.js';
4
+ export { box, onSphere, sphere, torus } from './solids.js';
5
+ export { shapeFromTriangles, type TriangleOptions } from './triangles.js';
@@ -0,0 +1,5 @@
1
+ export { fieldFromInside as shapeFromInside, fieldFromPath as shapeFromPath, inflate, } from '../connect-graph/shape-field.js';
2
+ export { shapeFromImage, shapeFromSvg } from './flat.js';
3
+ export { shapeFromModel } from './model.js';
4
+ export { box, onSphere, sphere, torus } from './solids.js';
5
+ export { shapeFromTriangles } from './triangles.js';
@@ -0,0 +1,12 @@
1
+ import type { ShapeField } from '../connect-graph/shape-field.js';
2
+ import { type TriangleOptions } from './triangles.js';
3
+ /**
4
+ * A solid field from a glTF model, the web's standard format for one: a
5
+ * URL to a `.glb` or `.gltf` file, or the file's bytes. Every mesh in it is
6
+ * read, placed as the model places it.
7
+ *
8
+ * **three.js reads the file**, imported when this is first called, so a page
9
+ * that draws no model never downloads a loader. It is an optional peer of
10
+ * this package.
11
+ */
12
+ export declare const shapeFromModel: (source: string | ArrayBuffer, options?: TriangleOptions) => Promise<ShapeField>;
@@ -0,0 +1,31 @@
1
+ import { shapeFromTriangles } from './triangles.js';
2
+ export const shapeFromModel = async (source, options = {}) => {
3
+ const [{ GLTFLoader }, { Mesh, Vector3 }] = await Promise.all([
4
+ import('three/addons/loaders/GLTFLoader.js'),
5
+ import('three'),
6
+ ]);
7
+ const loader = new GLTFLoader();
8
+ const model = typeof source === 'string'
9
+ ? await loader.loadAsync(source)
10
+ : await loader.parseAsync(source, '');
11
+ model.scene.updateMatrixWorld(true);
12
+ const corners = [];
13
+ const point = new Vector3();
14
+ model.scene.traverse((object) => {
15
+ if (!(object instanceof Mesh)) {
16
+ return;
17
+ }
18
+ const geometry = object.geometry;
19
+ const place = geometry.getAttribute('position');
20
+ if (!place) {
21
+ return;
22
+ }
23
+ const index = geometry.getIndex();
24
+ const count = index ? index.count : place.count;
25
+ for (let at = 0; at < count; at++) {
26
+ point.fromBufferAttribute(place, index ? index.getX(at) : at).applyMatrix4(object.matrixWorld);
27
+ corners.push(point.x, point.y, point.z);
28
+ }
29
+ });
30
+ return shapeFromTriangles(corners, null, options);
31
+ };
@@ -0,0 +1,28 @@
1
+ import type { ShapeField } from '../connect-graph/shape-field.js';
2
+ /** A ball `radius` across from its middle. */
3
+ export declare const sphere: ({ radius }?: {
4
+ radius?: number;
5
+ }) => ShapeField;
6
+ /** A ring: `radius` from its middle to the middle of its tube, which is `tube` thick from its own middle out. */
7
+ export declare const torus: ({ radius, tube, }?: {
8
+ radius?: number;
9
+ tube?: number;
10
+ }) => ShapeField;
11
+ /** A box `width` by `height` by `depth`, its edges rounded `round` in. */
12
+ export declare const box: ({ depth, height, round, width, }?: {
13
+ depth?: number;
14
+ height?: number;
15
+ round?: number;
16
+ width?: number;
17
+ }) => ShapeField;
18
+ /**
19
+ * A flat shape wrapped round a ball, as a map of the world is round a globe:
20
+ * the flat shape's width is the way round and its height pole to pole, as an
21
+ * equirectangular map is drawn. The solid is a shell `thickness` deep at the
22
+ * ball's surface, wherever the flat shape is, so notes placed in it draw the
23
+ * map in three dimensions.
24
+ */
25
+ export declare const onSphere: (flat: ShapeField, { radius, thickness }?: {
26
+ radius?: number;
27
+ thickness?: number;
28
+ }) => ShapeField;
@@ -0,0 +1,47 @@
1
+ export const sphere = ({ radius = 50 } = {}) => ({
2
+ depth: radius * 2,
3
+ distance: (x, y, z = radius) => Math.hypot(x - radius, y - radius, z - radius) - radius,
4
+ height: radius * 2,
5
+ width: radius * 2,
6
+ });
7
+ export const torus = ({ radius = 50, tube = 18, } = {}) => {
8
+ const reach = radius + tube;
9
+ return {
10
+ depth: tube * 2,
11
+ distance: (x, y, z = tube) => Math.hypot(Math.hypot(x - reach, y - reach) - radius, z - tube) - tube,
12
+ height: reach * 2,
13
+ width: reach * 2,
14
+ };
15
+ };
16
+ export const box = ({ depth = 100, height = 100, round = 0, width = 100, } = {}) => ({
17
+ depth,
18
+ distance: (x, y, z = depth / 2) => {
19
+ const qx = Math.abs(x - width / 2) - width / 2 + round;
20
+ const qy = Math.abs(y - height / 2) - height / 2 + round;
21
+ const qz = Math.abs(z - depth / 2) - depth / 2 + round;
22
+ return (Math.hypot(Math.max(qx, 0), Math.max(qy, 0), Math.max(qz, 0)) +
23
+ Math.min(Math.max(qx, qy, qz), 0) -
24
+ round);
25
+ },
26
+ height,
27
+ width,
28
+ });
29
+ export const onSphere = (flat, { radius = 50, thickness = radius * 0.08 } = {}) => {
30
+ const along = (Math.PI * 2 * radius) / Math.max(flat.width, 1e-9);
31
+ return {
32
+ depth: radius * 2,
33
+ distance: (x, y, z = radius) => {
34
+ const dx = x - radius;
35
+ const dy = y - radius;
36
+ const dz = z - radius;
37
+ const out = Math.hypot(dx, dy, dz);
38
+ const shell = Math.abs(out - (radius - thickness / 2)) - thickness / 2;
39
+ const longitude = Math.atan2(dx, dz);
40
+ const latitude = Math.acos(Math.min(Math.max(dy / (out || 1), -1), 1));
41
+ const map = flat.distance(((longitude + Math.PI) / (Math.PI * 2)) * flat.width, (latitude / Math.PI) * flat.height);
42
+ return Math.max(shell, map * along);
43
+ },
44
+ height: radius * 2,
45
+ width: radius * 2,
46
+ };
47
+ };
@@ -0,0 +1,20 @@
1
+ import { type ShapeField } from '../connect-graph/shape-field.js';
2
+ /** How a solid is read from triangles. */
3
+ export interface TriangleOptions {
4
+ /** How many cells the field has along the solid's longest side. 96 by default. */
5
+ resolution?: number | undefined;
6
+ }
7
+ /**
8
+ * A solid field from a closed surface of triangles: `positions` three
9
+ * numbers a corner, and `index` three corners a triangle, or every three
10
+ * corners in order where there is none. Its y points up, as a model's does,
11
+ * and its box is the surface's own bounds, a cell of room around them.
12
+ *
13
+ * **Read a column of cells at a time**: each triangle marks where it crosses
14
+ * the columns under it, and a cell is inside where an odd number of
15
+ * crossings lie in front of it. A model of a hundred thousand triangles is
16
+ * read in a moment, where testing each cell against each triangle would take
17
+ * minutes. A surface with gaps is read as closed where it can be, and a gap
18
+ * shows as a column filled or emptied through.
19
+ */
20
+ export declare const shapeFromTriangles: (positions: ArrayLike<number>, index?: ArrayLike<number> | null | undefined, { resolution }?: TriangleOptions) => ShapeField;
@@ -0,0 +1,78 @@
1
+ import { fieldFromCells, SOLID } from '../connect-graph/shape-field.js';
2
+ const NUDGE = 1e-6 * Math.PI;
3
+ export const shapeFromTriangles = (positions, index, { resolution = SOLID } = {}) => {
4
+ const corners = Math.floor(positions.length / 3);
5
+ const low = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY];
6
+ const high = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
7
+ for (let at = 0; at < corners; at++) {
8
+ for (let axis = 0; axis < 3; axis++) {
9
+ const value = positions[at * 3 + axis];
10
+ low[axis] = Math.min(low[axis], value);
11
+ high[axis] = Math.max(high[axis], value);
12
+ }
13
+ }
14
+ if (corners === 0) {
15
+ return fieldFromCells(new Uint8Array(1), [1, 1, 1], 1, { depth: 1, height: 1, width: 1 });
16
+ }
17
+ const span = Math.max(high[0] - low[0], high[1] - low[1], high[2] - low[2], 1e-9);
18
+ const cell = span / Math.max(resolution - 2, 1);
19
+ const origin = low.map((value) => value - cell);
20
+ const nx = Math.ceil((high[0] - low[0]) / cell) + 2;
21
+ const ny = Math.ceil((high[1] - low[1]) / cell) + 2;
22
+ const nz = Math.ceil((high[2] - low[2]) / cell) + 2;
23
+ const crossings = Array.from({ length: nx * ny }, () => []);
24
+ const triangles = index ? Math.floor(index.length / 3) : Math.floor(corners / 3);
25
+ const corner = (triangle, which) => {
26
+ const at = index ? index[triangle * 3 + which] : triangle * 3 + which;
27
+ return [
28
+ (positions[at * 3] - origin[0]) / cell,
29
+ (high[1] + cell - positions[at * 3 + 1]) / cell,
30
+ (positions[at * 3 + 2] - origin[2]) / cell,
31
+ ];
32
+ };
33
+ for (let triangle = 0; triangle < triangles; triangle++) {
34
+ const [ax, ay, az] = corner(triangle, 0);
35
+ const [bx, by, bz] = corner(triangle, 1);
36
+ const [cx, cy, cz] = corner(triangle, 2);
37
+ const area = (bx - ax) * (cy - ay) - (cx - ax) * (by - ay);
38
+ if (Math.abs(area) < 1e-12) {
39
+ continue;
40
+ }
41
+ const left = Math.max(0, Math.ceil(Math.min(ax, bx, cx) - 0.5));
42
+ const right = Math.min(nx - 1, Math.floor(Math.max(ax, bx, cx) - 0.5));
43
+ const top = Math.max(0, Math.ceil(Math.min(ay, by, cy) - 0.5));
44
+ const bottom = Math.min(ny - 1, Math.floor(Math.max(ay, by, cy) - 0.5));
45
+ for (let row = top; row <= bottom; row++) {
46
+ for (let column = left; column <= right; column++) {
47
+ const px = column + 0.5 + NUDGE;
48
+ const py = row + 0.5 + NUDGE * Math.SQRT2;
49
+ const u = ((bx - px) * (cy - py) - (cx - px) * (by - py)) / area;
50
+ const v = ((cx - px) * (ay - py) - (ax - px) * (cy - py)) / area;
51
+ const w = 1 - u - v;
52
+ if (u < 0 || v < 0 || w < 0) {
53
+ continue;
54
+ }
55
+ ;
56
+ crossings[row * nx + column].push(u * az + v * bz + w * cz);
57
+ }
58
+ }
59
+ }
60
+ const cells = new Uint8Array(nx * ny * nz);
61
+ for (let row = 0; row < ny; row++) {
62
+ for (let column = 0; column < nx; column++) {
63
+ const along = crossings[row * nx + column].sort((one, other) => one - other);
64
+ for (let pair = 0; pair + 1 < along.length; pair += 2) {
65
+ const from = Math.max(0, Math.ceil(along[pair] - 0.5));
66
+ const to = Math.min(nz - 1, Math.floor(along[pair + 1] - 0.5));
67
+ for (let layer = from; layer <= to; layer++) {
68
+ cells[layer * nx * ny + row * nx + column] = 1;
69
+ }
70
+ }
71
+ }
72
+ }
73
+ return fieldFromCells(cells, [nx, ny, nz], cell, {
74
+ depth: nz * cell,
75
+ height: ny * cell,
76
+ width: nx * cell,
77
+ });
78
+ };
@@ -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,13 @@
1
+ import type { ShapeField } from '../connect-graph/shape-field.js';
2
+ /**
3
+ * The surface of a solid field where its distance is `at`, as triangles:
4
+ * corners three numbers each in the field's box, and triangles three corners
5
+ * each. Surface nets: a corner in each cell the surface passes through, at
6
+ * the middle of where it crosses the cell's edges, and two triangles across
7
+ * each edge it crosses. Smooth enough to hide what is behind a shape, and
8
+ * made in a moment.
9
+ */
10
+ export declare const surfaceOf: (field: ShapeField, at?: number) => {
11
+ corners: Float32Array;
12
+ triangles: Uint32Array;
13
+ };
@@ -0,0 +1,112 @@
1
+ const CELLS = 72;
2
+ export const surfaceOf = (field, at = 0) => {
3
+ const { depth, height, width } = field;
4
+ const cell = Math.max(width, height, depth) / CELLS;
5
+ const nx = Math.ceil(width / cell) + 3;
6
+ const ny = Math.ceil(height / cell) + 3;
7
+ const nz = Math.ceil(depth / cell) + 3;
8
+ const value = new Float32Array(nx * ny * nz);
9
+ const spot = (x, y, z) => (z * ny + y) * nx + x;
10
+ for (let z = 0; z < nz; z++) {
11
+ for (let y = 0; y < ny; y++) {
12
+ for (let x = 0; x < nx; x++) {
13
+ value[spot(x, y, z)] = field.distance((x - 1) * cell, (y - 1) * cell, (z - 1) * cell) - at;
14
+ }
15
+ }
16
+ }
17
+ const cornerOf = new Int32Array((nx - 1) * (ny - 1) * (nz - 1)).fill(-1);
18
+ const cellSpot = (x, y, z) => (z * (ny - 1) + y) * (nx - 1) + x;
19
+ const corners = [];
20
+ const edges = [
21
+ [0, 1],
22
+ [2, 3],
23
+ [4, 5],
24
+ [6, 7],
25
+ [0, 2],
26
+ [1, 3],
27
+ [4, 6],
28
+ [5, 7],
29
+ [0, 4],
30
+ [1, 5],
31
+ [2, 6],
32
+ [3, 7],
33
+ ];
34
+ const offsets = [
35
+ [0, 0, 0],
36
+ [1, 0, 0],
37
+ [0, 1, 0],
38
+ [1, 1, 0],
39
+ [0, 0, 1],
40
+ [1, 0, 1],
41
+ [0, 1, 1],
42
+ [1, 1, 1],
43
+ ];
44
+ const values = new Float32Array(8);
45
+ for (let z = 0; z < nz - 1; z++) {
46
+ for (let y = 0; y < ny - 1; y++) {
47
+ for (let x = 0; x < nx - 1; x++) {
48
+ let inside = 0;
49
+ for (let corner = 0; corner < 8; corner++) {
50
+ const [dx, dy, dz] = offsets[corner];
51
+ const sample = value[spot(x + dx, y + dy, z + dz)];
52
+ values[corner] = sample;
53
+ if (sample < 0) {
54
+ inside += 1;
55
+ }
56
+ }
57
+ if (inside === 0 || inside === 8) {
58
+ continue;
59
+ }
60
+ let sx = 0;
61
+ let sy = 0;
62
+ let sz = 0;
63
+ let crossed = 0;
64
+ for (const [from, to] of edges) {
65
+ const a = values[from];
66
+ const b = values[to];
67
+ if (a < 0 === b < 0) {
68
+ continue;
69
+ }
70
+ const t = a / (a - b);
71
+ const [ax, ay, az] = offsets[from];
72
+ const [bx, by, bz] = offsets[to];
73
+ sx += ax + (bx - ax) * t;
74
+ sy += ay + (by - ay) * t;
75
+ sz += az + (bz - az) * t;
76
+ crossed += 1;
77
+ }
78
+ cornerOf[cellSpot(x, y, z)] = corners.length / 3;
79
+ corners.push((x + sx / crossed - 1) * cell, (y + sy / crossed - 1) * cell, (z + sz / crossed - 1) * cell);
80
+ }
81
+ }
82
+ }
83
+ const triangles = [];
84
+ const quad = (a, b, c, d, flip) => {
85
+ if (a < 0 || b < 0 || c < 0 || d < 0) {
86
+ return;
87
+ }
88
+ if (flip) {
89
+ triangles.push(a, c, b, a, d, c);
90
+ }
91
+ else {
92
+ triangles.push(a, b, c, a, c, d);
93
+ }
94
+ };
95
+ for (let z = 1; z < nz - 1; z++) {
96
+ for (let y = 1; y < ny - 1; y++) {
97
+ for (let x = 1; x < nx - 1; x++) {
98
+ const here = value[spot(x, y, z)] < 0;
99
+ if (x < nx - 1 && here !== value[spot(x + 1, y, z)] < 0) {
100
+ quad(cornerOf[cellSpot(x, y - 1, z - 1)], cornerOf[cellSpot(x, y, z - 1)], cornerOf[cellSpot(x, y, z)], cornerOf[cellSpot(x, y - 1, z)], here);
101
+ }
102
+ if (y < ny - 1 && here !== value[spot(x, y + 1, z)] < 0) {
103
+ quad(cornerOf[cellSpot(x - 1, y, z - 1)], cornerOf[cellSpot(x - 1, y, z)], cornerOf[cellSpot(x, y, z)], cornerOf[cellSpot(x, y, z - 1)], here);
104
+ }
105
+ if (z < nz - 1 && here !== value[spot(x, y, z + 1)] < 0) {
106
+ quad(cornerOf[cellSpot(x - 1, y - 1, z)], cornerOf[cellSpot(x, y - 1, z)], cornerOf[cellSpot(x, y, z)], cornerOf[cellSpot(x - 1, y, z)], here);
107
+ }
108
+ }
109
+ }
110
+ }
111
+ return { corners: new Float32Array(corners), triangles: new Uint32Array(triangles) };
112
+ };
@@ -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;