@arvarus/perlin-noise 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/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2024 @arvarus/perlin-noise contributors
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # @arvarus/perlin-noise
2
+
3
+ Perlin noise implementation in TypeScript.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @arvarus/perlin-noise
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Example
14
+
15
+ ```typescript
16
+ import { PerlinNoise } from '@arvarus/perlin-noise';
17
+
18
+ // Create a Perlin noise instance
19
+ const noise = new PerlinNoise({
20
+ seed: 42, // Optional: seed for reproducible results
21
+ gridSize: [64, 64] // Optional: grid size for each dimension (default: [64, 64, 64])
22
+ });
23
+
24
+ // Generate noise value at coordinates
25
+ const value = noise.noise([10.5, 20.3]);
26
+ console.log(value); // Output: a value approximately in range [-1, 1]
27
+ ```
28
+
29
+ ### 1D Noise
30
+
31
+ ```typescript
32
+ const noise = new PerlinNoise({
33
+ seed: 123,
34
+ gridSize: [100] // 1D grid
35
+ });
36
+
37
+ // Generate 1D noise along a line
38
+ for (let x = 0; x < 10; x += 0.1) {
39
+ const value = noise.noise([x]);
40
+ console.log(`x: ${x}, noise: ${value}`);
41
+ }
42
+ ```
43
+
44
+ ### Using a Fixed Seed for Reproducible Results
45
+
46
+ ```typescript
47
+ // Same seed produces same noise pattern
48
+ const noise1 = new PerlinNoise({ seed: 42, gridSize: [64, 64] });
49
+ const noise2 = new PerlinNoise({ seed: 42, gridSize: [64, 64] });
50
+
51
+ const value1 = noise1.noise([5.0, 5.0]);
52
+ const value2 = noise2.noise([5.0, 5.0]);
53
+
54
+ console.log(value1 === value2); // true - same seed produces same result
55
+ ```
56
+
57
+ ## API Reference
58
+
59
+ ### `PerlinNoise`
60
+
61
+ Main class for generating Perlin noise values. Supports 1 to 10 dimensions.
62
+
63
+ #### Constructor
64
+
65
+ ```typescript
66
+ new PerlinNoise(options?: PerlinNoiseOptions)
67
+ ```
68
+
69
+ Creates a new PerlinNoise instance.
70
+
71
+ **Parameters:**
72
+ - `options` (optional): Configuration object
73
+ - `seed?: number` - Seed for random number generation. If not provided, a random seed is used.
74
+ - `gridSize?: number[]` - Grid size for each dimension. Default: `[64, 64, 64]`. The grid wraps around, so smaller values can be used for repeating patterns. Must be an array of length 1 to 10.
75
+
76
+ **Example:**
77
+ ```typescript
78
+ // Default 3D noise
79
+ const noise1 = new PerlinNoise();
80
+
81
+ // 2D noise with fixed seed
82
+ const noise2 = new PerlinNoise({ seed: 42, gridSize: [64, 64] });
83
+
84
+ // 1D noise
85
+ const noise3 = new PerlinNoise({ gridSize: [100] });
86
+ ```
87
+
88
+ #### Methods
89
+
90
+ ##### `noise(coordinates: number[]): number`
91
+
92
+ Generates a noise value at the given coordinates.
93
+
94
+ **Parameters:**
95
+ - `coordinates: number[]` - Array of coordinates. The length must match the grid dimension specified in the constructor.
96
+
97
+ **Returns:**
98
+ - `number` - Noise value approximately in the range `[-1, 1]`
99
+
100
+ **Example:**
101
+ ```typescript
102
+ const noise = new PerlinNoise({ gridSize: [64, 64] });
103
+
104
+ // 2D coordinates for 2D grid
105
+ const value = noise.noise([10.5, 20.3]);
106
+
107
+ // Coordinates wrap around the grid bounds
108
+ const wrapped = noise.noise([100.0, 200.0]); // Wraps to [36, 8] for 64x64 grid
109
+ ```
110
+
111
+ **Throws:**
112
+ - `Error` - If the coordinates array length doesn't match the grid dimension
113
+
114
+ ### `PerlinNoiseOptions`
115
+
116
+ Configuration interface for PerlinNoise constructor.
117
+
118
+ ```typescript
119
+ interface PerlinNoiseOptions {
120
+ seed?: number;
121
+ gridSize?: number[];
122
+ }
123
+ ```
124
+
125
+ **Properties:**
126
+ - `seed?: number` - Seed for random number generation. Same seed produces the same noise pattern.
127
+ - `gridSize?: number[]` - Grid size for each dimension. Default: `[64, 64, 64]`. Must be an array of length 1 to 10. The grid wraps around, allowing for seamless repeating patterns.
128
+
129
+ ## License
130
+
131
+ GPL-3.0
132
+
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const grid_1 = require("../grid");
4
+ describe('generateGradientGrid', () => {
5
+ describe('1D grid', () => {
6
+ it('should generate correct number of grid points', () => {
7
+ const grid = (0, grid_1.generateGradientGrid)(1, [5], 123);
8
+ expect(grid.size).toBe(6); // size + 1
9
+ });
10
+ it('should generate scalars between -1 and 1', () => {
11
+ const grid = (0, grid_1.generateGradientGrid)(1, [10], 456);
12
+ for (const gradient of grid.values()) {
13
+ expect(typeof gradient).toBe('number');
14
+ const scalar = gradient;
15
+ expect(scalar).toBeGreaterThanOrEqual(-1);
16
+ expect(scalar).toBeLessThanOrEqual(1);
17
+ }
18
+ });
19
+ it('should generate same grid with same seed', () => {
20
+ const grid1 = (0, grid_1.generateGradientGrid)(1, [5], 789);
21
+ const grid2 = (0, grid_1.generateGradientGrid)(1, [5], 789);
22
+ expect(grid1.size).toBe(grid2.size);
23
+ for (const [key, value] of grid1.entries()) {
24
+ expect(grid2.get(key)).toBe(value);
25
+ }
26
+ });
27
+ it('should generate different grid with different seed', () => {
28
+ const grid1 = (0, grid_1.generateGradientGrid)(1, [5], 100);
29
+ const grid2 = (0, grid_1.generateGradientGrid)(1, [5], 200);
30
+ // At least some values should be different
31
+ let hasDifference = false;
32
+ for (const [key, value] of grid1.entries()) {
33
+ if (grid2.get(key) !== value) {
34
+ hasDifference = true;
35
+ break;
36
+ }
37
+ }
38
+ expect(hasDifference).toBe(true);
39
+ });
40
+ it('should have correct keys for 1D grid', () => {
41
+ const grid = (0, grid_1.generateGradientGrid)(1, [3], 123);
42
+ expect(grid.has('0')).toBe(true);
43
+ expect(grid.has('1')).toBe(true);
44
+ expect(grid.has('2')).toBe(true);
45
+ expect(grid.has('3')).toBe(true);
46
+ expect(grid.has('4')).toBe(false);
47
+ });
48
+ });
49
+ describe('2D grid', () => {
50
+ it('should generate correct number of grid points', () => {
51
+ const grid = (0, grid_1.generateGradientGrid)(2, [3, 3], 123);
52
+ expect(grid.size).toBe(16); // (3 + 1) * (3 + 1) = 16
53
+ });
54
+ it('should generate unit-length 2D vectors', () => {
55
+ const grid = (0, grid_1.generateGradientGrid)(2, [5, 5], 456);
56
+ for (const gradient of grid.values()) {
57
+ expect(Array.isArray(gradient)).toBe(true);
58
+ const vector = gradient;
59
+ expect(vector.length).toBe(2);
60
+ // Check unit length (within floating point precision)
61
+ const magnitude = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
62
+ expect(magnitude).toBeCloseTo(1, 10);
63
+ }
64
+ });
65
+ it('should generate same grid with same seed', () => {
66
+ const grid1 = (0, grid_1.generateGradientGrid)(2, [3, 3], 789);
67
+ const grid2 = (0, grid_1.generateGradientGrid)(2, [3, 3], 789);
68
+ expect(grid1.size).toBe(grid2.size);
69
+ for (const [key, value] of grid1.entries()) {
70
+ const v1 = value;
71
+ const v2 = grid2.get(key);
72
+ expect(v2).toBeDefined();
73
+ expect(v1[0]).toBeCloseTo(v2[0], 10);
74
+ expect(v1[1]).toBeCloseTo(v2[1], 10);
75
+ }
76
+ });
77
+ it('should have correct keys for 2D grid', () => {
78
+ const grid = (0, grid_1.generateGradientGrid)(2, [2, 2], 123);
79
+ expect(grid.has('0,0')).toBe(true);
80
+ expect(grid.has('0,1')).toBe(true);
81
+ expect(grid.has('1,0')).toBe(true);
82
+ expect(grid.has('2,2')).toBe(true);
83
+ expect(grid.has('3,3')).toBe(false); // size is 2, so max is 2
84
+ });
85
+ it('should support different sizes per dimension', () => {
86
+ const grid = (0, grid_1.generateGradientGrid)(2, [2, 3], 123);
87
+ // First dimension: 2 + 1 = 3 points (0, 1, 2)
88
+ // Second dimension: 3 + 1 = 4 points (0, 1, 2, 3)
89
+ // Total: 3 * 4 = 12 points
90
+ expect(grid.size).toBe(12);
91
+ expect(grid.has('0,0')).toBe(true);
92
+ expect(grid.has('2,3')).toBe(true);
93
+ expect(grid.has('3,3')).toBe(false); // First dimension max is 2
94
+ expect(grid.has('2,4')).toBe(false); // Second dimension max is 3
95
+ });
96
+ });
97
+ describe('3D grid', () => {
98
+ it('should generate correct number of grid points', () => {
99
+ const grid = (0, grid_1.generateGradientGrid)(3, [2, 2, 2], 123);
100
+ expect(grid.size).toBe(27); // (2 + 1) ^ 3 = 27
101
+ });
102
+ it('should generate unit-length 3D vectors', () => {
103
+ const grid = (0, grid_1.generateGradientGrid)(3, [3, 3, 3], 456);
104
+ for (const gradient of grid.values()) {
105
+ expect(Array.isArray(gradient)).toBe(true);
106
+ const vector = gradient;
107
+ expect(vector.length).toBe(3);
108
+ // Check unit length
109
+ const magnitude = Math.sqrt(vector[0] * vector[0] +
110
+ vector[1] * vector[1] +
111
+ vector[2] * vector[2]);
112
+ expect(magnitude).toBeCloseTo(1, 10);
113
+ }
114
+ });
115
+ it('should generate same grid with same seed', () => {
116
+ const grid1 = (0, grid_1.generateGradientGrid)(3, [2, 2, 2], 789);
117
+ const grid2 = (0, grid_1.generateGradientGrid)(3, [2, 2, 2], 789);
118
+ expect(grid1.size).toBe(grid2.size);
119
+ for (const [key, value] of grid1.entries()) {
120
+ const v1 = value;
121
+ const v2 = grid2.get(key);
122
+ expect(v2).toBeDefined();
123
+ expect(v1[0]).toBeCloseTo(v2[0], 10);
124
+ expect(v1[1]).toBeCloseTo(v2[1], 10);
125
+ expect(v1[2]).toBeCloseTo(v2[2], 10);
126
+ }
127
+ });
128
+ it('should have correct keys for 3D grid', () => {
129
+ const grid = (0, grid_1.generateGradientGrid)(3, [1, 1, 1], 123);
130
+ expect(grid.has('0,0,0')).toBe(true);
131
+ expect(grid.has('1,1,1')).toBe(true);
132
+ expect(grid.has('2,2,2')).toBe(false); // size is 1, so max is 1
133
+ });
134
+ });
135
+ describe('4D grid', () => {
136
+ it('should generate correct number of grid points', () => {
137
+ const grid = (0, grid_1.generateGradientGrid)(4, [2, 2, 2, 2], 123);
138
+ expect(grid.size).toBe(81); // (2 + 1) ^ 4 = 81
139
+ });
140
+ it('should generate unit-length 4D vectors', () => {
141
+ const grid = (0, grid_1.generateGradientGrid)(4, [2, 2, 2, 2], 456);
142
+ for (const gradient of grid.values()) {
143
+ expect(Array.isArray(gradient)).toBe(true);
144
+ const vector = gradient;
145
+ expect(vector.length).toBe(4);
146
+ // Check unit length
147
+ const magnitude = Math.sqrt(vector[0] * vector[0] +
148
+ vector[1] * vector[1] +
149
+ vector[2] * vector[2] +
150
+ vector[3] * vector[3]);
151
+ expect(magnitude).toBeCloseTo(1, 10);
152
+ }
153
+ });
154
+ it('should generate same grid with same seed', () => {
155
+ const grid1 = (0, grid_1.generateGradientGrid)(4, [1, 1, 1, 1], 789);
156
+ const grid2 = (0, grid_1.generateGradientGrid)(4, [1, 1, 1, 1], 789);
157
+ expect(grid1.size).toBe(grid2.size);
158
+ for (const [key, value] of grid1.entries()) {
159
+ const v1 = value;
160
+ const v2 = grid2.get(key);
161
+ expect(v2).toBeDefined();
162
+ expect(v1[0]).toBeCloseTo(v2[0], 10);
163
+ expect(v1[1]).toBeCloseTo(v2[1], 10);
164
+ expect(v1[2]).toBeCloseTo(v2[2], 10);
165
+ expect(v1[3]).toBeCloseTo(v2[3], 10);
166
+ }
167
+ });
168
+ it('should have correct keys for 4D grid', () => {
169
+ const grid = (0, grid_1.generateGradientGrid)(4, [1, 1, 1, 1], 123);
170
+ expect(grid.has('0,0,0,0')).toBe(true);
171
+ expect(grid.has('1,1,1,1')).toBe(true);
172
+ expect(grid.has('2,2,2,2')).toBe(false); // size is 1, so max is 1
173
+ });
174
+ });
175
+ describe('higher dimensions', () => {
176
+ it('should support 5D grids', () => {
177
+ const grid = (0, grid_1.generateGradientGrid)(5, [1, 1, 1, 1, 1], 123);
178
+ expect(grid.size).toBe(32); // (1 + 1) ^ 5 = 32
179
+ expect(grid.has('0,0,0,0,0')).toBe(true);
180
+ expect(grid.has('1,1,1,1,1')).toBe(true);
181
+ expect(grid.has('2,2,2,2,2')).toBe(false);
182
+ // Check that gradients are arrays of length 5
183
+ for (const gradient of grid.values()) {
184
+ expect(Array.isArray(gradient)).toBe(true);
185
+ const vector = gradient;
186
+ expect(vector.length).toBe(5);
187
+ // Check unit length
188
+ const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
189
+ expect(magnitude).toBeCloseTo(1, 10);
190
+ }
191
+ });
192
+ it('should support 6D grids', () => {
193
+ const grid = (0, grid_1.generateGradientGrid)(6, [1, 1, 1, 1, 1, 1], 123);
194
+ expect(grid.size).toBe(64); // (1 + 1) ^ 6 = 64
195
+ expect(grid.has('0,0,0,0,0,0')).toBe(true);
196
+ expect(grid.has('1,1,1,1,1,1')).toBe(true);
197
+ });
198
+ });
199
+ describe('edge cases', () => {
200
+ it('should handle size 0', () => {
201
+ const grid = (0, grid_1.generateGradientGrid)(1, [0], 123);
202
+ expect(grid.size).toBe(1); // 0 + 1 = 1
203
+ expect(grid.has('0')).toBe(true);
204
+ });
205
+ it('should handle size 0 for 2D', () => {
206
+ const grid = (0, grid_1.generateGradientGrid)(2, [0, 0], 123);
207
+ expect(grid.size).toBe(1); // (0 + 1) ^ 2 = 1
208
+ expect(grid.has('0,0')).toBe(true);
209
+ });
210
+ it('should use different default seeds when seed not provided', () => {
211
+ const grid1 = (0, grid_1.generateGradientGrid)(1, [5]);
212
+ const grid2 = (0, grid_1.generateGradientGrid)(1, [5]);
213
+ // They might be the same by chance, but very unlikely
214
+ // We'll just check that the function doesn't crash
215
+ expect(grid1.size).toBe(6);
216
+ expect(grid2.size).toBe(6);
217
+ });
218
+ it('should throw error when size array length does not match dimension', () => {
219
+ expect(() => (0, grid_1.generateGradientGrid)(2, [3], 123)).toThrow();
220
+ expect(() => (0, grid_1.generateGradientGrid)(2, [3, 4, 5], 123)).toThrow();
221
+ expect(() => (0, grid_1.generateGradientGrid)(3, [2, 2], 123)).toThrow();
222
+ });
223
+ it('should throw error when dimension is not a positive integer', () => {
224
+ expect(() => (0, grid_1.generateGradientGrid)(0, [], 123)).toThrow();
225
+ expect(() => (0, grid_1.generateGradientGrid)(-1, [-1], 123)).toThrow();
226
+ expect(() => (0, grid_1.generateGradientGrid)(1.5, [1], 123)).toThrow();
227
+ expect(() => (0, grid_1.generateGradientGrid)(2.0, [1, 1], 123)).not.toThrow(); // 2.0 is valid
228
+ });
229
+ });
230
+ });
231
+ describe('getGradientAt', () => {
232
+ it('should retrieve gradient from 1D grid', () => {
233
+ const grid = (0, grid_1.generateGradientGrid)(1, [5], 123);
234
+ const gradient = (0, grid_1.getGradientAt)(grid, [2]);
235
+ expect(gradient).toBeDefined();
236
+ expect(typeof gradient).toBe('number');
237
+ expect(gradient).toBe(grid.get('2'));
238
+ });
239
+ it('should retrieve gradient from 2D grid', () => {
240
+ const grid = (0, grid_1.generateGradientGrid)(2, [3, 3], 123);
241
+ const gradient = (0, grid_1.getGradientAt)(grid, [1, 2]);
242
+ expect(gradient).toBeDefined();
243
+ expect(Array.isArray(gradient)).toBe(true);
244
+ expect(gradient).toEqual(grid.get('1,2'));
245
+ });
246
+ it('should retrieve gradient from 3D grid', () => {
247
+ const grid = (0, grid_1.generateGradientGrid)(3, [2, 2, 2], 123);
248
+ const gradient = (0, grid_1.getGradientAt)(grid, [1, 0, 2]);
249
+ expect(gradient).toBeDefined();
250
+ expect(Array.isArray(gradient)).toBe(true);
251
+ expect(gradient).toEqual(grid.get('1,0,2'));
252
+ });
253
+ it('should retrieve gradient from 4D grid', () => {
254
+ const grid = (0, grid_1.generateGradientGrid)(4, [1, 1, 1, 1], 123);
255
+ const gradient = (0, grid_1.getGradientAt)(grid, [1, 1, 0, 1]);
256
+ expect(gradient).toBeDefined();
257
+ expect(Array.isArray(gradient)).toBe(true);
258
+ expect(gradient).toEqual(grid.get('1,1,0,1'));
259
+ });
260
+ it('should return undefined for non-existent coordinates', () => {
261
+ const grid = (0, grid_1.generateGradientGrid)(2, [3, 3], 123);
262
+ const gradient = (0, grid_1.getGradientAt)(grid, [10, 10]);
263
+ expect(gradient).toBeUndefined();
264
+ });
265
+ it('should return undefined for empty grid', () => {
266
+ const emptyGrid = new Map();
267
+ const gradient = (0, grid_1.getGradientAt)(emptyGrid, [0, 0]);
268
+ expect(gradient).toBeUndefined();
269
+ });
270
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("../index");
4
+ describe('PerlinNoise', () => {
5
+ describe('constructor', () => {
6
+ it('should create instance with default options', () => {
7
+ const noise = new index_1.PerlinNoise();
8
+ expect(noise).toBeInstanceOf(index_1.PerlinNoise);
9
+ });
10
+ it('should create instance with options object', () => {
11
+ const options = { seed: 456, gridSize: [10, 10, 10] };
12
+ const noise = new index_1.PerlinNoise(options);
13
+ expect(noise).toBeInstanceOf(index_1.PerlinNoise);
14
+ });
15
+ it('should use provided seed', () => {
16
+ const noise1 = new index_1.PerlinNoise({ seed: 789, gridSize: [10, 10, 10] });
17
+ const noise2 = new index_1.PerlinNoise({ seed: 789, gridSize: [10, 10, 10] });
18
+ // Same seed should produce same results
19
+ expect(noise1.noise([1.0, 1.0, 1.0])).toBeCloseTo(noise2.noise([1.0, 1.0, 1.0]), 10);
20
+ });
21
+ it('should use default grid size when not provided', () => {
22
+ const noise = new index_1.PerlinNoise({ seed: 123 });
23
+ // Should work with 3D coordinates (default dimension)
24
+ const value = noise.noise([1.0, 1.0, 1.0]);
25
+ expect(typeof value).toBe('number');
26
+ });
27
+ it('should throw error for invalid grid size length', () => {
28
+ expect(() => new index_1.PerlinNoise({ gridSize: [] })).toThrow();
29
+ expect(() => new index_1.PerlinNoise({ gridSize: Array(11).fill(10) })).toThrow();
30
+ });
31
+ });
32
+ describe('1D noise', () => {
33
+ it('should generate noise value for 1D coordinates', () => {
34
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10] });
35
+ const value = noise.noise([1.5]);
36
+ expect(typeof value).toBe('number');
37
+ expect(value).toBeGreaterThanOrEqual(-2);
38
+ expect(value).toBeLessThanOrEqual(2);
39
+ });
40
+ it('should produce consistent results for same point', () => {
41
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10] });
42
+ const value1 = noise.noise([2.3]);
43
+ const value2 = noise.noise([2.3]);
44
+ expect(value1).toBeCloseTo(value2, 10);
45
+ });
46
+ it('should produce different results for different points', () => {
47
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10] });
48
+ const value1 = noise.noise([1.0]);
49
+ const value2 = noise.noise([5.0]);
50
+ // They might be the same by chance, but very unlikely
51
+ expect(typeof value1).toBe('number');
52
+ expect(typeof value2).toBe('number');
53
+ });
54
+ it('should handle negative coordinates', () => {
55
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10] });
56
+ const value = noise.noise([-1.5]);
57
+ expect(typeof value).toBe('number');
58
+ });
59
+ it('should handle large coordinates (wrapping)', () => {
60
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10] });
61
+ const value1 = noise.noise([1.5]);
62
+ const value2 = noise.noise([11.5]); // Should wrap to 1.5
63
+ expect(value1).toBeCloseTo(value2, 10);
64
+ });
65
+ it('should handle coordinates at grid boundaries', () => {
66
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10] });
67
+ const value = noise.noise([10.0]);
68
+ expect(typeof value).toBe('number');
69
+ });
70
+ });
71
+ describe('2D noise', () => {
72
+ it('should generate noise value for 2D coordinates', () => {
73
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10] });
74
+ const value = noise.noise([1.5, 2.3]);
75
+ expect(typeof value).toBe('number');
76
+ expect(value).toBeGreaterThanOrEqual(-2);
77
+ expect(value).toBeLessThanOrEqual(2);
78
+ });
79
+ it('should produce consistent results for same point', () => {
80
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10] });
81
+ const value1 = noise.noise([2.3, 4.7]);
82
+ const value2 = noise.noise([2.3, 4.7]);
83
+ expect(value1).toBeCloseTo(value2, 10);
84
+ });
85
+ it('should produce different results for different points', () => {
86
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10] });
87
+ const value1 = noise.noise([1.0, 1.0]);
88
+ const value2 = noise.noise([5.0, 5.0]);
89
+ expect(typeof value1).toBe('number');
90
+ expect(typeof value2).toBe('number');
91
+ });
92
+ it('should handle negative coordinates', () => {
93
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10] });
94
+ const value = noise.noise([-1.5, -2.3]);
95
+ expect(typeof value).toBe('number');
96
+ });
97
+ it('should handle large coordinates (wrapping)', () => {
98
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10] });
99
+ const value1 = noise.noise([1.5, 2.3]);
100
+ const value2 = noise.noise([11.5, 12.3]); // Should wrap
101
+ expect(value1).toBeCloseTo(value2, 10);
102
+ });
103
+ it('should produce smooth transitions', () => {
104
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10] });
105
+ const value1 = noise.noise([1.0, 1.0]);
106
+ const value2 = noise.noise([1.1, 1.0]);
107
+ const value3 = noise.noise([1.2, 1.0]);
108
+ // Values should change gradually (not jump dramatically)
109
+ expect(typeof value1).toBe('number');
110
+ expect(typeof value2).toBe('number');
111
+ expect(typeof value3).toBe('number');
112
+ });
113
+ });
114
+ describe('3D noise', () => {
115
+ it('should generate noise value for 3D coordinates', () => {
116
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
117
+ const value = noise.noise([1.5, 2.3, 3.7]);
118
+ expect(typeof value).toBe('number');
119
+ expect(value).toBeGreaterThanOrEqual(-2);
120
+ expect(value).toBeLessThanOrEqual(2);
121
+ });
122
+ it('should produce consistent results for same point', () => {
123
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
124
+ const value1 = noise.noise([2.3, 4.7, 1.2]);
125
+ const value2 = noise.noise([2.3, 4.7, 1.2]);
126
+ expect(value1).toBeCloseTo(value2, 10);
127
+ });
128
+ it('should produce different results for different points', () => {
129
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
130
+ const value1 = noise.noise([1.0, 1.0, 1.0]);
131
+ const value2 = noise.noise([5.0, 5.0, 5.0]);
132
+ expect(typeof value1).toBe('number');
133
+ expect(typeof value2).toBe('number');
134
+ });
135
+ it('should handle negative coordinates', () => {
136
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
137
+ const value = noise.noise([-1.5, -2.3, -3.7]);
138
+ expect(typeof value).toBe('number');
139
+ });
140
+ it('should handle large coordinates (wrapping)', () => {
141
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
142
+ const value1 = noise.noise([1.5, 2.3, 3.7]);
143
+ const value2 = noise.noise([11.5, 12.3, 13.7]); // Should wrap
144
+ expect(value1).toBeCloseTo(value2, 10);
145
+ });
146
+ });
147
+ describe('dimension validation', () => {
148
+ it('should throw error when noise dimension does not match grid dimension', () => {
149
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10] });
150
+ expect(() => noise.noise([1.0])).toThrow(); // 1D noise on 2D grid
151
+ expect(() => noise.noise([1.0, 1.0, 1.0])).toThrow(); // 3D noise on 2D grid
152
+ });
153
+ it('should allow 1D noise on 1D grid', () => {
154
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10] });
155
+ expect(() => noise.noise([1.0])).not.toThrow();
156
+ });
157
+ it('should allow 2D noise on 2D grid', () => {
158
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10] });
159
+ expect(() => noise.noise([1.0, 1.0])).not.toThrow();
160
+ });
161
+ it('should allow 3D noise on 3D grid', () => {
162
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
163
+ expect(() => noise.noise([1.0, 1.0, 1.0])).not.toThrow();
164
+ });
165
+ });
166
+ describe('seed behavior', () => {
167
+ it('should produce different results with different seeds', () => {
168
+ const noise1 = new index_1.PerlinNoise({ seed: 100, gridSize: [10, 10, 10] });
169
+ const noise2 = new index_1.PerlinNoise({ seed: 200, gridSize: [10, 10, 10] });
170
+ const value1 = noise1.noise([1.0, 1.0, 1.0]);
171
+ const value2 = noise2.noise([1.0, 1.0, 1.0]);
172
+ // They might be the same by chance, but very unlikely
173
+ expect(typeof value1).toBe('number');
174
+ expect(typeof value2).toBe('number');
175
+ });
176
+ it('should produce same results with same seed', () => {
177
+ const noise1 = new index_1.PerlinNoise({ seed: 500, gridSize: [10, 10, 10] });
178
+ const noise2 = new index_1.PerlinNoise({ seed: 500, gridSize: [10, 10, 10] });
179
+ const value1 = noise1.noise([2.5, 3.7, 1.2]);
180
+ const value2 = noise2.noise([2.5, 3.7, 1.2]);
181
+ expect(value1).toBeCloseTo(value2, 10);
182
+ });
183
+ });
184
+ describe('edge cases', () => {
185
+ it('should handle zero coordinates', () => {
186
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
187
+ const value = noise.noise([0.0, 0.0, 0.0]);
188
+ expect(typeof value).toBe('number');
189
+ });
190
+ it('should handle very small coordinates', () => {
191
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
192
+ const value = noise.noise([0.0001, 0.0001, 0.0001]);
193
+ expect(typeof value).toBe('number');
194
+ });
195
+ it('should handle very large coordinates', () => {
196
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
197
+ const value = noise.noise([1000.5, 2000.3, 3000.7]);
198
+ expect(typeof value).toBe('number');
199
+ });
200
+ it('should handle coordinates at exact grid boundaries', () => {
201
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [10, 10, 10] });
202
+ const value = noise.noise([10.0, 10.0, 10.0]);
203
+ expect(typeof value).toBe('number');
204
+ });
205
+ it('should handle different grid sizes', () => {
206
+ const noise1 = new index_1.PerlinNoise({ seed: 123, gridSize: [5, 5, 5] });
207
+ const noise2 = new index_1.PerlinNoise({ seed: 123, gridSize: [20, 20, 20] });
208
+ const value1 = noise1.noise([1.0, 1.0, 1.0]);
209
+ const value2 = noise2.noise([1.0, 1.0, 1.0]);
210
+ // Different grid sizes with same seed should produce different results
211
+ expect(typeof value1).toBe('number');
212
+ expect(typeof value2).toBe('number');
213
+ });
214
+ });
215
+ describe('higher dimensions', () => {
216
+ it('should support 4D noise', () => {
217
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [5, 5, 5, 5] });
218
+ const value = noise.noise([1.0, 1.0, 1.0, 1.0]);
219
+ expect(typeof value).toBe('number');
220
+ expect(value).toBeGreaterThanOrEqual(-2);
221
+ expect(value).toBeLessThanOrEqual(2);
222
+ });
223
+ it('should support 5D noise', () => {
224
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [5, 5, 5, 5, 5] });
225
+ const value = noise.noise([1.0, 1.0, 1.0, 1.0, 1.0]);
226
+ expect(typeof value).toBe('number');
227
+ });
228
+ it('should support 10D noise (maximum)', () => {
229
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2] });
230
+ const value = noise.noise([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
231
+ expect(typeof value).toBe('number');
232
+ });
233
+ it('should produce consistent results for same point in higher dimensions', () => {
234
+ const noise = new index_1.PerlinNoise({ seed: 123, gridSize: [5, 5, 5, 5] });
235
+ const value1 = noise.noise([2.3, 4.7, 1.2, 3.5]);
236
+ const value2 = noise.noise([2.3, 4.7, 1.2, 3.5]);
237
+ expect(value1).toBeCloseTo(value2, 10);
238
+ });
239
+ });
240
+ });
@@ -0,0 +1 @@
1
+ export {};