gloop-sim 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +111 -2
- package/dist/assert.d.ts +1 -0
- package/dist/assert.js +6 -0
- package/dist/assert.js.map +1 -0
- package/dist/backend/cpu.d.ts +2 -0
- package/dist/backend/cpu.js +23 -0
- package/dist/backend/cpu.js.map +1 -0
- package/dist/backend/gpu.d.ts +2 -0
- package/dist/backend/gpu.js +145 -0
- package/dist/backend/gpu.js.map +1 -0
- package/dist/backend/index.d.ts +14 -0
- package/dist/backend/index.js +23 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/device.d.ts +11 -0
- package/dist/device.js +66 -0
- package/dist/device.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/initializer/fixed.d.ts +2 -0
- package/dist/initializer/fixed.js +12 -0
- package/dist/initializer/fixed.js.map +1 -0
- package/dist/initializer/index.d.ts +14 -0
- package/dist/initializer/index.js +23 -0
- package/dist/initializer/index.js.map +1 -0
- package/dist/initializer/random.d.ts +2 -0
- package/dist/initializer/random.js +16 -0
- package/dist/initializer/random.js.map +1 -0
- package/dist/integrator/euler.d.ts +2 -0
- package/dist/integrator/euler.js +34 -0
- package/dist/integrator/euler.js.map +1 -0
- package/dist/integrator/index.d.ts +14 -0
- package/dist/integrator/index.js +23 -0
- package/dist/integrator/index.js.map +1 -0
- package/dist/integrator/semi-implicit-euler.d.ts +2 -0
- package/dist/integrator/semi-implicit-euler.js +36 -0
- package/dist/integrator/semi-implicit-euler.js.map +1 -0
- package/dist/model/elastic-network/cpu.d.ts +2 -0
- package/dist/model/elastic-network/cpu.js +90 -0
- package/dist/model/elastic-network/cpu.js.map +1 -0
- package/dist/model/elastic-network/gpu.d.ts +2 -0
- package/dist/model/elastic-network/gpu.js +114 -0
- package/dist/model/elastic-network/gpu.js.map +1 -0
- package/dist/model/elastic-network/index.d.ts +2 -0
- package/dist/model/elastic-network/index.js +69 -0
- package/dist/model/elastic-network/index.js.map +1 -0
- package/dist/model/index.d.ts +15 -0
- package/dist/model/index.js +24 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/inertial.d.ts +2 -0
- package/dist/model/inertial.js +17 -0
- package/dist/model/inertial.js.map +1 -0
- package/dist/render/backend/channels.d.ts +17 -0
- package/dist/render/backend/channels.js +35 -0
- package/dist/render/backend/channels.js.map +1 -0
- package/dist/render/backend/cloud.d.ts +77 -0
- package/dist/render/backend/cloud.js +29 -0
- package/dist/render/backend/cloud.js.map +1 -0
- package/dist/render/backend/compile.d.ts +31 -0
- package/dist/render/backend/compile.js +73 -0
- package/dist/render/backend/compile.js.map +1 -0
- package/dist/render/backend/glyph.d.ts +8 -0
- package/dist/render/backend/glyph.js +38 -0
- package/dist/render/backend/glyph.js.map +1 -0
- package/dist/render/backend/glyphs.d.ts +6 -0
- package/dist/render/backend/glyphs.js +146 -0
- package/dist/render/backend/glyphs.js.map +1 -0
- package/dist/render/backend/gpu.d.ts +12 -0
- package/dist/render/backend/gpu.js +278 -0
- package/dist/render/backend/gpu.js.map +1 -0
- package/dist/render/backend/material.d.ts +30 -0
- package/dist/render/backend/material.js +57 -0
- package/dist/render/backend/material.js.map +1 -0
- package/dist/render/backend/particles.d.ts +5 -0
- package/dist/render/backend/particles.js +22 -0
- package/dist/render/backend/particles.js.map +1 -0
- package/dist/render/backend/post.d.ts +29 -0
- package/dist/render/backend/post.js +77 -0
- package/dist/render/backend/post.js.map +1 -0
- package/dist/render/backend/solids.d.ts +10 -0
- package/dist/render/backend/solids.js +134 -0
- package/dist/render/backend/solids.js.map +1 -0
- package/dist/render/camera.d.ts +73 -0
- package/dist/render/camera.js +32 -0
- package/dist/render/camera.js.map +1 -0
- package/dist/render/channel.d.ts +25 -0
- package/dist/render/channel.js +9 -0
- package/dist/render/channel.js.map +1 -0
- package/dist/render/color.d.ts +34 -0
- package/dist/render/color.js +56 -0
- package/dist/render/color.js.map +1 -0
- package/dist/render/effect.d.ts +81 -0
- package/dist/render/effect.js +30 -0
- package/dist/render/effect.js.map +1 -0
- package/dist/render/frame.d.ts +60 -0
- package/dist/render/frame.js +52 -0
- package/dist/render/frame.js.map +1 -0
- package/dist/render/index.d.ts +12 -0
- package/dist/render/index.js +2 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/layer.d.ts +90 -0
- package/dist/render/layer.js +54 -0
- package/dist/render/layer.js.map +1 -0
- package/dist/render/ramp.d.ts +93 -0
- package/dist/render/ramp.js +120 -0
- package/dist/render/ramp.js.map +1 -0
- package/dist/render/sampler.d.ts +34 -0
- package/dist/render/sampler.js +53 -0
- package/dist/render/sampler.js.map +1 -0
- package/dist/render/signal.d.ts +66 -0
- package/dist/render/signal.js +51 -0
- package/dist/render/signal.js.map +1 -0
- package/dist/render/sources.d.ts +33 -0
- package/dist/render/sources.js +46 -0
- package/dist/render/sources.js.map +1 -0
- package/dist/render/springs/anchors.d.ts +37 -0
- package/dist/render/springs/anchors.js +34 -0
- package/dist/render/springs/anchors.js.map +1 -0
- package/dist/render/springs/construction.d.ts +30 -0
- package/dist/render/springs/construction.js +66 -0
- package/dist/render/springs/construction.js.map +1 -0
- package/dist/render/springs/impulses.d.ts +45 -0
- package/dist/render/springs/impulses.js +144 -0
- package/dist/render/springs/impulses.js.map +1 -0
- package/dist/render/springs/index.d.ts +9 -0
- package/dist/render/springs/index.js +2 -0
- package/dist/render/springs/index.js.map +1 -0
- package/dist/render/springs/link.d.ts +51 -0
- package/dist/render/springs/link.js +42 -0
- package/dist/render/springs/link.js.map +1 -0
- package/dist/render/springs/links.d.ts +70 -0
- package/dist/render/springs/links.js +210 -0
- package/dist/render/springs/links.js.map +1 -0
- package/dist/render/springs/palette.d.ts +36 -0
- package/dist/render/springs/palette.js +22 -0
- package/dist/render/springs/palette.js.map +1 -0
- package/dist/render/springs/preset.d.ts +65 -0
- package/dist/render/springs/preset.js +114 -0
- package/dist/render/springs/preset.js.map +1 -0
- package/dist/render/springs/segment.d.ts +40 -0
- package/dist/render/springs/segment.js +81 -0
- package/dist/render/springs/segment.js.map +1 -0
- package/dist/render/springs/source.d.ts +35 -0
- package/dist/render/springs/source.js +70 -0
- package/dist/render/springs/source.js.map +1 -0
- package/dist/render/springs/springs.d.ts +40 -0
- package/dist/render/springs/springs.js +34 -0
- package/dist/render/springs/springs.js.map +1 -0
- package/dist/render/springs/strain.d.ts +21 -0
- package/dist/render/springs/strain.js +39 -0
- package/dist/render/springs/strain.js.map +1 -0
- package/dist/render/stage.d.ts +96 -0
- package/dist/render/stage.js +2 -0
- package/dist/render/stage.js.map +1 -0
- package/dist/render/view.d.ts +63 -0
- package/dist/render/view.js +63 -0
- package/dist/render/view.js.map +1 -0
- package/dist/swarm.d.ts +5 -0
- package/dist/swarm.js +24 -0
- package/dist/swarm.js.map +1 -0
- package/dist/types.d.ts +290 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/assets/logo.svg +112 -0
- package/package.json +102 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BufferAttribute, BufferGeometry, DynamicDrawUsage, Mesh } from "three/webgpu";
|
|
2
|
+
/**
|
|
3
|
+
* The number of vertices a thick segment expands to.
|
|
4
|
+
*/
|
|
5
|
+
export const VERTICES_PER_SEGMENT = 6;
|
|
6
|
+
const FLOATS_PER_VERTEX = 3;
|
|
7
|
+
const FLOATS_PER_SEGMENT = VERTICES_PER_SEGMENT * FLOATS_PER_VERTEX;
|
|
8
|
+
/**
|
|
9
|
+
* A dynamic buffer attribute holding `itemSize` floats per vertex for `count`
|
|
10
|
+
* segments.
|
|
11
|
+
*/
|
|
12
|
+
export function segmentAttribute(count, itemSize) {
|
|
13
|
+
const attribute = new BufferAttribute(new Float32Array(count * VERTICES_PER_SEGMENT * itemSize), itemSize);
|
|
14
|
+
attribute.setUsage(DynamicDrawUsage);
|
|
15
|
+
return attribute;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A geometry for `count` segments, with a dynamic position attribute that
|
|
19
|
+
* {@link writeSegment} fills.
|
|
20
|
+
*/
|
|
21
|
+
export function segmentGeometry(count) {
|
|
22
|
+
const geometry = new BufferGeometry();
|
|
23
|
+
const positions = segmentAttribute(count, FLOATS_PER_VERTEX);
|
|
24
|
+
geometry.setAttribute("position", positions);
|
|
25
|
+
return { geometry, positions };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A mesh over segment geometry. Frustum culling is off, since the positions are
|
|
29
|
+
* rewritten every frame and the geometry's bounds are never recomputed.
|
|
30
|
+
*/
|
|
31
|
+
export function segmentMesh(geometry, material) {
|
|
32
|
+
const mesh = new Mesh(geometry, material);
|
|
33
|
+
mesh.frustumCulled = false;
|
|
34
|
+
return mesh;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The unit vector at a right angle to the segment from `a` to `b`, in the
|
|
38
|
+
* screen (x, y) plane. Falls back to the x axis when the endpoints coincide.
|
|
39
|
+
*/
|
|
40
|
+
function perpendicular(a, b) {
|
|
41
|
+
const px = b[1] - a[1];
|
|
42
|
+
const py = -(b[0] - a[0]);
|
|
43
|
+
const length = Math.hypot(px, py);
|
|
44
|
+
if (length < 1e-9) {
|
|
45
|
+
return [1, 0];
|
|
46
|
+
}
|
|
47
|
+
return [px / length, py / length];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Writes a thick segment between endpoints `a` and `b` into `target`, drawn as
|
|
51
|
+
* two triangles that lie flat in the screen plane. The width is added at a right
|
|
52
|
+
* angle to the segment, and `shift` slides the whole quad that same way, for
|
|
53
|
+
* drawing offset copies. `segment` picks which slice of `target` to fill.
|
|
54
|
+
*/
|
|
55
|
+
export function writeSegment({ target, segment, a, b, halfWidth, shift = 0 }) {
|
|
56
|
+
const [nx, ny] = perpendicular(a, b);
|
|
57
|
+
const ox = nx * halfWidth;
|
|
58
|
+
const oy = ny * halfWidth;
|
|
59
|
+
const [ax, ay, az] = a;
|
|
60
|
+
const [bx, by, bz] = b;
|
|
61
|
+
const x0 = ax + nx * shift;
|
|
62
|
+
const y0 = ay + ny * shift;
|
|
63
|
+
const x1 = bx + nx * shift;
|
|
64
|
+
const y1 = by + ny * shift;
|
|
65
|
+
const corners = [
|
|
66
|
+
[x0 - ox, y0 - oy, az],
|
|
67
|
+
[x1 - ox, y1 - oy, bz],
|
|
68
|
+
[x1 + ox, y1 + oy, bz],
|
|
69
|
+
[x0 - ox, y0 - oy, az],
|
|
70
|
+
[x1 + ox, y1 + oy, bz],
|
|
71
|
+
[x0 + ox, y0 + oy, az],
|
|
72
|
+
];
|
|
73
|
+
const base = segment * FLOATS_PER_SEGMENT;
|
|
74
|
+
for (const [vertex, [x, y, z]] of corners.entries()) {
|
|
75
|
+
const offset = base + vertex * FLOATS_PER_VERTEX;
|
|
76
|
+
target[offset] = x;
|
|
77
|
+
target[offset + 1] = y;
|
|
78
|
+
target[offset + 2] = z;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=segment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment.js","sourceRoot":"","sources":["../../../src/render/springs/segment.ts"],"names":[],"mappings":"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","sourcesContent":["import { BufferAttribute, BufferGeometry, DynamicDrawUsage, type Material, Mesh } from \"three/webgpu\";\n\n/**\n * The number of vertices a thick segment expands to.\n */\nexport const VERTICES_PER_SEGMENT = 6;\n\nconst FLOATS_PER_VERTEX = 3;\nconst FLOATS_PER_SEGMENT = VERTICES_PER_SEGMENT * FLOATS_PER_VERTEX;\n\ntype Point = readonly [number, number, number];\n\n/**\n * A dynamic buffer attribute holding `itemSize` floats per vertex for `count`\n * segments.\n */\nexport function segmentAttribute(count: number, itemSize: number): BufferAttribute {\n const attribute = new BufferAttribute(new Float32Array(count * VERTICES_PER_SEGMENT * itemSize), itemSize);\n attribute.setUsage(DynamicDrawUsage);\n return attribute;\n}\n\n/**\n * A geometry for `count` segments, with a dynamic position attribute that\n * {@link writeSegment} fills.\n */\nexport function segmentGeometry(count: number): { geometry: BufferGeometry; positions: BufferAttribute } {\n const geometry = new BufferGeometry();\n const positions = segmentAttribute(count, FLOATS_PER_VERTEX);\n geometry.setAttribute(\"position\", positions);\n return { geometry, positions };\n}\n\n/**\n * A mesh over segment geometry. Frustum culling is off, since the positions are\n * rewritten every frame and the geometry's bounds are never recomputed.\n */\nexport function segmentMesh(geometry: BufferGeometry, material: Material): Mesh {\n const mesh = new Mesh(geometry, material);\n mesh.frustumCulled = false;\n return mesh;\n}\n\n/**\n * The unit vector at a right angle to the segment from `a` to `b`, in the\n * screen (x, y) plane. Falls back to the x axis when the endpoints coincide.\n */\nfunction perpendicular(a: Point, b: Point): readonly [number, number] {\n const px = b[1] - a[1];\n const py = -(b[0] - a[0]);\n const length = Math.hypot(px, py);\n if (length < 1e-9) {\n return [1, 0];\n }\n return [px / length, py / length];\n}\n\ninterface WriteSegmentOptions {\n target: Float32Array;\n segment: number;\n a: Point;\n b: Point;\n halfWidth: number;\n shift?: number;\n}\n\n/**\n * Writes a thick segment between endpoints `a` and `b` into `target`, drawn as\n * two triangles that lie flat in the screen plane. The width is added at a right\n * angle to the segment, and `shift` slides the whole quad that same way, for\n * drawing offset copies. `segment` picks which slice of `target` to fill.\n */\nexport function writeSegment({ target, segment, a, b, halfWidth, shift = 0 }: WriteSegmentOptions): void {\n const [nx, ny] = perpendicular(a, b);\n const ox = nx * halfWidth;\n const oy = ny * halfWidth;\n\n const [ax, ay, az] = a;\n const [bx, by, bz] = b;\n const x0 = ax + nx * shift;\n const y0 = ay + ny * shift;\n const x1 = bx + nx * shift;\n const y1 = by + ny * shift;\n\n const corners: readonly Point[] = [\n [x0 - ox, y0 - oy, az],\n [x1 - ox, y1 - oy, bz],\n [x1 + ox, y1 + oy, bz],\n [x0 - ox, y0 - oy, az],\n [x1 + ox, y1 + oy, bz],\n [x0 + ox, y0 + oy, az],\n ];\n\n const base = segment * FLOATS_PER_SEGMENT;\n for (const [vertex, [x, y, z]] of corners.entries()) {\n const offset = base + vertex * FLOATS_PER_VERTEX;\n target[offset] = x;\n target[offset + 1] = y;\n target[offset + 2] = z;\n }\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Signal, type SignalOptions } from "../../render/signal.js";
|
|
2
|
+
import type { Source } from "../../render/sources.js";
|
|
3
|
+
import { type Link, type LinkSpecification } from "../../render/springs/link.js";
|
|
4
|
+
/**
|
|
5
|
+
* The springs the renderer draws: the link list, the particles treated as
|
|
6
|
+
* anchors, and the strain derived from each frame.
|
|
7
|
+
*/
|
|
8
|
+
export interface Springs extends Source {
|
|
9
|
+
/**
|
|
10
|
+
* Every link to draw, each already classified into a group.
|
|
11
|
+
*/
|
|
12
|
+
readonly links: readonly Link[];
|
|
13
|
+
/**
|
|
14
|
+
* The particle indices treated as anchors.
|
|
15
|
+
*/
|
|
16
|
+
readonly anchors: ReadonlySet<number>;
|
|
17
|
+
/**
|
|
18
|
+
* Each link's unsigned deviation from its rest length for the current frame,
|
|
19
|
+
* as a fraction of it. The array is live and owned by the source.
|
|
20
|
+
*/
|
|
21
|
+
strains(): Float32Array;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates springs from a link list, optionally naming which particles are
|
|
25
|
+
* anchors. Every link that carries no group is tagged `pinned` or `free` from
|
|
26
|
+
* those anchors.
|
|
27
|
+
*/
|
|
28
|
+
export declare function of({ links, anchors }: {
|
|
29
|
+
links: readonly LinkSpecification[];
|
|
30
|
+
anchors?: readonly number[];
|
|
31
|
+
}): Springs;
|
|
32
|
+
/**
|
|
33
|
+
* A signal over a link's fractional deviation from its rest length.
|
|
34
|
+
*/
|
|
35
|
+
export declare function strain(options: SignalOptions): Signal<Springs>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { assert } from "../../assert.js";
|
|
2
|
+
import { channel as defineChannel } from "../../render/channel.js";
|
|
3
|
+
import { PackedPositions } from "../../render/frame.js";
|
|
4
|
+
import { signal as coreSignal } from "../../render/signal.js";
|
|
5
|
+
import { anchorsFrom, classifyLinks, validateLinks } from "../../render/springs/link.js";
|
|
6
|
+
import { computeStrain, measureLengths } from "../../render/springs/strain.js";
|
|
7
|
+
const UNSET_REST_LENGTH = -1;
|
|
8
|
+
class SimpleSprings {
|
|
9
|
+
links;
|
|
10
|
+
anchors;
|
|
11
|
+
lengths;
|
|
12
|
+
restLengths;
|
|
13
|
+
strainBuffer;
|
|
14
|
+
captured = false;
|
|
15
|
+
constructor(links, anchors) {
|
|
16
|
+
this.links = links;
|
|
17
|
+
this.anchors = anchors;
|
|
18
|
+
this.lengths = new Float32Array(links.length);
|
|
19
|
+
this.strainBuffer = new Float32Array(links.length);
|
|
20
|
+
this.restLengths = new Float32Array(links.length);
|
|
21
|
+
for (const [index, link] of links.entries()) {
|
|
22
|
+
this.restLengths[index] = link.restLength ?? UNSET_REST_LENGTH;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
strains() {
|
|
26
|
+
return this.strainBuffer;
|
|
27
|
+
}
|
|
28
|
+
update(_state, frame) {
|
|
29
|
+
measureLengths({ points: PackedPositions.of(frame.positions), links: this.links, lengths: this.lengths });
|
|
30
|
+
this.captureRestLengths();
|
|
31
|
+
computeStrain({ lengths: this.lengths, restLengths: this.restLengths, strain: this.strainBuffer });
|
|
32
|
+
}
|
|
33
|
+
captureRestLengths() {
|
|
34
|
+
if (this.captured) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
for (let index = 0; index < this.restLengths.length; index++) {
|
|
38
|
+
if (this.restLengths[index] === UNSET_REST_LENGTH) {
|
|
39
|
+
const observed = this.lengths[index];
|
|
40
|
+
assert(observed !== undefined, `link ${index} length is missing when capturing rest lengths`);
|
|
41
|
+
this.restLengths[index] = observed;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
this.captured = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Each link's unsigned deviation from its rest length, as a fraction of it.
|
|
49
|
+
*/
|
|
50
|
+
const strainChannel = defineChannel({
|
|
51
|
+
name: "strain",
|
|
52
|
+
values: (source) => source.strains(),
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Creates springs from a link list, optionally naming which particles are
|
|
56
|
+
* anchors. Every link that carries no group is tagged `pinned` or `free` from
|
|
57
|
+
* those anchors.
|
|
58
|
+
*/
|
|
59
|
+
export function of({ links, anchors }) {
|
|
60
|
+
validateLinks(links);
|
|
61
|
+
const anchorSet = anchorsFrom(anchors);
|
|
62
|
+
return new SimpleSprings(classifyLinks(links, anchorSet), anchorSet);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A signal over a link's fractional deviation from its rest length.
|
|
66
|
+
*/
|
|
67
|
+
export function strain(options) {
|
|
68
|
+
return coreSignal.of(strainChannel, options);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../../src/render/springs/source.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert } from \"@/assert.js\";\nimport { type Channel, channel as defineChannel } from \"@/render/channel.js\";\nimport { type Frame, PackedPositions } from \"@/render/frame.js\";\nimport { signal as coreSignal, type Signal, type SignalOptions } from \"@/render/signal.js\";\nimport type { Source } from \"@/render/sources.js\";\nimport { anchorsFrom, classifyLinks, type Link, type LinkSpecification, validateLinks } from \"@/render/springs/link.js\";\nimport { computeStrain, measureLengths } from \"@/render/springs/strain.js\";\n\nconst UNSET_REST_LENGTH = -1;\n\n/**\n * The springs the renderer draws: the link list, the particles treated as\n * anchors, and the strain derived from each frame.\n */\nexport interface Springs extends Source {\n /**\n * Every link to draw, each already classified into a group.\n */\n readonly links: readonly Link[];\n /**\n * The particle indices treated as anchors.\n */\n readonly anchors: ReadonlySet<number>;\n /**\n * Each link's unsigned deviation from its rest length for the current frame,\n * as a fraction of it. The array is live and owned by the source.\n */\n strains(): Float32Array;\n}\n\nclass SimpleSprings implements Springs {\n private readonly lengths: Float32Array;\n private readonly restLengths: Float32Array;\n private readonly strainBuffer: Float32Array;\n private captured = false;\n\n constructor(\n readonly links: readonly Link[],\n readonly anchors: ReadonlySet<number>,\n ) {\n this.lengths = new Float32Array(links.length);\n this.strainBuffer = new Float32Array(links.length);\n this.restLengths = new Float32Array(links.length);\n for (const [index, link] of links.entries()) {\n this.restLengths[index] = link.restLength ?? UNSET_REST_LENGTH;\n }\n }\n\n strains(): Float32Array {\n return this.strainBuffer;\n }\n\n update(_state: unknown, frame: Frame): void {\n measureLengths({ points: PackedPositions.of(frame.positions), links: this.links, lengths: this.lengths });\n this.captureRestLengths();\n computeStrain({ lengths: this.lengths, restLengths: this.restLengths, strain: this.strainBuffer });\n }\n\n private captureRestLengths(): void {\n if (this.captured) {\n return;\n }\n for (let index = 0; index < this.restLengths.length; index++) {\n if (this.restLengths[index] === UNSET_REST_LENGTH) {\n const observed = this.lengths[index];\n assert(observed !== undefined, `link ${index} length is missing when capturing rest lengths`);\n this.restLengths[index] = observed;\n }\n }\n this.captured = true;\n }\n}\n\n/**\n * Each link's unsigned deviation from its rest length, as a fraction of it.\n */\nconst strainChannel: Channel<Springs> = defineChannel<Springs>({\n name: \"strain\",\n values: (source) => source.strains(),\n});\n\n/**\n * Creates springs from a link list, optionally naming which particles are\n * anchors. Every link that carries no group is tagged `pinned` or `free` from\n * those anchors.\n */\nexport function of({ links, anchors }: { links: readonly LinkSpecification[]; anchors?: readonly number[] }): Springs {\n validateLinks(links);\n const anchorSet = anchorsFrom(anchors);\n return new SimpleSprings(classifyLinks(links, anchorSet), anchorSet);\n}\n\n/**\n * A signal over a link's fractional deviation from its rest length.\n */\nexport function strain(options: SignalOptions): Signal<Springs> {\n return coreSignal.of(strainChannel, options);\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { anchors } from "../../render/springs/anchors.js";
|
|
2
|
+
import { construction } from "../../render/springs/construction.js";
|
|
3
|
+
import { impulses } from "../../render/springs/impulses.js";
|
|
4
|
+
import { links } from "../../render/springs/links.js";
|
|
5
|
+
import { palette } from "../../render/springs/palette.js";
|
|
6
|
+
import { preset } from "../../render/springs/preset.js";
|
|
7
|
+
import { of, strain } from "../../render/springs/source.js";
|
|
8
|
+
/**
|
|
9
|
+
* The spring-network rendering namespace.
|
|
10
|
+
*/
|
|
11
|
+
export declare const springs: {
|
|
12
|
+
/**
|
|
13
|
+
* Creates springs over the swarm's particles from a link list.
|
|
14
|
+
*/
|
|
15
|
+
readonly of: typeof of;
|
|
16
|
+
/**
|
|
17
|
+
* A namespace of layers that draw springs, composed onto a scene in
|
|
18
|
+
* draw order.
|
|
19
|
+
*/
|
|
20
|
+
readonly layer: {
|
|
21
|
+
readonly construction: typeof construction;
|
|
22
|
+
readonly links: typeof links;
|
|
23
|
+
readonly impulses: typeof impulses;
|
|
24
|
+
readonly anchors: typeof anchors;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A namespace of signals over springs.
|
|
28
|
+
*/
|
|
29
|
+
readonly signal: {
|
|
30
|
+
readonly strain: typeof strain;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* A namespace of composable spring-network presets.
|
|
34
|
+
*/
|
|
35
|
+
readonly preset: typeof preset;
|
|
36
|
+
/**
|
|
37
|
+
* A namespace of predefined spring-network color palettes.
|
|
38
|
+
*/
|
|
39
|
+
readonly palette: typeof palette;
|
|
40
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { anchors } from "../../render/springs/anchors.js";
|
|
2
|
+
import { construction } from "../../render/springs/construction.js";
|
|
3
|
+
import { impulses } from "../../render/springs/impulses.js";
|
|
4
|
+
import { links } from "../../render/springs/links.js";
|
|
5
|
+
import { palette } from "../../render/springs/palette.js";
|
|
6
|
+
import { preset } from "../../render/springs/preset.js";
|
|
7
|
+
import { of, strain } from "../../render/springs/source.js";
|
|
8
|
+
/**
|
|
9
|
+
* The spring-network rendering namespace.
|
|
10
|
+
*/
|
|
11
|
+
export const springs = {
|
|
12
|
+
/**
|
|
13
|
+
* Creates springs over the swarm's particles from a link list.
|
|
14
|
+
*/
|
|
15
|
+
of,
|
|
16
|
+
/**
|
|
17
|
+
* A namespace of layers that draw springs, composed onto a scene in
|
|
18
|
+
* draw order.
|
|
19
|
+
*/
|
|
20
|
+
layer: { construction, links, impulses, anchors },
|
|
21
|
+
/**
|
|
22
|
+
* A namespace of signals over springs.
|
|
23
|
+
*/
|
|
24
|
+
signal: { strain },
|
|
25
|
+
/**
|
|
26
|
+
* A namespace of composable spring-network presets.
|
|
27
|
+
*/
|
|
28
|
+
preset,
|
|
29
|
+
/**
|
|
30
|
+
* A namespace of predefined spring-network color palettes.
|
|
31
|
+
*/
|
|
32
|
+
palette,
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=springs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"springs.js","sourceRoot":"","sources":["../../../src/render/springs/springs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAExD;;GAEG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB;;OAEG;IACH,EAAE;IAEF;;;OAGG;IACH,KAAK,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE;IAEjD;;OAEG;IACH,MAAM,EAAE,EAAE,MAAM,EAAE;IAElB;;OAEG;IACH,MAAM;IAEN;;OAEG;IACH,OAAO;CACC,CAAC","sourcesContent":["import { anchors } from \"@/render/springs/anchors.js\";\nimport { construction } from \"@/render/springs/construction.js\";\nimport { impulses } from \"@/render/springs/impulses.js\";\nimport { links } from \"@/render/springs/links.js\";\nimport { palette } from \"@/render/springs/palette.js\";\nimport { preset } from \"@/render/springs/preset.js\";\nimport { of, strain } from \"@/render/springs/source.js\";\n\n/**\n * The spring-network rendering namespace.\n */\nexport const springs = {\n /**\n * Creates springs over the swarm's particles from a link list.\n */\n of,\n\n /**\n * A namespace of layers that draw springs, composed onto a scene in\n * draw order.\n */\n layer: { construction, links, impulses, anchors },\n\n /**\n * A namespace of signals over springs.\n */\n signal: { strain },\n\n /**\n * A namespace of composable spring-network presets.\n */\n preset,\n\n /**\n * A namespace of predefined spring-network color palettes.\n */\n palette,\n} as const;\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PackedPositions } from "../../render/frame.js";
|
|
2
|
+
import type { LinkSpecification } from "../../render/springs/link.js";
|
|
3
|
+
/**
|
|
4
|
+
* Computes the current length of every link from the particle positions.
|
|
5
|
+
* `lengths` is written in place and returned.
|
|
6
|
+
*/
|
|
7
|
+
export declare function measureLengths({ points, links, lengths, }: {
|
|
8
|
+
points: PackedPositions;
|
|
9
|
+
links: readonly LinkSpecification[];
|
|
10
|
+
lengths: Float32Array;
|
|
11
|
+
}): Float32Array;
|
|
12
|
+
/**
|
|
13
|
+
* Computes the raw strain of every link: the unsigned fractional deviation of
|
|
14
|
+
* its current length from its rest length, `|length - rest| / rest`. A
|
|
15
|
+
* non-positive rest length yields zero strain rather than dividing by zero.
|
|
16
|
+
*/
|
|
17
|
+
export declare function computeStrain({ lengths, restLengths, strain, }: {
|
|
18
|
+
lengths: Float32Array;
|
|
19
|
+
restLengths: Float32Array;
|
|
20
|
+
strain: Float32Array;
|
|
21
|
+
}): Float32Array;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { assert } from "../../assert.js";
|
|
2
|
+
function component(buffer, index) {
|
|
3
|
+
const value = buffer[index];
|
|
4
|
+
assert(value !== undefined, `buffer index ${index} is out of range (length ${buffer.length})`);
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
function distance(points, i, j) {
|
|
8
|
+
const dx = points.x(i) - points.x(j);
|
|
9
|
+
const dy = points.y(i) - points.y(j);
|
|
10
|
+
const dz = points.z(i) - points.z(j);
|
|
11
|
+
return Math.hypot(dx, dy, dz);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Computes the current length of every link from the particle positions.
|
|
15
|
+
* `lengths` is written in place and returned.
|
|
16
|
+
*/
|
|
17
|
+
export function measureLengths({ points, links, lengths, }) {
|
|
18
|
+
assert(lengths.length === links.length, `lengths buffer holds ${lengths.length} links, but there are ${links.length}`);
|
|
19
|
+
for (const [index, link] of links.entries()) {
|
|
20
|
+
const [i, j] = link.between;
|
|
21
|
+
lengths[index] = distance(points, i, j);
|
|
22
|
+
}
|
|
23
|
+
return lengths;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Computes the raw strain of every link: the unsigned fractional deviation of
|
|
27
|
+
* its current length from its rest length, `|length - rest| / rest`. A
|
|
28
|
+
* non-positive rest length yields zero strain rather than dividing by zero.
|
|
29
|
+
*/
|
|
30
|
+
export function computeStrain({ lengths, restLengths, strain, }) {
|
|
31
|
+
assert(lengths.length === restLengths.length && lengths.length === strain.length, `strain buffers disagree: ${lengths.length} lengths, ${restLengths.length} rest lengths, ${strain.length} strain`);
|
|
32
|
+
for (let index = 0; index < lengths.length; index++) {
|
|
33
|
+
const rest = component(restLengths, index);
|
|
34
|
+
const length = component(lengths, index);
|
|
35
|
+
strain[index] = rest > 0 ? Math.abs(length - rest) / rest : 0;
|
|
36
|
+
}
|
|
37
|
+
return strain;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=strain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strain.js","sourceRoot":"","sources":["../../../src/render/springs/strain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,SAAS,SAAS,CAAC,MAAoB,EAAE,KAAa;IACpD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5B,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,gBAAgB,KAAK,4BAA4B,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/F,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,MAAuB,EAAE,CAAS,EAAE,CAAS;IAC7D,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,KAAK,EACL,OAAO,GAKR;IACC,MAAM,CACJ,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAC/B,wBAAwB,OAAO,CAAC,MAAM,yBAAyB,KAAK,CAAC,MAAM,EAAE,CAC9E,CAAC;IACF,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,OAAO,EACP,WAAW,EACX,MAAM,GAKP;IACC,MAAM,CACJ,OAAO,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EACzE,4BAA4B,OAAO,CAAC,MAAM,aAAa,WAAW,CAAC,MAAM,kBAAkB,MAAM,CAAC,MAAM,SAAS,CAClH,CAAC;IACF,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { assert } from \"@/assert.js\";\nimport type { PackedPositions } from \"@/render/frame.js\";\nimport type { LinkSpecification } from \"@/render/springs/link.js\";\n\nfunction component(buffer: Float32Array, index: number): number {\n const value = buffer[index];\n assert(value !== undefined, `buffer index ${index} is out of range (length ${buffer.length})`);\n return value;\n}\n\nfunction distance(points: PackedPositions, i: number, j: number): number {\n const dx = points.x(i) - points.x(j);\n const dy = points.y(i) - points.y(j);\n const dz = points.z(i) - points.z(j);\n return Math.hypot(dx, dy, dz);\n}\n\n/**\n * Computes the current length of every link from the particle positions.\n * `lengths` is written in place and returned.\n */\nexport function measureLengths({\n points,\n links,\n lengths,\n}: {\n points: PackedPositions;\n links: readonly LinkSpecification[];\n lengths: Float32Array;\n}): Float32Array {\n assert(\n lengths.length === links.length,\n `lengths buffer holds ${lengths.length} links, but there are ${links.length}`,\n );\n for (const [index, link] of links.entries()) {\n const [i, j] = link.between;\n lengths[index] = distance(points, i, j);\n }\n return lengths;\n}\n\n/**\n * Computes the raw strain of every link: the unsigned fractional deviation of\n * its current length from its rest length, `|length - rest| / rest`. A\n * non-positive rest length yields zero strain rather than dividing by zero.\n */\nexport function computeStrain({\n lengths,\n restLengths,\n strain,\n}: {\n lengths: Float32Array;\n restLengths: Float32Array;\n strain: Float32Array;\n}): Float32Array {\n assert(\n lengths.length === restLengths.length && lengths.length === strain.length,\n `strain buffers disagree: ${lengths.length} lengths, ${restLengths.length} rest lengths, ${strain.length} strain`,\n );\n for (let index = 0; index < lengths.length; index++) {\n const rest = component(restLengths, index);\n const length = component(lengths, index);\n strain[index] = rest > 0 ? Math.abs(length - rest) / rest : 0;\n }\n return strain;\n}\n"]}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Camera } from "../render/camera.js";
|
|
2
|
+
import type { Color } from "../render/color.js";
|
|
3
|
+
import type { Effect } from "../render/effect.js";
|
|
4
|
+
import type { Layer } from "../render/layer.js";
|
|
5
|
+
import type { Source } from "../render/sources.js";
|
|
6
|
+
import type { Swarm } from "../types.js";
|
|
7
|
+
export interface RenderBackend {
|
|
8
|
+
/**
|
|
9
|
+
* Discriminates the concrete backend.
|
|
10
|
+
*/
|
|
11
|
+
readonly kind: "gpu";
|
|
12
|
+
}
|
|
13
|
+
export interface StageOptions {
|
|
14
|
+
/**
|
|
15
|
+
* The canvas the stage renders into.
|
|
16
|
+
*/
|
|
17
|
+
readonly canvas: HTMLCanvasElement;
|
|
18
|
+
/**
|
|
19
|
+
* The rendering device.
|
|
20
|
+
*/
|
|
21
|
+
readonly backend: RenderBackend;
|
|
22
|
+
/**
|
|
23
|
+
* The background clear color.
|
|
24
|
+
*/
|
|
25
|
+
readonly clear?: Color;
|
|
26
|
+
/**
|
|
27
|
+
* An upper bound applied to the device pixel ratio.
|
|
28
|
+
*/
|
|
29
|
+
readonly pixelRatio?: number;
|
|
30
|
+
/**
|
|
31
|
+
* The initial camera configuration.
|
|
32
|
+
*/
|
|
33
|
+
readonly camera?: Camera;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A composition of layers used for rendering a swarm.
|
|
37
|
+
*/
|
|
38
|
+
export interface Scene {
|
|
39
|
+
/**
|
|
40
|
+
* Replaces the scene's layers, in back-to-front draw order. Returns the
|
|
41
|
+
* same scene for chaining.
|
|
42
|
+
*/
|
|
43
|
+
layers(layers: readonly Layer<Source>[]): Scene;
|
|
44
|
+
/**
|
|
45
|
+
* Releases every GPU resource this scene owns. Does nothing if already
|
|
46
|
+
* disposed.
|
|
47
|
+
*/
|
|
48
|
+
dispose(): void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A component that can create a {@link Scene} for rendering a swarm. It also
|
|
52
|
+
* manages other rendering configuration such as the camera and post-processing
|
|
53
|
+
* effects.
|
|
54
|
+
*/
|
|
55
|
+
export interface Stage {
|
|
56
|
+
/**
|
|
57
|
+
* Begins observing a swarm, returning a scene to compose layers onto.
|
|
58
|
+
*/
|
|
59
|
+
observe(swarm: Swarm): Scene;
|
|
60
|
+
/**
|
|
61
|
+
* Replaces the active camera configuration.
|
|
62
|
+
*/
|
|
63
|
+
camera(camera: Camera): void;
|
|
64
|
+
/**
|
|
65
|
+
* Replaces the post-processing effect chain, applied in array order.
|
|
66
|
+
*/
|
|
67
|
+
effects(effects: readonly Effect[]): void;
|
|
68
|
+
/**
|
|
69
|
+
* Sets the depth-fog range, in scene units, over which geometry fades toward
|
|
70
|
+
* the background void, or `null` to disable fog. Applies to every observed
|
|
71
|
+
* scene.
|
|
72
|
+
*/
|
|
73
|
+
fog(range: {
|
|
74
|
+
readonly near: number;
|
|
75
|
+
readonly far: number;
|
|
76
|
+
} | null): void;
|
|
77
|
+
/**
|
|
78
|
+
* Samples the observed swarm and draws the scene once, from the most recent
|
|
79
|
+
* state. Only the first call causes the state to be read, with later ones
|
|
80
|
+
* drawing the state already read.
|
|
81
|
+
*/
|
|
82
|
+
render(scene: Scene): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Resizes the drawing buffer to `width` by `height` CSS pixels, honoring the
|
|
85
|
+
* pixel-ratio cap. When omitted, the stage reads the canvas client size.
|
|
86
|
+
*/
|
|
87
|
+
resize(size?: {
|
|
88
|
+
readonly width: number;
|
|
89
|
+
readonly height: number;
|
|
90
|
+
}): void;
|
|
91
|
+
/**
|
|
92
|
+
* Releases the stage's device, listeners, and remaining resources. Does nothing
|
|
93
|
+
* if already disposed.
|
|
94
|
+
*/
|
|
95
|
+
dispose(): void;
|
|
96
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage.js","sourceRoot":"","sources":["../../src/render/stage.ts"],"names":[],"mappings":"","sourcesContent":["import type { Camera } from \"@/render/camera.js\";\nimport type { Color } from \"@/render/color.js\";\nimport type { Effect } from \"@/render/effect.js\";\nimport type { Layer } from \"@/render/layer.js\";\nimport type { Source } from \"@/render/sources.js\";\nimport type { Swarm } from \"@/types.js\";\n\nexport interface RenderBackend {\n /**\n * Discriminates the concrete backend.\n */\n readonly kind: \"gpu\";\n}\n\nexport interface StageOptions {\n /**\n * The canvas the stage renders into.\n */\n readonly canvas: HTMLCanvasElement;\n\n /**\n * The rendering device.\n */\n readonly backend: RenderBackend;\n\n /**\n * The background clear color.\n */\n readonly clear?: Color;\n\n /**\n * An upper bound applied to the device pixel ratio.\n */\n readonly pixelRatio?: number;\n\n /**\n * The initial camera configuration.\n */\n readonly camera?: Camera;\n}\n\n/**\n * A composition of layers used for rendering a swarm.\n */\nexport interface Scene {\n /**\n * Replaces the scene's layers, in back-to-front draw order. Returns the\n * same scene for chaining.\n */\n layers(layers: readonly Layer<Source>[]): Scene;\n\n /**\n * Releases every GPU resource this scene owns. Does nothing if already\n * disposed.\n */\n dispose(): void;\n}\n\n/**\n * A component that can create a {@link Scene} for rendering a swarm. It also\n * manages other rendering configuration such as the camera and post-processing\n * effects.\n */\nexport interface Stage {\n /**\n * Begins observing a swarm, returning a scene to compose layers onto.\n */\n observe(swarm: Swarm): Scene;\n\n /**\n * Replaces the active camera configuration.\n */\n camera(camera: Camera): void;\n\n /**\n * Replaces the post-processing effect chain, applied in array order.\n */\n effects(effects: readonly Effect[]): void;\n\n /**\n * Sets the depth-fog range, in scene units, over which geometry fades toward\n * the background void, or `null` to disable fog. Applies to every observed\n * scene.\n */\n fog(range: { readonly near: number; readonly far: number } | null): void;\n\n /**\n * Samples the observed swarm and draws the scene once, from the most recent\n * state. Only the first call causes the state to be read, with later ones\n * drawing the state already read.\n */\n render(scene: Scene): Promise<void>;\n\n /**\n * Resizes the drawing buffer to `width` by `height` CSS pixels, honoring the\n * pixel-ratio cap. When omitted, the stage reads the canvas client size.\n */\n resize(size?: { readonly width: number; readonly height: number }): void;\n\n /**\n * Releases the stage's device, listeners, and remaining resources. Does nothing\n * if already disposed.\n */\n dispose(): void;\n}\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { backend } from "../render/backend/gpu.js";
|
|
2
|
+
import { camera } from "../render/camera.js";
|
|
3
|
+
import { type Channel, type ChannelOptions } from "../render/channel.js";
|
|
4
|
+
import { color } from "../render/color.js";
|
|
5
|
+
import { effect } from "../render/effect.js";
|
|
6
|
+
import { layer } from "../render/layer.js";
|
|
7
|
+
import { ramp } from "../render/ramp.js";
|
|
8
|
+
import { Sampler, type SamplerOptions } from "../render/sampler.js";
|
|
9
|
+
import { signal } from "../render/signal.js";
|
|
10
|
+
import { type Source, sources } from "../render/sources.js";
|
|
11
|
+
import type { Stage, StageOptions } from "../render/stage.js";
|
|
12
|
+
export declare const view: {
|
|
13
|
+
/**
|
|
14
|
+
* Creates a stage that allows a swarm to be rendered.
|
|
15
|
+
*/
|
|
16
|
+
readonly stage: (options: StageOptions) => Promise<Stage>;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a sampler reading one swarm's state into buffers a renderer can
|
|
19
|
+
* draw from.
|
|
20
|
+
*/
|
|
21
|
+
readonly sampler: (options: SamplerOptions) => Sampler;
|
|
22
|
+
/**
|
|
23
|
+
* Defines a channel, that, when used together with a {@link Signal} and {@link Ramp},
|
|
24
|
+
* provides values for rendering options (e.g. color, size, or opacity) for each element
|
|
25
|
+
* that is rendered.
|
|
26
|
+
*/
|
|
27
|
+
readonly channel: <S extends Source>(options: ChannelOptions<S>) => Channel<S>;
|
|
28
|
+
/**
|
|
29
|
+
* A namespace for predefined sources.
|
|
30
|
+
*/
|
|
31
|
+
readonly sources: typeof sources;
|
|
32
|
+
/**
|
|
33
|
+
* Rendering backends.
|
|
34
|
+
*/
|
|
35
|
+
readonly backend: typeof backend;
|
|
36
|
+
/**
|
|
37
|
+
* A namespace for predefined components that render data from
|
|
38
|
+
* sources. The layers are composed onto a scene in draw order.
|
|
39
|
+
*/
|
|
40
|
+
readonly layer: typeof layer;
|
|
41
|
+
/**
|
|
42
|
+
* A namespace for predefined signal components. Signals map values
|
|
43
|
+
* derived from source data to a normalized range.
|
|
44
|
+
*/
|
|
45
|
+
readonly signal: typeof signal;
|
|
46
|
+
/**
|
|
47
|
+
* A namespace for predefined ramp components. Ramps define mappings
|
|
48
|
+
* from signal values to rendering options (e.g. color, size, or opacity).
|
|
49
|
+
*/
|
|
50
|
+
readonly ramp: typeof ramp;
|
|
51
|
+
/**
|
|
52
|
+
* A namespace for predefined camera configurations.
|
|
53
|
+
*/
|
|
54
|
+
readonly camera: typeof camera;
|
|
55
|
+
/**
|
|
56
|
+
* A namespace for predefined post-processing effects.
|
|
57
|
+
*/
|
|
58
|
+
readonly effect: typeof effect;
|
|
59
|
+
/**
|
|
60
|
+
* A namespace of functions for manipulating colors.
|
|
61
|
+
*/
|
|
62
|
+
readonly color: typeof color;
|
|
63
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { backend, createStage } from "../render/backend/gpu.js";
|
|
2
|
+
import { camera } from "../render/camera.js";
|
|
3
|
+
import { channel } from "../render/channel.js";
|
|
4
|
+
import { color } from "../render/color.js";
|
|
5
|
+
import { effect } from "../render/effect.js";
|
|
6
|
+
import { layer } from "../render/layer.js";
|
|
7
|
+
import { ramp } from "../render/ramp.js";
|
|
8
|
+
import { Sampler } from "../render/sampler.js";
|
|
9
|
+
import { signal } from "../render/signal.js";
|
|
10
|
+
import { sources } from "../render/sources.js";
|
|
11
|
+
export const view = {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a stage that allows a swarm to be rendered.
|
|
14
|
+
*/
|
|
15
|
+
stage: (options) => createStage(options),
|
|
16
|
+
/**
|
|
17
|
+
* Creates a sampler reading one swarm's state into buffers a renderer can
|
|
18
|
+
* draw from.
|
|
19
|
+
*/
|
|
20
|
+
sampler: (options) => new Sampler(options),
|
|
21
|
+
/**
|
|
22
|
+
* Defines a channel, that, when used together with a {@link Signal} and {@link Ramp},
|
|
23
|
+
* provides values for rendering options (e.g. color, size, or opacity) for each element
|
|
24
|
+
* that is rendered.
|
|
25
|
+
*/
|
|
26
|
+
channel: (options) => channel(options),
|
|
27
|
+
/**
|
|
28
|
+
* A namespace for predefined sources.
|
|
29
|
+
*/
|
|
30
|
+
sources,
|
|
31
|
+
/**
|
|
32
|
+
* Rendering backends.
|
|
33
|
+
*/
|
|
34
|
+
backend,
|
|
35
|
+
/**
|
|
36
|
+
* A namespace for predefined components that render data from
|
|
37
|
+
* sources. The layers are composed onto a scene in draw order.
|
|
38
|
+
*/
|
|
39
|
+
layer,
|
|
40
|
+
/**
|
|
41
|
+
* A namespace for predefined signal components. Signals map values
|
|
42
|
+
* derived from source data to a normalized range.
|
|
43
|
+
*/
|
|
44
|
+
signal,
|
|
45
|
+
/**
|
|
46
|
+
* A namespace for predefined ramp components. Ramps define mappings
|
|
47
|
+
* from signal values to rendering options (e.g. color, size, or opacity).
|
|
48
|
+
*/
|
|
49
|
+
ramp,
|
|
50
|
+
/**
|
|
51
|
+
* A namespace for predefined camera configurations.
|
|
52
|
+
*/
|
|
53
|
+
camera,
|
|
54
|
+
/**
|
|
55
|
+
* A namespace for predefined post-processing effects.
|
|
56
|
+
*/
|
|
57
|
+
effect,
|
|
58
|
+
/**
|
|
59
|
+
* A namespace of functions for manipulating colors.
|
|
60
|
+
*/
|
|
61
|
+
color,
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.js","sourceRoot":"","sources":["../../src/render/view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAqC,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAuB,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAe,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAG3D,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB;;OAEG;IACH,KAAK,EAAE,CAAC,OAAqB,EAAkB,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC;IAEtE;;;OAGG;IACH,OAAO,EAAE,CAAC,OAAuB,EAAW,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC;IAEnE;;;;OAIG;IACH,OAAO,EAAE,CAAmB,OAA0B,EAAc,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;IAEvF;;OAEG;IACH,OAAO;IAEP;;OAEG;IACH,OAAO;IAEP;;;OAGG;IACH,KAAK;IAEL;;;OAGG;IACH,MAAM;IAEN;;;OAGG;IACH,IAAI;IAEJ;;OAEG;IACH,MAAM;IAEN;;OAEG;IACH,MAAM;IAEN;;OAEG;IACH,KAAK;CACG,CAAC","sourcesContent":["import { backend, createStage } from \"@/render/backend/gpu.js\";\nimport { camera } from \"@/render/camera.js\";\nimport { type Channel, type ChannelOptions, channel } from \"@/render/channel.js\";\nimport { color } from \"@/render/color.js\";\nimport { effect } from \"@/render/effect.js\";\nimport { layer } from \"@/render/layer.js\";\nimport { ramp } from \"@/render/ramp.js\";\nimport { Sampler, type SamplerOptions } from \"@/render/sampler.js\";\nimport { signal } from \"@/render/signal.js\";\nimport { type Source, sources } from \"@/render/sources.js\";\nimport type { Stage, StageOptions } from \"@/render/stage.js\";\n\nexport const view = {\n /**\n * Creates a stage that allows a swarm to be rendered.\n */\n stage: (options: StageOptions): Promise<Stage> => createStage(options),\n\n /**\n * Creates a sampler reading one swarm's state into buffers a renderer can\n * draw from.\n */\n sampler: (options: SamplerOptions): Sampler => new Sampler(options),\n\n /**\n * Defines a channel, that, when used together with a {@link Signal} and {@link Ramp},\n * provides values for rendering options (e.g. color, size, or opacity) for each element\n * that is rendered.\n */\n channel: <S extends Source>(options: ChannelOptions<S>): Channel<S> => channel(options),\n\n /**\n * A namespace for predefined sources.\n */\n sources,\n\n /**\n * Rendering backends.\n */\n backend,\n\n /**\n * A namespace for predefined components that render data from\n * sources. The layers are composed onto a scene in draw order.\n */\n layer,\n\n /**\n * A namespace for predefined signal components. Signals map values\n * derived from source data to a normalized range.\n */\n signal,\n\n /**\n * A namespace for predefined ramp components. Ramps define mappings\n * from signal values to rendering options (e.g. color, size, or opacity).\n */\n ramp,\n\n /**\n * A namespace for predefined camera configurations.\n */\n camera,\n\n /**\n * A namespace for predefined post-processing effects.\n */\n effect,\n\n /**\n * A namespace of functions for manipulating colors.\n */\n color,\n} as const;\n"]}
|
package/dist/swarm.d.ts
ADDED
package/dist/swarm.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { assert } from "./assert.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a swarm using the specified options.
|
|
4
|
+
*/
|
|
5
|
+
export async function swarm(options) {
|
|
6
|
+
options.model.validate?.({ particleCount: options.particleCount });
|
|
7
|
+
const executable = await options.backend.compile({
|
|
8
|
+
particles: options.initializer.initialize(options.particleCount),
|
|
9
|
+
model: options.model,
|
|
10
|
+
integrator: options.integrator,
|
|
11
|
+
});
|
|
12
|
+
let time = 0;
|
|
13
|
+
return {
|
|
14
|
+
advance({ time: seconds }) {
|
|
15
|
+
assert(Number.isFinite(seconds) && seconds >= 0, `cannot advance by ${seconds} seconds`);
|
|
16
|
+
executable.step(seconds);
|
|
17
|
+
time += seconds;
|
|
18
|
+
},
|
|
19
|
+
async snapshot() {
|
|
20
|
+
return { time, particles: await executable.snapshot() };
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=swarm.js.map
|