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 @@
|
|
|
1
|
+
{"version":3,"file":"swarm.js","sourceRoot":"","sources":["../src/swarm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAAqB;IAC/C,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAEnE,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAC/C,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC;QAChE,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAC;IAEH,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,OAAO;QACL,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAkB;YACvC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,qBAAqB,OAAO,UAAU,CAAC,CAAC;YAEzF,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,IAAI,IAAI,OAAO,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,QAAQ;YACZ,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC1D,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { assert } from \"@/assert.js\";\nimport type { AdvanceOptions, Swarm, SwarmOptions, SwarmState } from \"@/types.js\";\n\n/**\n * Creates a swarm using the specified options.\n */\nexport async function swarm(options: SwarmOptions): Promise<Swarm> {\n options.model.validate?.({ particleCount: options.particleCount });\n\n const executable = await options.backend.compile({\n particles: options.initializer.initialize(options.particleCount),\n model: options.model,\n integrator: options.integrator,\n });\n\n let time = 0;\n\n return {\n advance({ time: seconds }: AdvanceOptions): void {\n assert(Number.isFinite(seconds) && seconds >= 0, `cannot advance by ${seconds} seconds`);\n\n executable.step(seconds);\n time += seconds;\n },\n async snapshot(): Promise<SwarmState> {\n return { time, particles: await executable.snapshot() };\n },\n };\n}\n"]}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import type { Node } from "three/webgpu";
|
|
2
|
+
/**
|
|
3
|
+
* An accelerator device, such as a GPU.
|
|
4
|
+
*/
|
|
5
|
+
export type Device = GPUDevice;
|
|
6
|
+
/**
|
|
7
|
+
* A position or velocity in simulation space.
|
|
8
|
+
*/
|
|
9
|
+
export type Vector = readonly [x: number, y: number, z: number];
|
|
10
|
+
export interface Particles {
|
|
11
|
+
/**
|
|
12
|
+
* How many particles there are.
|
|
13
|
+
*/
|
|
14
|
+
readonly count: number;
|
|
15
|
+
/**
|
|
16
|
+
* Particle positions, indexed by particle.
|
|
17
|
+
*/
|
|
18
|
+
readonly positions: readonly Vector[];
|
|
19
|
+
/**
|
|
20
|
+
* Particle velocities, indexed by particle.
|
|
21
|
+
*/
|
|
22
|
+
readonly velocities: readonly Vector[];
|
|
23
|
+
}
|
|
24
|
+
export interface SwarmState {
|
|
25
|
+
/**
|
|
26
|
+
* Simulated time the swarm has advanced by, in seconds.
|
|
27
|
+
*/
|
|
28
|
+
readonly time: number;
|
|
29
|
+
/**
|
|
30
|
+
* The particles as of that time.
|
|
31
|
+
*/
|
|
32
|
+
readonly particles: Particles;
|
|
33
|
+
}
|
|
34
|
+
export interface Initializer {
|
|
35
|
+
/**
|
|
36
|
+
* Builds the starting particles for a swarm of `particleCount` particles.
|
|
37
|
+
*/
|
|
38
|
+
initialize(particleCount: number): Particles;
|
|
39
|
+
}
|
|
40
|
+
export interface CpuModel {
|
|
41
|
+
/**
|
|
42
|
+
* Accelerations acting on each particle, indexed by particle.
|
|
43
|
+
*/
|
|
44
|
+
accelerate(particles: Particles): readonly Vector[];
|
|
45
|
+
}
|
|
46
|
+
export interface CpuIntegratorOptions {
|
|
47
|
+
/**
|
|
48
|
+
* The particles the step starts from.
|
|
49
|
+
*/
|
|
50
|
+
readonly particles: Particles;
|
|
51
|
+
/**
|
|
52
|
+
* Accelerations at `particles`, indexed by particle.
|
|
53
|
+
*/
|
|
54
|
+
readonly accelerations: readonly Vector[];
|
|
55
|
+
/**
|
|
56
|
+
* Simulated time the step covers, in seconds.
|
|
57
|
+
*/
|
|
58
|
+
readonly seconds: number;
|
|
59
|
+
}
|
|
60
|
+
export interface CpuIntegrator {
|
|
61
|
+
/**
|
|
62
|
+
* Returns the particles `seconds` later, given the accelerations at them.
|
|
63
|
+
*/
|
|
64
|
+
step(options: CpuIntegratorOptions): Particles;
|
|
65
|
+
}
|
|
66
|
+
export interface GpuParticle {
|
|
67
|
+
/**
|
|
68
|
+
* The position of the particle the compute invocation is running for.
|
|
69
|
+
*/
|
|
70
|
+
readonly position: Node<"vec3">;
|
|
71
|
+
/**
|
|
72
|
+
* The velocity of the particle the compute invocation is running for.
|
|
73
|
+
*/
|
|
74
|
+
readonly velocity: Node<"vec3">;
|
|
75
|
+
}
|
|
76
|
+
export interface GpuParticles {
|
|
77
|
+
/**
|
|
78
|
+
* How many particles there are.
|
|
79
|
+
*/
|
|
80
|
+
readonly count: number;
|
|
81
|
+
/**
|
|
82
|
+
* The position of the particle at `index`.
|
|
83
|
+
*/
|
|
84
|
+
position(index: Node | number): Node<"vec3">;
|
|
85
|
+
/**
|
|
86
|
+
* The velocity of the particle at `index`.
|
|
87
|
+
*/
|
|
88
|
+
velocity(index: Node | number): Node<"vec3">;
|
|
89
|
+
}
|
|
90
|
+
export interface GpuModelOptions {
|
|
91
|
+
/**
|
|
92
|
+
* Every particle's state in the swarm.
|
|
93
|
+
*/
|
|
94
|
+
readonly particles: GpuParticles;
|
|
95
|
+
/**
|
|
96
|
+
* The index of the particle the acceleration is computed for.
|
|
97
|
+
*/
|
|
98
|
+
readonly index: Node<"uint">;
|
|
99
|
+
}
|
|
100
|
+
export interface GpuModel {
|
|
101
|
+
/**
|
|
102
|
+
* The acceleration acting on the particle at `index`.
|
|
103
|
+
*/
|
|
104
|
+
acceleration(options: GpuModelOptions): Node<"vec3">;
|
|
105
|
+
}
|
|
106
|
+
export interface GpuIntegratorOptions {
|
|
107
|
+
/**
|
|
108
|
+
* The particle the step starts from.
|
|
109
|
+
*/
|
|
110
|
+
readonly particle: GpuParticle;
|
|
111
|
+
/**
|
|
112
|
+
* The acceleration acting on `particle`.
|
|
113
|
+
*/
|
|
114
|
+
readonly acceleration: Node<"vec3">;
|
|
115
|
+
/**
|
|
116
|
+
* Simulated time the step covers, in seconds.
|
|
117
|
+
*/
|
|
118
|
+
readonly seconds: Node<"float">;
|
|
119
|
+
}
|
|
120
|
+
export interface GpuIntegrator {
|
|
121
|
+
/**
|
|
122
|
+
* Returns the particle `seconds` later.
|
|
123
|
+
*/
|
|
124
|
+
step(options: GpuIntegratorOptions): GpuParticle;
|
|
125
|
+
}
|
|
126
|
+
export interface ModelValidationContext {
|
|
127
|
+
/**
|
|
128
|
+
* The number of particles in the swarm the model is being combined with.
|
|
129
|
+
*/
|
|
130
|
+
readonly particleCount: number;
|
|
131
|
+
}
|
|
132
|
+
export interface Model {
|
|
133
|
+
/**
|
|
134
|
+
* The model's CPU implementation, if available.
|
|
135
|
+
*/
|
|
136
|
+
readonly cpu?: CpuModel;
|
|
137
|
+
/**
|
|
138
|
+
* The model's GPU implementation, if available.
|
|
139
|
+
*/
|
|
140
|
+
readonly gpu?: GpuModel;
|
|
141
|
+
/**
|
|
142
|
+
* Checks the model against the swarm it is being combined with, throwing if
|
|
143
|
+
* the two disagree.
|
|
144
|
+
*/
|
|
145
|
+
validate?(context: ModelValidationContext): void;
|
|
146
|
+
}
|
|
147
|
+
export interface Integrator {
|
|
148
|
+
/**
|
|
149
|
+
* The integrator's CPU implementation.
|
|
150
|
+
*/
|
|
151
|
+
readonly cpu: CpuIntegrator;
|
|
152
|
+
/**
|
|
153
|
+
* The integrator's GPU implementation.
|
|
154
|
+
*/
|
|
155
|
+
readonly gpu: GpuIntegrator;
|
|
156
|
+
}
|
|
157
|
+
export interface CompileOptions {
|
|
158
|
+
/**
|
|
159
|
+
* The swarm's starting particles.
|
|
160
|
+
*/
|
|
161
|
+
readonly particles: Particles;
|
|
162
|
+
/**
|
|
163
|
+
* Defines the forces acting on the particles.
|
|
164
|
+
*/
|
|
165
|
+
readonly model: Model;
|
|
166
|
+
/**
|
|
167
|
+
* Advances positions and velocities under those forces.
|
|
168
|
+
*/
|
|
169
|
+
readonly integrator: Integrator;
|
|
170
|
+
}
|
|
171
|
+
export interface Executable {
|
|
172
|
+
/**
|
|
173
|
+
* Advances the held particles by `seconds` of simulated time.
|
|
174
|
+
*/
|
|
175
|
+
step(seconds: number): void;
|
|
176
|
+
/**
|
|
177
|
+
* Reads back the current particles.
|
|
178
|
+
*/
|
|
179
|
+
snapshot(): Promise<Particles>;
|
|
180
|
+
}
|
|
181
|
+
export interface Backend {
|
|
182
|
+
/**
|
|
183
|
+
* Combines a model and an integrator into an executable holding `particles`.
|
|
184
|
+
*/
|
|
185
|
+
compile(options: CompileOptions): Promise<Executable>;
|
|
186
|
+
}
|
|
187
|
+
export interface GpuBackendOptions {
|
|
188
|
+
/**
|
|
189
|
+
* The device to use for the accelerated computations. If unspecified, a default device
|
|
190
|
+
* will be used.
|
|
191
|
+
*/
|
|
192
|
+
readonly device?: Device;
|
|
193
|
+
}
|
|
194
|
+
export interface SwarmOptions {
|
|
195
|
+
/**
|
|
196
|
+
* Number of particles to simulate.
|
|
197
|
+
*/
|
|
198
|
+
readonly particleCount: number;
|
|
199
|
+
/**
|
|
200
|
+
* Supplies the particles' starting positions and velocities.
|
|
201
|
+
*/
|
|
202
|
+
readonly initializer: Initializer;
|
|
203
|
+
/**
|
|
204
|
+
* Defines the forces acting on the particles.
|
|
205
|
+
*/
|
|
206
|
+
readonly model: Model;
|
|
207
|
+
/**
|
|
208
|
+
* Advances positions and velocities under those forces.
|
|
209
|
+
*/
|
|
210
|
+
readonly integrator: Integrator;
|
|
211
|
+
/**
|
|
212
|
+
* Executes the model and integrator.
|
|
213
|
+
*/
|
|
214
|
+
readonly backend: Backend;
|
|
215
|
+
}
|
|
216
|
+
export interface AdvanceOptions {
|
|
217
|
+
/**
|
|
218
|
+
* Simulated time to advance by, in seconds.
|
|
219
|
+
*/
|
|
220
|
+
readonly time: number;
|
|
221
|
+
}
|
|
222
|
+
export interface Swarm {
|
|
223
|
+
/**
|
|
224
|
+
* Progresses the swarm by a specified amount of simulated time.
|
|
225
|
+
*/
|
|
226
|
+
advance(options: AdvanceOptions): void;
|
|
227
|
+
/**
|
|
228
|
+
* Returns a read-only copy of the swarm's observable state.
|
|
229
|
+
*/
|
|
230
|
+
snapshot(): Promise<SwarmState>;
|
|
231
|
+
}
|
|
232
|
+
export interface FixedInitializerOptions {
|
|
233
|
+
/**
|
|
234
|
+
* Caller-owned starting positions, one per particle.
|
|
235
|
+
*/
|
|
236
|
+
readonly positions: readonly Vector[];
|
|
237
|
+
/**
|
|
238
|
+
* Caller-owned starting velocities, one per particle.
|
|
239
|
+
*/
|
|
240
|
+
readonly velocities: readonly Vector[];
|
|
241
|
+
}
|
|
242
|
+
export interface Connection {
|
|
243
|
+
/**
|
|
244
|
+
* The two particles the spring joins, by index. The connection is
|
|
245
|
+
* undirected, so the order of the pair does not affect the forces.
|
|
246
|
+
*/
|
|
247
|
+
readonly between: readonly [number, number];
|
|
248
|
+
/**
|
|
249
|
+
* The length at which the spring produces no elastic force. A longer
|
|
250
|
+
* separation pulls the particles together, a shorter one pushes them apart.
|
|
251
|
+
*/
|
|
252
|
+
readonly restLength: number;
|
|
253
|
+
}
|
|
254
|
+
export interface ElasticNetworkOptions {
|
|
255
|
+
/**
|
|
256
|
+
* The mass of each particle, indexed by particle. Every mass must be a
|
|
257
|
+
* positive, finite number.
|
|
258
|
+
*/
|
|
259
|
+
readonly masses: readonly number[];
|
|
260
|
+
/**
|
|
261
|
+
* The springs joining particle pairs. If empty, no particles will be connected.
|
|
262
|
+
*/
|
|
263
|
+
readonly connections?: readonly Connection[];
|
|
264
|
+
/**
|
|
265
|
+
* The rest position each particle's anchor pulls toward, indexed by
|
|
266
|
+
* particle. If omitted, particles will be unanchored.
|
|
267
|
+
*/
|
|
268
|
+
readonly restPositions?: readonly Vector[];
|
|
269
|
+
/**
|
|
270
|
+
* The spring stiffness shared by every connection, controlling how strongly
|
|
271
|
+
* a connection resists changing from its rest length. If omitted, it will be
|
|
272
|
+
* assumed that there is no spring force between connected particles.
|
|
273
|
+
*/
|
|
274
|
+
readonly stiffness?: number;
|
|
275
|
+
/**
|
|
276
|
+
* The spring damping shared by every connection, opposing relative motion
|
|
277
|
+
* along each connection. If omitted, it will be assumed that there is no damping.
|
|
278
|
+
*/
|
|
279
|
+
readonly springDamping?: number;
|
|
280
|
+
/**
|
|
281
|
+
* The anchor strength pulling each particle toward its rest position.
|
|
282
|
+
* If omitted, it will be assumed that there is no anchoring force.
|
|
283
|
+
*/
|
|
284
|
+
readonly anchorStrength?: number;
|
|
285
|
+
/**
|
|
286
|
+
* The global damping opposing every particle's velocity independently of its
|
|
287
|
+
* connections. If omitted, it will be assumed that there is no global damping.
|
|
288
|
+
*/
|
|
289
|
+
readonly globalDamping?: number;
|
|
290
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Node } from \"three/webgpu\";\n\n/**\n * An accelerator device, such as a GPU.\n */\nexport type Device = GPUDevice;\n\n/**\n * A position or velocity in simulation space.\n */\nexport type Vector = readonly [x: number, y: number, z: number];\n\nexport interface Particles {\n /**\n * How many particles there are.\n */\n readonly count: number;\n\n /**\n * Particle positions, indexed by particle.\n */\n readonly positions: readonly Vector[];\n\n /**\n * Particle velocities, indexed by particle.\n */\n readonly velocities: readonly Vector[];\n}\n\nexport interface SwarmState {\n /**\n * Simulated time the swarm has advanced by, in seconds.\n */\n readonly time: number;\n\n /**\n * The particles as of that time.\n */\n readonly particles: Particles;\n}\n\nexport interface Initializer {\n /**\n * Builds the starting particles for a swarm of `particleCount` particles.\n */\n initialize(particleCount: number): Particles;\n}\n\nexport interface CpuModel {\n /**\n * Accelerations acting on each particle, indexed by particle.\n */\n accelerate(particles: Particles): readonly Vector[];\n}\n\nexport interface CpuIntegratorOptions {\n /**\n * The particles the step starts from.\n */\n readonly particles: Particles;\n /**\n * Accelerations at `particles`, indexed by particle.\n */\n readonly accelerations: readonly Vector[];\n /**\n * Simulated time the step covers, in seconds.\n */\n readonly seconds: number;\n}\n\nexport interface CpuIntegrator {\n /**\n * Returns the particles `seconds` later, given the accelerations at them.\n */\n step(options: CpuIntegratorOptions): Particles;\n}\n\nexport interface GpuParticle {\n /**\n * The position of the particle the compute invocation is running for.\n */\n readonly position: Node<\"vec3\">;\n /**\n * The velocity of the particle the compute invocation is running for.\n */\n readonly velocity: Node<\"vec3\">;\n}\n\nexport interface GpuParticles {\n /**\n * How many particles there are.\n */\n readonly count: number;\n /**\n * The position of the particle at `index`.\n */\n position(index: Node | number): Node<\"vec3\">;\n /**\n * The velocity of the particle at `index`.\n */\n velocity(index: Node | number): Node<\"vec3\">;\n}\n\nexport interface GpuModelOptions {\n /**\n * Every particle's state in the swarm.\n */\n readonly particles: GpuParticles;\n /**\n * The index of the particle the acceleration is computed for.\n */\n readonly index: Node<\"uint\">;\n}\n\nexport interface GpuModel {\n /**\n * The acceleration acting on the particle at `index`.\n */\n acceleration(options: GpuModelOptions): Node<\"vec3\">;\n}\n\nexport interface GpuIntegratorOptions {\n /**\n * The particle the step starts from.\n */\n readonly particle: GpuParticle;\n /**\n * The acceleration acting on `particle`.\n */\n readonly acceleration: Node<\"vec3\">;\n /**\n * Simulated time the step covers, in seconds.\n */\n readonly seconds: Node<\"float\">;\n}\n\nexport interface GpuIntegrator {\n /**\n * Returns the particle `seconds` later.\n */\n step(options: GpuIntegratorOptions): GpuParticle;\n}\n\nexport interface ModelValidationContext {\n /**\n * The number of particles in the swarm the model is being combined with.\n */\n readonly particleCount: number;\n}\n\nexport interface Model {\n /**\n * The model's CPU implementation, if available.\n */\n readonly cpu?: CpuModel;\n /**\n * The model's GPU implementation, if available.\n */\n readonly gpu?: GpuModel;\n /**\n * Checks the model against the swarm it is being combined with, throwing if\n * the two disagree.\n */\n validate?(context: ModelValidationContext): void;\n}\n\nexport interface Integrator {\n /**\n * The integrator's CPU implementation.\n */\n readonly cpu: CpuIntegrator;\n /**\n * The integrator's GPU implementation.\n */\n readonly gpu: GpuIntegrator;\n}\n\nexport interface CompileOptions {\n /**\n * The swarm's starting particles.\n */\n readonly particles: Particles;\n /**\n * Defines the forces acting on the particles.\n */\n readonly model: Model;\n /**\n * Advances positions and velocities under those forces.\n */\n readonly integrator: Integrator;\n}\n\nexport interface Executable {\n /**\n * Advances the held particles by `seconds` of simulated time.\n */\n step(seconds: number): void;\n /**\n * Reads back the current particles.\n */\n snapshot(): Promise<Particles>;\n}\n\nexport interface Backend {\n /**\n * Combines a model and an integrator into an executable holding `particles`.\n */\n compile(options: CompileOptions): Promise<Executable>;\n}\n\nexport interface GpuBackendOptions {\n /**\n * The device to use for the accelerated computations. If unspecified, a default device\n * will be used.\n */\n readonly device?: Device;\n}\n\nexport interface SwarmOptions {\n /**\n * Number of particles to simulate.\n */\n readonly particleCount: number;\n /**\n * Supplies the particles' starting positions and velocities.\n */\n readonly initializer: Initializer;\n /**\n * Defines the forces acting on the particles.\n */\n readonly model: Model;\n /**\n * Advances positions and velocities under those forces.\n */\n readonly integrator: Integrator;\n /**\n * Executes the model and integrator.\n */\n readonly backend: Backend;\n}\n\nexport interface AdvanceOptions {\n /**\n * Simulated time to advance by, in seconds.\n */\n readonly time: number;\n}\n\nexport interface Swarm {\n /**\n * Progresses the swarm by a specified amount of simulated time.\n */\n advance(options: AdvanceOptions): void;\n /**\n * Returns a read-only copy of the swarm's observable state.\n */\n snapshot(): Promise<SwarmState>;\n}\n\nexport interface FixedInitializerOptions {\n /**\n * Caller-owned starting positions, one per particle.\n */\n readonly positions: readonly Vector[];\n /**\n * Caller-owned starting velocities, one per particle.\n */\n readonly velocities: readonly Vector[];\n}\n\nexport interface Connection {\n /**\n * The two particles the spring joins, by index. The connection is\n * undirected, so the order of the pair does not affect the forces.\n */\n readonly between: readonly [number, number];\n /**\n * The length at which the spring produces no elastic force. A longer\n * separation pulls the particles together, a shorter one pushes them apart.\n */\n readonly restLength: number;\n}\n\nexport interface ElasticNetworkOptions {\n /**\n * The mass of each particle, indexed by particle. Every mass must be a\n * positive, finite number.\n */\n readonly masses: readonly number[];\n /**\n * The springs joining particle pairs. If empty, no particles will be connected.\n */\n readonly connections?: readonly Connection[];\n /**\n * The rest position each particle's anchor pulls toward, indexed by\n * particle. If omitted, particles will be unanchored.\n */\n readonly restPositions?: readonly Vector[];\n /**\n * The spring stiffness shared by every connection, controlling how strongly\n * a connection resists changing from its rest length. If omitted, it will be\n * assumed that there is no spring force between connected particles.\n */\n readonly stiffness?: number;\n /**\n * The spring damping shared by every connection, opposing relative motion\n * along each connection. If omitted, it will be assumed that there is no damping.\n */\n readonly springDamping?: number;\n /**\n * The anchor strength pulling each particle toward its rest position.\n * If omitted, it will be assumed that there is no anchoring force.\n */\n readonly anchorStrength?: number;\n /**\n * The global damping opposing every particle's velocity independently of its\n * connections. If omitted, it will be assumed that there is no global damping.\n */\n readonly globalDamping?: number;\n}\n"]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<svg viewBox="40 40 420 420" xmlns="http://www.w3.org/2000/svg" aria-labelledby="gloop-logo-title" style="vertical-align: middle;">
|
|
2
|
+
<title id="gloop-logo-title">Gloop Logo</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<!-- Deep Purple & Cyan Glow Filters -->
|
|
5
|
+
<filter id="bloom" x="-50%" y="-50%" width="200%" height="200%">
|
|
6
|
+
<feGaussianBlur stdDeviation="6" result="blur"></feGaussianBlur>
|
|
7
|
+
<feMerge>
|
|
8
|
+
<feMergeNode in="blur"></feMergeNode>
|
|
9
|
+
<feMergeNode in="SourceGraphic"></feMergeNode>
|
|
10
|
+
</feMerge>
|
|
11
|
+
</filter>
|
|
12
|
+
|
|
13
|
+
<filter id="intense-bloom" x="-50%" y="-50%" width="200%" height="200%">
|
|
14
|
+
<feGaussianBlur stdDeviation="12" result="blur"></feGaussianBlur>
|
|
15
|
+
<feMerge>
|
|
16
|
+
<feMergeNode in="blur"></feMergeNode>
|
|
17
|
+
<feMergeNode in="SourceGraphic"></feMergeNode>
|
|
18
|
+
</feMerge>
|
|
19
|
+
</filter>
|
|
20
|
+
|
|
21
|
+
<!-- Kinematic Linkage Gradient -->
|
|
22
|
+
<linearGradient id="linkageGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
23
|
+
<stop offset="0%" stop-color="#B8D8D8"></stop>
|
|
24
|
+
<stop offset="50%" stop-color="#9D4EDD"></stop>
|
|
25
|
+
<stop offset="100%" stop-color="#B8D8D8"></stop>
|
|
26
|
+
</linearGradient>
|
|
27
|
+
|
|
28
|
+
<!-- Background Void Energy -->
|
|
29
|
+
<radialGradient id="voidEnergy" cx="50%" cy="50%" r="50%">
|
|
30
|
+
<stop offset="0%" stop-color="#9D4EDD" stop-opacity="0.15"></stop>
|
|
31
|
+
<stop offset="50%" stop-color="#B8D8D8" stop-opacity="0.05"></stop>
|
|
32
|
+
<stop offset="100%" stop-color="#07090B" stop-opacity="0"></stop>
|
|
33
|
+
</radialGradient>
|
|
34
|
+
</defs>
|
|
35
|
+
|
|
36
|
+
<!-- Void Purple Energy Core -->
|
|
37
|
+
<circle cx="250" cy="250" r="200" fill="url(#voidEnergy)"></circle>
|
|
38
|
+
|
|
39
|
+
<!-- Da Vinci Construction Lines -->
|
|
40
|
+
<g stroke="#263238" stroke-width="1" fill="none">
|
|
41
|
+
<!-- Orbits -->
|
|
42
|
+
<circle cx="250" cy="250" r="160"></circle>
|
|
43
|
+
<circle cx="250" cy="250" r="120" stroke-dasharray="4 6"></circle>
|
|
44
|
+
<circle cx="250" cy="250" r="60" stroke-opacity="0.5"></circle>
|
|
45
|
+
|
|
46
|
+
<!-- Axes -->
|
|
47
|
+
<line x1="50" y1="250" x2="450" y2="250"></line>
|
|
48
|
+
<line x1="250" y1="50" x2="250" y2="450"></line>
|
|
49
|
+
|
|
50
|
+
<!-- 60-degree Diagonals -->
|
|
51
|
+
<line x1="111" y1="10" x2="389" y2="490" stroke-opacity="0.3"></line>
|
|
52
|
+
<line x1="389" y1="10" x2="111" y2="490" stroke-opacity="0.3"></line>
|
|
53
|
+
</g>
|
|
54
|
+
|
|
55
|
+
<!-- Secondary "Sinew" / Tissue connections -->
|
|
56
|
+
<g stroke="#546E7A" stroke-width="0.75" fill="none" opacity="0.6">
|
|
57
|
+
<line x1="310" y1="146" x2="370" y2="90"></line>
|
|
58
|
+
<line x1="190" y1="146" x2="130" y2="90"></line>
|
|
59
|
+
<line x1="190" y1="354" x2="130" y2="410"></line>
|
|
60
|
+
<line x1="310" y1="354" x2="370" y2="410"></line>
|
|
61
|
+
<line x1="250" y1="250" x2="250" y2="354" stroke-dasharray="2 4"></line>
|
|
62
|
+
</g>
|
|
63
|
+
|
|
64
|
+
<!-- Main "G" Kinematic Linkage (Hexagonal trace) -->
|
|
65
|
+
<!-- Base Glow -->
|
|
66
|
+
<polyline points="310,146 190,146 130,250 190,354 310,354 370,250 250,250" stroke="url(#linkageGradient)" stroke-width="16" fill="none" stroke-linejoin="miter" stroke-miterlimit="4" opacity="0.2" filter="url(#bloom)"></polyline>
|
|
67
|
+
|
|
68
|
+
<!-- Outer Strut Ring -->
|
|
69
|
+
<polyline points="310,146 190,146 130,250 190,354 310,354 370,250 250,250" stroke="url(#linkageGradient)" stroke-width="12" fill="none" stroke-linejoin="miter"></polyline>
|
|
70
|
+
|
|
71
|
+
<!-- Hollow Core Track -->
|
|
72
|
+
<polyline points="310,146 190,146 130,250 190,354 310,354 370,250 250,250" stroke="#07090B" stroke-width="6" fill="none" stroke-linejoin="miter"></polyline>
|
|
73
|
+
|
|
74
|
+
<!-- Icy Center Rail / Sparkline -->
|
|
75
|
+
<polyline points="310,146 190,146 130,250 190,354 310,354 370,250 250,250" stroke="#F0F4F8" stroke-width="1.5" fill="none" stroke-linejoin="miter" opacity="0.9"></polyline>
|
|
76
|
+
|
|
77
|
+
<!-- Particles (Technical Glyphs & Joints) -->
|
|
78
|
+
<g fill="#07090B" stroke-width="1.5">
|
|
79
|
+
<!-- P6: Top Right -->
|
|
80
|
+
<circle cx="310" cy="146" r="6" stroke="#B8D8D8"></circle>
|
|
81
|
+
<circle cx="310" cy="146" r="2" fill="#B8D8D8" stroke="none"></circle>
|
|
82
|
+
|
|
83
|
+
<!-- P5: Top Left -->
|
|
84
|
+
<circle cx="190" cy="146" r="6" stroke="#9D4EDD" filter="url(#bloom)"></circle>
|
|
85
|
+
|
|
86
|
+
<!-- P4: Mid Left -->
|
|
87
|
+
<circle cx="130" cy="250" r="8" stroke="#546E7A"></circle>
|
|
88
|
+
<circle cx="130" cy="250" r="3" stroke="#B8D8D8"></circle>
|
|
89
|
+
|
|
90
|
+
<!-- P3: Bottom Left -->
|
|
91
|
+
<circle cx="190" cy="354" r="6" stroke="#9D4EDD" filter="url(#bloom)"></circle>
|
|
92
|
+
|
|
93
|
+
<!-- P2: Bottom Right -->
|
|
94
|
+
<circle cx="310" cy="354" r="6" stroke="#B8D8D8"></circle>
|
|
95
|
+
|
|
96
|
+
<!-- P1: Mid Right -->
|
|
97
|
+
<circle cx="370" cy="250" r="8" stroke="#546E7A"></circle>
|
|
98
|
+
<path d="M 364 250 L 376 250 M 370 244 L 370 256" stroke="#B8D8D8" stroke-width="1"></path>
|
|
99
|
+
</g>
|
|
100
|
+
|
|
101
|
+
<!-- Center Actuator Particle (P0) -->
|
|
102
|
+
<g>
|
|
103
|
+
<!-- Purple energy ring -->
|
|
104
|
+
<circle cx="250" cy="250" r="14" fill="#07090B" stroke="#9D4EDD" stroke-width="2.5" filter="url(#intense-bloom)"></circle>
|
|
105
|
+
<!-- Cyan mechanical ring -->
|
|
106
|
+
<circle cx="250" cy="250" r="10" fill="none" stroke="#B8D8D8" stroke-width="1" stroke-dasharray="2 2"></circle>
|
|
107
|
+
<!-- Core pin -->
|
|
108
|
+
<circle cx="250" cy="250" r="4" fill="#F0F4F8" filter="url(#bloom)"></circle>
|
|
109
|
+
<!-- Reticle Cross -->
|
|
110
|
+
<path d="M 230 250 L 270 250 M 250 230 L 250 270" stroke="#F0F4F8" stroke-width="1" opacity="0.6"></path>
|
|
111
|
+
</g>
|
|
112
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,105 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gloop-sim",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A particle swarm simulation and rendering library for running in the browser.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"webgpu",
|
|
8
|
+
"swarm",
|
|
9
|
+
"elastic",
|
|
10
|
+
"particles",
|
|
11
|
+
"simulation",
|
|
12
|
+
"browser",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Zurab Mujirishvili <zurab.mu@gmail.com>",
|
|
17
|
+
"homepage": "https://zurabm.gitlab.io/gloop/",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://gitlab.com/zurabm/gloop.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://gitlab.com/zurabm/gloop/-/issues"
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"module": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./render": {
|
|
35
|
+
"types": "./dist/render/index.d.ts",
|
|
36
|
+
"import": "./dist/render/index.js",
|
|
37
|
+
"default": "./dist/render/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./render/springs": {
|
|
40
|
+
"types": "./dist/render/springs/index.d.ts",
|
|
41
|
+
"import": "./dist/render/springs/index.js",
|
|
42
|
+
"default": "./dist/render/springs/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"sideEffects": false,
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"docs/assets/logo.svg"
|
|
50
|
+
],
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=20"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@webgpu/types": "^0.1.66"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"three": "0.185.1"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@biomejs/biome": "^2.4.16",
|
|
65
|
+
"@types/node": "^25.9.2",
|
|
66
|
+
"@types/three": "0.185.1",
|
|
67
|
+
"@vitest/browser-playwright": "^5.0.3",
|
|
68
|
+
"@vitest/coverage-v8": "^5.0.3",
|
|
69
|
+
"concurrently": "^10.0.3",
|
|
70
|
+
"cspell": "^10.0.1",
|
|
71
|
+
"cutline": "0.0.4",
|
|
72
|
+
"lefthook": "^2.1.9",
|
|
73
|
+
"playwright": "^1.61.1",
|
|
74
|
+
"three": "0.185.1",
|
|
75
|
+
"tsc-alias": "^1.9.1",
|
|
76
|
+
"typescript": "^6.0.3",
|
|
77
|
+
"vite": "^8.0.16",
|
|
78
|
+
"vitest": "^5.0.3"
|
|
79
|
+
},
|
|
80
|
+
"scripts": {
|
|
81
|
+
"dev": "pnpm --filter gloop-lab dev",
|
|
82
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
83
|
+
"build": "pnpm run build:lib && pnpm run build:lab",
|
|
84
|
+
"build:lib": "pnpm run clean && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
85
|
+
"build:lab": "pnpm --filter gloop-lab build",
|
|
86
|
+
"docs:lab": "pnpm --filter gloop-lab build:embed",
|
|
87
|
+
"docs:api": "node scripts/docs-api.mjs",
|
|
88
|
+
"docs:install": "node scripts/docs-install.mjs",
|
|
89
|
+
"docs:build": "pnpm run docs:lab && pnpm run docs:api && pnpm run docs:install && uvx zensical build",
|
|
90
|
+
"docs:serve": "uvx zensical serve",
|
|
91
|
+
"docs:watch": "pnpm run docs:lab && pnpm run docs:api && pnpm run docs:install && concurrently -k -n lab,docs -c green,cyan \"pnpm --filter gloop-lab build:embed:watch\" \"uvx zensical serve\"",
|
|
92
|
+
"test": "vitest run",
|
|
93
|
+
"test:watch": "vitest",
|
|
94
|
+
"test:webgpu": "vitest --config vitest.webgpu.config.ts",
|
|
95
|
+
"test:coverage": "vitest run --coverage",
|
|
96
|
+
"bench": "node -e \"require('fs').rmSync('generated/bench/latest',{recursive:true,force:true})\" && vitest bench --run --config vitest.bench.config.ts && pnpm --filter gloop-bench report",
|
|
97
|
+
"bench:report": "pnpm --filter gloop-bench report",
|
|
98
|
+
"lint": "biome check .",
|
|
99
|
+
"lint:fix": "biome check --write .",
|
|
100
|
+
"spellcheck": "cspell --no-progress --gitignore .",
|
|
101
|
+
"typecheck": "tsc --noEmit && pnpm --filter gloop-lab typecheck && pnpm --filter gloop-bench typecheck",
|
|
102
|
+
"format": "biome format --write .",
|
|
103
|
+
"check": "pnpm run lint && pnpm run spellcheck && pnpm run typecheck && pnpm run test && pnpm run build"
|
|
104
|
+
}
|
|
6
105
|
}
|