@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/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
+ }
@@ -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
+ }