From 7ed2d6537ad2d7bdb63c49cd8530f4ac9afe855d Mon Sep 17 00:00:00 2001 From: Luke Winship Date: Thu, 6 Jun 2024 23:09:21 +0100 Subject: [PATCH 1/2] implement get*NoiseValue functions Not yet fully implemented to the point of being able to merge in. will need to update documentation and update tests --- src/mod.ts | 192 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 129 insertions(+), 63 deletions(-) diff --git a/src/mod.ts b/src/mod.ts index 12f3118..31afc0e 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -28,13 +28,7 @@ export function makeCuboid( height: number, depth: number, noise3: Noise3Fn, - { - amplitude = defaultAmplitude, - frequency = defaultFrequency, - octaves = defaultOctaves, - persistence = defaultPersistence, - scale, - }: Partial = {}, + options: Options, ): number[][][] { const field: number[][][] = new Array(width); for (let x = 0; x < width; x++) { @@ -42,14 +36,7 @@ export function makeCuboid( for (let y = 0; y < height; y++) { field[x][y] = new Array(depth); for (let z = 0; z < depth; z++) { - let value = 0.0; - for (let octave = 0; octave < octaves; octave++) { - const freq = frequency * Math.pow(2, octave); - value += noise3(x * freq, y * freq, z * freq) * - (amplitude * Math.pow(persistence, octave)); - } - field[x][y][z] = value / (2 - 1 / Math.pow(2, octaves - 1)); - if (scale) field[x][y][z] = scale(field[x][y][z]); + field[x][y][z] = getCuboidNoiseValue(noise3, options, x, y, z); } } } @@ -60,30 +47,14 @@ export function makeCylinderSurface( circumference: number, height: number, noise3: Noise3Fn, - { - amplitude = defaultAmplitude, - frequency = defaultFrequency, - octaves = defaultOctaves, - persistence = defaultPersistence, - scale, - }: Partial = {}, + options: Options, ): number[][] { - const radius = circumference / TWO_PI; + const radius = getCircleRadius(circumference); const field: number[][] = new Array(circumference); for (let x = 0; x < circumference; x++) { field[x] = new Array(height); for (let y = 0; y < height; y++) { - let value = 0.0; - for (let octave = 0; octave < octaves; octave++) { - const freq = frequency * Math.pow(2, octave); - const nx = x / circumference; - const rdx = nx * TWO_PI; - const [a, b] = [radius * Math.sin(rdx), radius * Math.cos(rdx)]; - value += noise3(a * freq, b * freq, y * freq) * - (amplitude * Math.pow(persistence, octave)); - } - field[x][y] = value / (2 - 1 / Math.pow(2, octaves - 1)); - if (scale) field[x][y] = scale(field[x][y]); + field[x][y] = getCylinderSurfaceNoiseValue(noise3, options, circumference, radius, x, y); } } return field; @@ -92,23 +63,11 @@ export function makeCylinderSurface( export function makeLine( length: number, noise1: Noise1Fn, - { - amplitude = defaultAmplitude, - frequency = defaultFrequency, - octaves = defaultOctaves, - persistence = defaultPersistence, - scale, - }: Partial = {}, + options: Options, ): number[] { const field: number[] = new Array(length); for (let x = 0; x < length; x++) { - let value = 0.0; - for (let octave = 0; octave < octaves; octave++) { - const freq = frequency * Math.pow(2, octaves); - value += noise1(x * freq) * (amplitude * Math.pow(persistence, octave)); - } - field[x] = value / (2 - 1 / Math.pow(2, octaves - 1)); - if (scale) field[x] = scale(field[x]); + field[x] = getLineNoiseValue(noise1, options, x); } return field; } @@ -117,26 +76,13 @@ export function makeRectangle( width: number, height: number, noise2: Noise2Fn, - { - amplitude = defaultAmplitude, - frequency = defaultFrequency, - octaves = defaultOctaves, - persistence = defaultPersistence, - scale, - }: Partial = {}, + options: Options, ): number[][] { const field: number[][] = new Array(width); for (let x = 0; x < width; x++) { field[x] = new Array(height); for (let y = 0; y < height; y++) { - let value = 0.0; - for (let octave = 0; octave < octaves; octave++) { - const freq = frequency * Math.pow(2, octave); - value += noise2(x * freq, y * freq) * - (amplitude * Math.pow(persistence, octave)); - } - field[x][y] = value / (2 - 1 / Math.pow(2, octaves - 1)); - if (scale) field[x][y] = scale(field[x][y]); + field[x][y] = getRectangleNoiseValue(noise2, options, x, y); } } return field; @@ -176,3 +122,123 @@ export function makeSphereSurface( } return field; } + +export function fractalNoiseOptions({ + amplitude = defaultAmplitude, + frequency = defaultFrequency, + octaves = defaultOctaves, + persistence = defaultPersistence, + scale, +}: Partial = {}): Options { + return { amplitude, frequency, octaves, persistence, scale }; +} + +export function getCircleRadius( + circumference: number, +): number { + return circumference / TWO_PI; +} + +export function getCuboidNoiseValue( + noise3: Noise3Fn, + options: Options, + x: number, + y: number, + z: number, +): number { + let value = 0.0; + for (let octave = 0; octave < options.octaves; octave++) { + const freq = options.frequency * Math.pow(2, octave); + value += noise3(x * freq, y * freq, z * freq) * + (options.amplitude * Math.pow(options.persistence, octave)); + } + const result = normalizeFractalNoiseValue(options, value); + if (options.scale) return options.scale(result); + return result; +} + +export function getCylinderSurfaceNoiseValue( + noise3: Noise3Fn, + options: Options, + circumference: number, + radius: number, + x: number, + y: number, +): number { + let value = 0.0; + for (let octave = 0; octave < options.octaves; octave++) { + const freq = options.frequency * Math.pow(2, octave); + const nx = x / circumference; + const rdx = nx * TWO_PI; + const [a, b] = [radius * Math.sin(rdx), radius * Math.cos(rdx)]; + value += noise3(a * freq, b * freq, y * freq) * + (options.amplitude * Math.pow(options.persistence, octave)); + } + const result = normalizeFractalNoiseValue(options, value); + if (options.scale) return options.scale(result); + return result; +} + +export function getLineNoiseValue( + noise1: Noise1Fn, + options: Options, + x: number, +): number { + let value = 0.0; + for (let octave = 0; octave < options.octaves; octave++) { + const freq = options.frequency * Math.pow(2, octave); + value += noise1(x * freq) * (options.amplitude * Math.pow(options.persistence, octave)); + } + const result = normalizeFractalNoiseValue(options, value); + if (options.scale) return options.scale(result); + return result; +} + +export function getRectangleNoiseValue( + noise2: Noise2Fn, + options: Options, + x: number, + y: number, +): number { + let value = 0.0; + for (let octave = 0; octave < options.octaves; octave++) { + const freq = options.frequency * Math.pow(2, octave); + value += noise2(x * freq, y * freq) * (options.amplitude * Math.pow(options.persistence, octave)); + } + const result = normalizeFractalNoiseValue(options, value); + if (options.scale) return options.scale(result); + return result; +} + +export function getSphereSurfaceNoiseValue( + noise3: Noise3Fn, + options: Options, + circumference: number, + circumferenceSemi: number, + x: number, + y: number, +): number { + const [nx, ny] = [x / circumference, y / circumferenceSemi]; + const [rdx, rdy] = [nx * TWO_PI, ny * Math.PI]; + const sinY = Math.sin(rdy + Math.PI); + const a = TWO_PI * Math.sin(rdx) * sinY; + const b = TWO_PI * Math.cos(rdx) * sinY; + const d = TWO_PI * Math.cos(rdy); + let value = 0.0; + for (let octave = 0; octave < options.octaves; octave++) { + const freq = options.frequency * Math.pow(2, octave); + value += noise3(a * freq, b * freq, d * freq) * + (options.amplitude * Math.pow(options.persistence, octave)); + } + const result = normalizeFractalNoiseValue(options, value); + if (options.scale) return options.scale(result); + return result; +} + +/** + * Normalize the result so that it's within a similar range regardless of the + * number of octaves. + */ +function normalizeFractalNoiseValue(options: Options, value: number) { + return value / (2 - 1 / Math.pow(2, options.octaves - 1)); +} From 6c6dcf9f9d94bf34546f81594f9c212c74612314 Mon Sep 17 00:00:00 2001 From: Luke Winship Date: Fri, 7 Jun 2024 19:49:35 +0100 Subject: [PATCH 2/2] revert the more liberal options input in top-level functions AND use getSphereSurfaceNoiseValue --- src/mod.ts | 54 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/mod.ts b/src/mod.ts index 31afc0e..3989f04 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -28,8 +28,15 @@ export function makeCuboid( height: number, depth: number, noise3: Noise3Fn, - options: Options, + { + amplitude = defaultAmplitude, + frequency = defaultFrequency, + octaves = defaultOctaves, + persistence = defaultPersistence, + scale, + }: Partial = {}, ): number[][][] { + const options: Options = { amplitude, frequency, octaves, persistence, scale }; const field: number[][][] = new Array(width); for (let x = 0; x < width; x++) { field[x] = new Array(height); @@ -47,8 +54,15 @@ export function makeCylinderSurface( circumference: number, height: number, noise3: Noise3Fn, - options: Options, + { + amplitude = defaultAmplitude, + frequency = defaultFrequency, + octaves = defaultOctaves, + persistence = defaultPersistence, + scale, + }: Partial = {}, ): number[][] { + const options: Options = { amplitude, frequency, octaves, persistence, scale }; const radius = getCircleRadius(circumference); const field: number[][] = new Array(circumference); for (let x = 0; x < circumference; x++) { @@ -63,8 +77,15 @@ export function makeCylinderSurface( export function makeLine( length: number, noise1: Noise1Fn, - options: Options, + { + amplitude = defaultAmplitude, + frequency = defaultFrequency, + octaves = defaultOctaves, + persistence = defaultPersistence, + scale, + }: Partial = {}, ): number[] { + const options: Options = { amplitude, frequency, octaves, persistence, scale }; const field: number[] = new Array(length); for (let x = 0; x < length; x++) { field[x] = getLineNoiseValue(noise1, options, x); @@ -76,8 +97,15 @@ export function makeRectangle( width: number, height: number, noise2: Noise2Fn, - options: Options, + { + amplitude = defaultAmplitude, + frequency = defaultFrequency, + octaves = defaultOctaves, + persistence = defaultPersistence, + scale, + }: Partial = {}, ): number[][] { + const options: Options = { amplitude, frequency, octaves, persistence, scale }; const field: number[][] = new Array(width); for (let x = 0; x < width; x++) { field[x] = new Array(height); @@ -99,25 +127,13 @@ export function makeSphereSurface( scale, }: Partial = {}, ): number[][] { + const options: Options = { amplitude, frequency, octaves, persistence, scale }; + const circumferenceSemi = circumference / 2; const field: number[][] = new Array(circumference); for (let x = 0; x < circumference; x++) { - const circumferenceSemi = circumference / 2; field[x] = new Array(circumferenceSemi); for (let y = 0; y < circumferenceSemi; y++) { - const [nx, ny] = [x / circumference, y / circumferenceSemi]; - const [rdx, rdy] = [nx * TWO_PI, ny * Math.PI]; - const sinY = Math.sin(rdy + Math.PI); - const a = TWO_PI * Math.sin(rdx) * sinY; - const b = TWO_PI * Math.cos(rdx) * sinY; - const d = TWO_PI * Math.cos(rdy); - let value = 0.0; - for (let octave = 0; octave < octaves; octave++) { - const freq = frequency * Math.pow(2, octave); - value += noise3(a * freq, b * freq, d * freq) * - (amplitude * Math.pow(persistence, octave)); - } - field[x][y] = value / (2 - 1 / Math.pow(2, octaves - 1)); - if (scale) field[x][y] = scale(field[x][y]); + field[x][y] = getSphereSurfaceNoiseValue(noise3, options, circumference, circumferenceSemi, x, y); } } return field;