diff --git a/.gitignore b/.gitignore index 4deb427..eb8af1e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ env *.egg *.egg-info .coverage +/PKG-INFO diff --git a/CHANGES.txt b/CHANGES.txt index b2520f2..8368069 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,9 @@ +1.2.3 -- August 8, 2018 + + - Added randomize function to get a new permuation table + + - Fixed lacunarity not settable for 4d simplex noise + 1.2.2 -- March 26, 2015 - AppVeyor support for Windows builds (Thanks to Federico Tomassetti) diff --git a/__init__.py b/__init__.py index d790a66..662b27e 100644 --- a/__init__.py +++ b/__init__.py @@ -7,10 +7,11 @@ Copyright (c) 2008, Casey Duncan (casey dot duncan at gmail dot com) """ -__version__ = "1.2.1" +__version__ = "1.2.3" from . import _perlin, _simplex +randomize = _simplex.randomize snoise2 = _simplex.noise2 snoise3 = _simplex.noise3 snoise4 = _simplex.noise4 diff --git a/_noise.h b/_noise.h index 95ee6c9..2697d6b 100644 --- a/_noise.h +++ b/_noise.h @@ -26,7 +26,10 @@ const float GRAD4[][4] = { // At the possible cost of unaligned access, we use char instead of // int here to try to ensure that this table fits in L1 cache -const unsigned char PERM[] = { +#define DEFAULTPERIOD 256 +int period = DEFAULTPERIOD; +unsigned char *PERM; +unsigned char DEFAULTPERM[] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, diff --git a/_perlin.c b/_perlin.c index a535d71..e80924e 100644 --- a/_perlin.c +++ b/_perlin.c @@ -28,8 +28,8 @@ noise1(float x, const int repeat, const int base) float fx; int i = (int)floorf(x) % repeat; int ii = (i + 1) % repeat; - i = (i & 255) + base; - ii = (ii & 255) + base; + i = (i % period) + base; + ii = (ii % period) + base; x -= floorf(x); fx = x*x*x * (x * (x * 6 - 15) + 10); @@ -92,10 +92,10 @@ noise2(float x, float y, const float repeatx, const float repeaty, const int bas int j = (int)floorf(fmodf(y, repeaty)); int ii = (int)fmodf(i + 1, repeatx); int jj = (int)fmodf(j + 1, repeaty); - i = (i & 255) + base; - j = (j & 255) + base; - ii = (ii & 255) + base; - jj = (jj & 255) + base; + i = (i % period) + base; + j = (j % period) + base; + ii = (ii % period) + base; + jj = (jj % period) + base; x -= floorf(x); y -= floorf(y); fx = x*x*x * (x * (x * 6 - 15) + 10); @@ -173,12 +173,12 @@ noise3(float x, float y, float z, const int repeatx, const int repeaty, const in int ii = (int)fmodf(i + 1, repeatx); int jj = (int)fmodf(j + 1, repeaty); int kk = (int)fmodf(k + 1, repeatz); - i = (i & 255) + base; - j = (j & 255) + base; - k = (k & 255) + base; - ii = (ii & 255) + base; - jj = (jj & 255) + base; - kk = (kk & 255) + base; + i = (i % period) + base; + j = (j % period) + base; + k = (k % period) + base; + ii = (ii % period) + base; + jj = (jj % period) + base; + kk = (kk % period) + base; x -= floorf(x); y -= floorf(y); z -= floorf(z); fx = x*x*x * (x * (x * 6 - 15) + 10); @@ -254,7 +254,7 @@ static PyMethodDef perlin_functions[] = { "noise2(x, y, octaves=1, persistence=0.5, lacunarity=2.0, repeatx=1024, repeaty=1024, base=0.0)\n\n" "2 dimensional perlin improved noise function (see noise3 for more info)"}, {"noise3", (PyCFunction) py_noise3, METH_VARARGS | METH_KEYWORDS, - "noise3(x, y, z, octaves=1, persistence=0.5, lacunarity=2.0, " + "noise3(x, y, z, octaves=1, persistence=0.5, lacunarity=2.0" "repeatx=1024, repeaty=1024, repeatz=1024, base=0.0)\n\n" "return perlin \"improved\" noise value for specified coordinate\n\n" "octaves -- specifies the number of passes for generating fBm noise,\n" @@ -291,6 +291,7 @@ static struct PyModuleDef moduledef = { PyObject * PyInit__perlin(void) { + PERM = &DEFAULTPERM; return PyModule_Create(&moduledef); } @@ -299,6 +300,7 @@ PyInit__perlin(void) void init_perlin(void) { + PERM = &DEFAULTPERM; Py_InitModule3("_perlin", perlin_functions, module_doc); } diff --git a/_simplex.c b/_simplex.c index 0ec88d5..abf516e 100644 --- a/_simplex.c +++ b/_simplex.c @@ -5,6 +5,7 @@ #include "Python.h" #include #include +#include #include "_noise.h" // 2D simplex skew factors @@ -35,8 +36,8 @@ noise2(float x, float y) xx[1] = xx[0] - i1 + G2; yy[1] = yy[0] - j1 + G2; - I = (int) i & 255; - J = (int) j & 255; + I = (int) i % period; + J = (int) j % period; g[0] = PERM[I + PERM[J]] % 12; g[1] = PERM[I + i1 + PERM[J + j1]] % 12; g[2] = PERM[I + 1 + PERM[J + 1]] % 12; @@ -105,9 +106,9 @@ noise3(float x, float y, float z) pos[1][c] = pos[0][c] - o1[c] + G3; } - I = (int) i & 255; - J = (int) j & 255; - K = (int) k & 255; + I = (int) i % period; + J = (int) j % period; + K = (int) k % period; g[0] = PERM[I + PERM[J + PERM[K]]] % 12; g[1] = PERM[I + o1[0] + PERM[J + o1[1] + PERM[o1[2] + K]]] % 12; g[2] = PERM[I + o2[0] + PERM[J + o2[1] + PERM[o2[2] + K]]] % 12; @@ -195,10 +196,10 @@ noise4(float x, float y, float z, float w) { float z4 = z0 - 1.0f + 4.0f*G4; float w4 = w0 - 1.0f + 4.0f*G4; - int I = (int)i & 255; - int J = (int)j & 255; - int K = (int)k & 255; - int L = (int)l & 255; + int I = (int)i % period; + int J = (int)j % period; + int K = (int)k % period; + int L = (int)l % period; int gi0 = PERM[I + PERM[J + PERM[K + PERM[L]]]] & 0x1f; int gi1 = PERM[I + i1 + PERM[J + j1 + PERM[K + k1 + PERM[L + l1]]]] & 0x1f; int gi2 = PERM[I + i2 + PERM[J + j2 + PERM[K + k2 + PERM[L + l2]]]] & 0x1f; @@ -353,12 +354,12 @@ py_noise4(PyObject *self, PyObject *args, PyObject *kwargs) float x, y, z, w; int octaves = 1; float persistence = 0.5f; - float lacunarity = 2.0f; + float lacunarity = 2.0f; static char *kwlist[] = {"x", "y", "z", "w", "octaves", "persistence", "lacunarity", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ffff|iff:snoise4", kwlist, - &x, &y, &z, &w, &octaves, &persistence)) + &x, &y, &z, &w, &octaves, &persistence, &lacunarity)) return NULL; if (octaves == 1) { @@ -373,6 +374,41 @@ py_noise4(PyObject *self, PyObject *args, PyObject *kwargs) } } +static PyObject * +randomize(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int pperiod=DEFAULTPERIOD; + int pseed=0; + + static char *kwlist[] = {"period", "seed", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii:randomize", kwlist, + &pperiod, &pseed)) + return NULL; + + period=pperiod; + + if(PERM != &DEFAULTPERM) + free(PERM); + PERM = malloc(period*2*sizeof(unsigned char)); + if(PERM==NULL) + { + period=DEFAULTPERIOD; + PERM = &DEFAULTPERM; + PyErr_SetString(PyExc_ValueError, "Failed to allocate memory"); + return NULL; + } + + srand(pseed); + + for(int i=0; i < period; i++){ + PERM[i]=(unsigned char)(rand() / (RAND_MAX / 255 + 1)); + PERM[i+period]=PERM[i]; + } + + Py_RETURN_TRUE; +} + static PyMethodDef simplex_functions[] = { {"noise2", (PyCFunction)py_noise2, METH_VARARGS | METH_KEYWORDS, "noise2(x, y, octaves=1, persistence=0.5, lacunarity=2.0, repeatx=None, repeaty=None, base=0.0) " @@ -406,6 +442,9 @@ static PyMethodDef simplex_functions[] = { "is halved). Note the amplitude of the first pass is always 1.0.\n\n" "lacunarity -- specifies the frequency of each successive octave relative\n" "to the one below it, similar to persistence. Defaults to 2.0."}, + {"randomize", (PyCFunction)randomize, METH_VARARGS | METH_KEYWORDS, + "randomize(period, seed) creates a new random permutation with period " + "and specified random seed"}, {NULL} }; @@ -428,6 +467,7 @@ static struct PyModuleDef moduledef = { PyObject * PyInit__simplex(void) { + PERM = &DEFAULTPERM; return PyModule_Create(&moduledef); } @@ -436,6 +476,7 @@ PyInit__simplex(void) void init_simplex(void) { + PERM = &DEFAULTPERM; Py_InitModule3("_simplex", simplex_functions, module_doc); } diff --git a/setup.py b/setup.py index 74d08c2..80bf5ee 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='noise', - version='1.2.2', + version='1.2.3', description='Perlin noise for Python', long_description='''\ Perlin noise is ubiquitous in modern CGI. Used for procedural texturing, @@ -30,6 +30,8 @@ motion) noise by combining multiple octaves of Perlin noise. Shader functions for convenient generation of turbulent noise are also included. +- 1.2.3 Adds ability to randomize permutation table to the C implementations + - 1.2.2 AppVeyor support for Windows builds (Thanks to Federico Tomassetti) - 1.2.1 Fixes MSVC compatibility (Thanks to Christoph Gohlke) diff --git a/test.py b/test.py index dfdb9c6..9616936 100644 --- a/test.py +++ b/test.py @@ -76,6 +76,9 @@ def test_perlin_3d_base(self): class SimplexTestCase(unittest.TestCase): + def test_randomize(self): + from noise import randomize + self.assertTrue(randomize(4096,23490)) def test_simplex_2d_range(self): from noise import snoise2