Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added function to randomize permutation table at runtime #22

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ env
*.egg
*.egg-info
.coverage
/PKG-INFO
6 changes: 6 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
3 changes: 2 additions & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion _noise.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
28 changes: 15 additions & 13 deletions _perlin.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -291,6 +291,7 @@ static struct PyModuleDef moduledef = {
PyObject *
PyInit__perlin(void)
{
PERM = &DEFAULTPERM;
return PyModule_Create(&moduledef);
}

Expand All @@ -299,6 +300,7 @@ PyInit__perlin(void)
void
init_perlin(void)
{
PERM = &DEFAULTPERM;
Py_InitModule3("_perlin", perlin_functions, module_doc);
}

Expand Down
63 changes: 52 additions & 11 deletions _simplex.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Python.h"
#include <math.h>
#include <float.h>
#include <stdlib.h>
#include "_noise.h"

// 2D simplex skew factors
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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) "
Expand Down Expand Up @@ -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}
};

Expand All @@ -428,6 +467,7 @@ static struct PyModuleDef moduledef = {
PyObject *
PyInit__simplex(void)
{
PERM = &DEFAULTPERM;
return PyModule_Create(&moduledef);
}

Expand All @@ -436,6 +476,7 @@ PyInit__simplex(void)
void
init_simplex(void)
{
PERM = &DEFAULTPERM;
Py_InitModule3("_simplex", simplex_functions, module_doc);
}

Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down