Created
February 22, 2021 11:00
-
-
Save tatarize/72b947ffb7a7764739de6574be3ab86d to your computer and use it in GitHub Desktop.
vnoise port of `noise` project c-code into python.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Code is derived from the C noise code: | |
# Copyright (c) 2008, Casey Duncan (casey dot duncan at gmail dot com) | |
# MIT LICENSE. | |
# This port is also, MIT LICENSE. | |
from numpy import floor, fmod | |
M_1_PI = 0.31830988618379067154 | |
GRAD3 = ( | |
(1, 1, 0), (-1, 1, 0), (1, -1, 0), (-1, -1, 0), | |
(1, 0, 1), (-1, 0, 1), (1, 0, -1), (-1, 0, -1), | |
(0, 1, 1), (0, -1, 1), (0, 1, -1), (0, -1, -1), | |
(1, 0, -1), (-1, 0, -1), (0, -1, 1), (0, 1, 1)) | |
GRAD4 = ( | |
(0, 1, 1, 1), (0, 1, 1, -1), (0, 1, -1, 1), (0, 1, -1, -1), | |
(0, -1, 1, 1), (0, -1, 1, -1), (0, -1, -1, 1), (0, -1, -1, -1), | |
(1, 0, 1, 1), (1, 0, 1, -1), (1, 0, -1, 1), (1, 0, -1, -1), | |
(-1, 0, 1, 1), (-1, 0, 1, -1), (-1, 0, -1, 1), (-1, 0, -1, -1), | |
(1, 1, 0, 1), (1, 1, 0, -1), (1, -1, 0, 1), (1, -1, 0, -1), | |
(-1, 1, 0, 1), (-1, 1, 0, -1), (-1, -1, 0, 1), (-1, -1, 0, -1), | |
(1, 1, 1, 0), (1, 1, -1, 0), (1, -1, 1, 0), (1, -1, -1, 0), | |
(-1, 1, 1, 0), (-1, 1, -1, 0), (-1, -1, 1, 0), (-1, -1, -1, 0)) | |
PERM = ( | |
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, | |
88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, | |
134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, | |
230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, | |
1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, | |
116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, | |
124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, | |
47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, | |
154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, | |
108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, | |
242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, | |
239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, | |
50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, | |
141, 128, 195, 78, 66, 215, 61, 156, 180, 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, 88, 237, 149, 56, 87, 174, 20, 125, | |
136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, | |
231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, | |
40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, | |
208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, | |
173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, | |
255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, | |
183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, | |
43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, | |
112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, | |
162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, | |
106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, | |
205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, | |
180) | |
SIMPLEX = ( | |
(0, 1, 2, 3), (0, 1, 3, 2), (0, 0, 0, 0), (0, 2, 3, 1), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), | |
(1, 2, 3, 0), (0, 2, 1, 3), (0, 0, 0, 0), (0, 3, 1, 2), (0, 3, 2, 1), (0, 0, 0, 0), (0, 0, 0, 0), | |
(0, 0, 0, 0), (1, 3, 2, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), | |
(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (1, 2, 0, 3), (0, 0, 0, 0), (1, 3, 0, 2), (0, 0, 0, 0), | |
(0, 0, 0, 0), (0, 0, 0, 0), (2, 3, 0, 1), (2, 3, 1, 0), (1, 0, 2, 3), (1, 0, 3, 2), (0, 0, 0, 0), | |
(0, 0, 0, 0), (0, 0, 0, 0), (2, 0, 3, 1), (0, 0, 0, 0), (2, 1, 3, 0), (0, 0, 0, 0), (0, 0, 0, 0), | |
(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (2, 0, 1, 3), | |
(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (3, 0, 1, 2), (3, 0, 2, 1), (0, 0, 0, 0), (3, 1, 2, 0), | |
(2, 1, 0, 3), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (3, 1, 0, 2), (0, 0, 0, 0), (3, 2, 0, 1), | |
(3, 2, 1, 0)) | |
def lerp(t, a, b): | |
return a + t * (b - a) | |
def grad1(hash: int, x: float): | |
g = (hash & 7) + 1.0 | |
if hash & 8: | |
g = -1 | |
return g * x | |
def noise1(x: float, repeat: int, base: int) -> float: | |
fx = 0.0 | |
i = int(floor(x) % repeat) | |
ii = (i + 1) % repeat | |
i = (i & 255) + base | |
ii = (ii & 255) + base | |
x -= floor(x) | |
fx = x * x * x * (x * (x * 6 - 15) + 10) | |
return lerp(fx, grad1(PERM[i], x), grad1(PERM[ii], x - 1)) * 0.4 | |
def grad2(hash: int, x: float, y: float) -> float: | |
h = hash & 15 | |
return x * GRAD3[h][0] + y * GRAD3[h][1] | |
def noise2(x: float, y: float , repeatx: float, repeaty: float, base: int) -> float: | |
i = int(floor(fmod(x, repeatx))) | |
j = int(floor(fmod(y, repeaty))) | |
ii = int(fmod(i + 1, repeatx)) | |
jj = int(fmod(j + 1, repeaty)) | |
i = (i & 255) + base | |
j = (j & 255) + base | |
ii = (ii & 255) + base | |
jj = (jj & 255) + base | |
x -= floor(x) | |
y -= floor(y) | |
fx = x*x*x * (x * (x * 6 - 15) + 10) | |
fy = y*y*y * (y * (y * 6 - 15) + 10) | |
A = PERM[i] | |
AA = PERM[A + j] | |
AB = PERM[A + jj] | |
B = PERM[ii] | |
BA = PERM[B + j] | |
BB = PERM[B + jj] | |
return lerp(fy, lerp(fx, grad2(PERM[AA], x, y), | |
grad2(PERM[BA], x - 1, y)), | |
lerp(fx, grad2(PERM[AB], x, y - 1), | |
grad2(PERM[BB], x - 1, y - 1))) | |
def grad3(hash: int, x: float, y: float, z: float) -> float: | |
h = hash & 15 | |
return x * GRAD3[h][0] + y * GRAD3[h][1] + z * GRAD3[h][2] | |
def noise3(x: float, y: float, z: float, repeatx: int, repeaty: int, repeatz: int, base: int) -> float: | |
i = int(floor(fmod(x, repeatx))) | |
j = int(floor(fmod(y, repeaty))) | |
k = int(floor(fmod(z, repeatz))) | |
ii = int(fmod(i + 1, repeatx)) | |
jj = int(fmod(j + 1, repeaty)) | |
kk = int(fmod(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 | |
x -= floor(x) | |
y -= floor(y) | |
z -= floor(z) | |
fx = x*x*x * (x * (x * 6 - 15) + 10) | |
fy = y*y*y * (y * (y * 6 - 15) + 10) | |
fz = z*z*z * (z * (z * 6 - 15) + 10) | |
A = PERM[i] | |
AA = PERM[A + j] | |
AB = PERM[A + jj] | |
B = PERM[ii] | |
BA = PERM[B + j] | |
BB = PERM[B + jj] | |
return lerp(fz, lerp(fy, lerp(fx, grad3(PERM[AA + k], x, y, z), | |
grad3(PERM[BA + k], x - 1, y, z)), | |
lerp(fx, grad3(PERM[AB + k], x, y - 1, z), | |
grad3(PERM[BB + k], x - 1, y - 1, z))), | |
lerp(fy, lerp(fx, grad3(PERM[AA + kk], x, y, z - 1), | |
grad3(PERM[BA + kk], x - 1, y, z - 1)), | |
lerp(fx, grad3(PERM[AB + kk], x, y - 1, z - 1), | |
grad3(PERM[BB + kk], x - 1, y - 1, z - 1)))) | |
""" | |
static PyObject * | |
py_noise1(PyObject *self, PyObject *args, PyObject *kwargs) | |
{ | |
float x; | |
int octaves = 1; | |
float persistence = 0.5f; | |
float lacunarity = 2.0f; | |
int repeat = 1024; // arbitrary | |
int base = 0; | |
static char *kwlist[] = {"x", "octaves", "persistence", "lacunarity", "repeat", "base", NULL}; | |
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "f|iffii:noise1", kwlist, | |
&x, &octaves, &persistence, &lacunarity, &repeat, &base)) | |
return NULL; | |
if (octaves == 1) { | |
// Single octave, return simple noise | |
return (PyObject *) PyFloat_FromDouble((double) noise1(x, repeat, base)); | |
} else if (octaves > 1) { | |
int i; | |
float freq = 1.0f; | |
float amp = 1.0f; | |
float max = 0.0f; | |
float total = 0.0f; | |
for (i = 0; i < octaves; i++) { | |
total += noise1(x * freq, (const int)(repeat * freq), base) * amp; | |
max += amp; | |
freq *= lacunarity; | |
amp *= persistence; | |
} | |
return (PyObject *) PyFloat_FromDouble((double) (total / max)); | |
} else { | |
PyErr_SetString(PyExc_ValueError, "Expected octaves value > 0"); | |
return NULL; | |
} | |
} | |
""" | |
""" | |
static PyObject * | |
py_noise2(PyObject *self, PyObject *args, PyObject *kwargs) | |
{ | |
float x, y; | |
int octaves = 1; | |
float persistence = 0.5f; | |
float lacunarity = 2.0f; | |
float repeatx = 1024; // arbitrary | |
float repeaty = 1024; // arbitrary | |
int base = 0; | |
static char *kwlist[] = {"x", "y", "octaves", "persistence", "lacunarity", "repeatx", "repeaty", "base", NULL}; | |
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ff|iffffi:noise2", kwlist, | |
&x, &y, &octaves, &persistence, &lacunarity, &repeatx, &repeaty, &base)) | |
return NULL; | |
if (octaves == 1) { | |
// Single octave, return simple noise | |
return (PyObject *) PyFloat_FromDouble((double) noise2(x, y, repeatx, repeaty, base)); | |
} else if (octaves > 1) { | |
int i; | |
float freq = 1.0f; | |
float amp = 1.0f; | |
float max = 0.0f; | |
float total = 0.0f; | |
for (i = 0; i < octaves; i++) { | |
total += noise2(x * freq, y * freq, repeatx * freq, repeaty * freq, base) * amp; | |
max += amp; | |
freq *= lacunarity; | |
amp *= persistence; | |
} | |
return (PyObject *) PyFloat_FromDouble((double) (total / max)); | |
} else { | |
PyErr_SetString(PyExc_ValueError, "Expected octaves value > 0"); | |
return NULL; | |
} | |
} | |
""" | |
""" | |
static PyObject * | |
py_noise3(PyObject *self, PyObject *args, PyObject *kwargs) | |
{ | |
float x, y, z; | |
int octaves = 1; | |
float persistence = 0.5f; | |
float lacunarity = 2.0f; | |
int repeatx = 1024; // arbitrary | |
int repeaty = 1024; // arbitrary | |
int repeatz = 1024; // arbitrary | |
int base = 0; | |
static char *kwlist[] = {"x", "y", "z", "octaves", "persistence", "lacunarity", | |
"repeatx", "repeaty", "repeatz", "base", NULL}; | |
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "fff|iffiiii:noise3", kwlist, | |
&x, &y, &z, &octaves, &persistence, &lacunarity, &repeatx, &repeaty, &repeatz, &base)) | |
return NULL; | |
if (octaves == 1) { | |
// Single octave, return simple noise | |
return (PyObject *) PyFloat_FromDouble((double) noise3(x, y, z, | |
repeatx, repeaty, repeatz, base)); | |
} else if (octaves > 1) { | |
int i; | |
float freq = 1.0f; | |
float amp = 1.0f; | |
float max = 0.0f; | |
float total = 0.0f; | |
for (i = 0; i < octaves; i++) { | |
total += noise3(x * freq, y * freq, z * freq, | |
(const int)(repeatx*freq), (const int)(repeaty*freq), (const int)(repeatz*freq), base) * amp; | |
max += amp; | |
freq *= lacunarity; | |
amp *= persistence; | |
} | |
return (PyObject *) PyFloat_FromDouble((double) (total / max)); | |
} else { | |
PyErr_SetString(PyExc_ValueError, "Expected octaves value > 0"); | |
return NULL; | |
} | |
} | |
static PyMethodDef perlin_functions[] = { | |
{"noise1", (PyCFunction) py_noise1, METH_VARARGS | METH_KEYWORDS, | |
"noise1(x, octaves=1, persistence=0.5, lacunarity=2.0, repeat=1024, base=0.0)\n\n" | |
"1 dimensional perlin improved noise function (see noise3 for more info)"}, | |
{"noise2", (PyCFunction) py_noise2, METH_VARARGS | METH_KEYWORDS, | |
"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, " | |
"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" | |
"defaults to 1 (simple noise).\n\n" | |
"persistence -- specifies the amplitude of each successive octave relative\n" | |
"to the one below it. Defaults to 0.5 (each higher octave's amplitude\n" | |
"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.\n\n" | |
"repeatx, repeaty, repeatz -- specifies the interval along each axis when \n" | |
"the noise values repeat. This can be used as the tile size for creating \n" | |
"tileable textures\n\n" | |
"base -- specifies a fixed offset for the input coordinates. Useful for\n" | |
"generating different noise textures with the same repeat interval"}, | |
{NULL} | |
}; | |
PyDoc_STRVAR(module_doc, "Native-code tileable Perlin \"improved\" noise functions"); | |
#if PY_MAJOR_VERSION >= 3 | |
static struct PyModuleDef moduledef = { | |
PyModuleDef_HEAD_INIT, | |
"_perlin", | |
module_doc, | |
-1, /* m_size */ | |
perlin_functions, /* m_methods */ | |
NULL, /* m_reload (unused) */ | |
NULL, /* m_traverse */ | |
NULL, /* m_clear */ | |
NULL /* m_free */ | |
}; | |
PyObject * | |
PyInit__perlin(void) | |
{ | |
return PyModule_Create(&moduledef); | |
} | |
#else | |
void | |
init_perlin(void) | |
{ | |
Py_InitModule3("_perlin", perlin_functions, module_doc); | |
} | |
#endif | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment