@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 +18 -0
- package/README.md +132 -0
- package/dist/__tests__/grid.test.d.ts +1 -0
- package/dist/__tests__/grid.test.js +270 -0
- package/dist/__tests__/index.test.d.ts +1 -0
- package/dist/__tests__/index.test.js +240 -0
- package/dist/__tests__/interpolation.test.d.ts +1 -0
- package/dist/__tests__/interpolation.test.js +253 -0
- package/dist/__tests__/scalar.test.d.ts +1 -0
- package/dist/__tests__/scalar.test.js +210 -0
- package/dist/grid.d.ts +23 -0
- package/dist/grid.js +107 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +54 -0
- package/dist/interpolation.d.ts +57 -0
- package/dist/interpolation.js +117 -0
- package/dist/scalar.d.ts +14 -0
- package/dist/scalar.js +85 -0
- package/package.json +40 -0
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 {};
|