@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/dist/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @arvarus/perlin-noise
|
|
4
|
+
* Perlin noise implementation in TypeScript
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.PerlinNoise = exports.getGradientAt = exports.generateGradientGrid = void 0;
|
|
8
|
+
const grid_1 = require("./grid");
|
|
9
|
+
const scalar_1 = require("./scalar");
|
|
10
|
+
const interpolation_1 = require("./interpolation");
|
|
11
|
+
var grid_2 = require("./grid");
|
|
12
|
+
Object.defineProperty(exports, "generateGradientGrid", { enumerable: true, get: function () { return grid_2.generateGradientGrid; } });
|
|
13
|
+
Object.defineProperty(exports, "getGradientAt", { enumerable: true, get: function () { return grid_2.getGradientAt; } });
|
|
14
|
+
/**
|
|
15
|
+
* PerlinNoise class for generating Perlin noise values
|
|
16
|
+
*/
|
|
17
|
+
class PerlinNoise {
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new PerlinNoise instance
|
|
20
|
+
*
|
|
21
|
+
* @param options - Configuration options (seed and grid size)
|
|
22
|
+
*/
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.seed = options?.seed ?? Math.floor(Math.random() * 1000000);
|
|
25
|
+
this.gridSize = options?.gridSize ?? [64, 64, 64];
|
|
26
|
+
this.dimension = this.gridSize.length;
|
|
27
|
+
if (this.gridSize.length < 1 || this.gridSize.length > 10) {
|
|
28
|
+
throw new Error(`Grid size array length must be between 1 and 10, got ${this.gridSize.length}`);
|
|
29
|
+
}
|
|
30
|
+
this.grid = (0, grid_1.generateGradientGrid)(this.dimension, this.gridSize, this.seed);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate noise value at given coordinates
|
|
34
|
+
* Supports noise generation for any dimension (1 to 10)
|
|
35
|
+
*
|
|
36
|
+
* @param coordinates - Array of coordinates, length must match grid dimension
|
|
37
|
+
* @returns Noise value in the range approximately [-1, 1]
|
|
38
|
+
*/
|
|
39
|
+
noise(coordinates) {
|
|
40
|
+
if (coordinates.length !== this.dimension) {
|
|
41
|
+
throw new Error(`Coordinates array length (${coordinates.length}) must match grid dimension (${this.dimension})`);
|
|
42
|
+
}
|
|
43
|
+
const point = coordinates;
|
|
44
|
+
const wrappedPoint = point.map((coord, i) => {
|
|
45
|
+
const size = this.gridSize[i];
|
|
46
|
+
const wrapped = ((coord % size) + size) % size;
|
|
47
|
+
return wrapped;
|
|
48
|
+
});
|
|
49
|
+
const scalarValues = (0, scalar_1.calculateScalarValues)(this.grid, wrappedPoint);
|
|
50
|
+
const noiseValue = (0, interpolation_1.interpolateScalarValues)(scalarValues, wrappedPoint);
|
|
51
|
+
return noiseValue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.PerlinNoise = PerlinNoise;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interpolation for Perlin noise
|
|
3
|
+
* Interpolates between the 2^n scalar products calculated at the vertices of the cell
|
|
4
|
+
* containing point P. This ensures that the noise function returns 0 at the grid vertices.
|
|
5
|
+
*
|
|
6
|
+
* The interpolation uses a function whose first derivative (and possibly
|
|
7
|
+
* the second derivative) is zero at the 2^n grid nodes. This has the effect that
|
|
8
|
+
* the gradient of the resulting noise function at each grid node coincides
|
|
9
|
+
* with the precomputed random gradient vector.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Classic smoothstep function used for interpolation
|
|
13
|
+
* This function ensures that the first derivative is zero at the endpoints (0 and 1)
|
|
14
|
+
*
|
|
15
|
+
* @param t - Interpolation value between 0 and 1
|
|
16
|
+
* @returns Interpolated value between 0 and 1
|
|
17
|
+
*/
|
|
18
|
+
export declare function smoothstep(t: number): number;
|
|
19
|
+
/**
|
|
20
|
+
* Calculates the fractional coordinates of the point within its cell
|
|
21
|
+
* Fractional coordinates are in the interval [0, 1] for each dimension
|
|
22
|
+
*
|
|
23
|
+
* @param point - Point P as an array of coordinates
|
|
24
|
+
* @returns Array of fractional coordinates within the cell
|
|
25
|
+
*/
|
|
26
|
+
export declare function calculateFractionalCoordinates(point: number[]): number[];
|
|
27
|
+
/**
|
|
28
|
+
* Interpolates between two values using smoothstep
|
|
29
|
+
* For n=1, this function interpolates between a0 at node 0 and a1 at node 1
|
|
30
|
+
* Formula: f(x) = a0 + smoothstep(x) * (a1 - a0) for 0 ≤ x ≤ 1
|
|
31
|
+
*
|
|
32
|
+
* @param a0 - Value at node 0
|
|
33
|
+
* @param a1 - Value at node 1
|
|
34
|
+
* @param t - Interpolation parameter between 0 and 1
|
|
35
|
+
* @returns Interpolated value
|
|
36
|
+
*/
|
|
37
|
+
export declare function interpolate1D(a0: number, a1: number, t: number): number;
|
|
38
|
+
/**
|
|
39
|
+
* Interpolates between the 2^n scalar products calculated at the vertices of the cell
|
|
40
|
+
* containing point P. This function ensures that the noise returns 0 at the grid
|
|
41
|
+
* vertices thanks to the use of smoothstep.
|
|
42
|
+
*
|
|
43
|
+
* @param scalarValues - Array of scalar values at the cell vertices (2^n values)
|
|
44
|
+
* @param point - Point P as an array of coordinates
|
|
45
|
+
* @returns Interpolated noise value
|
|
46
|
+
*/
|
|
47
|
+
export declare function interpolateScalarValues(scalarValues: number[], point: number[]): number;
|
|
48
|
+
/**
|
|
49
|
+
* Scales an interpolated value to be in the interval [-1.0, 1.0]
|
|
50
|
+
* Noise functions used in computer graphics typically produce
|
|
51
|
+
* values within this interval.
|
|
52
|
+
*
|
|
53
|
+
* @param value - Value to scale
|
|
54
|
+
* @param scaleFactor - Scale factor (default 1.0, no scaling)
|
|
55
|
+
* @returns Scaled value
|
|
56
|
+
*/
|
|
57
|
+
export declare function scaleNoiseValue(value: number, scaleFactor?: number): number;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Interpolation for Perlin noise
|
|
4
|
+
* Interpolates between the 2^n scalar products calculated at the vertices of the cell
|
|
5
|
+
* containing point P. This ensures that the noise function returns 0 at the grid vertices.
|
|
6
|
+
*
|
|
7
|
+
* The interpolation uses a function whose first derivative (and possibly
|
|
8
|
+
* the second derivative) is zero at the 2^n grid nodes. This has the effect that
|
|
9
|
+
* the gradient of the resulting noise function at each grid node coincides
|
|
10
|
+
* with the precomputed random gradient vector.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.smoothstep = smoothstep;
|
|
14
|
+
exports.calculateFractionalCoordinates = calculateFractionalCoordinates;
|
|
15
|
+
exports.interpolate1D = interpolate1D;
|
|
16
|
+
exports.interpolateScalarValues = interpolateScalarValues;
|
|
17
|
+
exports.scaleNoiseValue = scaleNoiseValue;
|
|
18
|
+
/**
|
|
19
|
+
* Classic smoothstep function used for interpolation
|
|
20
|
+
* This function ensures that the first derivative is zero at the endpoints (0 and 1)
|
|
21
|
+
*
|
|
22
|
+
* @param t - Interpolation value between 0 and 1
|
|
23
|
+
* @returns Interpolated value between 0 and 1
|
|
24
|
+
*/
|
|
25
|
+
function smoothstep(t) {
|
|
26
|
+
const clamped = Math.max(0, Math.min(1, t));
|
|
27
|
+
// Smoothstep formula: t² * (3 - 2t)
|
|
28
|
+
return clamped * clamped * (3 - 2 * clamped);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Calculates the fractional coordinates of the point within its cell
|
|
32
|
+
* Fractional coordinates are in the interval [0, 1] for each dimension
|
|
33
|
+
*
|
|
34
|
+
* @param point - Point P as an array of coordinates
|
|
35
|
+
* @returns Array of fractional coordinates within the cell
|
|
36
|
+
*/
|
|
37
|
+
function calculateFractionalCoordinates(point) {
|
|
38
|
+
return point.map(coord => {
|
|
39
|
+
const cellCoord = Math.floor(coord);
|
|
40
|
+
return coord - cellCoord;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Interpolates between two values using smoothstep
|
|
45
|
+
* For n=1, this function interpolates between a0 at node 0 and a1 at node 1
|
|
46
|
+
* Formula: f(x) = a0 + smoothstep(x) * (a1 - a0) for 0 ≤ x ≤ 1
|
|
47
|
+
*
|
|
48
|
+
* @param a0 - Value at node 0
|
|
49
|
+
* @param a1 - Value at node 1
|
|
50
|
+
* @param t - Interpolation parameter between 0 and 1
|
|
51
|
+
* @returns Interpolated value
|
|
52
|
+
*/
|
|
53
|
+
function interpolate1D(a0, a1, t) {
|
|
54
|
+
const s = smoothstep(t);
|
|
55
|
+
return a0 + s * (a1 - a0);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Recursively interpolates between scalar values for a given dimension
|
|
59
|
+
* This function progressively reduces the number of values by interpolating
|
|
60
|
+
* dimension by dimension
|
|
61
|
+
*
|
|
62
|
+
* @param scalarValues - Array of scalar values (2^n values)
|
|
63
|
+
* @param fractionalCoords - Fractional coordinates of the point within the cell
|
|
64
|
+
* @param dimension - Current dimension to interpolate (starts at 0)
|
|
65
|
+
* @returns Final interpolated value
|
|
66
|
+
*/
|
|
67
|
+
function interpolateRecursive(scalarValues, fractionalCoords, dimension = 0) {
|
|
68
|
+
if (scalarValues.length === 1) {
|
|
69
|
+
return scalarValues[0];
|
|
70
|
+
}
|
|
71
|
+
if (dimension >= fractionalCoords.length) {
|
|
72
|
+
if (scalarValues.length === 2) {
|
|
73
|
+
return interpolate1D(scalarValues[0], scalarValues[1], fractionalCoords[dimension - 1] || 0);
|
|
74
|
+
}
|
|
75
|
+
// If we have more than 2 values, take the average (should not happen normally)
|
|
76
|
+
return scalarValues.reduce((sum, val) => sum + val, 0) / scalarValues.length;
|
|
77
|
+
}
|
|
78
|
+
const t = fractionalCoords[dimension];
|
|
79
|
+
const numValues = scalarValues.length;
|
|
80
|
+
const valuesPerHalf = numValues / 2;
|
|
81
|
+
const lowerValues = scalarValues.slice(0, valuesPerHalf);
|
|
82
|
+
const upperValues = scalarValues.slice(valuesPerHalf);
|
|
83
|
+
const lowerInterpolated = interpolateRecursive(lowerValues, fractionalCoords, dimension + 1);
|
|
84
|
+
const upperInterpolated = interpolateRecursive(upperValues, fractionalCoords, dimension + 1);
|
|
85
|
+
return interpolate1D(lowerInterpolated, upperInterpolated, t);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Interpolates between the 2^n scalar products calculated at the vertices of the cell
|
|
89
|
+
* containing point P. This function ensures that the noise returns 0 at the grid
|
|
90
|
+
* vertices thanks to the use of smoothstep.
|
|
91
|
+
*
|
|
92
|
+
* @param scalarValues - Array of scalar values at the cell vertices (2^n values)
|
|
93
|
+
* @param point - Point P as an array of coordinates
|
|
94
|
+
* @returns Interpolated noise value
|
|
95
|
+
*/
|
|
96
|
+
function interpolateScalarValues(scalarValues, point) {
|
|
97
|
+
// Check that the number of scalar values corresponds to 2^n where n is the dimension
|
|
98
|
+
const dimension = point.length;
|
|
99
|
+
const expectedCount = Math.pow(2, dimension);
|
|
100
|
+
if (scalarValues.length !== expectedCount) {
|
|
101
|
+
throw new Error(`The number of scalar values (${scalarValues.length}) must be equal to 2^${dimension} = ${expectedCount}`);
|
|
102
|
+
}
|
|
103
|
+
const fractionalCoords = calculateFractionalCoordinates(point);
|
|
104
|
+
return interpolateRecursive(scalarValues, fractionalCoords, 0);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Scales an interpolated value to be in the interval [-1.0, 1.0]
|
|
108
|
+
* Noise functions used in computer graphics typically produce
|
|
109
|
+
* values within this interval.
|
|
110
|
+
*
|
|
111
|
+
* @param value - Value to scale
|
|
112
|
+
* @param scaleFactor - Scale factor (default 1.0, no scaling)
|
|
113
|
+
* @returns Scaled value
|
|
114
|
+
*/
|
|
115
|
+
function scaleNoiseValue(value, scaleFactor = 1.0) {
|
|
116
|
+
return value * scaleFactor;
|
|
117
|
+
}
|
package/dist/scalar.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scalar calculation for Perlin noise
|
|
3
|
+
* Calculates noise value at a given point by computing dot products
|
|
4
|
+
* between gradient vectors and distance vectors
|
|
5
|
+
*/
|
|
6
|
+
import { GradientVector } from './grid';
|
|
7
|
+
/**
|
|
8
|
+
* Calculates the dot product values for all vertices of the cell containing point P
|
|
9
|
+
*
|
|
10
|
+
* @param grid - The gradient grid
|
|
11
|
+
* @param point - The point P as an array of coordinates
|
|
12
|
+
* @returns An array of dot product values, one for each vertex of the cell
|
|
13
|
+
*/
|
|
14
|
+
export declare function calculateScalarValues(grid: Map<string, GradientVector>, point: number[]): number[];
|
package/dist/scalar.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Scalar calculation for Perlin noise
|
|
4
|
+
* Calculates noise value at a given point by computing dot products
|
|
5
|
+
* between gradient vectors and distance vectors
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.calculateScalarValues = calculateScalarValues;
|
|
9
|
+
const grid_1 = require("./grid");
|
|
10
|
+
/**
|
|
11
|
+
* Calculates the dot product between two vectors
|
|
12
|
+
* For 1D case, treats the scalar as a vector of length 1
|
|
13
|
+
*/
|
|
14
|
+
function dotProduct(gradient, distanceVector) {
|
|
15
|
+
if (typeof gradient === 'number') {
|
|
16
|
+
// 1D case: gradient is a scalar, distanceVector has one element
|
|
17
|
+
return gradient * distanceVector[0];
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
// Multi-dimensional case: both are vectors
|
|
21
|
+
if (gradient.length !== distanceVector.length) {
|
|
22
|
+
throw new Error(`Gradient vector length (${gradient.length}) must match distance vector length (${distanceVector.length})`);
|
|
23
|
+
}
|
|
24
|
+
return gradient.reduce((sum, val, i) => sum + val * distanceVector[i], 0);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generates all vertices of a hypercube cell in n dimensions
|
|
29
|
+
* Each vertex is represented as an array of coordinates
|
|
30
|
+
* For a cell at [x0, x1, ..., xn], vertices are at [x0 or x0+1, x1 or x1+1, ..., xn or xn+1]
|
|
31
|
+
*/
|
|
32
|
+
function generateCellVertices(cellCoordinates) {
|
|
33
|
+
const dimension = cellCoordinates.length;
|
|
34
|
+
const vertices = [];
|
|
35
|
+
// Generate all 2^dimension combinations
|
|
36
|
+
const numVertices = Math.pow(2, dimension);
|
|
37
|
+
for (let i = 0; i < numVertices; i++) {
|
|
38
|
+
const vertex = [];
|
|
39
|
+
for (let d = 0; d < dimension; d++) {
|
|
40
|
+
// Use bit manipulation to determine if this dimension should be +1
|
|
41
|
+
const bit = (i >> d) & 1;
|
|
42
|
+
vertex.push(cellCoordinates[d] + bit);
|
|
43
|
+
}
|
|
44
|
+
vertices.push(vertex);
|
|
45
|
+
}
|
|
46
|
+
return vertices;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Calculates the distance vector from point P to a vertex
|
|
50
|
+
*/
|
|
51
|
+
function calculateDistanceVector(point, vertex) {
|
|
52
|
+
if (point.length !== vertex.length) {
|
|
53
|
+
throw new Error(`Point dimension (${point.length}) must match vertex dimension (${vertex.length})`);
|
|
54
|
+
}
|
|
55
|
+
return point.map((coord, i) => coord - vertex[i]);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Determines which cell of the grid contains the given point P
|
|
59
|
+
* Returns the cell coordinates (floor of each coordinate)
|
|
60
|
+
*/
|
|
61
|
+
function findCell(point) {
|
|
62
|
+
return point.map(coord => Math.floor(coord));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Calculates the dot product values for all vertices of the cell containing point P
|
|
66
|
+
*
|
|
67
|
+
* @param grid - The gradient grid
|
|
68
|
+
* @param point - The point P as an array of coordinates
|
|
69
|
+
* @returns An array of dot product values, one for each vertex of the cell
|
|
70
|
+
*/
|
|
71
|
+
function calculateScalarValues(grid, point) {
|
|
72
|
+
const cellCoordinates = findCell(point);
|
|
73
|
+
const vertices = generateCellVertices(cellCoordinates);
|
|
74
|
+
const scalarValues = [];
|
|
75
|
+
for (const vertex of vertices) {
|
|
76
|
+
const distanceVector = calculateDistanceVector(point, vertex);
|
|
77
|
+
const gradient = (0, grid_1.getGradientAt)(grid, vertex);
|
|
78
|
+
if (gradient === undefined) {
|
|
79
|
+
throw new Error(`Gradient not found at vertex ${vertex.join(',')}. Point may be outside grid bounds.`);
|
|
80
|
+
}
|
|
81
|
+
const dotProductValue = dotProduct(gradient, distanceVector);
|
|
82
|
+
scalarValues.push(dotProductValue);
|
|
83
|
+
}
|
|
84
|
+
return scalarValues;
|
|
85
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arvarus/perlin-noise",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Perlin noise implementation in TypeScript",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"watch": "tsc --watch",
|
|
10
|
+
"clean": "rm -rf dist",
|
|
11
|
+
"lint": "eslint src --ext .ts",
|
|
12
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"perlin",
|
|
20
|
+
"noise",
|
|
21
|
+
"procedural",
|
|
22
|
+
"generation"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "GPL-3.0",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/jest": "^29.5.0",
|
|
28
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
29
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
30
|
+
"eslint": "^8.50.0",
|
|
31
|
+
"jest": "^29.5.0",
|
|
32
|
+
"ts-jest": "^29.1.0",
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"LICENSE",
|
|
38
|
+
"README.md"
|
|
39
|
+
]
|
|
40
|
+
}
|