@guinetik/gcanvas 1.0.2 → 1.0.4
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/dist/gcanvas.es.js +25656 -0
- package/dist/gcanvas.es.min.js +1 -0
- package/dist/gcanvas.umd.js +1 -0
- package/dist/gcanvas.umd.min.js +1 -0
- package/package.json +23 -6
- package/src/game/objects/index.js +1 -0
- package/src/game/objects/spritesheet.js +260 -0
- package/src/game/ui/theme.js +6 -0
- package/src/io/keys.js +9 -1
- package/src/math/boolean.js +481 -0
- package/src/math/index.js +1 -0
- package/.github/workflows/release.yaml +0 -70
- package/.jshintrc +0 -4
- package/.vscode/settings.json +0 -22
- package/CLAUDE.md +0 -310
- package/blackhole.jpg +0 -0
- package/demo.png +0 -0
- package/demos/CNAME +0 -1
- package/demos/animations.html +0 -31
- package/demos/basic.html +0 -38
- package/demos/baskara.html +0 -31
- package/demos/bezier.html +0 -35
- package/demos/beziersignature.html +0 -29
- package/demos/blackhole.html +0 -28
- package/demos/blob.html +0 -35
- package/demos/coordinates.html +0 -698
- package/demos/cube3d.html +0 -23
- package/demos/demos.css +0 -303
- package/demos/dino.html +0 -42
- package/demos/easing.html +0 -28
- package/demos/events.html +0 -195
- package/demos/fluent.html +0 -647
- package/demos/fluid-simple.html +0 -22
- package/demos/fluid.html +0 -37
- package/demos/fractals.html +0 -36
- package/demos/gameobjects.html +0 -626
- package/demos/genart.html +0 -26
- package/demos/gendream.html +0 -26
- package/demos/group.html +0 -36
- package/demos/home.html +0 -587
- package/demos/index.html +0 -376
- package/demos/isometric.html +0 -34
- package/demos/js/animations.js +0 -452
- package/demos/js/basic.js +0 -204
- package/demos/js/baskara.js +0 -751
- package/demos/js/bezier.js +0 -692
- package/demos/js/beziersignature.js +0 -241
- package/demos/js/blackhole/accretiondisk.obj.js +0 -379
- package/demos/js/blackhole/blackhole.obj.js +0 -318
- package/demos/js/blackhole/index.js +0 -409
- package/demos/js/blackhole/particle.js +0 -56
- package/demos/js/blackhole/starfield.obj.js +0 -218
- package/demos/js/blob.js +0 -2276
- package/demos/js/coordinates.js +0 -840
- package/demos/js/cube3d.js +0 -789
- package/demos/js/dino.js +0 -1420
- package/demos/js/easing.js +0 -477
- package/demos/js/fluent.js +0 -183
- package/demos/js/fluid-simple.js +0 -253
- package/demos/js/fluid.js +0 -527
- package/demos/js/fractals.js +0 -931
- package/demos/js/fractalworker.js +0 -93
- package/demos/js/gameobjects.js +0 -176
- package/demos/js/genart.js +0 -268
- package/demos/js/gendream.js +0 -209
- package/demos/js/group.js +0 -140
- package/demos/js/info-toggle.js +0 -25
- package/demos/js/isometric.js +0 -863
- package/demos/js/kerr.js +0 -1556
- package/demos/js/lavalamp.js +0 -590
- package/demos/js/layout.js +0 -354
- package/demos/js/mondrian.js +0 -285
- package/demos/js/opacity.js +0 -275
- package/demos/js/painter.js +0 -484
- package/demos/js/particles-showcase.js +0 -514
- package/demos/js/particles.js +0 -299
- package/demos/js/patterns.js +0 -397
- package/demos/js/penrose/artifact.js +0 -69
- package/demos/js/penrose/blackhole.js +0 -121
- package/demos/js/penrose/constants.js +0 -73
- package/demos/js/penrose/game.js +0 -943
- package/demos/js/penrose/lore.js +0 -278
- package/demos/js/penrose/penrosescene.js +0 -892
- package/demos/js/penrose/ship.js +0 -216
- package/demos/js/penrose/sounds.js +0 -211
- package/demos/js/penrose/voidparticle.js +0 -55
- package/demos/js/penrose/voidscene.js +0 -258
- package/demos/js/penrose/voidship.js +0 -144
- package/demos/js/penrose/wormhole.js +0 -46
- package/demos/js/pipeline.js +0 -555
- package/demos/js/plane3d.js +0 -256
- package/demos/js/platformer.js +0 -1579
- package/demos/js/scene.js +0 -304
- package/demos/js/scenes.js +0 -320
- package/demos/js/schrodinger.js +0 -410
- package/demos/js/schwarzschild.js +0 -1023
- package/demos/js/shapes.js +0 -628
- package/demos/js/space/alien.js +0 -171
- package/demos/js/space/boom.js +0 -98
- package/demos/js/space/boss.js +0 -353
- package/demos/js/space/buff.js +0 -73
- package/demos/js/space/bullet.js +0 -102
- package/demos/js/space/constants.js +0 -85
- package/demos/js/space/game.js +0 -1884
- package/demos/js/space/hud.js +0 -112
- package/demos/js/space/laserbeam.js +0 -179
- package/demos/js/space/lightning.js +0 -277
- package/demos/js/space/minion.js +0 -192
- package/demos/js/space/missile.js +0 -212
- package/demos/js/space/player.js +0 -430
- package/demos/js/space/powerup.js +0 -90
- package/demos/js/space/starfield.js +0 -58
- package/demos/js/space/starpower.js +0 -90
- package/demos/js/spacetime.js +0 -559
- package/demos/js/sphere3d.js +0 -229
- package/demos/js/sprite.js +0 -473
- package/demos/js/svgtween.js +0 -204
- package/demos/js/tde/accretiondisk.js +0 -471
- package/demos/js/tde/blackhole.js +0 -219
- package/demos/js/tde/blackholescene.js +0 -209
- package/demos/js/tde/config.js +0 -59
- package/demos/js/tde/index.js +0 -820
- package/demos/js/tde/jets.js +0 -290
- package/demos/js/tde/lensedstarfield.js +0 -154
- package/demos/js/tde/tdestar.js +0 -297
- package/demos/js/tde/tidalstream.js +0 -372
- package/demos/js/tde_old/blackhole.obj.js +0 -354
- package/demos/js/tde_old/debris.obj.js +0 -791
- package/demos/js/tde_old/flare.obj.js +0 -239
- package/demos/js/tde_old/index.js +0 -448
- package/demos/js/tde_old/star.obj.js +0 -812
- package/demos/js/tiles.js +0 -312
- package/demos/js/tweendemo.js +0 -79
- package/demos/js/visibility.js +0 -102
- package/demos/kerr.html +0 -28
- package/demos/lavalamp.html +0 -27
- package/demos/layouts.html +0 -37
- package/demos/logo.svg +0 -4
- package/demos/loop.html +0 -84
- package/demos/mondrian.html +0 -32
- package/demos/og_image.png +0 -0
- package/demos/opacity.html +0 -36
- package/demos/painter.html +0 -39
- package/demos/particles-showcase.html +0 -28
- package/demos/particles.html +0 -24
- package/demos/patterns.html +0 -33
- package/demos/penrose-game.html +0 -31
- package/demos/pipeline.html +0 -737
- package/demos/plane3d.html +0 -24
- package/demos/platformer.html +0 -43
- package/demos/scene.html +0 -33
- package/demos/scenes.html +0 -96
- package/demos/schrodinger.html +0 -27
- package/demos/schwarzschild.html +0 -27
- package/demos/shapes.html +0 -16
- package/demos/space.html +0 -85
- package/demos/spacetime.html +0 -27
- package/demos/sphere3d.html +0 -24
- package/demos/sprite.html +0 -18
- package/demos/svgtween.html +0 -29
- package/demos/tde.html +0 -28
- package/demos/tiles.html +0 -28
- package/demos/transforms.html +0 -400
- package/demos/tween.html +0 -45
- package/demos/visibility.html +0 -33
- package/docs/README.md +0 -230
- package/docs/api/FluidSystem.md +0 -173
- package/docs/concepts/architecture-overview.md +0 -204
- package/docs/concepts/coordinate-system.md +0 -384
- package/docs/concepts/lifecycle.md +0 -255
- package/docs/concepts/rendering-pipeline.md +0 -279
- package/docs/concepts/shapes-vs-gameobjects.md +0 -187
- package/docs/concepts/tde-zorder.md +0 -106
- package/docs/concepts/two-layer-architecture.md +0 -229
- package/docs/fluid-dynamics.md +0 -99
- package/docs/getting-started/first-game.md +0 -354
- package/docs/getting-started/hello-world.md +0 -269
- package/docs/getting-started/installation.md +0 -175
- package/docs/modules/collision/README.md +0 -453
- package/docs/modules/fluent/README.md +0 -1075
- package/docs/modules/game/README.md +0 -303
- package/docs/modules/isometric-camera.md +0 -210
- package/docs/modules/isometric.md +0 -275
- package/docs/modules/painter/README.md +0 -328
- package/docs/modules/particle/README.md +0 -559
- package/docs/modules/shapes/README.md +0 -221
- package/docs/modules/shapes/base/euclidian.md +0 -123
- package/docs/modules/shapes/base/geometry2d.md +0 -204
- package/docs/modules/shapes/base/renderable.md +0 -215
- package/docs/modules/shapes/base/shape.md +0 -262
- package/docs/modules/shapes/base/transformable.md +0 -243
- package/docs/modules/shapes/hierarchy.md +0 -218
- package/docs/modules/state/README.md +0 -577
- package/docs/modules/util/README.md +0 -99
- package/docs/modules/util/camera3d.md +0 -412
- package/docs/modules/util/scene3d.md +0 -395
- package/index.html +0 -17
- package/jsdoc.json +0 -50
- package/scripts/build-demo.js +0 -69
- package/scripts/bundle4llm.js +0 -276
- package/scripts/clearconsole.js +0 -48
- package/test/math/orbital.test.js +0 -61
- package/test/math/tensor.test.js +0 -114
- package/test/particle/emitter.test.js +0 -204
- package/test/particle/particle-system.test.js +0 -310
- package/test/particle/particle.test.js +0 -116
- package/test/particle/updaters.test.js +0 -386
- package/test/setup.js +0 -120
- package/test/shapes/euclidian.test.js +0 -44
- package/test/shapes/geometry.test.js +0 -86
- package/test/shapes/group.test.js +0 -86
- package/test/shapes/rectangle.test.js +0 -64
- package/test/shapes/transform.test.js +0 -379
- package/test/util/camera3d.test.js +0 -428
- package/test/util/scene3d.test.js +0 -352
- package/vite.config.js +0 -50
- package/vitest.config.js +0 -13
package/scripts/bundle4llm.js
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* bundle4llm
|
|
5
|
-
* -------------
|
|
6
|
-
* Create a comprehensive, single-file, import/export-free JavaScript bundle
|
|
7
|
-
* ideal for feeding into LLMs without worrying about dependencies.
|
|
8
|
-
*
|
|
9
|
-
* Features:
|
|
10
|
-
* - Recursively resolves and includes all exported modules
|
|
11
|
-
* - Flattens directory structure
|
|
12
|
-
* - Orders files by export order in index files
|
|
13
|
-
* - Sanitizes code based on selected preset (defaults to ES2020)
|
|
14
|
-
* - Skips index.js files from output (used only for ordering)
|
|
15
|
-
* - Ensures comprehensive module inclusion
|
|
16
|
-
* - Supports verbose logging with --verbose or -v
|
|
17
|
-
* - Optional comment stripping with --strip-comments
|
|
18
|
-
* - Accurate token estimation for various LLM models
|
|
19
|
-
*
|
|
20
|
-
* Usage:
|
|
21
|
-
* bundle4llm --src ./src --out ./dist --file bundle.js --preset es2020 --strip-comments --model claude -v
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import fs from 'fs';
|
|
25
|
-
import path from 'path';
|
|
26
|
-
import { argv } from 'process';
|
|
27
|
-
|
|
28
|
-
const args = Object.fromEntries(
|
|
29
|
-
argv.slice(2).map(arg => {
|
|
30
|
-
const [key, val] = arg.replace(/^--?/, '').split('=');
|
|
31
|
-
return [key, val ?? true];
|
|
32
|
-
})
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
const srcDir = path.resolve(args.src || './src');
|
|
36
|
-
const outDir = path.resolve(args.out || './dist');
|
|
37
|
-
const outFile = args.file || 'llm-bundle.js';
|
|
38
|
-
const verbose = args.v || args.verbose;
|
|
39
|
-
const stripComments = args['strip-comments'] || false;
|
|
40
|
-
const preset = args.preset || 'es2020';
|
|
41
|
-
const model = args.model || 'claude'; // Default to Claude model
|
|
42
|
-
|
|
43
|
-
// Deep file collection with recursive export resolution
|
|
44
|
-
function collectModules(indexPath, fileMap = {}, visited = new Set(), depth = 0) {
|
|
45
|
-
// Prevent infinite recursion
|
|
46
|
-
if (depth > 10) return fileMap;
|
|
47
|
-
|
|
48
|
-
// Prevent revisiting the same index file
|
|
49
|
-
if (visited.has(indexPath)) return fileMap;
|
|
50
|
-
visited.add(indexPath);
|
|
51
|
-
|
|
52
|
-
// Read index file contents
|
|
53
|
-
if (!fs.existsSync(indexPath)) return fileMap;
|
|
54
|
-
const content = fs.readFileSync(indexPath, 'utf-8');
|
|
55
|
-
|
|
56
|
-
// Find all export declarations
|
|
57
|
-
const exportMatches = [
|
|
58
|
-
...content.matchAll(/export\s+(?:\*|{[^}]*})\s*from\s+['"](.+)['"]/g),
|
|
59
|
-
...content.matchAll(/export\s+{[^}]*}\s*from\s+['"](.+)['"]/g)
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
// Resolve and process each exported module
|
|
63
|
-
for (const match of exportMatches) {
|
|
64
|
-
const rawPath = match[1];
|
|
65
|
-
const modulePath = rawPath.startsWith('.')
|
|
66
|
-
? path.resolve(path.dirname(indexPath), rawPath)
|
|
67
|
-
: path.resolve(srcDir, rawPath);
|
|
68
|
-
|
|
69
|
-
// Normalize path
|
|
70
|
-
const normalizedPath = modulePath.replace(/\.js$/, '');
|
|
71
|
-
const jsPath = normalizedPath + '.js';
|
|
72
|
-
|
|
73
|
-
// If it's a directory, look for its index file
|
|
74
|
-
const indexInDir = path.join(normalizedPath, 'index.js');
|
|
75
|
-
|
|
76
|
-
if (fs.existsSync(jsPath) && !fileMap[jsPath]) {
|
|
77
|
-
// Add the module file
|
|
78
|
-
fileMap[jsPath] = jsPath;
|
|
79
|
-
|
|
80
|
-
// If it's a directory with an index, recursively collect its exports
|
|
81
|
-
if (fs.existsSync(indexInDir)) {
|
|
82
|
-
collectModules(indexInDir, fileMap, visited, depth + 1);
|
|
83
|
-
}
|
|
84
|
-
} else if (fs.existsSync(indexInDir)) {
|
|
85
|
-
// Recursively collect exports from subdirectory index
|
|
86
|
-
collectModules(indexInDir, fileMap, visited, depth + 1);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return fileMap;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Order modules based on index file export order
|
|
94
|
-
function orderModules(srcDir) {
|
|
95
|
-
const indexPath = path.join(srcDir, 'index.js');
|
|
96
|
-
const fileMap = collectModules(indexPath);
|
|
97
|
-
|
|
98
|
-
// Convert to array and sort
|
|
99
|
-
return Object.values(fileMap)
|
|
100
|
-
.filter(filePath => !filePath.endsWith('index.js'));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Sanitization presets
|
|
104
|
-
const sanitizationPresets = {
|
|
105
|
-
// ES2020 sanitization - strips imports and optionally comments
|
|
106
|
-
es2020: (code, options = {}) => {
|
|
107
|
-
let result = code;
|
|
108
|
-
|
|
109
|
-
// Strip import statements
|
|
110
|
-
result = result
|
|
111
|
-
.split('\n')
|
|
112
|
-
.filter(line => !/^\s*import\b/.test(line))
|
|
113
|
-
.join('\n');
|
|
114
|
-
|
|
115
|
-
// Optionally strip comments
|
|
116
|
-
if (options.stripComments) {
|
|
117
|
-
// Strip block comments
|
|
118
|
-
result = result.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
119
|
-
// Strip line comments
|
|
120
|
-
result = result.replace(/\/\/.*$/gm, '');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return result.trim();
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
// ES2015 sanitization - more conservative approach
|
|
127
|
-
es2015: (code, options = {}) => {
|
|
128
|
-
let result = code;
|
|
129
|
-
|
|
130
|
-
// Strip import statements
|
|
131
|
-
result = result
|
|
132
|
-
.split('\n')
|
|
133
|
-
.filter(line => !/^\s*import\b/.test(line))
|
|
134
|
-
.join('\n');
|
|
135
|
-
|
|
136
|
-
// Optionally strip comments
|
|
137
|
-
if (options.stripComments) {
|
|
138
|
-
// Strip block comments
|
|
139
|
-
result = result.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
140
|
-
// Strip line comments
|
|
141
|
-
result = result.replace(/\/\/.*$/gm, '');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return result.trim();
|
|
145
|
-
},
|
|
146
|
-
|
|
147
|
-
// Minimal sanitization - only strips imports
|
|
148
|
-
minimal: (code, options = {}) => {
|
|
149
|
-
let result = code
|
|
150
|
-
.split('\n')
|
|
151
|
-
.filter(line => !/^\s*import\b/.test(line))
|
|
152
|
-
.join('\n');
|
|
153
|
-
|
|
154
|
-
return result.trim();
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
// Generic sanitize function that uses the selected preset
|
|
159
|
-
function sanitizeCode(code, presetName, options = {}) {
|
|
160
|
-
const sanitizer = sanitizationPresets[presetName] || sanitizationPresets.es2020;
|
|
161
|
-
return sanitizer(code, options);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Improved token estimation function supporting multiple LLM models
|
|
166
|
-
*
|
|
167
|
-
* @param {string} text - The text to estimate tokens for
|
|
168
|
-
* @param {string} model - Model identifier ('claude', 'gpt3', 'gpt4', etc.)
|
|
169
|
-
* @return {number} Estimated token count
|
|
170
|
-
*/
|
|
171
|
-
function estimateTokens(text, model = 'claude') {
|
|
172
|
-
// Basic character counting
|
|
173
|
-
const charCount = text.length;
|
|
174
|
-
const wordCount = text.split(/\s+/).filter(Boolean).length;
|
|
175
|
-
|
|
176
|
-
// Model-specific token ratio estimations
|
|
177
|
-
const modelRatios = {
|
|
178
|
-
// Anthropic Claude models
|
|
179
|
-
'claude': {
|
|
180
|
-
charRatio: 3.5, // ~3.5 chars per token for Claude
|
|
181
|
-
wordRatio: 0.75, // ~0.75 words per token for Claude
|
|
182
|
-
},
|
|
183
|
-
// OpenAI models
|
|
184
|
-
'gpt3': {
|
|
185
|
-
charRatio: 4.0, // ~4 chars per token for GPT-3
|
|
186
|
-
wordRatio: 0.75, // ~0.75 words per token for GPT-3
|
|
187
|
-
},
|
|
188
|
-
'gpt4': {
|
|
189
|
-
charRatio: 3.8, // ~3.8 chars per token for GPT-4
|
|
190
|
-
wordRatio: 0.7, // ~0.7 words per token for GPT-4
|
|
191
|
-
},
|
|
192
|
-
// Default fallback
|
|
193
|
-
'default': {
|
|
194
|
-
charRatio: 4.0,
|
|
195
|
-
wordRatio: 0.75,
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
// Get the appropriate ratio for the model
|
|
200
|
-
const ratio = modelRatios[model] || modelRatios.default;
|
|
201
|
-
|
|
202
|
-
// Estimate based on character count and word count, using an average of both methods
|
|
203
|
-
const charBasedEstimate = Math.ceil(charCount / ratio.charRatio);
|
|
204
|
-
const wordBasedEstimate = Math.ceil(wordCount / ratio.wordRatio);
|
|
205
|
-
|
|
206
|
-
// Language features that tend to increase token count
|
|
207
|
-
const codeFeatures = {
|
|
208
|
-
symbols: (text.match(/[{}[\]()<>:;,."'`~!@#$%^&*+=|\\/?-]/g) || []).length,
|
|
209
|
-
indentation: (text.match(/^ +/gm) || []).length,
|
|
210
|
-
camelCase: (text.match(/[a-z][A-Z]/g) || []).length,
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// Adjustment for code-specific features (symbols, indentation, camelCase)
|
|
214
|
-
const codeAdjustment = codeFeatures.symbols * 0.1 +
|
|
215
|
-
codeFeatures.indentation * 0.05 +
|
|
216
|
-
codeFeatures.camelCase * 0.2;
|
|
217
|
-
|
|
218
|
-
// Calculate weighted average with more weight on char-based for code
|
|
219
|
-
const weightedEstimate = (charBasedEstimate * 0.7) + (wordBasedEstimate * 0.3);
|
|
220
|
-
|
|
221
|
-
// Add code-specific adjustment
|
|
222
|
-
const finalEstimate = Math.ceil(weightedEstimate + codeAdjustment);
|
|
223
|
-
|
|
224
|
-
return finalEstimate;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Main bundling function
|
|
228
|
-
function bundleLLMBuild(srcDir, outDir, outFile, options = {}) {
|
|
229
|
-
const orderedFiles = orderModules(srcDir);
|
|
230
|
-
const { verbose, stripComments, preset, model } = options;
|
|
231
|
-
|
|
232
|
-
let output = '/**\n * bundle4llm Output\n';
|
|
233
|
-
output += ` * Generated: ${new Date().toISOString()}\n`;
|
|
234
|
-
output += ` * Source: ${srcDir}\n`;
|
|
235
|
-
output += ` * Preset: ${preset}\n`;
|
|
236
|
-
if (stripComments) output += ` * Comments: Stripped\n`;
|
|
237
|
-
output += ` */\n\n`;
|
|
238
|
-
|
|
239
|
-
let currentDir = '';
|
|
240
|
-
for (const file of orderedFiles) {
|
|
241
|
-
const fileDir = path.dirname(file).replace(srcDir, '');
|
|
242
|
-
if (fileDir !== currentDir) {
|
|
243
|
-
currentDir = fileDir;
|
|
244
|
-
output += `\n// =========================================\n`;
|
|
245
|
-
output += `// DIRECTORY: ${currentDir || '/'}\n`;
|
|
246
|
-
output += `// =========================================\n\n`;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
let contents = fs.readFileSync(file, 'utf-8');
|
|
250
|
-
contents = sanitizeCode(contents, preset, { stripComments });
|
|
251
|
-
|
|
252
|
-
if (verbose) console.log('📦 Including:', file);
|
|
253
|
-
output += contents + '\n\n';
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Ensure output directory exists
|
|
257
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
258
|
-
|
|
259
|
-
// Write the bundled file
|
|
260
|
-
const fullOutputPath = path.join(outDir, outFile);
|
|
261
|
-
fs.writeFileSync(fullOutputPath, output);
|
|
262
|
-
|
|
263
|
-
const totalTokens = estimateTokens(output, model).toLocaleString();
|
|
264
|
-
console.log(`✅ Created LLM build → ${fullOutputPath}`);
|
|
265
|
-
console.log(`📊 Total modules included: ${orderedFiles.length}`);
|
|
266
|
-
console.log(`🔧 Preset: ${preset}${stripComments ? ', comments stripped' : ''}`);
|
|
267
|
-
console.log(`🔍 Estimated tokens (${model}): ${totalTokens}`);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Execute the bundle
|
|
271
|
-
bundleLLMBuild(srcDir, outDir, outFile, {
|
|
272
|
-
verbose,
|
|
273
|
-
stripComments,
|
|
274
|
-
preset,
|
|
275
|
-
model
|
|
276
|
-
});
|
package/scripts/clearconsole.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
|
|
6
|
-
const targetDir = process.argv[2] || "./src"; // default to ./src
|
|
7
|
-
|
|
8
|
-
function isLineCommented(line) {
|
|
9
|
-
return /^\s*\/\/.*console\.log/.test(line);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function isInsideBlockComment(lines, index) {
|
|
13
|
-
for (let i = index; i >= 0; i--) {
|
|
14
|
-
if (lines[i].includes("*/")) break;
|
|
15
|
-
if (lines[i].includes("/*")) return true;
|
|
16
|
-
}
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function processFile(filePath) {
|
|
21
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
22
|
-
const lines = content.split("\n");
|
|
23
|
-
const modified = lines.map((line, i) => {
|
|
24
|
-
if (
|
|
25
|
-
line.includes("console.log") &&
|
|
26
|
-
!isLineCommented(line) &&
|
|
27
|
-
!isInsideBlockComment(lines, i)
|
|
28
|
-
) {
|
|
29
|
-
return line.replace(/(.*?)(console\.log.*)/, "$1// $2");
|
|
30
|
-
}
|
|
31
|
-
return line;
|
|
32
|
-
});
|
|
33
|
-
fs.writeFileSync(filePath, modified.join("\n"), "utf8");
|
|
34
|
-
console.log(`✔ Commented console.log in: ${filePath}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function walkDir(dir) {
|
|
38
|
-
fs.readdirSync(dir).forEach((file) => {
|
|
39
|
-
const fullPath = path.join(dir, file);
|
|
40
|
-
if (fs.statSync(fullPath).isDirectory()) {
|
|
41
|
-
walkDir(fullPath);
|
|
42
|
-
} else if (/\.(js|ts|jsx|tsx)$/.test(fullPath)) {
|
|
43
|
-
processFile(fullPath);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
walkDir(targetDir);
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { decayingOrbitalRadius, getTerminalTrajectory } from "../../src/math/orbital";
|
|
3
|
-
|
|
4
|
-
describe("Orbital Math Utilities", () => {
|
|
5
|
-
describe("decayingOrbitalRadius", () => {
|
|
6
|
-
it("should return the initial radius when t=0", () => {
|
|
7
|
-
const r0 = 100;
|
|
8
|
-
const decay = 0.5;
|
|
9
|
-
const r = decayingOrbitalRadius(r0, decay, 0);
|
|
10
|
-
expect(r).toBe(r0);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("should decay the radius over time", () => {
|
|
14
|
-
const r0 = 100;
|
|
15
|
-
const decay = 0.5;
|
|
16
|
-
const r1 = decayingOrbitalRadius(r0, decay, 1);
|
|
17
|
-
const r2 = decayingOrbitalRadius(r0, decay, 2);
|
|
18
|
-
|
|
19
|
-
expect(r1).toBeLessThan(r0);
|
|
20
|
-
expect(r2).toBeLessThan(r1);
|
|
21
|
-
expect(r1).toBeCloseTo(100 * Math.exp(-0.5), 5);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("should handle zero decay factor", () => {
|
|
25
|
-
const r0 = 100;
|
|
26
|
-
const r = decayingOrbitalRadius(r0, 0, 10);
|
|
27
|
-
expect(r).toBe(r0);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("getTerminalTrajectory", () => {
|
|
32
|
-
it("should return start position at progress=0", () => {
|
|
33
|
-
const start = { x: 100, y: 50, z: 25 };
|
|
34
|
-
const pos = getTerminalTrajectory(start.x, start.y, start.z, 0);
|
|
35
|
-
expect(pos).toEqual(start);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("should return origin at progress=1", () => {
|
|
39
|
-
const start = { x: 100, y: 50, z: 25 };
|
|
40
|
-
const pos = getTerminalTrajectory(start.x, start.y, start.z, 1);
|
|
41
|
-
expect(pos).toEqual({ x: 0, y: 0, z: 0 });
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("should interpolate linearly by default", () => {
|
|
45
|
-
const start = { x: 100, y: 100, z: 100 };
|
|
46
|
-
const pos = getTerminalTrajectory(start.x, start.y, start.z, 0.5);
|
|
47
|
-
expect(pos).toEqual({ x: 50, y: 50, z: 50 });
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("should apply easing function if provided", () => {
|
|
51
|
-
const start = { x: 100, y: 100, z: 100 };
|
|
52
|
-
const easeInQuad = (t) => t * t;
|
|
53
|
-
const pos = getTerminalTrajectory(start.x, start.y, start.z, 0.5, easeInQuad);
|
|
54
|
-
// 0.5 * 0.5 = 0.25
|
|
55
|
-
// 100 * (1 - 0.25) = 75
|
|
56
|
-
expect(pos.x).toBe(75);
|
|
57
|
-
expect(pos.y).toBe(75);
|
|
58
|
-
expect(pos.z).toBe(75);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
});
|
package/test/math/tensor.test.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { Tensor } from "../../src/math/tensor";
|
|
3
|
-
|
|
4
|
-
describe("Tensor class", () => {
|
|
5
|
-
describe("Basic Operations", () => {
|
|
6
|
-
it("should create a tensor from components", () => {
|
|
7
|
-
const components = [
|
|
8
|
-
[1, 2],
|
|
9
|
-
[3, 4],
|
|
10
|
-
];
|
|
11
|
-
const t = new Tensor(components);
|
|
12
|
-
expect(t.get(0, 0)).toBe(1);
|
|
13
|
-
expect(t.get(1, 1)).toBe(4);
|
|
14
|
-
expect(t.dimension).toBe(2);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("should be immutable", () => {
|
|
18
|
-
const components = [[1, 2], [3, 4]];
|
|
19
|
-
const t1 = new Tensor(components);
|
|
20
|
-
const t2 = t1.set(0, 0, 9);
|
|
21
|
-
|
|
22
|
-
expect(t1.get(0, 0)).toBe(1);
|
|
23
|
-
expect(t2.get(0, 0)).toBe(9);
|
|
24
|
-
expect(t1).not.toBe(t2);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should add two tensors", () => {
|
|
28
|
-
const t1 = new Tensor([[1, 0], [0, 1]]);
|
|
29
|
-
const t2 = new Tensor([[1, 2], [3, 4]]);
|
|
30
|
-
const sum = t1.add(t2);
|
|
31
|
-
expect(sum.get(0, 1)).toBe(2);
|
|
32
|
-
expect(sum.get(1, 1)).toBe(5);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should scale a tensor", () => {
|
|
36
|
-
const t = new Tensor([[1, 2], [3, 4]]);
|
|
37
|
-
const scaled = t.scale(2);
|
|
38
|
-
expect(scaled.get(0, 0)).toBe(2);
|
|
39
|
-
expect(scaled.get(1, 1)).toBe(8);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("Inversion & Determinant (Diagonal Optimizations)", () => {
|
|
44
|
-
it("should compute inverse of a diagonal tensor (fast path)", () => {
|
|
45
|
-
const t = Tensor.diagonal([-1, 0.5, 2, 4]);
|
|
46
|
-
const inv = t.inverse();
|
|
47
|
-
expect(inv.get(0, 0)).toBe(-1);
|
|
48
|
-
expect(inv.get(1, 1)).toBe(2);
|
|
49
|
-
expect(inv.get(2, 2)).toBe(0.5);
|
|
50
|
-
expect(inv.get(3, 3)).toBe(0.25);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("should compute determinant of a diagonal matrix (fast path)", () => {
|
|
54
|
-
const t = Tensor.diagonal([-1, 1, 1, 1]);
|
|
55
|
-
expect(t.determinant()).toBe(-1);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("should compute inverse of a non-diagonal matrix (Gaussian elimination)", () => {
|
|
59
|
-
const t = new Tensor([
|
|
60
|
-
[1, 2],
|
|
61
|
-
[3, 4]
|
|
62
|
-
]);
|
|
63
|
-
const inv = t.inverse();
|
|
64
|
-
// det = 1*4 - 2*3 = -2
|
|
65
|
-
// inv = (-1/2) * [4, -2; -3, 1] = [-2, 1; 1.5, -0.5]
|
|
66
|
-
expect(inv.get(0, 0)).toBeCloseTo(-2, 10);
|
|
67
|
-
expect(inv.get(0, 1)).toBeCloseTo(1, 10);
|
|
68
|
-
expect(inv.get(1, 0)).toBeCloseTo(1.5, 10);
|
|
69
|
-
expect(inv.get(1, 1)).toBeCloseTo(-0.5, 10);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe("GR Metrics", () => {
|
|
74
|
-
it("should create Schwarzschild metric", () => {
|
|
75
|
-
const g = Tensor.schwarzschild(10, 2);
|
|
76
|
-
expect(g.name).toBe("Schwarzschild");
|
|
77
|
-
// factor = 1 - 2/10 = 0.8
|
|
78
|
-
expect(g.get(0, 0)).toBe(-0.8);
|
|
79
|
-
expect(g.get(1, 1)).toBeCloseTo(1 / 0.8, 5);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("should create contravariant Schwarzschild metric directly", () => {
|
|
83
|
-
const gInv = Tensor.schwarzschildContravariant(10, 2);
|
|
84
|
-
expect(gInv.get(0, 0)).toBeCloseTo(-1 / 0.8, 5);
|
|
85
|
-
expect(gInv.get(1, 1)).toBe(0.8);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should match numerical inverse for Kerr metric", () => {
|
|
89
|
-
const r = 10, theta = Math.PI / 4, M = 1, a = 0.6;
|
|
90
|
-
const g = Tensor.kerr(r, theta, M, a);
|
|
91
|
-
const gInvNumerical = g.inverse();
|
|
92
|
-
const gInvAnalytical = Tensor.kerrContravariant(r, theta, M, a);
|
|
93
|
-
|
|
94
|
-
for (let i = 0; i < 4; i++) {
|
|
95
|
-
for (let j = 0; j < 4; j++) {
|
|
96
|
-
expect(gInvAnalytical.get(i, j)).toBeCloseTo(gInvNumerical.get(i, j), 8);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should compute analytical Christoffel symbols for Schwarzschild", () => {
|
|
102
|
-
const r = 10, rs = 2, theta = Math.PI / 2;
|
|
103
|
-
const pos = [0, r, theta, 0];
|
|
104
|
-
pos._rs = rs;
|
|
105
|
-
|
|
106
|
-
const gamma = Tensor.christoffel((p) => Tensor.schwarzschild(p[1], rs, p[2]), pos);
|
|
107
|
-
|
|
108
|
-
// factor = 0.8
|
|
109
|
-
// Gamma^t_tr = rs / (2r^2 * factor) = 2 / (200 * 0.8) = 2 / 160 = 0.0125
|
|
110
|
-
expect(gamma[0][0][1]).toBeCloseTo(0.0125, 8);
|
|
111
|
-
expect(gamma[0][1][0]).toBeCloseTo(0.0125, 8);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
});
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { ParticleEmitter } from "../../src/particle/emitter";
|
|
3
|
-
import { Particle } from "../../src/particle/particle";
|
|
4
|
-
|
|
5
|
-
describe("ParticleEmitter", () => {
|
|
6
|
-
describe("constructor", () => {
|
|
7
|
-
it("should initialize with default values", () => {
|
|
8
|
-
const emitter = new ParticleEmitter();
|
|
9
|
-
|
|
10
|
-
expect(emitter.rate).toBe(10);
|
|
11
|
-
expect(emitter.position).toEqual({ x: 0, y: 0, z: 0 });
|
|
12
|
-
expect(emitter.spread).toEqual({ x: 0, y: 0, z: 0 });
|
|
13
|
-
expect(emitter.velocity).toEqual({ x: 0, y: 0, z: 0 });
|
|
14
|
-
expect(emitter.velocitySpread).toEqual({ x: 0, y: 0, z: 0 });
|
|
15
|
-
expect(emitter.lifetime).toEqual({ min: 1, max: 2 });
|
|
16
|
-
expect(emitter.size).toEqual({ min: 1, max: 1 });
|
|
17
|
-
expect(emitter.color).toEqual({ r: 255, g: 255, b: 255, a: 1 });
|
|
18
|
-
expect(emitter.shape).toBe("circle");
|
|
19
|
-
expect(emitter.active).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should accept custom options", () => {
|
|
23
|
-
const emitter = new ParticleEmitter({
|
|
24
|
-
rate: 50,
|
|
25
|
-
position: { x: 100, y: 200 },
|
|
26
|
-
velocity: { y: -100 },
|
|
27
|
-
lifetime: { min: 0.5, max: 1.5 },
|
|
28
|
-
size: { min: 2, max: 5 },
|
|
29
|
-
color: { r: 255, g: 0, b: 0, a: 0.8 },
|
|
30
|
-
shape: "square",
|
|
31
|
-
active: false,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
expect(emitter.rate).toBe(50);
|
|
35
|
-
expect(emitter.position).toEqual({ x: 100, y: 200, z: 0 });
|
|
36
|
-
expect(emitter.velocity).toEqual({ x: 0, y: -100, z: 0 });
|
|
37
|
-
expect(emitter.lifetime).toEqual({ min: 0.5, max: 1.5 });
|
|
38
|
-
expect(emitter.size).toEqual({ min: 2, max: 5 });
|
|
39
|
-
expect(emitter.color).toEqual({ r: 255, g: 0, b: 0, a: 0.8 });
|
|
40
|
-
expect(emitter.shape).toBe("square");
|
|
41
|
-
expect(emitter.active).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe("emit", () => {
|
|
46
|
-
it("should initialize particle with emitter settings", () => {
|
|
47
|
-
const emitter = new ParticleEmitter({
|
|
48
|
-
position: { x: 100, y: 200, z: 50 },
|
|
49
|
-
velocity: { x: 10, y: -20, z: 5 },
|
|
50
|
-
lifetime: { min: 2, max: 2 },
|
|
51
|
-
size: { min: 3, max: 3 },
|
|
52
|
-
color: { r: 128, g: 64, b: 32, a: 0.5 },
|
|
53
|
-
shape: "triangle",
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const p = new Particle();
|
|
57
|
-
emitter.emit(p);
|
|
58
|
-
|
|
59
|
-
expect(p.x).toBe(100);
|
|
60
|
-
expect(p.y).toBe(200);
|
|
61
|
-
expect(p.z).toBe(50);
|
|
62
|
-
expect(p.vx).toBe(10);
|
|
63
|
-
expect(p.vy).toBe(-20);
|
|
64
|
-
expect(p.vz).toBe(5);
|
|
65
|
-
expect(p.lifetime).toBe(2);
|
|
66
|
-
expect(p.size).toBe(3);
|
|
67
|
-
expect(p.color).toEqual({ r: 128, g: 64, b: 32, a: 0.5 });
|
|
68
|
-
expect(p.shape).toBe("triangle");
|
|
69
|
-
expect(p.age).toBe(0);
|
|
70
|
-
expect(p.alive).toBe(true);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("should apply position spread", () => {
|
|
74
|
-
const emitter = new ParticleEmitter({
|
|
75
|
-
position: { x: 0, y: 0, z: 0 },
|
|
76
|
-
spread: { x: 100, y: 100, z: 100 },
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Mock Math.random to return predictable values
|
|
80
|
-
const mockRandom = vi.spyOn(Math, "random");
|
|
81
|
-
mockRandom.mockReturnValue(0.5); // Mid-point = 0 spread
|
|
82
|
-
|
|
83
|
-
const p = new Particle();
|
|
84
|
-
emitter.emit(p);
|
|
85
|
-
|
|
86
|
-
// With Math.random() = 0.5, spread = (0.5 - 0.5) * 2 * spread = 0
|
|
87
|
-
expect(p.x).toBe(0);
|
|
88
|
-
expect(p.y).toBe(0);
|
|
89
|
-
expect(p.z).toBe(0);
|
|
90
|
-
|
|
91
|
-
mockRandom.mockRestore();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("should apply velocity spread", () => {
|
|
95
|
-
const emitter = new ParticleEmitter({
|
|
96
|
-
velocity: { x: 100 },
|
|
97
|
-
velocitySpread: { x: 50 },
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const mockRandom = vi.spyOn(Math, "random");
|
|
101
|
-
mockRandom.mockReturnValue(0); // (0 - 0.5) * 2 * 50 = -50
|
|
102
|
-
|
|
103
|
-
const p = new Particle();
|
|
104
|
-
emitter.emit(p);
|
|
105
|
-
|
|
106
|
-
expect(p.vx).toBe(50); // 100 + (-50)
|
|
107
|
-
|
|
108
|
-
mockRandom.mockRestore();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("should randomize lifetime within range", () => {
|
|
112
|
-
const emitter = new ParticleEmitter({
|
|
113
|
-
lifetime: { min: 1, max: 3 },
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const mockRandom = vi.spyOn(Math, "random");
|
|
117
|
-
mockRandom.mockReturnValue(0.5);
|
|
118
|
-
|
|
119
|
-
const p = new Particle();
|
|
120
|
-
emitter.emit(p);
|
|
121
|
-
|
|
122
|
-
expect(p.lifetime).toBe(2); // 1 + 0.5 * (3 - 1)
|
|
123
|
-
|
|
124
|
-
mockRandom.mockRestore();
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("should randomize size within range", () => {
|
|
128
|
-
const emitter = new ParticleEmitter({
|
|
129
|
-
size: { min: 2, max: 10 },
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const mockRandom = vi.spyOn(Math, "random");
|
|
133
|
-
mockRandom.mockReturnValue(0.25);
|
|
134
|
-
|
|
135
|
-
const p = new Particle();
|
|
136
|
-
emitter.emit(p);
|
|
137
|
-
|
|
138
|
-
expect(p.size).toBe(4); // 2 + 0.25 * (10 - 2)
|
|
139
|
-
|
|
140
|
-
mockRandom.mockRestore();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("update", () => {
|
|
145
|
-
it("should return 0 when inactive", () => {
|
|
146
|
-
const emitter = new ParticleEmitter({ rate: 100, active: false });
|
|
147
|
-
|
|
148
|
-
expect(emitter.update(1)).toBe(0);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("should return 0 when rate is 0", () => {
|
|
152
|
-
const emitter = new ParticleEmitter({ rate: 0 });
|
|
153
|
-
|
|
154
|
-
expect(emitter.update(1)).toBe(0);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("should emit particles based on rate", () => {
|
|
158
|
-
const emitter = new ParticleEmitter({ rate: 10 }); // 10 particles/second
|
|
159
|
-
|
|
160
|
-
// 0.2 seconds should spawn 2 particles
|
|
161
|
-
const count = emitter.update(0.2);
|
|
162
|
-
|
|
163
|
-
expect(count).toBe(2);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("should accumulate time between frames", () => {
|
|
167
|
-
const emitter = new ParticleEmitter({ rate: 10 }); // interval = 0.1s
|
|
168
|
-
|
|
169
|
-
// First frame: 0.05s (not enough for a particle)
|
|
170
|
-
expect(emitter.update(0.05)).toBe(0);
|
|
171
|
-
|
|
172
|
-
// Second frame: 0.05s more (total 0.1s = 1 particle)
|
|
173
|
-
expect(emitter.update(0.05)).toBe(1);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("should handle high frame rates correctly", () => {
|
|
177
|
-
const emitter = new ParticleEmitter({ rate: 60 });
|
|
178
|
-
|
|
179
|
-
// One frame at 60fps = ~0.0167s
|
|
180
|
-
// At 60 particles/second, interval = 0.0167s, so 1 particle per frame
|
|
181
|
-
let total = 0;
|
|
182
|
-
for (let i = 0; i < 60; i++) {
|
|
183
|
-
total += emitter.update(1 / 60);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
expect(total).toBe(60);
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
describe("reset", () => {
|
|
191
|
-
it("should reset the emission timer", () => {
|
|
192
|
-
const emitter = new ParticleEmitter({ rate: 10 });
|
|
193
|
-
|
|
194
|
-
// Accumulate some time
|
|
195
|
-
emitter.update(0.05);
|
|
196
|
-
expect(emitter._timer).toBeGreaterThan(0);
|
|
197
|
-
|
|
198
|
-
// Reset
|
|
199
|
-
emitter.reset();
|
|
200
|
-
|
|
201
|
-
expect(emitter._timer).toBe(0);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
});
|