@autumnsgrove/gossamer 0.0.1 → 0.1.1
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/animation.d.ts +80 -0
- package/dist/animation.d.ts.map +1 -0
- package/dist/characters.d.ts +49 -0
- package/dist/characters.d.ts.map +1 -0
- package/dist/colors.d.ts +312 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/index.d.ts +39 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1826 -2
- package/dist/index.js.map +1 -1
- package/dist/patterns.d.ts +217 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/renderer.d.ts +140 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/style.css +124 -0
- package/dist/svelte/GossamerBorder.svelte.d.ts +1 -0
- package/dist/svelte/GossamerClouds.svelte.d.ts +1 -0
- package/dist/svelte/GossamerImage.svelte.d.ts +1 -0
- package/dist/svelte/GossamerOverlay.svelte.d.ts +1 -0
- package/dist/svelte/GossamerText.svelte.d.ts +1 -0
- package/dist/svelte/index.d.ts +20 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +3648 -0
- package/dist/svelte/index.js.map +1 -0
- package/dist/svelte/presets.d.ts +38 -0
- package/dist/svelte/presets.d.ts.map +1 -0
- package/dist/utils/canvas.d.ts +73 -0
- package/dist/utils/canvas.d.ts.map +1 -0
- package/dist/utils/image.d.ts +74 -0
- package/dist/utils/image.d.ts.map +1 -0
- package/dist/utils/performance.d.ts +86 -0
- package/dist/utils/performance.d.ts.map +1 -0
- package/package.json +34 -7
- package/src/animation.test.ts +254 -0
- package/src/animation.ts +243 -0
- package/src/characters.test.ts +148 -0
- package/src/characters.ts +219 -0
- package/src/colors.ts +234 -0
- package/src/index.test.ts +115 -0
- package/src/index.ts +164 -11
- package/src/patterns.test.ts +273 -0
- package/src/patterns.ts +760 -0
- package/src/renderer.ts +470 -0
- package/src/svelte/GossamerBorder.svelte +326 -0
- package/src/svelte/GossamerClouds.svelte +269 -0
- package/src/svelte/GossamerImage.svelte +266 -0
- package/src/svelte/GossamerOverlay.svelte +232 -0
- package/src/svelte/GossamerText.svelte +239 -0
- package/src/svelte/index.ts +75 -0
- package/src/svelte/presets.ts +174 -0
- package/src/utils/canvas.ts +210 -0
- package/src/utils/image.ts +275 -0
- package/src/utils/performance.ts +282 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for pattern generators
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import {
|
|
6
|
+
perlinNoise2D,
|
|
7
|
+
fbmNoise,
|
|
8
|
+
wavePattern,
|
|
9
|
+
ripplePattern,
|
|
10
|
+
staticNoise,
|
|
11
|
+
seededNoise2D,
|
|
12
|
+
generateBrightnessGrid,
|
|
13
|
+
gridToImageData,
|
|
14
|
+
DEFAULT_PATTERN_CONFIG,
|
|
15
|
+
} from './patterns';
|
|
16
|
+
|
|
17
|
+
describe('perlinNoise2D', () => {
|
|
18
|
+
it('should return value between -1 and 1', () => {
|
|
19
|
+
for (let i = 0; i < 100; i++) {
|
|
20
|
+
const x = Math.random() * 100;
|
|
21
|
+
const y = Math.random() * 100;
|
|
22
|
+
const value = perlinNoise2D(x, y);
|
|
23
|
+
expect(value).toBeGreaterThanOrEqual(-1);
|
|
24
|
+
expect(value).toBeLessThanOrEqual(1);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should be deterministic (same input = same output)', () => {
|
|
29
|
+
const val1 = perlinNoise2D(5.5, 3.2);
|
|
30
|
+
const val2 = perlinNoise2D(5.5, 3.2);
|
|
31
|
+
expect(val1).toBe(val2);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should vary with position', () => {
|
|
35
|
+
// Use non-integer coordinates (Perlin returns 0 at integer coords)
|
|
36
|
+
const val1 = perlinNoise2D(0.5, 0.5);
|
|
37
|
+
const val2 = perlinNoise2D(10.5, 10.5);
|
|
38
|
+
expect(val1).not.toBe(val2);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should produce smooth transitions', () => {
|
|
42
|
+
const val1 = perlinNoise2D(1.0, 1.0);
|
|
43
|
+
const val2 = perlinNoise2D(1.01, 1.01);
|
|
44
|
+
const diff = Math.abs(val1 - val2);
|
|
45
|
+
expect(diff).toBeLessThan(0.1);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('fbmNoise', () => {
|
|
50
|
+
it('should return value between -1 and 1', () => {
|
|
51
|
+
for (let i = 0; i < 50; i++) {
|
|
52
|
+
const x = Math.random() * 100;
|
|
53
|
+
const y = Math.random() * 100;
|
|
54
|
+
const value = fbmNoise(x, y);
|
|
55
|
+
expect(value).toBeGreaterThanOrEqual(-1);
|
|
56
|
+
expect(value).toBeLessThanOrEqual(1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should be deterministic', () => {
|
|
61
|
+
const val1 = fbmNoise(3.3, 7.7);
|
|
62
|
+
const val2 = fbmNoise(3.3, 7.7);
|
|
63
|
+
expect(val1).toBe(val2);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should accept octaves parameter', () => {
|
|
67
|
+
// Use non-integer coordinates (noise returns 0 at integer coords)
|
|
68
|
+
const val1 = fbmNoise(5.5, 5.5, 2);
|
|
69
|
+
const val2 = fbmNoise(5.5, 5.5, 8);
|
|
70
|
+
// Different octaves should produce different results
|
|
71
|
+
expect(val1).not.toBe(val2);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should accept persistence parameter', () => {
|
|
75
|
+
// Use non-integer coordinates (noise returns 0 at integer coords)
|
|
76
|
+
const val1 = fbmNoise(5.5, 5.5, 4, 0.3);
|
|
77
|
+
const val2 = fbmNoise(5.5, 5.5, 4, 0.7);
|
|
78
|
+
expect(val1).not.toBe(val2);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('wavePattern', () => {
|
|
83
|
+
it('should return value between -1 and 1', () => {
|
|
84
|
+
for (let t = 0; t < 10; t++) {
|
|
85
|
+
const value = wavePattern(50, 50, t);
|
|
86
|
+
expect(value).toBeGreaterThanOrEqual(-1);
|
|
87
|
+
expect(value).toBeLessThanOrEqual(1);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should vary with time', () => {
|
|
92
|
+
const val1 = wavePattern(10, 10, 0);
|
|
93
|
+
const val2 = wavePattern(10, 10, 5);
|
|
94
|
+
expect(val1).not.toBe(val2);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should use custom config', () => {
|
|
98
|
+
const config = { frequency: 0.1, amplitude: 0.5, speed: 1.0 };
|
|
99
|
+
const value = wavePattern(10, 10, 1, config);
|
|
100
|
+
expect(Math.abs(value)).toBeLessThanOrEqual(0.5);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('ripplePattern', () => {
|
|
105
|
+
it('should return value between -1 and 1', () => {
|
|
106
|
+
for (let i = 0; i < 50; i++) {
|
|
107
|
+
const value = ripplePattern(
|
|
108
|
+
Math.random() * 100,
|
|
109
|
+
Math.random() * 100,
|
|
110
|
+
50,
|
|
111
|
+
50,
|
|
112
|
+
Math.random() * 10
|
|
113
|
+
);
|
|
114
|
+
expect(value).toBeGreaterThanOrEqual(-1);
|
|
115
|
+
expect(value).toBeLessThanOrEqual(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should vary with distance from center', () => {
|
|
120
|
+
const centerVal = ripplePattern(50, 50, 50, 50, 0);
|
|
121
|
+
const edgeVal = ripplePattern(100, 50, 50, 50, 0);
|
|
122
|
+
expect(centerVal).not.toBe(edgeVal);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should animate over time', () => {
|
|
126
|
+
const val1 = ripplePattern(60, 60, 50, 50, 0);
|
|
127
|
+
const val2 = ripplePattern(60, 60, 50, 50, 1);
|
|
128
|
+
expect(val1).not.toBe(val2);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('staticNoise', () => {
|
|
133
|
+
it('should return value between 0 and 1', () => {
|
|
134
|
+
for (let i = 0; i < 100; i++) {
|
|
135
|
+
const value = staticNoise();
|
|
136
|
+
expect(value).toBeGreaterThanOrEqual(0);
|
|
137
|
+
expect(value).toBeLessThanOrEqual(1);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should be deterministic with seed', () => {
|
|
142
|
+
const val1 = staticNoise(12345);
|
|
143
|
+
const val2 = staticNoise(12345);
|
|
144
|
+
expect(val1).toBe(val2);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should vary with different seeds', () => {
|
|
148
|
+
const val1 = staticNoise(1);
|
|
149
|
+
const val2 = staticNoise(2);
|
|
150
|
+
expect(val1).not.toBe(val2);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('seededNoise2D', () => {
|
|
155
|
+
it('should return value between 0 and 1', () => {
|
|
156
|
+
for (let i = 0; i < 100; i++) {
|
|
157
|
+
const value = seededNoise2D(Math.random() * 100, Math.random() * 100);
|
|
158
|
+
expect(value).toBeGreaterThanOrEqual(0);
|
|
159
|
+
expect(value).toBeLessThanOrEqual(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should be deterministic', () => {
|
|
164
|
+
const val1 = seededNoise2D(5, 10, 42);
|
|
165
|
+
const val2 = seededNoise2D(5, 10, 42);
|
|
166
|
+
expect(val1).toBe(val2);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should vary with coordinates', () => {
|
|
170
|
+
const val1 = seededNoise2D(0, 0);
|
|
171
|
+
const val2 = seededNoise2D(10, 10);
|
|
172
|
+
expect(val1).not.toBe(val2);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should vary with seed', () => {
|
|
176
|
+
const val1 = seededNoise2D(5, 5, 0);
|
|
177
|
+
const val2 = seededNoise2D(5, 5, 100);
|
|
178
|
+
expect(val1).not.toBe(val2);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('DEFAULT_PATTERN_CONFIG', () => {
|
|
183
|
+
it('should have expected default values', () => {
|
|
184
|
+
expect(DEFAULT_PATTERN_CONFIG.frequency).toBe(0.05);
|
|
185
|
+
expect(DEFAULT_PATTERN_CONFIG.amplitude).toBe(1.0);
|
|
186
|
+
expect(DEFAULT_PATTERN_CONFIG.speed).toBe(0.5);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('generateBrightnessGrid', () => {
|
|
191
|
+
it('should generate grid with correct dimensions', () => {
|
|
192
|
+
const grid = generateBrightnessGrid(10, 8, 'perlin');
|
|
193
|
+
expect(grid.length).toBe(8); // rows
|
|
194
|
+
expect(grid[0].length).toBe(10); // cols
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should generate brightness values between 0 and 255', () => {
|
|
198
|
+
const grid = generateBrightnessGrid(20, 15, 'perlin');
|
|
199
|
+
for (const row of grid) {
|
|
200
|
+
for (const value of row) {
|
|
201
|
+
expect(value).toBeGreaterThanOrEqual(0);
|
|
202
|
+
expect(value).toBeLessThanOrEqual(255);
|
|
203
|
+
expect(Number.isInteger(value)).toBe(true);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should support all pattern types', () => {
|
|
209
|
+
const patterns: Array<'perlin' | 'waves' | 'static' | 'ripple' | 'fbm'> = [
|
|
210
|
+
'perlin',
|
|
211
|
+
'waves',
|
|
212
|
+
'static',
|
|
213
|
+
'ripple',
|
|
214
|
+
'fbm',
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
for (const pattern of patterns) {
|
|
218
|
+
const grid = generateBrightnessGrid(5, 5, pattern);
|
|
219
|
+
expect(grid.length).toBe(5);
|
|
220
|
+
expect(grid[0].length).toBe(5);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should use time parameter for animation', () => {
|
|
225
|
+
const grid1 = generateBrightnessGrid(10, 10, 'perlin', 0);
|
|
226
|
+
const grid2 = generateBrightnessGrid(10, 10, 'perlin', 100);
|
|
227
|
+
|
|
228
|
+
// At least some values should differ
|
|
229
|
+
let hasDifference = false;
|
|
230
|
+
for (let r = 0; r < 10 && !hasDifference; r++) {
|
|
231
|
+
for (let c = 0; c < 10 && !hasDifference; c++) {
|
|
232
|
+
if (grid1[r][c] !== grid2[r][c]) {
|
|
233
|
+
hasDifference = true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
expect(hasDifference).toBe(true);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Note: gridToImageData tests require browser environment with canvas support
|
|
242
|
+
// These tests are skipped in Node.js/jsdom as ImageData is not available
|
|
243
|
+
describe.skip('gridToImageData (browser only)', () => {
|
|
244
|
+
it('should create ImageData with correct dimensions', () => {
|
|
245
|
+
const grid = [
|
|
246
|
+
[100, 150],
|
|
247
|
+
[200, 50],
|
|
248
|
+
];
|
|
249
|
+
const imageData = gridToImageData(grid, 8, 12);
|
|
250
|
+
|
|
251
|
+
expect(imageData.width).toBe(16); // 2 cols * 8
|
|
252
|
+
expect(imageData.height).toBe(24); // 2 rows * 12
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should fill cells with brightness values', () => {
|
|
256
|
+
const grid = [[128]]; // Single cell
|
|
257
|
+
const imageData = gridToImageData(grid, 2, 2);
|
|
258
|
+
|
|
259
|
+
// Check first pixel (should be brightness 128)
|
|
260
|
+
expect(imageData.data[0]).toBe(128); // R
|
|
261
|
+
expect(imageData.data[1]).toBe(128); // G
|
|
262
|
+
expect(imageData.data[2]).toBe(128); // B
|
|
263
|
+
expect(imageData.data[3]).toBe(255); // A (full opacity)
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should handle empty grid', () => {
|
|
267
|
+
const grid: number[][] = [];
|
|
268
|
+
const imageData = gridToImageData(grid, 8, 12);
|
|
269
|
+
|
|
270
|
+
expect(imageData.width).toBe(0);
|
|
271
|
+
expect(imageData.height).toBe(0);
|
|
272
|
+
});
|
|
273
|
+
});
|