From 6c55a8b964ce64b02583da45e6faaf8b588fc37b Mon Sep 17 00:00:00 2001 From: Chris Lexmond Date: Mon, 3 Oct 2022 11:27:54 -0400 Subject: [PATCH] Added octave noise method --- contracts/cairo_rand_64x61/simplex.cairo | 35 +++++++++++++-- contracts/cairo_rand_64x61/simplex_mock.cairo | 6 +++ package-lock.json | 4 +- package.json | 3 +- pyproject.toml | 2 +- requirements.txt | 2 +- test/simplex.spec.js | 45 +++++++++++++++++++ 7 files changed, 88 insertions(+), 9 deletions(-) diff --git a/contracts/cairo_rand_64x61/simplex.cairo b/contracts/cairo_rand_64x61/simplex.cairo index ab6c303..b3a07e6 100644 --- a/contracts/cairo_rand_64x61/simplex.cairo +++ b/contracts/cairo_rand_64x61/simplex.cairo @@ -7,6 +7,7 @@ from cairo_math_64x61.vec64x61 import Vec64x61 namespace Simplex { // Primary method to calculate Simplex 3D noise at a 3D point + // @param v A 64.61 fixed point 3d vector func noise3{range_check_ptr}(v: (felt, felt, felt)) -> felt { alloc_locals; @@ -251,9 +252,21 @@ namespace Simplex { return Math64x61.mul(res_sum, 42 * Math64x61.ONE); } - // Returns the average noise value given a percentile (from 0 to 1) for a single - // octave of simplex noise scaled to a range from 0 to 1 - // Arg and return are both in 64.61 + // Returns multiple octaves of noise with a persistence dropoff + // @param v A 64.61 fixed point 3d vector + // @param octaves A simple felt indicating the number of iterations to add together + // @param persistence A 64.61 fixed point value used to decrease (or increase) the impact of each iteration + func noise3_octaves{range_check_ptr}(v: (felt, felt, felt), octaves: felt, persistence: felt) -> felt { + let noise = _noise3_octaves_loop{persistence = persistence, v = v}( + noise = 0, octaves = octaves, scale = Math64x61.ONE + ); + + return noise; + } + + // Returns the average noise value given a percentile for a single octave of simplex noise scaled + // to a range from 0 to 1 + // @param percentile A 64.61 fixed point value indicating the percentile (between 0 and 1) func noise3_at_percentile{range_check_ptr}(percentile: felt) -> felt { alloc_locals; let upper_half = is_le(1152921504606846976, percentile); // 0.5 @@ -274,6 +287,20 @@ namespace Simplex { return whole_noise_val + partial_noise_val; } + + func _noise3_octaves_loop{range_check_ptr, persistence: felt, v: (felt, felt, felt)}( + noise: felt, octaves: felt, scale: felt + ) -> felt { + if (octaves == 0) { + return noise; + } + + let resized_point = Vec64x61.div(v, scale); + let current_noise = noise3(resized_point); + let scaled_noise = Math64x61.mul(current_noise, scale); + let new_scale = Math64x61.mul(scale, persistence); + return _noise3_octaves_loop(noise = noise + scaled_noise, octaves = octaves - 1, scale = new_scale); + } } // Calculate x mod 289 @@ -392,4 +419,4 @@ func _simplex_dist_data() -> (data: felt*) { dw 1124809191307870208; dw 1137757040236560384; dw 1152921504606846976; -} \ No newline at end of file +} diff --git a/contracts/cairo_rand_64x61/simplex_mock.cairo b/contracts/cairo_rand_64x61/simplex_mock.cairo index a6b89a1..4facdb9 100644 --- a/contracts/cairo_rand_64x61/simplex_mock.cairo +++ b/contracts/cairo_rand_64x61/simplex_mock.cairo @@ -13,3 +13,9 @@ func noise3_at_percentile_test{range_check_ptr}(percentile: felt) -> (res: felt) let res = Simplex.noise3_at_percentile(percentile); return (res = res); } + +@view +func noise3_octaves_test{range_check_ptr}(v: (felt, felt, felt), octaves: felt, persistence: felt) -> (res: felt) { + let res = Simplex.noise3_octaves(v, octaves, persistence); + return (res = res); +} diff --git a/package-lock.json b/package-lock.json index 0620cad..01ac3fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@infuenceth/cairo-rand", - "version": "2.1.0", + "version": "2.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@infuenceth/cairo-rand", - "version": "2.1.0", + "version": "2.2.0", "license": "MIT", "devDependencies": { "@shardlabs/starknet-hardhat-plugin": "^0.6.6", diff --git a/package.json b/package.json index fecbf27..03700ec 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "@infuenceth/cairo-rand", - "version": "2.1.0", + "version": "2.2.0", "description": "Pseudorandom and procedural generation library using 64.61 fixed point", "repository": { "type": "git", "url": "git+https://github.com/influenceth/cairo-rand-64x61.git" }, "scripts": { + "compile": "npx hardhat starknet-compile ./contracts/cairo_rand_64x61/*.cairo", "release": "python3.9 -m twine upload dist/*", "build": "python3.9 -m build", "postinstall": "pip install -r requirements.txt", diff --git a/pyproject.toml b/pyproject.toml index 7eb9652..879d81c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cairo_rand_64x61" -version = "2.1.0" +version = "2.2.0" authors = [ { name="Unstoppable Games, Inc.", email="info@unstoppablegames.io" }, ] diff --git a/requirements.txt b/requirements.txt index 8030582..210bfa1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ build==0.8.0 cairo-lang==0.10.0 -cairo-math-64x61==2.0.0 +cairo-math-64x61==2.1.0 starknet-devnet==0.3.1 twine==4.0.1 \ No newline at end of file diff --git a/test/simplex.spec.js b/test/simplex.spec.js index 6b3fce3..b5dfc25 100644 --- a/test/simplex.spec.js +++ b/test/simplex.spec.js @@ -46,4 +46,49 @@ describe('simplex', function () { expect(Number(from64x61(res).toFixed(5))).to.equal(expected[i]); } }); + + it('should return octave noise with one octave matching ', async () => { + const argsList = [ + [ 0.0, 0.0, 0.0 ], + [ 0.5, -1.23, 1.63 ], + [ -1.94, -1.25, -1.63 ], + [ -9.99, 8.25, 6.98 ], + [ -0.005, 12.578, -2.87 ] + ]; + + const expected = [ -0.4122, 0.6335, 0.21512, -0.72603, -0.50797 ]; + + for (const [ i, args ] of argsList.entries()) { + const v = args.map((a) => to64x61(a)); + const { res } = await contract.call('noise3_octaves_test', { v, octaves: 1, persistence: to64x61(1) }); + expect(Number(from64x61(res).toFixed(5))).to.equal(expected[i]); + } + }); + + it('should return octave noise with multiple octaves', async () => { + const argsList = [ + { v: [ 0.5, -1.23, 1.63 ], octaves: 3, persistence: 0.5 }, + { v: [ -1.94, -1.25, -1.63 ], octaves: 2, persistence: 0.33 }, + { v: [ -9.99, 8.25, 6.98 ], octaves: 4, persistence: 0.25 } + ]; + + for (const [ i, args ] of argsList.entries()) { + let expectedNoise = 0; + + for (let i = 0; i < args.octaves; i++) { + const v = args.v.map((a) => to64x61(a / Math.pow(args.persistence, i))); + const { res } = await contract.call('noise3_test', { v }); + const currentNoise = from64x61(res) * Math.pow(args.persistence, i); + expectedNoise += currentNoise; + } + + const { res } = await contract.call('noise3_octaves_test', { + v: args.v.map((a) => to64x61(a)), + octaves: args.octaves, + persistence: to64x61(args.persistence) + }); + + expect(Number(from64x61(res).toFixed(5))).to.equal(Number(expectedNoise.toFixed(5))); + } + }); });